1use crate::common::load_targets_from_store;
6use crate::Result;
7
8use clap::Parser;
9
10use clap_complete::ArgValueCompleter;
11use xvc_core::{
12 util::completer::xvc_path_completer, ContentDigest, XvcCachePath, XvcFileType, XvcMetadata,
13 XvcRoot,
14};
15use xvc_core::{HStore, XvcStore};
16use xvc_core::{error, XvcOutputSender};
17use xvc_storage::{
18 storage::{get_storage_record, storage_identifier_completer},
19 StorageIdentifier, XvcStorageOperations,
20};
21
22#[derive(Debug, Clone, PartialEq, Eq, Parser)]
29#[command(rename_all = "kebab-case")]
30pub struct SendCLI {
31 #[arg(long, short, alias = "to", add = ArgValueCompleter::new(storage_identifier_completer))]
33 storage: StorageIdentifier,
34
35 #[arg(long)]
37 force: bool,
38
39 #[arg(add = ArgValueCompleter::new(xvc_path_completer))]
41 targets: Option<Vec<String>>,
42}
43
44pub fn cmd_send(output_snd: &XvcOutputSender, xvc_root: &XvcRoot, opts: SendCLI) -> Result<()> {
46 let storage = get_storage_record(output_snd, xvc_root, &opts.storage)?;
47 let current_dir = xvc_root.config().current_dir()?;
48 let targets = load_targets_from_store(output_snd, xvc_root, current_dir, &opts.targets)?;
49
50 let target_file_xvc_metadata = xvc_root
51 .load_store::<XvcMetadata>()?
52 .subset(targets.keys().copied())?
53 .filter(|_, xmd| xmd.file_type == XvcFileType::File)
54 .cloned();
55
56 let target_files = targets.subset(target_file_xvc_metadata.keys().copied())?;
57
58 let content_digest_store: XvcStore<ContentDigest> = xvc_root.load_store()?;
60
61 let target_content_digests = content_digest_store.subset(target_files.keys().copied())?;
62
63 assert! {
64 target_content_digests.len() == target_files.len(),
65 "All files should have a content digest"
66 }
67
68 let cache_paths: HStore<XvcCachePath> = target_content_digests
69 .iter()
70 .filter_map(|(xe, content_digest)| {
71 target_files.get(xe).and_then(|xvc_path| {
72 XvcCachePath::new(xvc_path, content_digest)
73 .map_err(|e| {
74 error!(output_snd, "{e}");
75 e
76 })
77 .ok()
78 .map(|cache_path| (*xe, cache_path))
79 })
80 })
81 .collect();
82
83 storage
84 .send(
85 output_snd,
86 xvc_root,
87 cache_paths
89 .values()
90 .cloned()
91 .collect::<Vec<XvcCachePath>>()
92 .as_slice(),
93 opts.force,
94 )
95 .map_err(|e| xvc_core::Error::from(anyhow::anyhow!("Remote error: {}", e)))?;
96
97 Ok(())
98}