#![warn(missing_docs)]
#![forbid(unsafe_code)]
pub mod common;
pub use common::compare;
pub mod bring;
pub mod carry_in;
pub mod copy;
pub mod error;
pub mod hash;
pub mod list;
pub mod mv;
pub mod recheck;
pub mod remove;
pub mod send;
pub mod share;
pub mod track;
pub mod untrack;
pub use bring::cmd_bring;
pub use carry_in::cmd_carry_in;
pub use copy::cmd_copy;
pub use hash::cmd_hash;
pub use list::cmd_list;
pub use mv::cmd_move;
pub use recheck::cmd_recheck;
pub use remove::cmd_remove;
pub use send::cmd_send;
use share::ShareCLI;
pub use track::cmd_track;
pub use untrack::cmd_untrack;
use crate::error::{Error, Result};
use crate::share::cmd_share;
use clap::Subcommand;
use crossbeam::thread;
use crossbeam_channel::bounded;
use log::{debug, error, info, warn, LevelFilter};
use std::io;
use std::io::Write;
use std::path::PathBuf;
use xvc_core::default_project_config;
use xvc_core::setup_logging;
use xvc_core::types::xvcroot::load_xvc_root;
use xvc_core::AbsolutePath;
use xvc_core::XvcConfigParams;
use xvc_core::XvcRoot;
use xvc_core::XvcVerbosity;
use xvc_core::CHANNEL_BOUND;
use xvc_core::{XvcOutputLine, XvcOutputSender};
pub use bring::BringCLI;
pub use carry_in::CarryInCLI;
pub use copy::CopyCLI;
pub use hash::HashCLI;
pub use list::ListCLI;
pub use mv::MoveCLI;
pub use recheck::RecheckCLI;
pub use remove::RemoveCLI;
pub use send::SendCLI;
pub use track::TrackCLI;
pub use untrack::UntrackCLI;
use clap::Parser;
#[derive(Debug, Clone, Subcommand)]
#[command(author, version)]
pub enum XvcFileSubCommand {
#[command(visible_aliases=&["t"])]
Track(TrackCLI),
#[command(visible_aliases=&["h"])]
Hash(HashCLI),
#[command(visible_aliases=&["checkout", "r"])]
Recheck(RecheckCLI),
#[command(visible_aliases = &[ "commit", "c"])]
CarryIn(CarryInCLI),
#[command(visible_aliases=&["C"])]
Copy(CopyCLI),
#[command(visible_aliases=&["M"])]
Move(MoveCLI),
#[command(visible_aliases=&["l"])]
List(ListCLI),
#[command(visible_aliases=&["s", "upload", "push"])]
Send(SendCLI),
#[command(visible_aliases=&["b", "download", "pull"])]
Bring(BringCLI),
#[command(visible_aliases=&["R"])]
Remove(RemoveCLI),
#[command(visible_aliases=&["U"])]
Untrack(UntrackCLI),
#[command(visible_aliases=&["S"])]
Share(ShareCLI),
}
#[derive(Debug, Clone, Parser)]
pub struct XvcFileCLI {
#[arg(
long = "verbose",
short,
action = clap::ArgAction::Count
)]
pub verbosity: u8,
#[arg(long, help = "Suppress error messages")]
pub quiet: bool,
#[arg(short = 'C', default_value = ".")]
pub workdir: String,
#[arg(long, short = 'c')]
pub config: Option<Vec<String>>,
#[arg(long)]
pub no_system_config: bool,
#[arg(long)]
pub no_user_config: bool,
#[arg(long)]
pub no_project_config: bool,
#[arg(long)]
pub no_local_config: bool,
#[arg(long)]
pub no_env_config: bool,
#[command(subcommand)]
subcommand: XvcFileSubCommand,
}
pub fn run(
output_snd: &XvcOutputSender,
xvc_root: Option<&XvcRoot>,
opts: XvcFileCLI,
) -> Result<()> {
match opts.subcommand {
XvcFileSubCommand::Track(opts) => cmd_track(
output_snd,
xvc_root.ok_or(Error::RequiresXvcRepository)?,
opts,
),
XvcFileSubCommand::Hash(opts) => cmd_hash(output_snd, xvc_root, opts),
XvcFileSubCommand::CarryIn(opts) => cmd_carry_in(
output_snd,
xvc_root.ok_or(Error::RequiresXvcRepository)?,
opts,
),
XvcFileSubCommand::Recheck(opts) => cmd_recheck(
output_snd,
xvc_root.ok_or(Error::RequiresXvcRepository)?,
opts,
),
XvcFileSubCommand::List(opts) => cmd_list(
output_snd,
xvc_root.ok_or(Error::RequiresXvcRepository)?,
opts,
),
XvcFileSubCommand::Send(opts) => cmd_send(
output_snd,
xvc_root.ok_or(Error::RequiresXvcRepository)?,
opts,
),
XvcFileSubCommand::Bring(opts) => cmd_bring(
output_snd,
xvc_root.ok_or(Error::RequiresXvcRepository)?,
opts,
),
XvcFileSubCommand::Copy(opts) => cmd_copy(
output_snd,
xvc_root.ok_or(Error::RequiresXvcRepository)?,
opts,
),
XvcFileSubCommand::Move(opts) => cmd_move(
output_snd,
xvc_root.ok_or(Error::RequiresXvcRepository)?,
opts,
),
XvcFileSubCommand::Untrack(opts) => cmd_untrack(
output_snd,
xvc_root.ok_or(Error::RequiresXvcRepository)?,
opts,
),
XvcFileSubCommand::Remove(opts) => cmd_remove(
output_snd,
xvc_root.ok_or(Error::RequiresXvcRepository)?,
opts,
),
XvcFileSubCommand::Share(opts) => cmd_share(
output_snd,
xvc_root.ok_or(Error::RequiresXvcRepository)?,
opts,
),
}
}
pub fn dispatch(cli_opts: XvcFileCLI) -> Result<()> {
let verbosity = if cli_opts.quiet {
XvcVerbosity::Quiet
} else {
match cli_opts.verbosity {
0 => XvcVerbosity::Default,
1 => XvcVerbosity::Warn,
2 => XvcVerbosity::Info,
3 => XvcVerbosity::Debug,
_ => XvcVerbosity::Trace,
}
};
let term_log_level = match verbosity {
XvcVerbosity::Quiet => LevelFilter::Off,
XvcVerbosity::Default => LevelFilter::Error,
XvcVerbosity::Warn => LevelFilter::Warn,
XvcVerbosity::Info => LevelFilter::Info,
XvcVerbosity::Debug => LevelFilter::Debug,
XvcVerbosity::Trace => LevelFilter::Trace,
};
setup_logging(Some(term_log_level), None);
let dir = PathBuf::from(cli_opts.workdir.clone());
let current_dir = if dir.is_absolute() {
AbsolutePath::from(dir)
} else {
AbsolutePath::from(std::env::current_dir()?.join(dir).canonicalize()?)
};
let xvc_config_params = XvcConfigParams {
current_dir,
include_system_config: !cli_opts.no_system_config,
include_user_config: !cli_opts.no_user_config,
project_config_path: None,
local_config_path: None,
include_environment_config: !cli_opts.no_env_config,
command_line_config: cli_opts.config.clone(),
default_configuration: default_project_config(true),
};
let xvc_root = match load_xvc_root(xvc_config_params) {
Ok(r) => Some(r),
Err(e) => {
e.info();
None
}
};
thread::scope(move |s| {
let (output_snd, output_rec) = bounded::<Option<XvcOutputLine>>(CHANNEL_BOUND);
s.spawn(move |_| {
let mut output = io::stdout();
while let Ok(Some(output_line)) = output_rec.recv() {
match output_line {
XvcOutputLine::Output(m) => writeln!(output, "{}", m).unwrap(),
XvcOutputLine::Info(m) => info!("[INFO] {}", m),
XvcOutputLine::Warn(m) => warn!("[WARN] {}", m),
XvcOutputLine::Error(m) => error!("[ERROR] {}", m),
XvcOutputLine::Panic(m) => panic!("[PANIC] {}", m),
XvcOutputLine::Debug(m) => debug!("[DEBUG] {}", m),
XvcOutputLine::Tick(_) => {}
}
}
});
s.spawn(move |_| run(&output_snd, xvc_root.as_ref(), cli_opts).map_err(|e| e.error()));
})
.map_err(|e| error!("{:?}", e))
.expect("Crossbeam scope error");
Ok(())
}
pub fn init(_xvc_root: &XvcRoot) -> Result<()> {
Ok(())
}
pub const CHANNEL_CAPACITY: usize = 100000;