1#![warn(missing_docs)]
8#![forbid(unsafe_code)]
9pub mod common;
10pub use common::compare;
11
12pub mod bring;
13pub mod carry_in;
14pub mod copy;
15pub mod error;
16pub mod hash;
17pub mod list;
18pub mod mv;
19pub mod recheck;
20pub mod remove;
21pub mod send;
22pub mod share;
23pub mod track;
24pub mod untrack;
25
26pub use bring::cmd_bring;
27pub use carry_in::cmd_carry_in;
28pub use copy::cmd_copy;
29pub use hash::cmd_hash;
30pub use list::cmd_list;
31pub use mv::cmd_move;
32pub use recheck::cmd_recheck;
33pub use remove::cmd_remove;
34pub use send::cmd_send;
35use share::ShareCLI;
36pub use track::cmd_track;
37pub use untrack::cmd_untrack;
38
39use crate::error::{Error, Result};
40use crate::share::cmd_share;
41use clap::Subcommand;
42use crossbeam::thread;
43use crossbeam_channel::bounded;
44
45use log::{debug, error, info, warn, LevelFilter};
46use std::io;
47use std::io::Write;
48use std::path::PathBuf;
49use xvc_core::default_project_config;
50use xvc_core::setup_logging;
51use xvc_core::types::xvcroot::load_xvc_root;
52use xvc_core::AbsolutePath;
53use xvc_core::XvcConfigParams;
54use xvc_core::XvcRoot;
55use xvc_core::XvcVerbosity;
56use xvc_core::CHANNEL_BOUND;
57use xvc_core::{XvcOutputLine, XvcOutputSender};
58
59pub use bring::BringCLI;
60pub use carry_in::CarryInCLI;
61pub use copy::CopyCLI;
62pub use hash::HashCLI;
63pub use list::ListCLI;
64pub use mv::MoveCLI;
65pub use recheck::RecheckCLI;
66pub use remove::RemoveCLI;
67pub use send::SendCLI;
68pub use track::TrackCLI;
69pub use untrack::UntrackCLI;
70
71use clap::Parser;
72
73#[derive(Debug, Clone, Subcommand)]
75#[command(author, version)]
76pub enum XvcFileSubCommand {
77 #[command(visible_aliases=&["t"])]
79 Track(TrackCLI),
80
81 #[command(visible_aliases=&["h"])]
83 Hash(HashCLI),
84
85 #[command(visible_aliases=&["checkout", "r"])]
87 Recheck(RecheckCLI),
88
89 #[command(visible_aliases = &[ "commit", "c"])]
91 CarryIn(CarryInCLI),
92
93 #[command(visible_aliases=&["C"])]
95 Copy(CopyCLI),
96
97 #[command(visible_aliases=&["M"])]
99 Move(MoveCLI),
100
101 #[command(visible_aliases=&["l"])]
103 List(ListCLI),
104
105 #[command(visible_aliases=&["s", "upload", "push"])]
107 Send(SendCLI),
108
109 #[command(visible_aliases=&["b", "download", "pull"])]
111 Bring(BringCLI),
112
113 #[command(visible_aliases=&["R"])]
115 Remove(RemoveCLI),
116
117 #[command(visible_aliases=&["U"])]
119 Untrack(UntrackCLI),
120
121 #[command(visible_aliases=&["S"])]
123 Share(ShareCLI),
124}
125
126#[derive(Debug, Clone, Parser)]
140pub struct XvcFileCLI {
141 #[arg(
143 long = "verbose",
144 short,
145 action = clap::ArgAction::Count
146 )]
147 pub verbosity: u8,
148
149 #[arg(long, help = "Suppress error messages")]
151 pub quiet: bool,
152
153 #[arg(short = 'C', default_value = ".")]
155 pub workdir: String,
156
157 #[arg(long, short = 'c')]
159 pub config: Option<Vec<String>>,
160
161 #[arg(long)]
163 pub no_system_config: bool,
164
165 #[arg(long)]
167 pub no_user_config: bool,
168
169 #[arg(long)]
171 pub no_project_config: bool,
172
173 #[arg(long)]
175 pub no_local_config: bool,
176
177 #[arg(long)]
179 pub no_env_config: bool,
180
181 #[command(subcommand)]
183 subcommand: XvcFileSubCommand,
184}
185
186pub fn run(
190 output_snd: &XvcOutputSender,
191 xvc_root: Option<&XvcRoot>,
192 opts: XvcFileCLI,
193) -> Result<()> {
194 match opts.subcommand {
195 XvcFileSubCommand::Track(opts) => cmd_track(
196 output_snd,
197 xvc_root.ok_or(Error::RequiresXvcRepository)?,
198 opts,
199 ),
200 XvcFileSubCommand::Hash(opts) => cmd_hash(output_snd, xvc_root, opts),
201 XvcFileSubCommand::CarryIn(opts) => cmd_carry_in(
202 output_snd,
203 xvc_root.ok_or(Error::RequiresXvcRepository)?,
204 opts,
205 ),
206 XvcFileSubCommand::Recheck(opts) => cmd_recheck(
207 output_snd,
208 xvc_root.ok_or(Error::RequiresXvcRepository)?,
209 opts,
210 ),
211 XvcFileSubCommand::List(opts) => cmd_list(
212 output_snd,
213 xvc_root.ok_or(Error::RequiresXvcRepository)?,
214 opts,
215 ),
216 XvcFileSubCommand::Send(opts) => cmd_send(
217 output_snd,
218 xvc_root.ok_or(Error::RequiresXvcRepository)?,
219 opts,
220 ),
221 XvcFileSubCommand::Bring(opts) => cmd_bring(
222 output_snd,
223 xvc_root.ok_or(Error::RequiresXvcRepository)?,
224 opts,
225 ),
226 XvcFileSubCommand::Copy(opts) => cmd_copy(
227 output_snd,
228 xvc_root.ok_or(Error::RequiresXvcRepository)?,
229 opts,
230 ),
231 XvcFileSubCommand::Move(opts) => cmd_move(
232 output_snd,
233 xvc_root.ok_or(Error::RequiresXvcRepository)?,
234 opts,
235 ),
236 XvcFileSubCommand::Untrack(opts) => cmd_untrack(
237 output_snd,
238 xvc_root.ok_or(Error::RequiresXvcRepository)?,
239 opts,
240 ),
241 XvcFileSubCommand::Remove(opts) => cmd_remove(
242 output_snd,
243 xvc_root.ok_or(Error::RequiresXvcRepository)?,
244 opts,
245 ),
246 XvcFileSubCommand::Share(opts) => cmd_share(
247 output_snd,
248 xvc_root.ok_or(Error::RequiresXvcRepository)?,
249 opts,
250 ),
251 }
252}
253
254pub fn dispatch(cli_opts: XvcFileCLI) -> Result<()> {
258 let verbosity = if cli_opts.quiet {
259 XvcVerbosity::Quiet
260 } else {
261 match cli_opts.verbosity {
262 0 => XvcVerbosity::Default,
263 1 => XvcVerbosity::Warn,
264 2 => XvcVerbosity::Info,
265 3 => XvcVerbosity::Debug,
266 _ => XvcVerbosity::Trace,
267 }
268 };
269
270 let term_log_level = match verbosity {
271 XvcVerbosity::Quiet => LevelFilter::Off,
272 XvcVerbosity::Default => LevelFilter::Error,
273 XvcVerbosity::Warn => LevelFilter::Warn,
274 XvcVerbosity::Info => LevelFilter::Info,
275 XvcVerbosity::Debug => LevelFilter::Debug,
276 XvcVerbosity::Trace => LevelFilter::Trace,
277 };
278
279 setup_logging(Some(term_log_level), None);
280 let dir = PathBuf::from(cli_opts.workdir.clone());
281 let current_dir = if dir.is_absolute() {
282 AbsolutePath::from(dir)
283 } else {
284 AbsolutePath::from(std::env::current_dir()?.join(dir).canonicalize()?)
285 };
286 let xvc_config_params = XvcConfigParams {
288 current_dir,
289 include_system_config: !cli_opts.no_system_config,
290 include_user_config: !cli_opts.no_user_config,
291 project_config_path: None,
292 local_config_path: None,
293 include_environment_config: !cli_opts.no_env_config,
294 command_line_config: cli_opts.config.clone(),
295 default_configuration: default_project_config(true),
296 };
297
298 let xvc_root = match load_xvc_root(xvc_config_params) {
299 Ok(r) => Some(r),
300 Err(e) => {
301 e.info();
302 None
303 }
304 };
305
306 thread::scope(move |s| {
307 let (output_snd, output_rec) = bounded::<Option<XvcOutputLine>>(CHANNEL_BOUND);
308 s.spawn(move |_| {
309 let mut output = io::stdout();
310 while let Ok(Some(output_line)) = output_rec.recv() {
311 match output_line {
312 XvcOutputLine::Output(m) => writeln!(output, "{}", m).unwrap(),
313 XvcOutputLine::Info(m) => info!("[INFO] {}", m),
314 XvcOutputLine::Warn(m) => warn!("[WARN] {}", m),
315 XvcOutputLine::Error(m) => error!("[ERROR] {}", m),
316 XvcOutputLine::Panic(m) => panic!("[PANIC] {}", m),
317 XvcOutputLine::Debug(m) => debug!("[DEBUG] {}", m),
318 XvcOutputLine::Tick(_) => {}
319 }
320 }
321 });
322
323 s.spawn(move |_| run(&output_snd, xvc_root.as_ref(), cli_opts).map_err(|e| e.error()));
324 })
325 .map_err(|e| error!("{:?}", e))
326 .expect("Crossbeam scope error");
327
328 Ok(())
329}
330
331pub fn init(_xvc_root: &XvcRoot) -> Result<()> {
335 Ok(())
336}
337
338pub const CHANNEL_CAPACITY: usize = 100000;