mars_agents/cli/
resolve_cmd.rs1use std::path::{Path, PathBuf};
4
5use crate::error::MarsError;
6use crate::hash;
7use crate::types::{ContentHash, DestPath};
8
9use super::output;
10
11#[derive(Debug, clap::Args)]
13pub struct ResolveArgs {
14 pub file: Option<PathBuf>,
16}
17
18pub fn run(args: &ResolveArgs, ctx: &super::MarsContext, json: bool) -> Result<i32, MarsError> {
20 let mut lock = crate::lock::load(&ctx.managed_root)?;
21 let mut resolved_files = Vec::new();
22 let mut still_conflicted = Vec::new();
23
24 let items_to_check: Vec<DestPath> = if let Some(file) = &args.file {
25 let rel = DestPath::from(file.as_path());
27 if lock.items.contains_key(&rel) {
28 vec![rel]
29 } else {
30 return Err(MarsError::Source {
31 source_name: "resolve".to_string(),
32 message: format!("{} is not a managed item", file.display()),
33 });
34 }
35 } else {
36 lock.items.keys().cloned().collect()
38 };
39
40 for dest_path_str in &items_to_check {
41 let disk_path = ctx.managed_root.join(dest_path_str);
42 if !disk_path.exists() {
43 continue;
44 }
45
46 if has_conflict_markers(&disk_path)? {
48 still_conflicted.push(dest_path_str.clone());
49 continue;
50 }
51
52 if let Some(item) = lock.items.get_mut(dest_path_str) {
54 let new_hash = hash::compute_hash(&disk_path, item.kind)?;
55 if new_hash != item.installed_checksum {
56 item.installed_checksum = ContentHash::from(new_hash);
57 resolved_files.push(dest_path_str.to_string());
58 }
59 }
60 }
61
62 if !resolved_files.is_empty() {
64 crate::lock::write(&ctx.managed_root, &lock)?;
65 }
66
67 if json {
68 output::print_json(&serde_json::json!({
69 "resolved": resolved_files,
70 "still_conflicted": still_conflicted,
71 }));
72 } else {
73 for file in &resolved_files {
74 output::print_success(&format!("resolved {file}"));
75 }
76 for file in &still_conflicted {
77 eprintln!(" ! {file} still has conflict markers");
78 }
79 if resolved_files.is_empty() && still_conflicted.is_empty() {
80 output::print_info("no conflicts to resolve");
81 }
82 }
83
84 if still_conflicted.is_empty() {
85 Ok(0)
86 } else {
87 Ok(1)
88 }
89}
90
91fn has_conflict_markers(path: &Path) -> Result<bool, MarsError> {
93 let content = std::fs::read_to_string(path)?;
94 Ok(content.contains("<<<<<<<") && content.contains(">>>>>>>"))
95}