Skip to main content

mini_cargo/
mini_cargo.rs

1//! Example: mini_cargo — a cargo-like CLI emulator.
2//!
3//! Demonstrates subcommands, global flags, smart completions, and
4//! all prompt types. Does NOT perform any real operations.
5//!
6//! Run:
7//!   cargo run --example mini_cargo -- --help
8//!   cargo run --example mini_cargo -- build --help
9//!   cargo run --example mini_cargo -- run --example mini_cargo
10//!   cargo run --example mini_cargo -- add serde --features derive
11//!   cargo run --example mini_cargo -- init my-project
12//!
13//! Completions:
14//!   cargo run --example mini_cargo -- --completions bash > /tmp/_cargo_mini
15//!   source /tmp/_cargo_mini
16//!   cargo-mini <Tab>
17
18#![allow(dead_code)]
19
20use cli_ui::styles::{paint, CYAN, DIM, OK};
21use cli_ui::{bail, header, ok, phase, step, summary, CliCommand, CliOptions};
22use std::path::PathBuf;
23
24// ── Completion providers ──────────────────────────────────────────────────────
25
26fn workspace_examples() -> Vec<String> {
27    let Ok(rd) = std::fs::read_dir("examples") else {
28        return Vec::new();
29    };
30    let mut v: Vec<String> = rd
31        .flatten()
32        .filter_map(|e| {
33            let p = e.path();
34            if p.is_file() && p.extension()?.to_str() == Some("rs") {
35                Some(p.file_stem()?.to_string_lossy().to_string())
36            } else {
37                None
38            }
39        })
40        .collect();
41    v.sort();
42    v
43}
44
45fn workspace_bins() -> Vec<String> {
46    let Ok(rd) = std::fs::read_dir("src/bin") else {
47        return Vec::new();
48    };
49    let mut v: Vec<String> = rd
50        .flatten()
51        .filter_map(|e| {
52            let p = e.path();
53            if p.is_file() && p.extension()?.to_str() == Some("rs") {
54                Some(p.file_stem()?.to_string_lossy().to_string())
55            } else {
56                None
57            }
58        })
59        .collect();
60    v.sort();
61    v
62}
63
64fn workspace_members() -> Vec<String> {
65    // Read [workspace.members] from Cargo.toml if present
66    let Ok(src) = std::fs::read_to_string("Cargo.toml") else {
67        return Vec::new();
68    };
69    let mut members = Vec::new();
70    let mut in_members = false;
71    for line in src.lines() {
72        let t = line.trim();
73        if t == "members = [" || t.starts_with("members = [") {
74            in_members = true;
75        }
76        if in_members {
77            if t == "]" {
78                break;
79            }
80            if let Some(s) = t
81                .trim_matches(|c| c == '"' || c == ',' || c == '[' || c == ']')
82                .split('"')
83                .next()
84            {
85                let clean = s.trim().trim_matches('"').trim_matches(',').trim();
86                if !clean.is_empty() && !clean.starts_with('[') {
87                    members.push(clean.to_string());
88                }
89            }
90        }
91    }
92    members
93}
94
95fn available_features() -> Vec<String> {
96    // Parse [features] section from Cargo.toml
97    let Ok(src) = std::fs::read_to_string("Cargo.toml") else {
98        return Vec::new();
99    };
100    let mut features = Vec::new();
101    let mut in_features = false;
102    for line in src.lines() {
103        let t = line.trim();
104        if t == "[features]" {
105            in_features = true;
106            continue;
107        }
108        if in_features {
109            if t.starts_with('[') {
110                break;
111            }
112            if let Some(name) = t.split('=').next() {
113                let n = name.trim();
114                if !n.is_empty() && !n.starts_with('#') {
115                    features.push(n.to_string());
116                }
117            }
118        }
119    }
120    features
121}
122
123fn known_profiles() -> Vec<String> {
124    vec![
125        "dev".into(),
126        "release".into(),
127        "test".into(),
128        "bench".into(),
129    ]
130}
131
132fn known_targets() -> Vec<String> {
133    vec![
134        "x86_64-unknown-linux-gnu".into(),
135        "x86_64-unknown-linux-musl".into(),
136        "x86_64-apple-darwin".into(),
137        "aarch64-apple-darwin".into(),
138        "x86_64-pc-windows-msvc".into(),
139        "wasm32-unknown-unknown".into(),
140        "wasm32-wasi".into(),
141        "aarch64-unknown-linux-gnu".into(),
142    ]
143}
144
145fn known_editions() -> Vec<String> {
146    vec!["2015".into(), "2018".into(), "2021".into(), "2024".into()]
147}
148
149fn known_vcs() -> Vec<String> {
150    vec![
151        "git".into(),
152        "hg".into(),
153        "pijul".into(),
154        "fossil".into(),
155        "none".into(),
156    ]
157}
158
159// ── Global options ────────────────────────────────────────────────────────────
160
161#[derive(CliOptions)]
162#[cli(about = "global flags for mini-cargo")]
163struct Global {
164    /// Use verbose output
165    #[arg(short = 'v', long = "verbose", negatable)]
166    verbose: bool,
167
168    /// Use quiet output (suppress most output)
169    #[arg(short = 'q', long = "quiet", negatable)]
170    quiet: bool,
171
172    /// Coloring: auto, always, never
173    #[arg(
174        long = "color",
175        default = "auto",
176        validate(one_of("auto", "always", "never"))
177    )]
178    color: String,
179
180    /// Path to Cargo.toml
181    #[arg(long = "manifest-path", validate(is_file, ext("toml")))]
182    manifest_path: Option<PathBuf>,
183
184    /// Directory for all generated artifacts
185    #[arg(long = "target-dir", validate(is_dir))]
186    target_dir: Option<PathBuf>,
187}
188
189// ── Subcommand options ────────────────────────────────────────────────────────
190
191/// Build the local package and all of its dependencies
192#[derive(CliOptions)]
193#[cli(
194    name = "build",
195    about = "Compile the current package",
196    tagline = "all targets by default",
197    example = "cargo-mini build --release",
198    example = "cargo-mini build --target wasm32-unknown-unknown"
199)]
200struct BuildOpt {
201    /// Build artifacts in release mode
202    #[arg(section = "Build", short = 'r', long = "release")]
203    release: bool,
204
205    /// Build the specified example
206    #[arg(section = "Build", long = "example", complete = workspace_examples)]
207    example: Option<String>,
208
209    /// Build the specified binary
210    #[arg(section = "Build", long = "bin", complete = workspace_bins)]
211    bin: Option<String>,
212
213    /// Space or comma separated list of features to activate
214    #[arg(section = "Features", short = 'F', long = "features",
215          complete = available_features)]
216    features: Option<String>,
217
218    /// Activate all available features
219    #[arg(section = "Features", long = "all-features")]
220    all_features: bool,
221
222    /// Do not activate the `default` feature
223    #[arg(section = "Features", long = "no-default-features")]
224    no_default_features: bool,
225
226    /// Build for the target triple
227    #[arg(section = "Target", long = "target", complete = known_targets)]
228    target: Option<String>,
229
230    /// Build profile to use
231    #[arg(section = "Target", long = "profile", complete = known_profiles)]
232    profile: Option<String>,
233
234    /// Number of parallel jobs
235    #[arg(
236        section = "Performance",
237        short = 'j',
238        long = "jobs",
239        default = "4",
240        validate(range(1, 256))
241    )]
242    jobs: usize,
243
244    /// Do not print cargo log messages
245    #[arg(section = "Output", long = "quiet", negatable)]
246    quiet: bool,
247
248    /// Use verbose output
249    #[arg(section = "Output", long = "verbose", negatable)]
250    verbose: bool,
251}
252
253/// Run a binary or example of the local package
254#[derive(CliOptions)]
255#[cli(
256    name = "run",
257    about = "Run a binary or example of the local package",
258    example = "cargo-mini run --example mini_cargo",
259    example = "cargo-mini run --release -- --help"
260)]
261struct RunOpt {
262    /// Name of the example to run
263    #[arg(section = "Target", long = "example", complete = workspace_examples)]
264    example: Option<String>,
265
266    /// Name of the binary to run
267    #[arg(section = "Target", long = "bin", complete = workspace_bins)]
268    bin: Option<String>,
269
270    /// Build in release mode
271    #[arg(section = "Build", short = 'r', long = "release")]
272    release: bool,
273
274    /// Features to activate
275    #[arg(section = "Features", short = 'F', long = "features",
276          complete = available_features)]
277    features: Option<String>,
278
279    /// Build profile
280    #[arg(section = "Build", long = "profile", complete = known_profiles)]
281    profile: Option<String>,
282
283    /// Target triple
284    #[arg(section = "Build", long = "target", complete = known_targets)]
285    target: Option<String>,
286
287    /// Arguments passed to the binary (after --)
288    #[arg(skip)]
289    _extra_args: Vec<String>,
290}
291
292/// Add a dependency to Cargo.toml
293#[derive(CliOptions)]
294#[cli(
295    name = "add",
296    about = "Add dependencies to a Cargo.toml manifest file",
297    example = "cargo-mini add serde --features derive",
298    example = "cargo-mini add tokio --features full --no-default-features"
299)]
300struct AddOpt {
301    /// Crate name(s) to add
302    #[arg(positional)]
303    crate_name: String,
304
305    /// Features to enable for the dependency
306    #[arg(section = "Dependency", short = 'F', long = "features")]
307    features: Option<String>,
308
309    /// Add as a dev-dependency
310    #[arg(section = "Dependency", long = "dev")]
311    dev: bool,
312
313    /// Add as a build-dependency
314    #[arg(section = "Dependency", long = "build")]
315    build: bool,
316
317    /// Package to modify (in workspace)
318    #[arg(section = "Workspace", short = 'p', long = "package",
319          complete = workspace_members)]
320    package: Option<String>,
321
322    /// Don't actually write to Cargo.toml
323    #[arg(section = "Misc", long = "dry-run")]
324    dry_run: bool,
325
326    /// Do not activate the `default` feature
327    #[arg(section = "Dependency", long = "no-default-features")]
328    no_default_features: bool,
329}
330
331/// Remove a dependency from Cargo.toml
332#[derive(CliOptions)]
333#[cli(
334    name = "remove",
335    about = "Remove dependencies from a Cargo.toml manifest file",
336    example = "cargo-mini remove serde"
337)]
338struct RemoveOpt {
339    /// Crate name to remove
340    #[arg(positional)]
341    crate_name: String,
342
343    /// Remove a dev-dependency
344    #[arg(section = "Dependency", long = "dev")]
345    dev: bool,
346
347    /// Remove a build-dependency
348    #[arg(section = "Dependency", long = "build")]
349    build: bool,
350
351    /// Package to modify
352    #[arg(section = "Workspace", short = 'p', long = "package",
353          complete = workspace_members)]
354    package: Option<String>,
355}
356
357/// Create a new Cargo package
358#[derive(CliOptions)]
359#[cli(
360    name = "new",
361    about = "Create a new Cargo package",
362    example = "cargo-mini new my-project --lib",
363    example = "cargo-mini new my-cli --edition 2021 --vcs git"
364)]
365struct NewOpt {
366    /// Package name / directory path
367    #[arg(positional)]
368    path: PathBuf,
369
370    /// Create as a library package
371    #[arg(section = "Template", long = "lib")]
372    lib: bool,
373
374    /// Create as a binary package (default)
375    #[arg(section = "Template", long = "bin")]
376    bin: bool,
377
378    /// Edition to use
379    #[arg(
380        section = "Manifest",
381        long = "edition",
382        default = "2021",
383        validate(one_of("2015", "2018", "2021", "2024"))
384    )]
385    edition: String,
386
387    /// VCS to use
388    #[arg(
389        section = "Manifest",
390        long = "vcs",
391        validate(one_of("git", "hg", "pijul", "fossil", "none"))
392    )]
393    vcs: Option<String>,
394
395    /// Set the package name (defaults to directory name)
396    #[arg(section = "Manifest", long = "name")]
397    name: Option<String>,
398}
399
400/// Create a new Cargo package in an existing directory
401#[derive(CliOptions)]
402#[cli(
403    name = "init",
404    about = "Create a new Cargo package in an existing directory",
405    example = "cargo-mini init",
406    example = "cargo-mini init my-dir --lib"
407)]
408struct InitOpt {
409    /// Directory to use (defaults to current)
410    #[arg(positional)]
411    path: Option<PathBuf>,
412
413    /// Create as a library
414    #[arg(section = "Template", long = "lib")]
415    lib: bool,
416
417    /// Create as a binary (default)
418    #[arg(section = "Template", long = "bin")]
419    bin: bool,
420
421    /// Edition to use
422    #[arg(
423        section = "Manifest",
424        long = "edition",
425        default = "2021",
426        validate(one_of("2015", "2018", "2021", "2024"))
427    )]
428    edition: String,
429
430    /// VCS to use
431    #[arg(
432        section = "Manifest",
433        long = "vcs",
434        validate(one_of("git", "hg", "pijul", "fossil", "none"))
435    )]
436    vcs: Option<String>,
437}
438
439/// Run the tests
440#[derive(CliOptions)]
441#[cli(
442    name = "test",
443    about = "Execute all unit and integration tests and build examples",
444    example = "cargo-mini test --lib",
445    example = "cargo-mini test integration_test -- --nocapture"
446)]
447struct TestOpt {
448    /// Test name filter
449    #[arg(positional)]
450    filter: Option<String>,
451
452    /// Test only this library's unit tests
453    #[arg(section = "Target", long = "lib")]
454    lib: bool,
455
456    /// Test the specified example
457    #[arg(section = "Target", long = "example", complete = workspace_examples)]
458    example: Option<String>,
459
460    /// Build in release mode
461    #[arg(section = "Build", long = "release")]
462    release: bool,
463
464    /// Features to activate
465    #[arg(section = "Features", short = 'F', long = "features",
466          complete = available_features)]
467    features: Option<String>,
468
469    /// Number of parallel test threads
470    #[arg(
471        section = "Output",
472        long = "test-threads",
473        default = "4",
474        validate(range(1, 256))
475    )]
476    test_threads: usize,
477
478    /// Don't capture stdout/stderr of each test
479    #[arg(section = "Output", long = "nocapture")]
480    nocapture: bool,
481}
482
483/// Check that the current package compiles without producing binaries
484#[derive(CliOptions)]
485#[cli(
486    name = "check",
487    about = "Check a local package and all of its dependencies for errors",
488    example = "cargo-mini check --all-targets"
489)]
490struct CheckOpt {
491    /// Check all targets
492    #[arg(section = "Target", long = "all-targets")]
493    all_targets: bool,
494
495    /// Features to activate
496    #[arg(section = "Features", short = 'F', long = "features",
497          complete = available_features)]
498    features: Option<String>,
499
500    /// Target triple
501    #[arg(section = "Target", long = "target", complete = known_targets)]
502    target: Option<String>,
503}
504
505/// Remove generated artifacts
506#[derive(CliOptions)]
507#[cli(
508    name = "clean",
509    about = "Remove artifacts that cargo has generated in the past",
510    example = "cargo-mini clean --release"
511)]
512struct CleanOpt {
513    /// Clean release artifacts
514    #[arg(section = "Target", long = "release")]
515    release: bool,
516
517    /// Target triple
518    #[arg(section = "Target", long = "target", complete = known_targets)]
519    target: Option<String>,
520
521    /// Package to clean
522    #[arg(section = "Workspace", short = 'p', long = "package",
523          complete = workspace_members)]
524    package: Option<String>,
525}
526
527/// Display information about the current package
528#[derive(CliOptions)]
529#[cli(
530    name = "metadata",
531    about = "Output the resolved dependencies of the current package as JSON"
532)]
533struct MetadataOpt {
534    /// Output format version
535    #[arg(
536        section = "Output",
537        long = "format-version",
538        default = "1",
539        validate(one_of("1"))
540    )]
541    format_version: u32,
542
543    /// Do not include dependencies of workspace members
544    #[arg(section = "Output", long = "no-deps")]
545    no_deps: bool,
546}
547
548/// Update dependencies as recorded in the lock file
549#[derive(CliOptions)]
550#[cli(
551    name = "update",
552    about = "Update dependencies as recorded in the lock file",
553    example = "cargo-mini update serde"
554)]
555struct UpdateOpt {
556    /// Specific crate to update
557    #[arg(positional)]
558    crate_name: Option<String>,
559
560    /// Don't actually write the lock file
561    #[arg(section = "Misc", long = "dry-run")]
562    dry_run: bool,
563
564    /// Update to pre-release versions
565    #[arg(section = "Misc", long = "unstable-features")]
566    unstable: bool,
567}
568
569/// Show information about a package
570#[derive(CliOptions)]
571#[cli(
572    name = "info",
573    about = "Display information about a package in the registry",
574    example = "cargo-mini info serde"
575)]
576struct InfoOpt {
577    /// Crate name to look up
578    #[arg(positional)]
579    crate_name: String,
580
581    /// Show a specific version
582    #[arg(section = "Filter", long = "version")]
583    version: Option<String>,
584}
585
586/// Import a list of dependencies from a CSV manifest.
587///
588/// Demonstrates *validation-driven* smart completion: the `--file` flag
589/// has no `complete = fn` provider — completion is derived entirely from
590/// `validate(ext("csv"))`. When the user presses Tab after `--file`, the
591/// shell only sees `.csv` files (plus directories, to drill into).
592#[derive(CliOptions)]
593#[cli(
594    name = "import",
595    about = "Import a list of dependencies from a CSV manifest",
596    example = "cargo-mini import --file deps.csv"
597)]
598struct ImportOpt {
599    /// CSV manifest with one `name,version` per line
600    #[arg(
601        section = "Input",
602        long = "file",
603        validate(exists, is_file, ext("csv"))
604    )]
605    file: PathBuf,
606
607    /// Don't actually write to Cargo.toml
608    #[arg(section = "Misc", long = "dry-run")]
609    dry_run: bool,
610}
611
612// ── Top-level command enum ────────────────────────────────────────────────────
613
614#[derive(CliCommand)]
615#[cli(
616    name    = "cargo-mini",
617    about   = "The Rust package manager (emulator)",
618    tagline = "no real side effects — safe to tab-complete",
619    theme   = "yellow",
620    global  = Global,
621    example = "cargo-mini build --release",
622    example = "cargo-mini run --example mini_cargo",
623    example = "cargo-mini add tokio --features full",
624)]
625enum Cmd {
626    /// Compile the current package
627    Build(BuildOpt),
628    /// Run a binary or example of the local package
629    #[cli(alias = "r")]
630    Run(RunOpt),
631    /// Add a dependency to Cargo.toml
632    Add(AddOpt),
633    /// Remove a dependency from Cargo.toml
634    #[cli(alias = "rm")]
635    Remove(RemoveOpt),
636    /// Create a new Cargo package
637    New(NewOpt),
638    /// Create a new Cargo package in an existing directory
639    Init(InitOpt),
640    /// Execute all unit and integration tests
641    #[cli(alias = "t")]
642    Test(TestOpt),
643    /// Check a local package for errors without building
644    Check(CheckOpt),
645    /// Remove generated artifacts
646    Clean(CleanOpt),
647    /// Output resolved dependency information as JSON
648    Metadata(MetadataOpt),
649    /// Update dependencies as recorded in the lock file
650    Update(UpdateOpt),
651    /// Display information about a package in the registry
652    Info(InfoOpt),
653    /// Import dependencies from a CSV manifest
654    Import(ImportOpt),
655}
656
657// ── main ──────────────────────────────────────────────────────────────────────
658
659fn main() {
660    let cmd = match Cmd::parse() {
661        Ok(c) => c,
662        Err(e) => bail!("{}", e),
663    };
664    let global = Cmd::global();
665
666    header!(
667        "cargo-mini",
668        env!("CARGO_PKG_VERSION"),
669        "The Rust package manager (emulator)",
670        "no real side effects — safe to tab-complete",
671        badge = cli_ui::styles::theme_styles("yellow").0
672    );
673
674    if global.verbose {
675        phase!("verbose", "verbose output enabled");
676    }
677
678    match cmd {
679        Cmd::Build(opt) => cmd_build(&opt, global),
680        Cmd::Run(opt) => cmd_run(&opt, global),
681        Cmd::Add(opt) => cmd_add(&opt, global),
682        Cmd::Remove(opt) => cmd_remove(&opt, global),
683        Cmd::New(opt) => cmd_new(&opt, global),
684        Cmd::Init(opt) => cmd_init(&opt, global),
685        Cmd::Test(opt) => cmd_test(&opt, global),
686        Cmd::Check(opt) => cmd_check(&opt, global),
687        Cmd::Clean(opt) => cmd_clean(&opt, global),
688        Cmd::Metadata(opt) => cmd_metadata(&opt, global),
689        Cmd::Update(opt) => cmd_update(&opt, global),
690        Cmd::Info(opt) => cmd_info(&opt, global),
691        Cmd::Import(opt) => cmd_import(&opt, global),
692    }
693}
694
695// ── Subcommand handlers (all fake) ────────────────────────────────────────────
696
697fn cmd_build(opt: &BuildOpt, global: &Global) {
698    let mode = if opt.release { "release" } else { "dev" };
699    phase!("compile", "Compiling packages in {} mode", mode);
700    if let Some(ref e) = opt.example {
701        phase!("target", "example = {e}");
702    }
703    if let Some(ref b) = opt.bin {
704        phase!("target", "bin = {b}");
705    }
706    if let Some(ref t) = opt.target {
707        phase!("target", "target = {t}");
708    }
709    if let Some(ref f) = opt.features {
710        phase!("features", "{f}");
711    }
712    fake_compile(opt.jobs);
713    ok!("target/debug/mini_cargo");
714    summary! {
715        done: "Build finished",
716        "mode"    => paint(OK,     mode),
717        "jobs"    => paint(DIM,    &opt.jobs.to_string()),
718        "verbose" => paint(DIM,    &global.verbose.to_string()),
719    }
720}
721
722fn cmd_run(opt: &RunOpt, global: &Global) {
723    let mode = if opt.release { "release" } else { "dev" };
724    phase!("compile", "Compiling in {} mode", mode);
725    if let Some(ref e) = opt.example {
726        phase!("run", "example = {e}");
727    }
728    if let Some(ref b) = opt.bin {
729        phase!("run", "bin = {b}");
730    }
731    if let Some(ref f) = opt.features {
732        phase!("features", "{f}");
733    }
734    fake_compile(4);
735    phase!("running", "Running `target/{}/...`", mode);
736    summary! {
737        done: "Run complete",
738        "mode"   => paint(OK, mode),
739        "quiet"  => paint(DIM, &global.quiet.to_string()),
740    }
741}
742
743fn cmd_add(opt: &AddOpt, _global: &Global) {
744    let kind = if opt.dev {
745        "dev-dependency"
746    } else if opt.build {
747        "build-dependency"
748    } else {
749        "dependency"
750    };
751    phase!("resolve", "Resolving {} `{}`", kind, opt.crate_name);
752    if let Some(ref f) = opt.features {
753        phase!("features", "enabling: {f}");
754    }
755    if opt.dry_run {
756        phase!("dry-run", "would write to Cargo.toml (dry run)");
757    } else {
758        phase!("update", "Updating Cargo.toml");
759        phase!("lock", "Updating Cargo.lock");
760    }
761    summary! {
762        done: "Dependency added",
763        "crate" => paint(CYAN, &opt.crate_name),
764        "kind"  => paint(DIM,  kind),
765    }
766}
767
768fn cmd_remove(opt: &RemoveOpt, _global: &Global) {
769    phase!("remove", "Removing `{}` from Cargo.toml", opt.crate_name);
770    summary! {
771        done: "Dependency removed",
772        "crate" => paint(CYAN, &opt.crate_name),
773    }
774}
775
776fn cmd_new(opt: &NewOpt, _global: &Global) {
777    let kind = if opt.lib { "library" } else { "binary" };
778    let name = opt
779        .name
780        .as_deref()
781        .or_else(|| opt.path.file_name()?.to_str())
782        .unwrap_or("my-package");
783    phase!("create", "Creating {} package `{}`", kind, name);
784    phase!("edition", "edition = {}", opt.edition);
785    if let Some(ref v) = opt.vcs {
786        phase!("vcs", "initializing {} repo", v);
787    }
788    ok!(opt.path.display());
789    summary! {
790        done: "Package created",
791        "name"    => paint(CYAN, name),
792        "kind"    => paint(DIM,  kind),
793        "edition" => paint(DIM,  &opt.edition),
794    }
795}
796
797fn cmd_init(opt: &InitOpt, _global: &Global) {
798    let dir = opt
799        .path
800        .as_deref()
801        .unwrap_or_else(|| std::path::Path::new("."));
802    let kind = if opt.lib { "library" } else { "binary" };
803    phase!(
804        "init",
805        "Initializing {} package in `{}`",
806        kind,
807        dir.display()
808    );
809    if let Some(ref v) = opt.vcs {
810        phase!("vcs", "initializing {} repo", v);
811    }
812    summary! {
813        done: "Package initialized",
814        "dir"     => paint(CYAN, &dir.display().to_string()),
815        "edition" => paint(DIM,  &opt.edition),
816    }
817}
818
819fn cmd_test(opt: &TestOpt, _global: &Global) {
820    let mode = if opt.release { "release" } else { "dev" };
821    phase!("compile", "Compiling test targets in {} mode", mode);
822    if let Some(ref f) = opt.filter {
823        phase!("filter", "running tests matching `{f}`");
824    }
825    if let Some(ref f) = opt.features {
826        phase!("features", "{f}");
827    }
828    fake_compile(4);
829    step!("running {} test thread(s)", opt.test_threads);
830    eprintln!();
831    eprintln!(
832        "   {}  test result: {}. {} passed; 0 failed",
833        paint(DIM, "│"),
834        paint(OK, "ok"),
835        paint(OK, "42"),
836    );
837    eprintln!();
838    summary! {
839        done: "Tests passed",
840        "mode"    => paint(OK,     mode),
841        "threads" => paint(DIM,    &opt.test_threads.to_string()),
842        "passed"  => paint(OK,     "42"),
843        "failed"  => paint(DIM,    "0"),
844    }
845}
846
847fn cmd_check(opt: &CheckOpt, _global: &Global) {
848    phase!("check", "Checking package");
849    if opt.all_targets {
850        phase!("targets", "checking all targets");
851    }
852    if let Some(ref f) = opt.features {
853        phase!("features", "{f}");
854    }
855    if let Some(ref t) = opt.target {
856        phase!("target", "{t}");
857    }
858    fake_compile(4);
859    summary! { done: "Check passed" }
860}
861
862fn cmd_clean(opt: &CleanOpt, _global: &Global) {
863    let mode = if opt.release { "release" } else { "debug" };
864    phase!("clean", "Removing target/{}", mode);
865    if let Some(ref p) = opt.package {
866        phase!("package", "{p}");
867    }
868    summary! {
869        done: "Clean complete",
870        "mode" => paint(DIM, mode),
871    }
872}
873
874fn cmd_metadata(opt: &MetadataOpt, _global: &Global) {
875    phase!("metadata", "Resolving dependency graph");
876    if opt.no_deps {
877        phase!("filter", "excluding transitive dependencies");
878    }
879    eprintln!();
880    eprintln!(
881        "   {}  {{\"packages\":[...],\"workspace_root\":\"{}\"}}",
882        paint(DIM, "│"),
883        paint(DIM, "."),
884    );
885    eprintln!();
886    summary! {
887        done:    "Metadata output",
888        "version" => paint(DIM, &opt.format_version.to_string()),
889    }
890}
891
892fn cmd_update(opt: &UpdateOpt, _global: &Global) {
893    if let Some(ref c) = opt.crate_name {
894        phase!("update", "Updating `{}`", c);
895    } else {
896        phase!("update", "Updating all dependencies");
897    }
898    if opt.dry_run {
899        phase!("dry-run", "would update Cargo.lock (dry run)");
900    }
901    summary! { done: "Lock file updated" }
902}
903
904fn cmd_info(opt: &InfoOpt, _global: &Global) {
905    phase!("fetch", "Fetching info for `{}`", opt.crate_name);
906    if let Some(ref v) = opt.version {
907        phase!("version", "showing v{v}");
908    }
909    eprintln!();
910    eprintln!(
911        "   {}  {} v1.0.0",
912        paint(DIM, "│"),
913        paint(CYAN, &opt.crate_name)
914    );
915    eprintln!(
916        "   {}  {}",
917        paint(DIM, "│"),
918        paint(DIM, "A well-known Rust crate (emulated)")
919    );
920    eprintln!(
921        "   {}  https://crates.io/crates/{}",
922        paint(DIM, "│"),
923        opt.crate_name
924    );
925    eprintln!();
926    summary! {
927        done: "Info retrieved",
928        "crate" => paint(CYAN, &opt.crate_name),
929    }
930}
931
932fn cmd_import(opt: &ImportOpt, _global: &Global) {
933    phase!("read", "Reading CSV manifest `{}`", opt.file.display());
934    if opt.dry_run {
935        phase!("dry-run", "would update Cargo.toml (dry run)");
936    } else {
937        phase!("update", "Updating Cargo.toml");
938    }
939    summary! {
940        done: "Import complete",
941        "file" => paint(CYAN, &opt.file.display().to_string()),
942    }
943}
944
945// ── Helpers ───────────────────────────────────────────────────────────────────
946
947fn fake_compile(jobs: usize) {
948    let pkgs = ["proc-macro2", "quote", "syn", "serde", "cli-ui"];
949    step!("compiling {} packages ({} jobs)", pkgs.len(), jobs);
950    eprintln!();
951    for pkg in &pkgs {
952        eprintln!(
953            "   {}  {} {}",
954            paint(DIM, "│"),
955            paint(DIM, "Compiling"),
956            paint(CYAN, pkg),
957        );
958    }
959    eprintln!();
960}