use std::path::PathBuf;
pub use crate::tool_schema::ToolSchemaFormat;
use clap::{Args, Parser, Subcommand, ValueEnum};
use serde::{Deserialize, Serialize};
pub use tokmd_types::{
AnalysisFormat, ChildIncludeMode, ChildrenMode, ConfigMode, ExportFormat, RedactMode,
TableFormat,
};
#[derive(Parser, Debug)]
#[command(name = "tokmd", version, long_about = None)]
pub struct Cli {
#[command(flatten)]
pub global: GlobalArgs,
#[command(flatten)]
pub lang: CliLangArgs,
#[command(subcommand)]
pub command: Option<Commands>,
#[arg(long, visible_alias = "view", global = true)]
pub profile: Option<String>,
}
#[derive(Args, Debug, Clone, Default)]
pub struct GlobalArgs {
#[arg(
long = "exclude",
visible_alias = "ignore",
value_name = "PATTERN",
global = true
)]
pub excluded: Vec<String>,
#[arg(long, value_enum, value_name = "MODE", default_value_t = ConfigMode::Auto)]
pub config: ConfigMode,
#[arg(long)]
pub hidden: bool,
#[arg(long)]
pub no_ignore: bool,
#[arg(long)]
pub no_ignore_parent: bool,
#[arg(long)]
pub no_ignore_dot: bool,
#[arg(long, visible_alias = "no-ignore-git")]
pub no_ignore_vcs: bool,
#[arg(long)]
pub treat_doc_strings_as_comments: bool,
#[arg(short = 'v', long = "verbose", action = clap::ArgAction::Count)]
pub verbose: u8,
#[arg(long, global = true)]
pub no_progress: bool,
}
#[derive(Subcommand, Debug, Clone)]
pub enum Commands {
Lang(CliLangArgs),
Module(CliModuleArgs),
Export(CliExportArgs),
Analyze(CliAnalyzeArgs),
Badge(BadgeArgs),
Init(InitArgs),
Completions(CompletionsArgs),
Run(RunArgs),
Diff(DiffArgs),
Context(CliContextArgs),
CheckIgnore(CliCheckIgnoreArgs),
Tools(ToolsArgs),
Gate(CliGateArgs),
Cockpit(CockpitArgs),
Baseline(BaselineArgs),
Handoff(HandoffArgs),
Sensor(SensorArgs),
}
#[derive(Args, Debug, Clone)]
pub struct RunArgs {
#[arg(value_name = "PATH", default_value = ".")]
pub paths: Vec<PathBuf>,
#[arg(long)]
pub output_dir: Option<PathBuf>,
#[arg(long)]
pub name: Option<String>,
#[arg(long, value_enum)]
pub analysis: Option<AnalysisPreset>,
#[arg(long, value_enum)]
pub redact: Option<RedactMode>,
}
#[derive(Args, Debug, Clone)]
pub struct DiffArgs {
#[arg(long)]
pub from: Option<String>,
#[arg(long)]
pub to: Option<String>,
#[arg(value_name = "REF", num_args = 2)]
pub refs: Vec<String>,
#[arg(long, value_enum, default_value_t = DiffFormat::Md)]
pub format: DiffFormat,
#[arg(long)]
pub compact: bool,
#[arg(long, value_enum, default_value_t = ColorMode::Auto)]
pub color: ColorMode,
}
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub enum DiffFormat {
#[default]
Md,
Json,
}
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub enum ColorMode {
#[default]
Auto,
Always,
Never,
}
#[derive(Args, Debug, Clone)]
pub struct CompletionsArgs {
#[arg(value_enum)]
pub shell: Shell,
}
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum Shell {
Bash,
Elvish,
Fish,
Powershell,
Zsh,
}
#[derive(Args, Debug, Clone, Default)]
pub struct CliLangArgs {
#[arg(value_name = "PATH")]
pub paths: Option<Vec<PathBuf>>,
#[arg(long, value_enum)]
pub format: Option<TableFormat>,
#[arg(long)]
pub top: Option<usize>,
#[arg(long)]
pub files: bool,
#[arg(long, value_enum)]
pub children: Option<ChildrenMode>,
}
#[derive(Args, Debug, Clone)]
pub struct CliModuleArgs {
#[arg(value_name = "PATH")]
pub paths: Option<Vec<PathBuf>>,
#[arg(long, value_enum)]
pub format: Option<TableFormat>,
#[arg(long)]
pub top: Option<usize>,
#[arg(long, value_delimiter = ',')]
pub module_roots: Option<Vec<String>>,
#[arg(long)]
pub module_depth: Option<usize>,
#[arg(long, value_enum)]
pub children: Option<ChildIncludeMode>,
}
#[derive(Args, Debug, Clone)]
pub struct CliExportArgs {
#[arg(value_name = "PATH")]
pub paths: Option<Vec<PathBuf>>,
#[arg(long, value_enum)]
pub format: Option<ExportFormat>,
#[arg(long, value_name = "PATH", visible_alias = "out")]
pub output: Option<PathBuf>,
#[arg(long, value_delimiter = ',')]
pub module_roots: Option<Vec<String>>,
#[arg(long)]
pub module_depth: Option<usize>,
#[arg(long, value_enum)]
pub children: Option<ChildIncludeMode>,
#[arg(long)]
pub min_code: Option<usize>,
#[arg(long)]
pub max_rows: Option<usize>,
#[arg(long, action = clap::ArgAction::Set)]
pub meta: Option<bool>,
#[arg(long, value_enum)]
pub redact: Option<RedactMode>,
#[arg(long, value_name = "PATH")]
pub strip_prefix: Option<PathBuf>,
}
#[derive(Args, Debug, Clone)]
pub struct CliAnalyzeArgs {
#[arg(value_name = "INPUT", default_value = ".")]
pub inputs: Vec<PathBuf>,
#[arg(long, value_enum)]
pub preset: Option<AnalysisPreset>,
#[arg(long, value_enum)]
pub format: Option<AnalysisFormat>,
#[arg(long)]
pub window: Option<usize>,
#[arg(long, action = clap::ArgAction::SetTrue, conflicts_with = "no_git")]
pub git: bool,
#[arg(long = "no-git", action = clap::ArgAction::SetTrue, conflicts_with = "git")]
pub no_git: bool,
#[arg(long)]
pub output_dir: Option<PathBuf>,
#[arg(long)]
pub max_files: Option<usize>,
#[arg(long)]
pub max_bytes: Option<u64>,
#[arg(long)]
pub max_file_bytes: Option<u64>,
#[arg(long)]
pub max_commits: Option<usize>,
#[arg(long)]
pub max_commit_files: Option<usize>,
#[arg(long, value_enum)]
pub granularity: Option<ImportGranularity>,
#[arg(long)]
pub effort_model: Option<EffortModelKind>,
#[arg(long)]
pub effort_layer: Option<EffortLayer>,
#[arg(long = "effort-base-ref")]
pub effort_base_ref: Option<String>,
#[arg(long = "effort-head-ref")]
pub effort_head_ref: Option<String>,
#[arg(long)]
pub monte_carlo: bool,
#[arg(long = "mc-iterations")]
pub mc_iterations: Option<usize>,
#[arg(long = "mc-seed")]
pub mc_seed: Option<u64>,
#[arg(long)]
pub detail_functions: bool,
#[arg(long)]
pub near_dup: bool,
#[arg(long, default_value = "0.80")]
pub near_dup_threshold: f64,
#[arg(long, default_value = "2000")]
pub near_dup_max_files: usize,
#[arg(long, value_enum)]
pub near_dup_scope: Option<NearDupScope>,
#[arg(long, default_value = "10000")]
pub near_dup_max_pairs: usize,
#[arg(long, value_name = "GLOB")]
pub near_dup_exclude: Vec<String>,
#[arg(long, value_name = "KEY")]
pub explain: Option<String>,
}
#[derive(Args, Debug, Clone)]
pub struct BadgeArgs {
#[arg(value_name = "INPUT", default_value = ".")]
pub inputs: Vec<PathBuf>,
#[arg(long, value_enum)]
pub metric: BadgeMetric,
#[arg(long, value_enum)]
pub preset: Option<AnalysisPreset>,
#[arg(long, action = clap::ArgAction::SetTrue, conflicts_with = "no_git")]
pub git: bool,
#[arg(long = "no-git", action = clap::ArgAction::SetTrue, conflicts_with = "git")]
pub no_git: bool,
#[arg(long)]
pub max_commits: Option<usize>,
#[arg(long)]
pub max_commit_files: Option<usize>,
#[arg(long, visible_alias = "out")]
pub output: Option<PathBuf>,
}
#[derive(Args, Debug, Clone)]
pub struct InitArgs {
#[arg(long, value_name = "DIR", default_value = ".")]
pub dir: PathBuf,
#[arg(long)]
pub force: bool,
#[arg(long)]
pub print: bool,
#[arg(long, value_enum, default_value_t = InitProfile::Default)]
pub template: InitProfile,
#[arg(long)]
pub non_interactive: bool,
}
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum AnalysisPreset {
Receipt,
Estimate,
Health,
Risk,
Supply,
Architecture,
Topics,
Security,
Identity,
Git,
Deep,
Fun,
}
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum ImportGranularity {
Module,
File,
}
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum EffortModelKind {
Cocomo81Basic,
Cocomo2Early,
Ensemble,
}
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum EffortLayer {
Headline,
Why,
Full,
}
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum BadgeMetric {
Lines,
Tokens,
Bytes,
Doc,
Blank,
Hotspot,
}
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum InitProfile {
Default,
Rust,
Node,
Mono,
Python,
Go,
Cpp,
}
#[derive(Args, Debug, Clone)]
pub struct CliContextArgs {
#[arg(value_name = "PATH")]
pub paths: Option<Vec<PathBuf>>,
#[arg(long, default_value = "128k")]
pub budget: String,
#[arg(long, value_enum, default_value_t = ContextStrategy::Greedy)]
pub strategy: ContextStrategy,
#[arg(long, value_enum, default_value_t = ValueMetric::Code)]
pub rank_by: ValueMetric,
#[arg(long = "mode", value_enum, default_value_t = ContextOutput::List)]
pub output_mode: ContextOutput,
#[arg(long)]
pub compress: bool,
#[arg(long)]
pub no_smart_exclude: bool,
#[arg(long, value_delimiter = ',')]
pub module_roots: Option<Vec<String>>,
#[arg(long)]
pub module_depth: Option<usize>,
#[arg(long)]
pub git: bool,
#[arg(long = "no-git")]
pub no_git: bool,
#[arg(long, default_value = "1000")]
pub max_commits: usize,
#[arg(long, default_value = "100")]
pub max_commit_files: usize,
#[arg(long, value_name = "PATH", visible_alias = "out")]
pub output: Option<PathBuf>,
#[arg(long)]
pub force: bool,
#[arg(long, value_name = "DIR", conflicts_with = "output")]
pub bundle_dir: Option<PathBuf>,
#[arg(long, default_value = "10485760")]
pub max_output_bytes: u64,
#[arg(long, value_name = "PATH")]
pub log: Option<PathBuf>,
#[arg(long, default_value = "0.15")]
pub max_file_pct: f64,
#[arg(long)]
pub max_file_tokens: Option<usize>,
#[arg(long)]
pub require_git_scores: bool,
}
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub enum ContextStrategy {
#[default]
Greedy,
Spread,
}
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub enum ValueMetric {
#[default]
Code,
Tokens,
Churn,
Hotspot,
}
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub enum ContextOutput {
#[default]
List,
Bundle,
Json,
}
#[derive(Args, Debug, Clone)]
pub struct CliCheckIgnoreArgs {
#[arg(value_name = "PATH", required = true)]
pub paths: Vec<PathBuf>,
#[arg(long, short = 'v')]
pub verbose: bool,
}
#[derive(Args, Debug, Clone)]
pub struct ToolsArgs {
#[arg(long, value_enum, default_value_t = ToolSchemaFormat::Jsonschema)]
pub format: ToolSchemaFormat,
#[arg(long)]
pub pretty: bool,
}
#[derive(Args, Debug, Clone)]
pub struct CliGateArgs {
#[arg(value_name = "INPUT")]
pub input: Option<PathBuf>,
#[arg(long)]
pub policy: Option<PathBuf>,
#[arg(long, value_name = "PATH")]
pub baseline: Option<PathBuf>,
#[arg(long, value_name = "PATH")]
pub ratchet_config: Option<PathBuf>,
#[arg(long, value_enum)]
pub preset: Option<AnalysisPreset>,
#[arg(long, value_enum, default_value_t = GateFormat::Text)]
pub format: GateFormat,
#[arg(long)]
pub fail_fast: bool,
}
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub enum GateFormat {
#[default]
Text,
Json,
}
#[derive(Args, Debug, Clone)]
pub struct CockpitArgs {
#[arg(long, default_value = "main")]
pub base: String,
#[arg(long, default_value = "HEAD")]
pub head: String,
#[arg(long, value_enum, default_value_t = CockpitFormat::Json)]
pub format: CockpitFormat,
#[arg(long, value_name = "PATH")]
pub output: Option<std::path::PathBuf>,
#[arg(long, value_name = "DIR")]
pub artifacts_dir: Option<std::path::PathBuf>,
#[arg(long, value_name = "PATH")]
pub baseline: Option<std::path::PathBuf>,
#[arg(long, value_enum, default_value_t = DiffRangeMode::TwoDot)]
pub diff_range: DiffRangeMode,
#[arg(long)]
pub sensor_mode: bool,
}
#[derive(Args, Debug, Clone)]
pub struct BaselineArgs {
#[arg(default_value = ".")]
pub path: PathBuf,
#[arg(long, default_value = ".tokmd/baseline.json")]
pub output: PathBuf,
#[arg(long)]
pub determinism: bool,
#[arg(long, short)]
pub force: bool,
}
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub enum CockpitFormat {
#[default]
Json,
Md,
Sections,
}
#[derive(Args, Debug, Clone)]
pub struct HandoffArgs {
#[arg(value_name = "PATH")]
pub paths: Option<Vec<PathBuf>>,
#[arg(long, default_value = ".handoff")]
pub out_dir: PathBuf,
#[arg(long, default_value = "128k")]
pub budget: String,
#[arg(long, value_enum, default_value_t = ContextStrategy::Greedy)]
pub strategy: ContextStrategy,
#[arg(long, value_enum, default_value_t = ValueMetric::Hotspot)]
pub rank_by: ValueMetric,
#[arg(long, value_enum, default_value_t = HandoffPreset::Risk)]
pub preset: HandoffPreset,
#[arg(long, value_delimiter = ',')]
pub module_roots: Option<Vec<String>>,
#[arg(long)]
pub module_depth: Option<usize>,
#[arg(long)]
pub force: bool,
#[arg(long)]
pub compress: bool,
#[arg(long)]
pub no_smart_exclude: bool,
#[arg(long = "no-git")]
pub no_git: bool,
#[arg(long, default_value = "1000")]
pub max_commits: usize,
#[arg(long, default_value = "100")]
pub max_commit_files: usize,
#[arg(long, default_value = "0.15")]
pub max_file_pct: f64,
#[arg(long)]
pub max_file_tokens: Option<usize>,
}
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub enum HandoffPreset {
Minimal,
Standard,
#[default]
Risk,
Deep,
}
#[derive(Args, Debug, Clone, Serialize, Deserialize)]
pub struct SensorArgs {
#[arg(long, default_value = "main")]
pub base: String,
#[arg(long, default_value = "HEAD")]
pub head: String,
#[arg(
long,
value_name = "PATH",
default_value = "artifacts/tokmd/report.json"
)]
pub output: std::path::PathBuf,
#[arg(long, value_enum, default_value_t = SensorFormat::Json)]
pub format: SensorFormat,
}
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub enum SensorFormat {
#[default]
Json,
Md,
}
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub enum NearDupScope {
#[default]
Module,
Lang,
Global,
}
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub enum DiffRangeMode {
#[default]
TwoDot,
ThreeDot,
}
pub use tokmd_settings::{
AnalyzeConfig, BadgeConfig, ContextConfig, ExportConfig, GateConfig, GateRule, ModuleConfig,
Profile, RatchetRuleConfig, ScanConfig, TomlConfig, TomlResult, UserConfig, ViewProfile,
};
impl From<&GlobalArgs> for tokmd_settings::ScanOptions {
fn from(g: &GlobalArgs) -> Self {
Self {
excluded: g.excluded.clone(),
config: g.config,
hidden: g.hidden,
no_ignore: g.no_ignore,
no_ignore_parent: g.no_ignore_parent,
no_ignore_dot: g.no_ignore_dot,
no_ignore_vcs: g.no_ignore_vcs,
treat_doc_strings_as_comments: g.treat_doc_strings_as_comments,
}
}
}
impl From<GlobalArgs> for tokmd_settings::ScanOptions {
fn from(g: GlobalArgs) -> Self {
Self::from(&g)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn user_config_default_is_empty() {
let c = UserConfig::default();
assert!(c.profiles.is_empty());
assert!(c.repos.is_empty());
}
#[test]
fn profile_default_all_none() {
let p = Profile::default();
assert!(p.format.is_none());
assert!(p.top.is_none());
assert!(p.files.is_none());
assert!(p.module_roots.is_none());
assert!(p.module_depth.is_none());
assert!(p.min_code.is_none());
assert!(p.max_rows.is_none());
assert!(p.redact.is_none());
assert!(p.meta.is_none());
assert!(p.children.is_none());
}
#[test]
fn global_args_default() {
let g = GlobalArgs::default();
assert!(g.excluded.is_empty());
assert_eq!(g.config, ConfigMode::Auto);
assert!(!g.hidden);
assert!(!g.no_ignore);
assert_eq!(g.verbose, 0);
}
#[test]
fn cli_lang_args_default() {
let a = CliLangArgs::default();
assert!(a.paths.is_none());
assert!(a.format.is_none());
assert!(a.top.is_none());
assert!(!a.files);
assert!(a.children.is_none());
}
#[test]
fn analysis_preset_serde_roundtrip() {
for variant in [
AnalysisPreset::Receipt,
AnalysisPreset::Estimate,
AnalysisPreset::Health,
AnalysisPreset::Risk,
AnalysisPreset::Supply,
AnalysisPreset::Architecture,
AnalysisPreset::Topics,
AnalysisPreset::Security,
AnalysisPreset::Identity,
AnalysisPreset::Git,
AnalysisPreset::Deep,
AnalysisPreset::Fun,
] {
let json = serde_json::to_string(&variant).unwrap();
let back: AnalysisPreset = serde_json::from_str(&json).unwrap();
assert_eq!(back, variant);
}
}
#[test]
fn diff_format_default_is_md() {
assert_eq!(DiffFormat::default(), DiffFormat::Md);
}
#[test]
fn diff_format_serde_roundtrip() {
for variant in [DiffFormat::Md, DiffFormat::Json] {
let json = serde_json::to_string(&variant).unwrap();
let back: DiffFormat = serde_json::from_str(&json).unwrap();
assert_eq!(back, variant);
}
}
#[test]
fn color_mode_default_is_auto() {
assert_eq!(ColorMode::default(), ColorMode::Auto);
}
#[test]
fn context_strategy_default_is_greedy() {
assert_eq!(ContextStrategy::default(), ContextStrategy::Greedy);
}
#[test]
fn value_metric_default_is_code() {
assert_eq!(ValueMetric::default(), ValueMetric::Code);
}
#[test]
fn context_output_default_is_list() {
assert_eq!(ContextOutput::default(), ContextOutput::List);
}
#[test]
fn gate_format_default_is_text() {
assert_eq!(GateFormat::default(), GateFormat::Text);
}
#[test]
fn cockpit_format_default_is_json() {
assert_eq!(CockpitFormat::default(), CockpitFormat::Json);
}
#[test]
fn handoff_preset_default_is_risk() {
assert_eq!(HandoffPreset::default(), HandoffPreset::Risk);
}
#[test]
fn sensor_format_default_is_json() {
assert_eq!(SensorFormat::default(), SensorFormat::Json);
}
#[test]
fn near_dup_scope_default_is_module() {
assert_eq!(NearDupScope::default(), NearDupScope::Module);
}
#[test]
fn diff_range_mode_default_is_two_dot() {
assert_eq!(DiffRangeMode::default(), DiffRangeMode::TwoDot);
}
#[test]
fn analysis_preset_uses_kebab_case() {
assert_eq!(
serde_json::to_string(&AnalysisPreset::Receipt).unwrap(),
"\"receipt\""
);
assert_eq!(
serde_json::to_string(&AnalysisPreset::Deep).unwrap(),
"\"deep\""
);
}
#[test]
fn context_strategy_uses_kebab_case() {
assert_eq!(
serde_json::to_string(&ContextStrategy::Greedy).unwrap(),
"\"greedy\""
);
assert_eq!(
serde_json::to_string(&ContextStrategy::Spread).unwrap(),
"\"spread\""
);
}
#[test]
fn value_metric_uses_kebab_case() {
assert_eq!(
serde_json::to_string(&ValueMetric::Hotspot).unwrap(),
"\"hotspot\""
);
}
#[test]
fn user_config_serde_roundtrip() {
let mut c = UserConfig::default();
c.profiles.insert(
"llm_safe".into(),
Profile {
format: Some("json".into()),
top: Some(10),
redact: Some(RedactMode::All),
..Profile::default()
},
);
c.repos.insert("owner/repo".into(), "llm_safe".into());
let json = serde_json::to_string(&c).unwrap();
let back: UserConfig = serde_json::from_str(&json).unwrap();
assert_eq!(back.profiles.len(), 1);
assert_eq!(back.repos.len(), 1);
assert_eq!(back.profiles["llm_safe"].top, Some(10));
}
#[test]
fn global_args_to_scan_options() {
let g = GlobalArgs {
excluded: vec!["target".into()],
config: ConfigMode::None,
hidden: true,
no_ignore: true,
no_ignore_parent: false,
no_ignore_dot: false,
no_ignore_vcs: false,
treat_doc_strings_as_comments: true,
verbose: 0,
no_progress: false,
};
let opts: tokmd_settings::ScanOptions = (&g).into();
assert_eq!(opts.excluded, vec!["target"]);
assert_eq!(opts.config, ConfigMode::None);
assert!(opts.hidden);
assert!(opts.no_ignore);
assert!(opts.treat_doc_strings_as_comments);
}
#[test]
fn global_args_owned_to_scan_options() {
let g = GlobalArgs {
excluded: vec!["vendor".into()],
config: ConfigMode::Auto,
hidden: false,
..GlobalArgs::default()
};
let opts: tokmd_settings::ScanOptions = g.into();
assert_eq!(opts.excluded, vec!["vendor"]);
assert!(!opts.hidden);
}
}