Loading acorn-cli/src/cli/mod.rs +1 −1 Original line number Diff line number Diff line Loading @@ -3,13 +3,13 @@ use acorn::prelude::PathBuf; use acorn::util::constants::{ENV_CACHE_TTL, ENV_DATABASE_BACKEND, ENV_DATABASE_PATH, ENV_NO_LOCAL_DATABASE}; use acorn::util::{regex_inverse, regex_join}; use bon::Builder; use fancy_regex::Regex; use clap::builder::{ styling::{Ansi256Color, AnsiColor}, Styles, }; use clap::{ArgAction, Parser, Subcommand, ValueHint}; use clap_verbosity_flag::Verbosity; use fancy_regex::Regex; use tracing::error; pub mod arguments; Loading acorn-cli/src/cli/tests/mod.rs +5 −20 Original line number Diff line number Diff line use crate::cli::{filter_by_pattern, resolve_paths, Arguments, CommandOptions}; use acorn::prelude::PathBuf; use acorn::prelude::{Path, PathBuf}; use clap::{CommandFactory, Parser}; use futures::executor::block_on; fn has_suffix(path: &PathBuf, suffix: &str) -> bool { fn has_suffix(path: &Path, suffix: &str) -> bool { path.to_string_lossy().replace('\\', "/").ends_with(suffix) } fn fixture_content_root() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../tests/fixtures/filter") } Loading Loading @@ -39,13 +38,7 @@ fn test_filter_paths_by_pattern_keeps_only_matching_relative_paths() { ]; let pattern = "^(?!.*(?:(?:acorn)|(?:sansr))).*$".to_string(); let filtered = filter_by_pattern(paths, pattern, root.clone()); assert_eq!( filtered, vec![ root.join("acorn/index.json"), root.join("sansr/index.yaml"), ] ); assert_eq!(filtered, vec![root.join("acorn/index.json"), root.join("sansr/index.yaml"),]); } #[test] fn test_filter_paths_by_pattern_applies_ignore_pattern_to_relative_paths() { Loading @@ -57,13 +50,7 @@ fn test_filter_paths_by_pattern_applies_ignore_pattern_to_relative_paths() { ]; let pattern = "(?:acorn)".to_string(); let filtered = filter_by_pattern(paths, pattern, root.clone()); assert_eq!( filtered, vec![ root.join("sansr/index.yaml"), root.join("other/index.json"), ] ); assert_eq!(filtered, vec![root.join("sansr/index.yaml"), root.join("other/index.json"),]); } #[test] fn test_filter_paths_by_pattern_returns_empty_for_invalid_regex() { Loading @@ -75,9 +62,7 @@ fn test_filter_paths_by_pattern_returns_empty_for_invalid_regex() { #[test] fn test_resolve_paths_applies_filter_to_relative_local_paths() { let root = fixture_content_root(); let options = CommandOptions::init() .maybe_filter(Some("(?:acorn)|(?:sansr)".to_string())) .build(); let options = CommandOptions::init().maybe_filter(Some("(?:acorn)|(?:sansr)".to_string())).build(); let resolved = block_on(resolve_paths(&Some(root), &options)); assert_eq!(resolved.len(), 2); assert!(resolved.iter().any(|path| has_suffix(path, "acorn/index.json"))); Loading acorn-cli/src/commands/check/mod.rs +4 −12 Original line number Diff line number Diff line Loading @@ -113,7 +113,7 @@ fn handle(issues: &[Check], paths: &[PathBuf], options: &CheckOptions) -> Result let CheckOptions { quiet, no_fail, terse, .. } = options.clone(); let print_summary = || { let file_count = paths.len(); let headers = vec!["Item", "Count"]; let headers = vec!["", "Count"]; let rows = (file_count > 1) .then(|| vec!["Files checked".to_string(), file_count.to_string()]) .into_iter() Loading @@ -123,7 +123,7 @@ fn handle(issues: &[Check], paths: &[PathBuf], options: &CheckOptions) -> Result }; if !quiet { render(issues, terse); if !(no_fail || terse) && has_failures(issues) { if !(no_fail || terse || issues.is_empty()) { print_summary(); } } Loading @@ -145,18 +145,10 @@ fn handle(issues: &[Check], paths: &[PathBuf], options: &CheckOptions) -> Result } } fn failure_count(issues: &[Check]) -> usize { issues .iter() .filter(|issue| issue.is_failure()) .map(Check::issue_count) .sum::<usize>() issues.iter().filter(|issue| issue.is_failure()).map(Check::issue_count).sum::<usize>() } fn filter_by_visibility(issues: &[Check], all: bool) -> Vec<Check> { issues .iter() .filter(|issue| all || issue.is_failure()) .cloned() .collect::<Vec<Check>>() issues.iter().filter(|issue| all || issue.is_failure()).cloned().collect::<Vec<Check>>() } fn has_failures(issues: &[Check]) -> bool { issues.iter().any(Check::is_failure) Loading acorn-cli/src/main.rs +2 −2 Original line number Diff line number Diff line Loading @@ -35,8 +35,8 @@ use futures::future::ready; use futures::TryFutureExt; use owo_colors::OwoColorize; use std::process::exit; use tracing::info; use tracing::instrument; use tracing::{debug, info}; use tracing_indicatif::IndicatifLayer; use tracing_log::AsTrace; use tracing_subscriber::fmt::{self}; Loading Loading @@ -142,8 +142,8 @@ async fn main() -> Void { env::set_var(ENV_CACHE_TTL, ttl.to_string()); } if threads > 0 { info!("{} {} threads", Label::using(), threads.cyan().bold()); rayon::ThreadPoolBuilder::new().num_threads(threads).build_global().unwrap(); debug!("{} {} threads", Label::using(), threads.cyan().bold()); } let database_initialization = if no_local_database { Ok(()) Loading acorn-lib/src/analyzer/mod.rs +25 −6 Original line number Diff line number Diff line Loading @@ -16,7 +16,7 @@ use crate::schema::standard::cff::{Cff, Identifier, IdentifierType, Reference}; use crate::schema::standard::text::Text; use crate::schema::{Organization, ProgrammingLanguage, Website}; use crate::util::constants::{APPLICATION, CUSTOM_VALE_PACKAGE_NAME, DEFAULT_VALE_PACKAGE_URL, DEFAULT_VALE_ROOT, VALE_RELEASES_URL, VALE_VERSION}; use crate::util::{Constant, Label, SemanticVersion, StringConversion}; use crate::util::{is_uri_or_path, Constant, Label, SemanticVersion, StringConversion}; use crate::{check, check_err, check_ok}; use crate::{cmd, skip}; use crate::{Location, Repository}; Loading Loading @@ -93,6 +93,8 @@ pub trait StaticAnalyzerConfig { async fn save(self) -> Self; /// Set parent path of configuration fn with_path(self, path: PathBuf) -> Self; /// Resolve package source: convert bare package name to download URL or pass through existing URL/path fn resolve_package(value: impl AsRef<str>) -> String; } #[async_trait] impl Analysis for Cff { Loading Loading @@ -312,10 +314,11 @@ impl Analysis for ResearchActivity { }); join_all(futures).await.into_iter().flatten().collect() } fn output_path(_path: &Path, data: &Self) -> PathBuf { fn output_path(path: &Path, data: &Self) -> PathBuf { let filename = path.file_name().and_then(|n| n.to_str()).unwrap_or("index.json"); standard_project_folder("check", None) .join(data.meta.identifier.to_lowercase()) .join("index.json") .join(filename) } } #[async_trait] Loading Loading @@ -712,6 +715,7 @@ impl StaticAnalyzerConfig for ValeConfig { disabled, .. } = self; let package_names = packages.clone(); let mut conf = Ini::new(); let package_repository = Repository::GitLab { id: None, Loading @@ -724,18 +728,33 @@ impl StaticAnalyzerConfig for ValeConfig { } | None => DEFAULT_VALE_PACKAGE_URL.to_string(), }; let package_sources = package_names .iter() .map(Self::resolve_package) .chain(core::iter::once(package_url)) .collect::<Vec<String>>(); // CAUTION: Order of attributes in INI file matter. "StylesPath" must come before "Vocab" conf.with_section::<String>(None) .set("StylesPath", "styles") .set("Vocab", vocabularies.join(", ")) .set("Packages", format!("{}, {}", packages.join(", "), package_url)); conf.with_section(Some("*")) .set("BasedOnStyles", format!("Vale, {}, {}", CUSTOM_VALE_PACKAGE_NAME, packages.join(", "))); .set("Packages", package_sources.join(", ")); conf.with_section(Some("*")).set( "BasedOnStyles", format!("Vale, {}, {}", CUSTOM_VALE_PACKAGE_NAME, package_names.join(", ")), ); disabled.iter().for_each(|rule| { conf.with_section(Some("*")).set(rule, "NO"); }); conf } fn resolve_package(value: impl AsRef<str>) -> String { let value = value.as_ref().trim(); if is_uri_or_path(value) { value.to_string() } else { format!("https://github.com/errata-ai/{value}/releases/latest/download/{value}.zip") } } async fn save(self) -> ValeConfig { let path = self.clone().path; let parent = path.parent().unwrap().to_path_buf(); Loading Loading
acorn-cli/src/cli/mod.rs +1 −1 Original line number Diff line number Diff line Loading @@ -3,13 +3,13 @@ use acorn::prelude::PathBuf; use acorn::util::constants::{ENV_CACHE_TTL, ENV_DATABASE_BACKEND, ENV_DATABASE_PATH, ENV_NO_LOCAL_DATABASE}; use acorn::util::{regex_inverse, regex_join}; use bon::Builder; use fancy_regex::Regex; use clap::builder::{ styling::{Ansi256Color, AnsiColor}, Styles, }; use clap::{ArgAction, Parser, Subcommand, ValueHint}; use clap_verbosity_flag::Verbosity; use fancy_regex::Regex; use tracing::error; pub mod arguments; Loading
acorn-cli/src/cli/tests/mod.rs +5 −20 Original line number Diff line number Diff line use crate::cli::{filter_by_pattern, resolve_paths, Arguments, CommandOptions}; use acorn::prelude::PathBuf; use acorn::prelude::{Path, PathBuf}; use clap::{CommandFactory, Parser}; use futures::executor::block_on; fn has_suffix(path: &PathBuf, suffix: &str) -> bool { fn has_suffix(path: &Path, suffix: &str) -> bool { path.to_string_lossy().replace('\\', "/").ends_with(suffix) } fn fixture_content_root() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../tests/fixtures/filter") } Loading Loading @@ -39,13 +38,7 @@ fn test_filter_paths_by_pattern_keeps_only_matching_relative_paths() { ]; let pattern = "^(?!.*(?:(?:acorn)|(?:sansr))).*$".to_string(); let filtered = filter_by_pattern(paths, pattern, root.clone()); assert_eq!( filtered, vec![ root.join("acorn/index.json"), root.join("sansr/index.yaml"), ] ); assert_eq!(filtered, vec![root.join("acorn/index.json"), root.join("sansr/index.yaml"),]); } #[test] fn test_filter_paths_by_pattern_applies_ignore_pattern_to_relative_paths() { Loading @@ -57,13 +50,7 @@ fn test_filter_paths_by_pattern_applies_ignore_pattern_to_relative_paths() { ]; let pattern = "(?:acorn)".to_string(); let filtered = filter_by_pattern(paths, pattern, root.clone()); assert_eq!( filtered, vec![ root.join("sansr/index.yaml"), root.join("other/index.json"), ] ); assert_eq!(filtered, vec![root.join("sansr/index.yaml"), root.join("other/index.json"),]); } #[test] fn test_filter_paths_by_pattern_returns_empty_for_invalid_regex() { Loading @@ -75,9 +62,7 @@ fn test_filter_paths_by_pattern_returns_empty_for_invalid_regex() { #[test] fn test_resolve_paths_applies_filter_to_relative_local_paths() { let root = fixture_content_root(); let options = CommandOptions::init() .maybe_filter(Some("(?:acorn)|(?:sansr)".to_string())) .build(); let options = CommandOptions::init().maybe_filter(Some("(?:acorn)|(?:sansr)".to_string())).build(); let resolved = block_on(resolve_paths(&Some(root), &options)); assert_eq!(resolved.len(), 2); assert!(resolved.iter().any(|path| has_suffix(path, "acorn/index.json"))); Loading
acorn-cli/src/commands/check/mod.rs +4 −12 Original line number Diff line number Diff line Loading @@ -113,7 +113,7 @@ fn handle(issues: &[Check], paths: &[PathBuf], options: &CheckOptions) -> Result let CheckOptions { quiet, no_fail, terse, .. } = options.clone(); let print_summary = || { let file_count = paths.len(); let headers = vec!["Item", "Count"]; let headers = vec!["", "Count"]; let rows = (file_count > 1) .then(|| vec!["Files checked".to_string(), file_count.to_string()]) .into_iter() Loading @@ -123,7 +123,7 @@ fn handle(issues: &[Check], paths: &[PathBuf], options: &CheckOptions) -> Result }; if !quiet { render(issues, terse); if !(no_fail || terse) && has_failures(issues) { if !(no_fail || terse || issues.is_empty()) { print_summary(); } } Loading @@ -145,18 +145,10 @@ fn handle(issues: &[Check], paths: &[PathBuf], options: &CheckOptions) -> Result } } fn failure_count(issues: &[Check]) -> usize { issues .iter() .filter(|issue| issue.is_failure()) .map(Check::issue_count) .sum::<usize>() issues.iter().filter(|issue| issue.is_failure()).map(Check::issue_count).sum::<usize>() } fn filter_by_visibility(issues: &[Check], all: bool) -> Vec<Check> { issues .iter() .filter(|issue| all || issue.is_failure()) .cloned() .collect::<Vec<Check>>() issues.iter().filter(|issue| all || issue.is_failure()).cloned().collect::<Vec<Check>>() } fn has_failures(issues: &[Check]) -> bool { issues.iter().any(Check::is_failure) Loading
acorn-cli/src/main.rs +2 −2 Original line number Diff line number Diff line Loading @@ -35,8 +35,8 @@ use futures::future::ready; use futures::TryFutureExt; use owo_colors::OwoColorize; use std::process::exit; use tracing::info; use tracing::instrument; use tracing::{debug, info}; use tracing_indicatif::IndicatifLayer; use tracing_log::AsTrace; use tracing_subscriber::fmt::{self}; Loading Loading @@ -142,8 +142,8 @@ async fn main() -> Void { env::set_var(ENV_CACHE_TTL, ttl.to_string()); } if threads > 0 { info!("{} {} threads", Label::using(), threads.cyan().bold()); rayon::ThreadPoolBuilder::new().num_threads(threads).build_global().unwrap(); debug!("{} {} threads", Label::using(), threads.cyan().bold()); } let database_initialization = if no_local_database { Ok(()) Loading
acorn-lib/src/analyzer/mod.rs +25 −6 Original line number Diff line number Diff line Loading @@ -16,7 +16,7 @@ use crate::schema::standard::cff::{Cff, Identifier, IdentifierType, Reference}; use crate::schema::standard::text::Text; use crate::schema::{Organization, ProgrammingLanguage, Website}; use crate::util::constants::{APPLICATION, CUSTOM_VALE_PACKAGE_NAME, DEFAULT_VALE_PACKAGE_URL, DEFAULT_VALE_ROOT, VALE_RELEASES_URL, VALE_VERSION}; use crate::util::{Constant, Label, SemanticVersion, StringConversion}; use crate::util::{is_uri_or_path, Constant, Label, SemanticVersion, StringConversion}; use crate::{check, check_err, check_ok}; use crate::{cmd, skip}; use crate::{Location, Repository}; Loading Loading @@ -93,6 +93,8 @@ pub trait StaticAnalyzerConfig { async fn save(self) -> Self; /// Set parent path of configuration fn with_path(self, path: PathBuf) -> Self; /// Resolve package source: convert bare package name to download URL or pass through existing URL/path fn resolve_package(value: impl AsRef<str>) -> String; } #[async_trait] impl Analysis for Cff { Loading Loading @@ -312,10 +314,11 @@ impl Analysis for ResearchActivity { }); join_all(futures).await.into_iter().flatten().collect() } fn output_path(_path: &Path, data: &Self) -> PathBuf { fn output_path(path: &Path, data: &Self) -> PathBuf { let filename = path.file_name().and_then(|n| n.to_str()).unwrap_or("index.json"); standard_project_folder("check", None) .join(data.meta.identifier.to_lowercase()) .join("index.json") .join(filename) } } #[async_trait] Loading Loading @@ -712,6 +715,7 @@ impl StaticAnalyzerConfig for ValeConfig { disabled, .. } = self; let package_names = packages.clone(); let mut conf = Ini::new(); let package_repository = Repository::GitLab { id: None, Loading @@ -724,18 +728,33 @@ impl StaticAnalyzerConfig for ValeConfig { } | None => DEFAULT_VALE_PACKAGE_URL.to_string(), }; let package_sources = package_names .iter() .map(Self::resolve_package) .chain(core::iter::once(package_url)) .collect::<Vec<String>>(); // CAUTION: Order of attributes in INI file matter. "StylesPath" must come before "Vocab" conf.with_section::<String>(None) .set("StylesPath", "styles") .set("Vocab", vocabularies.join(", ")) .set("Packages", format!("{}, {}", packages.join(", "), package_url)); conf.with_section(Some("*")) .set("BasedOnStyles", format!("Vale, {}, {}", CUSTOM_VALE_PACKAGE_NAME, packages.join(", "))); .set("Packages", package_sources.join(", ")); conf.with_section(Some("*")).set( "BasedOnStyles", format!("Vale, {}, {}", CUSTOM_VALE_PACKAGE_NAME, package_names.join(", ")), ); disabled.iter().for_each(|rule| { conf.with_section(Some("*")).set(rule, "NO"); }); conf } fn resolve_package(value: impl AsRef<str>) -> String { let value = value.as_ref().trim(); if is_uri_or_path(value) { value.to_string() } else { format!("https://github.com/errata-ai/{value}/releases/latest/download/{value}.zip") } } async fn save(self) -> ValeConfig { let path = self.clone().path; let parent = path.parent().unwrap().to_path_buf(); Loading