1use crate::error::MarsError;
4use crate::sync::{ResolutionMode, SyncOptions, SyncRequest};
5
6use super::output;
7
8#[derive(Debug, clap::Args)]
10pub struct SyncArgs {
11 #[arg(long)]
13 pub force: bool,
14
15 #[arg(long)]
17 pub diff: bool,
18
19 #[arg(long)]
21 pub frozen: bool,
22
23 #[arg(long, conflicts_with = "no_refresh_models")]
25 pub refresh_models: bool,
26
27 #[arg(long, conflicts_with = "refresh_models")]
29 pub no_refresh_models: bool,
30
31 #[arg(long)]
33 pub no_upgrade_hint: bool,
34
35 #[arg(long)]
37 pub verbose: bool,
38
39 #[arg(long)]
41 pub ignore_requires_mars: bool,
42
43 #[arg(long)]
45 pub ignore_requires_meridian: bool,
46}
47
48pub fn run(args: &SyncArgs, ctx: &super::MarsContext, json: bool) -> Result<i32, MarsError> {
50 let no_upgrade_hint = args.no_upgrade_hint || no_upgrade_hint_from_env();
51 let request = SyncRequest {
52 resolution: ResolutionMode::Normal,
53 mutation: None,
54 options: SyncOptions {
55 force: args.force,
56 dry_run: args.diff,
57 frozen: args.frozen,
58 refresh_models: args.refresh_models,
59 no_refresh_models: args.no_refresh_models,
60 check_upgrades: !no_upgrade_hint,
61 ignore_requires_mars: args.ignore_requires_mars,
62 ignore_requires_meridian: args.ignore_requires_meridian,
63 },
64 recovery: Default::default(),
65 lossiness_mode: if args.verbose {
66 crate::diagnostic::LossinessMode::Verbose
67 } else {
68 crate::diagnostic::LossinessMode::Surface
69 },
70 };
71
72 let report = crate::sync::execute(ctx, &request)?;
73
74 output::print_sync_report(&report, json, no_upgrade_hint);
75
76 Ok(0)
77}
78
79fn no_upgrade_hint_from_env() -> bool {
80 match std::env::var("MARS_NO_UPGRADE_HINT") {
81 Ok(value) => value.trim() == "1",
82 Err(_) => false,
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use crate::cli::{Cli, Command};
89 use clap::Parser;
90
91 #[test]
92 fn parses_no_refresh_models() {
93 let cli = Cli::try_parse_from(["mars", "sync", "--no-refresh-models"]).unwrap();
94 let Command::Sync(args) = cli.command else {
95 panic!("expected sync command");
96 };
97 assert!(args.no_refresh_models);
98 }
99
100 #[test]
101 fn parses_refresh_models() {
102 let cli = Cli::try_parse_from(["mars", "sync", "--refresh-models"]).unwrap();
103 let Command::Sync(args) = cli.command else {
104 panic!("expected sync command");
105 };
106 assert!(args.refresh_models);
107 }
108
109 #[test]
110 fn refresh_and_no_refresh_conflict() {
111 assert!(
112 Cli::try_parse_from(["mars", "sync", "--refresh-models", "--no-refresh-models"])
113 .is_err()
114 );
115 }
116
117 #[test]
118 fn parses_no_upgrade_hint() {
119 let cli = Cli::try_parse_from(["mars", "sync", "--no-upgrade-hint"]).unwrap();
120 let Command::Sync(args) = cli.command else {
121 panic!("expected sync command");
122 };
123 assert!(args.no_upgrade_hint);
124 }
125
126 #[test]
127 fn parses_verbose() {
128 let cli = Cli::try_parse_from(["mars", "sync", "--verbose"]).unwrap();
129 let Command::Sync(args) = cli.command else {
130 panic!("expected sync command");
131 };
132 assert!(args.verbose);
133 }
134
135 #[test]
136 fn engine_ignore_flags_parse_on_all_syncing_commands() {
137 for command in ["sync", "upgrade", "add", "repair"] {
138 let mut argv = vec!["mars", command];
139 if command == "add" {
140 argv.push("owner/repo");
141 }
142 argv.extend(["--ignore-requires-mars", "--ignore-requires-meridian"]);
143 let cli = Cli::try_parse_from(argv).unwrap();
144 match cli.command {
145 Command::Sync(args) => {
146 assert!(args.ignore_requires_mars);
147 assert!(args.ignore_requires_meridian);
148 }
149 Command::Upgrade(args) => {
150 assert!(args.ignore_requires_mars);
151 assert!(args.ignore_requires_meridian);
152 }
153 Command::Add(args) => {
154 assert!(args.ignore_requires_mars);
155 assert!(args.ignore_requires_meridian);
156 }
157 Command::Repair(args) => {
158 assert!(args.ignore_requires_mars);
159 assert!(args.ignore_requires_meridian);
160 }
161 _ => panic!("unexpected command"),
162 }
163 }
164 }
165}