diff --git a/Cargo.lock b/Cargo.lock index 1caf8ba9..70ae24ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2553,6 +2553,7 @@ dependencies = [ "clap", "clap-verbosity-flag", "dashmap", + "dialoguer", "dotenvy", "rari-deps", "rari-doc", diff --git a/Cargo.toml b/Cargo.toml index aae65a98..f0d86e77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -98,6 +98,7 @@ schemars = { version = "0.8", features = ["chrono"] } pretty_yaml = "0.5" yaml_parser = "0.2" const_format = "0.2" +dialoguer = "0.11" [dependencies] rari-doc.workspace = true @@ -114,6 +115,7 @@ tracing-subscriber.workspace = true anyhow.workspace = true dashmap.workspace = true schemars.workspace = true +dialoguer.workspace = true self_update = { version = "0.42", default-features = false, features = [ "rustls", @@ -126,3 +128,6 @@ tabwriter = "1" axum = "0.8" tokio = "1" dotenvy = "0.15" + +[lints.clippy] +print_stdout = "deny" diff --git a/crates/css-definition-syntax/src/tokenizer.rs b/crates/css-definition-syntax/src/tokenizer.rs index 7f385b5b..008d7ea0 100644 --- a/crates/css-definition-syntax/src/tokenizer.rs +++ b/crates/css-definition-syntax/src/tokenizer.rs @@ -73,7 +73,7 @@ impl Tokenizer { } pub fn error(&self, message: &str) { - println!("Tokenizer error: {}", message); + eprintln!("Tokenizer error: {}", message); panic!("Tokenizer error: {}", message); } } diff --git a/crates/rari-cli/main.rs b/crates/rari-cli/main.rs index 38382375..ecbfb164 100644 --- a/crates/rari-cli/main.rs +++ b/crates/rari-cli/main.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use std::collections::HashMap; use std::env; use std::fs::{self, File}; -use std::io::{self, BufWriter, Write}; +use std::io::{BufWriter, Write}; use std::path::PathBuf; use std::sync::mpsc::channel; use std::sync::Arc; @@ -12,6 +12,8 @@ use anyhow::{anyhow, Error}; use clap::{Args, Parser, Subcommand}; use clap_verbosity_flag::Verbosity; use dashmap::DashMap; +use dialoguer::theme::ColorfulTheme; +use dialoguer::Confirm; use rari_doc::build::{ build_blog_pages, build_contributor_spotlight_pages, build_curriculum_pages, build_docs, build_generic_pages, build_spas, build_top_level_meta, @@ -39,7 +41,8 @@ use rari_types::settings::Settings; use schemars::schema_for; use self_update::cargo_crate_version; use tabwriter::TabWriter; -use tracing::Level; +use tracing::level_filters::LevelFilter; +use tracing::{info, Level}; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::util::SubscriberInitExt; use tracing_subscriber::{filter, Layer}; @@ -211,7 +214,7 @@ fn main() -> Result<(), Error> { .unwrap_or(Cow::Borrowed(".env")) .as_ref(), ) { - println!("Using env_file: {}", env_file.display()) + info!("Using env_file: {}", env_file.display()) } let cli = Cli::parse(); if !cli.skip_updates { @@ -223,9 +226,18 @@ fn main() -> Result<(), Error> { rari_deps::popularities::update_popularities(rari_types::globals::data_dir())?; } - let fmt_filter = filter::Targets::new() - .with_target("rari_doc", cli.verbose.tracing_level_filter()) - .with_target("rari", cli.verbose.tracing_level_filter()); + let fmt_filter = + filter::Targets::new().with_target("rari_doc", cli.verbose.tracing_level_filter()); + + let cli_level = if cli.verbose.is_silent() { + LevelFilter::OFF + } else { + LevelFilter::INFO + }; + let cli_filter = filter::Targets::new() + .with_target("rari", cli_level) + .with_target("rari_tools", cli_level) + .with_target("rari_doc", LevelFilter::OFF); let memory_filter = filter::Targets::new() .with_target("rari_doc", Level::WARN) @@ -238,6 +250,14 @@ fn main() -> Result<(), Error> { .without_time() .with_filter(fmt_filter), ) + .with( + tracing_subscriber::fmt::layer() + .without_time() + .with_level(false) + .compact() + .with_target(false) + .with_filter(cli_filter), + ) .with(memory_layer.clone().with_filter(memory_filter)) .init(); @@ -270,13 +290,13 @@ fn main() -> Result<(), Error> { } let mut out = stats.into_iter().collect::>(); out.sort_by(|(_, a), (_, b)| b.cmp(a)); - println!("--- templ summary ---"); + info!("--- templ summary ---"); let mut tw = TabWriter::new(vec![]); for (i, (templ, count)) in out.iter().enumerate() { writeln!(&mut tw, "{:2}\t{templ}\t{count:4}", i + 1) .expect("unable to write"); } - print!("{}", String::from_utf8_lossy(&tw.into_inner().unwrap())); + info!("{}", String::from_utf8_lossy(&tw.into_inner().unwrap())); }); Some((recorder_handler, tx)) } else { @@ -294,7 +314,7 @@ fn main() -> Result<(), Error> { } let mut urls = Vec::new(); let mut docs = Vec::new(); - println!("Building everything 🛠️"); + info!("Building everything 🛠️"); if args.all || !args.no_basic || args.content || !args.files.is_empty() { let start = std::time::Instant::now(); docs = if !args.files.is_empty() { @@ -309,7 +329,7 @@ fn main() -> Result<(), Error> { } else { read_and_cache_doc_pages()? }; - println!( + info!( "Took: {: >10.3?} for reading {} docs", start.elapsed(), docs.len() @@ -320,7 +340,7 @@ fn main() -> Result<(), Error> { let spas = build_spas()?; let num = spas.len(); urls.extend(spas); - println!("Took: {: >10.3?} to build spas ({num})", start.elapsed(),); + info!("Took: {: >10.3?} to build spas ({num})", start.elapsed(),); } if args.all || !args.no_basic || args.content || !args.files.is_empty() { let start = std::time::Instant::now(); @@ -328,7 +348,7 @@ fn main() -> Result<(), Error> { build_top_level_meta(meta)?; let num = docs.len(); urls.extend(docs); - println!( + info!( "Took: {: >10.3?} to build content docs ({num})", start.elapsed() ); @@ -336,14 +356,14 @@ fn main() -> Result<(), Error> { if args.all || !args.no_basic || args.search_index { let start = std::time::Instant::now(); build_search_index(&docs)?; - println!("Took: {: >10.3?} to build search index", start.elapsed()); + info!("Took: {: >10.3?} to build search index", start.elapsed()); } if args.all || args.generics { let start = std::time::Instant::now(); let generic_pages = build_generic_pages()?; let num = generic_pages.len(); urls.extend(generic_pages); - println!( + info!( "Took: {: >10.3?} to build generic pages ({num})", start.elapsed() ); @@ -353,7 +373,7 @@ fn main() -> Result<(), Error> { let curriclum_pages = build_curriculum_pages()?; let num = curriclum_pages.len(); urls.extend(curriclum_pages); - println!( + info!( "Took: {: >10.3?} to build curriculum pages ({num})", start.elapsed() ); @@ -363,14 +383,14 @@ fn main() -> Result<(), Error> { let blog_pages = build_blog_pages()?; let num = blog_pages.len(); urls.extend(blog_pages); - println!("Took: {: >10.3?} to build blog ({num})", start.elapsed()); + info!("Took: {: >10.3?} to build blog ({num})", start.elapsed()); } if args.all || args.spotlights { let start = std::time::Instant::now(); let contributor_spotlight_pages = build_contributor_spotlight_pages()?; let num = contributor_spotlight_pages.len(); urls.extend(contributor_spotlight_pages); - println!( + info!( "Took: {: >10.3?} to build contributor spotlights ({num})", start.elapsed() ); @@ -381,7 +401,7 @@ fn main() -> Result<(), Error> { let out_path = build_out_root()?; fs::create_dir_all(out_path).unwrap(); sitemaps.write_all_sitemaps(out_path)?; - println!( + info!( "Took: {: >10.3?} to write sitemaps ({})", start.elapsed(), sitemaps.sitemap_meta.len() @@ -410,10 +430,10 @@ fn main() -> Result<(), Error> { serve::serve()? } Commands::GitHistory => { - println!("Gathering history 📜"); + info!("Gathering history 📜"); let start = std::time::Instant::now(); gather_history()?; - println!("Took: {:?}", start.elapsed()); + info!("Took: {:?}", start.elapsed()); } Commands::Content(content_subcommand) => match content_subcommand { ContentSubcommand::Move(args) => { @@ -498,19 +518,19 @@ fn update(version: Option) -> Result<(), Error> { let target_version = match (&latest, &target_release) { (None, None) => return Err(anyhow!("No latest release, specigy a version!")), (None, Some(target)) => { - println!("Updating rari to {}", target.version); + info!("Updating rari to {}", target.version); &target.version } (Some(latest), None) => { - println!("Updating rari to {} (latest)", latest.version); + info!("Updating rari to {} (latest)", latest.version); &latest.version } (Some(latest), Some(target)) if latest.version == target.version => { - println!("Updating rari to {} (latest)", latest.version); + info!("Updating rari to {} (latest)", latest.version); &latest.version } (Some(latest), Some(target)) => { - println!( + info!( "Updating rari to {} (latest {})", target.version, latest.version ); @@ -518,22 +538,21 @@ fn update(version: Option) -> Result<(), Error> { } }; - println!("rari `{target_version}` will be downloaded/extracted."); - println!( + info!("rari `{target_version}` will be downloaded/extracted."); + info!( "The current rari ({}) at version `{}` will be replaced.", update.bin_install_path().to_string_lossy(), update.current_version() ); - print!("Do you want to continue? [Y/n] "); - io::stdout().flush()?; - - let mut s = String::new(); - io::stdin().read_line(&mut s)?; - let s = s.trim().to_lowercase(); - if !s.is_empty() && s != "y" { + if !Confirm::with_theme(&ColorfulTheme::default()) + .with_prompt("Do you want to continue?") + .default(true) + .interact() + .unwrap_or_default() + { return Err(anyhow!("Update aborted")); } let status = update.update()?; - println!("\n\nrari updated to `{}`", status.version()); + info!("\n\nrari updated to `{}`", status.version()); Ok(()) } diff --git a/crates/rari-tools/Cargo.toml b/crates/rari-tools/Cargo.toml index 11162de1..3e0a5174 100644 --- a/crates/rari-tools/Cargo.toml +++ b/crates/rari-tools/Cargo.toml @@ -28,8 +28,8 @@ serde_yaml_ng.workspace = true pretty_yaml.workspace = true yaml_parser.workspace = true const_format.workspace = true +dialoguer.workspace = true -dialoguer = "0.11" csv = "1" [dev-dependencies] diff --git a/crates/rari-tools/src/add_redirect.rs b/crates/rari-tools/src/add_redirect.rs index 38161133..c50fc416 100644 --- a/crates/rari-tools/src/add_redirect.rs +++ b/crates/rari-tools/src/add_redirect.rs @@ -13,7 +13,7 @@ pub fn add_redirect(from_url: &str, to_url: &str) -> Result<(), ToolError> { let green = Style::new().green(); let bold = Style::new().bold(); - println!( + tracing::info!( "{} {} {} {}", green.apply_to("Saved"), bold.apply_to(from_url), diff --git a/crates/rari-tools/src/move.rs b/crates/rari-tools/src/move.rs index 04f2a7fb..a85e9126 100644 --- a/crates/rari-tools/src/move.rs +++ b/crates/rari-tools/src/move.rs @@ -37,10 +37,10 @@ pub fn r#move( let bold = Style::new().bold(); let changes = do_move(old_slug, new_slug, locale, true)?; if changes.is_empty() { - println!("{}", style("No changes would be made").green()); + tracing::info!("{}", style("No changes would be made").green()); return Ok(()); } else { - println!( + tracing::info!( "{} {} {} {} {} {}", green.apply_to("This will move"), bold.apply_to(changes.len()), @@ -50,7 +50,7 @@ pub fn r#move( green.apply_to(new_slug) ); for (old_slug, new_slug) in changes { - println!( + tracing::info!( "{} -> {}", red.apply_to(&old_slug), green.apply_to(&new_slug) @@ -66,7 +66,7 @@ pub fn r#move( .unwrap_or_default() { let moved = do_move(old_slug, new_slug, locale, false)?; - println!( + tracing::info!( "{} {} {}", green.apply_to("Moved"), bold.apply_to(moved.len()), @@ -130,7 +130,7 @@ fn do_move( doc.meta.slug = new_slug.to_string(); Some(doc.to_owned()) } else { - println!("This does not look like a document"); + tracing::info!("This does not look like a document"); None } }); diff --git a/crates/rari-tools/src/remove.rs b/crates/rari-tools/src/remove.rs index ccc021c0..e5d01e1a 100644 --- a/crates/rari-tools/src/remove.rs +++ b/crates/rari-tools/src/remove.rs @@ -38,20 +38,20 @@ pub fn remove( let bold = Style::new().bold(); let changes = do_remove(slug, locale, recursive, redirect, true)?; if changes.is_empty() { - println!("{}", green.apply_to("No changes would be made")); + tracing::info!("{}", green.apply_to("No changes would be made")); return Ok(()); } else { - println!( + tracing::info!( "{} {} {}", green.apply_to("This will delete"), bold.apply_to(changes.len()), green.apply_to("documents:"), ); for slug in changes { - println!("{}", red.apply_to(&slug)); + tracing::info!("{}", red.apply_to(&slug)); } if let Some(redirect) = redirect { - println!( + tracing::info!( "{} {} to: {}", green.apply_to("Redirecting"), green.apply_to(if recursive { @@ -62,7 +62,7 @@ pub fn remove( green.apply_to(&redirect), ); } else { - println!("{}", yellow.apply_to("Deleting without a redirect. Consider using the --redirect option with a related page instead.")); + tracing::info!("{}", yellow.apply_to("Deleting without a redirect. Consider using the --redirect option with a related page instead.")); } } @@ -78,19 +78,19 @@ pub fn remove( .iter() .map(|slug| build_url(slug, locale, PageCategory::Doc)) .collect::, DocError>>()?; - println!( + tracing::info!( "{} {} {}", green.apply_to("Deleted"), bold.apply_to(removed.len()), green.apply_to("documents:"), ); for url in &removed_urls { - println!("{}", red.apply_to(&url)); + tracing::info!("{}", red.apply_to(&url)); } // Find references to deleted documents and // list them for manual review - println!("Checking references to deleted documents..."); + tracing::info!("Checking references to deleted documents..."); let mut docs_path = PathBuf::from(root_for_locale(locale)?); docs_path.push(locale.as_folder_str()); @@ -109,18 +109,18 @@ pub fn remove( .collect(); if referencing_docs.is_empty() { - println!( + tracing::info!( "{}", green.apply_to("No file is referring to the deleted document."), ); } else { - println!( + tracing::info!( "{} {}", yellow.apply_to(referencing_docs.len()), yellow.apply_to("files are referring to the deleted documents. Please update the following files to remove the links:"), ); for url in &referencing_docs { - println!("{}", yellow.apply_to(url)); + tracing::info!("{}", yellow.apply_to(url)); } } } diff --git a/crates/rari-tools/src/sidebars.rs b/crates/rari-tools/src/sidebars.rs index bc3d55fe..ad446f18 100644 --- a/crates/rari-tools/src/sidebars.rs +++ b/crates/rari-tools/src/sidebars.rs @@ -467,7 +467,7 @@ mod test { path.push("sidebars"); path.push("sidebar_0.yaml"); let content = fs::read_to_string(&path).unwrap(); - // println!("{}", content); + // tracing::info!("{}", content); let sb = serde_yaml_ng::from_str::(&content).unwrap(); // replacement of link of the first child in the third item of the sidebar diff --git a/crates/rari-tools/src/sync_translated_content.rs b/crates/rari-tools/src/sync_translated_content.rs index 93a57155..545dc884 100644 --- a/crates/rari-tools/src/sync_translated_content.rs +++ b/crates/rari-tools/src/sync_translated_content.rs @@ -28,7 +28,7 @@ pub fn sync_translated_content( let bold = Style::new().bold(); if verbose { - println!( + tracing::info!( "{}", green.apply_to(format!( "Syncing translated content for locales: {:?}.\nFixing cross-locale redirects.", @@ -39,7 +39,7 @@ pub fn sync_translated_content( fix_redirects()?; if verbose { - println!("{}", green.apply_to("Reading all documents.")); + tracing::info!("{}", green.apply_to("Reading all documents.")); } let docs = read_all_doc_pages()?; @@ -67,7 +67,7 @@ pub fn sync_translated_content( (x, y + 1) } }); - println!( + tracing::info!( "{}", dim.apply_to(format!( "read {} docs: {} en-Us, {} translated.", @@ -98,7 +98,7 @@ pub fn sync_translated_content( ); } } else { - println!("Page is not a doc: {:?}", page); + tracing::info!("Page is not a doc: {:?}", page); } results }); @@ -122,34 +122,34 @@ pub fn sync_translated_content( if verbose { for (locale, result) in &res { - println!( + tracing::info!( "{}", green.apply_to(bold.apply_to(format!("Results for locale {}", locale))) ); - println!( + tracing::info!( " {}", green.apply_to(format!("Total of {} documents.", result.total_docs)) ); - println!( + tracing::info!( " {}", green.apply_to(format!("Moved {} documents.", result.moved_docs)) ); - println!( + tracing::info!( " {}", green.apply_to(format!("Renamed {} documents.", result.renamed_docs)) ); - println!( + tracing::info!( " {}", green.apply_to(format!( "Conflicting {} documents.", result.conflicting_docs )) ); - println!( + tracing::info!( " {}", green.apply_to(format!("Orphaned {} documents", result.orphaned_docs)) ); - println!( + tracing::info!( " {}", green.apply_to(format!( "Fixed {} redirected documents.", @@ -240,7 +240,7 @@ fn sync_translated_document( if status.orphaned { if verbose { - println!( + tracing::info!( "{}", yellow.apply_to(format!("orphaned: {}", doc.path().to_string_lossy())) ); @@ -258,7 +258,7 @@ fn sync_translated_document( } } else if status.moved && md_exists(&resolved_slug, doc.locale())? { if verbose { - println!( + tracing::info!( "{}", dim.apply_to(format!( "unrooting {} (conflicting translation)", diff --git a/crates/rari-tools/src/tests/fixtures/docs.rs b/crates/rari-tools/src/tests/fixtures/docs.rs index 900e0d7c..8556f4f9 100644 --- a/crates/rari-tools/src/tests/fixtures/docs.rs +++ b/crates/rari-tools/src/tests/fixtures/docs.rs @@ -92,7 +92,7 @@ impl DocFixtures { // overwrite file if it exists if fs::exists(&path).unwrap() { if path.is_dir() { - println!( + tracing::info!( "File path is a directory - replacing with file: {}", path.to_string_lossy() ); @@ -115,7 +115,7 @@ impl DocFixtures { impl Drop for DocFixtures { fn drop(&mut self) { if self.do_not_remove { - println!("Leaving doc fixtures in place for debugging"); + tracing::info!("Leaving doc fixtures in place for debugging"); return; } // Perform cleanup actions, recursively remove all files diff --git a/crates/rari-tools/src/tests/fixtures/redirects.rs b/crates/rari-tools/src/tests/fixtures/redirects.rs index 006a30dc..cafc3c28 100644 --- a/crates/rari-tools/src/tests/fixtures/redirects.rs +++ b/crates/rari-tools/src/tests/fixtures/redirects.rs @@ -53,7 +53,7 @@ impl RedirectFixtures { impl Drop for RedirectFixtures { fn drop(&mut self) { if self.do_not_remove { - println!( + tracing::info!( "Leaving redirects fixture {} in place for debugging", self.path.display() ); diff --git a/crates/rari-tools/src/tests/fixtures/sidebars.rs b/crates/rari-tools/src/tests/fixtures/sidebars.rs index 82ef5081..7d6fe86e 100644 --- a/crates/rari-tools/src/tests/fixtures/sidebars.rs +++ b/crates/rari-tools/src/tests/fixtures/sidebars.rs @@ -41,7 +41,7 @@ impl SidebarFixtures { impl Drop for SidebarFixtures { fn drop(&mut self) { if self.do_not_remove { - println!("Leaving doc fixtures in place for debugging"); + tracing::info!("Leaving doc fixtures in place for debugging"); return; } // Perform cleanup actions, recursively remove all files diff --git a/crates/rari-tools/src/tests/fixtures/wikihistory.rs b/crates/rari-tools/src/tests/fixtures/wikihistory.rs index 3d2e4f43..87a605a7 100644 --- a/crates/rari-tools/src/tests/fixtures/wikihistory.rs +++ b/crates/rari-tools/src/tests/fixtures/wikihistory.rs @@ -59,7 +59,7 @@ impl WikihistoryFixtures { impl Drop for WikihistoryFixtures { fn drop(&mut self) { if self.do_not_remove { - println!( + tracing::info!( "Leaving wikihistory fixture {} in place for debugging", self.path.display() );