1use crate::common::{load_targets_from_store, move_to_cache};
10
11use crate::{
12 recheck::{cmd_recheck, RecheckCLI},
13 Result,
14};
15
16use clap::Parser;
17
18use clap_complete::ArgValueCompleter;
19use xvc_core::util::completer::{strum_variants_completer, xvc_path_completer};
20use xvc_core::{debug, error, uwr, warn, XvcOutputSender};
21use xvc_core::{
22 ContentDigest, HStore, RecheckMethod, XvcCachePath, XvcFileType, XvcMetadata, XvcRoot, XvcStore,
23};
24
25use xvc_core::PathSync;
26use xvc_storage::storage::storage_identifier_completer;
27use xvc_storage::XvcStorageEvent;
28use xvc_storage::{storage::get_storage_record, StorageIdentifier, XvcStorageOperations};
29
30#[derive(Debug, Clone, PartialEq, Eq, Parser)]
35#[command(rename_all = "kebab-case")]
36pub struct BringCLI {
37 #[arg(long, short, alias = "from", add = ArgValueCompleter::new(storage_identifier_completer))]
39 storage: StorageIdentifier,
40
41 #[arg(long)]
43 force: bool,
44
45 #[arg(long)]
50 no_recheck: bool,
51
52 #[arg(long, alias = "as", add = ArgValueCompleter::new(strum_variants_completer::<RecheckMethod>))]
55 recheck_as: Option<RecheckMethod>,
56
57 #[arg(add = ArgValueCompleter::new(xvc_path_completer))]
59 targets: Option<Vec<String>>,
60}
61
62pub fn fetch(output_snd: &XvcOutputSender, xvc_root: &XvcRoot, opts: &BringCLI) -> Result<()> {
69 let storage = get_storage_record(output_snd, xvc_root, &opts.storage)?;
70
71 let current_dir = xvc_root.config().current_dir()?;
72 let targets = load_targets_from_store(output_snd, xvc_root, current_dir, &opts.targets)?;
73 let force = opts.force;
74
75 let target_xvc_metadata = xvc_root
76 .load_store::<XvcMetadata>()?
77 .subset(targets.keys().copied())?;
78
79 let target_file_xvc_metadata =
80 target_xvc_metadata.filter(|_, xmd| xmd.file_type == XvcFileType::File);
81
82 let target_files = targets.subset(target_file_xvc_metadata.keys().copied())?;
83
84 let content_digest_store: XvcStore<ContentDigest> = xvc_root.load_store()?;
86
87 let target_content_digests = content_digest_store.subset(target_files.keys().copied())?;
88
89 assert! {
90 target_content_digests.len() == target_files.len(),
91 "All files should have a content digest"
92 }
93
94 let cache_paths: HStore<XvcCachePath> = target_content_digests
95 .iter()
96 .filter_map(|(xe, cd)| {
97 let xvc_path = target_files.get(xe).unwrap();
98 match XvcCachePath::new(xvc_path, cd) {
99 Ok(cp) => Some((*xe, cp)),
100 Err(e) => {
101 warn!(output_snd, "Error: {}", e);
102 None
103 }
104 }
105 })
106 .filter(|(_, cp)| {
107 if force {
108 return true;
109 }
110 let cache_path = cp.to_absolute_path(xvc_root);
111 if cache_path.exists() {
112 debug!(output_snd, "Cache path already exists: {}", cache_path);
113 false
114 } else {
115 true
116 }
117 })
118 .collect();
119
120 let (temp_dir, event) = storage
121 .receive(
122 output_snd,
123 xvc_root,
124 cache_paths
125 .values()
126 .cloned()
127 .collect::<Vec<XvcCachePath>>()
128 .as_slice(),
129 opts.force,
130 )
131 .map_err(|e| xvc_core::Error::from(anyhow::anyhow!("Remote error: {}", e)))?;
132
133 let path_sync = PathSync::new();
134 for (_, cp) in cache_paths {
136 let cache_path = cp.to_absolute_path(xvc_root);
137 let temp_path = temp_dir.temp_cache_path(&cp)?;
138 if temp_path.exists() {
139 uwr!(
140 move_to_cache(&temp_path, &cache_path, &path_sync),
141 output_snd
142 );
143 } else {
144 error!(output_snd, "Could not download {}", cp);
145 }
146 }
147
148 xvc_root.with_store_mut(|store: &mut XvcStore<XvcStorageEvent>| {
149 store.insert(
150 xvc_root.new_entity(),
151 XvcStorageEvent::Receive(event.clone()),
152 );
153 Ok(())
154 })?;
155
156 Ok(())
157}
158
159pub fn cmd_bring(output_snd: &XvcOutputSender, xvc_root: &XvcRoot, opts: BringCLI) -> Result<()> {
164 fetch(output_snd, xvc_root, &opts)?;
165 if !opts.no_recheck {
166 let recheck_targets = opts.targets.clone();
167
168 let recheck_opts = RecheckCLI {
169 recheck_method: opts.recheck_as,
170 no_parallel: false,
171 force: opts.force,
172 targets: recheck_targets,
173 };
174
175 cmd_recheck(output_snd, xvc_root, recheck_opts)?;
176 }
177
178 Ok(())
179}