use crate::common::UNUSED_URL;
use crate::download_root::download_root;
use crate::error::{self, Result};
use clap::Parser;
use snafu::ResultExt;
use std::fs::File;
use std::num::NonZeroU64;
use std::path::PathBuf;
use tough::{ExpirationEnforcement, RepositoryLoader};
use url::Url;
#[derive(Debug, Parser)]
pub(crate) struct CloneArgs {
#[clap(
short = 'r',
long = "root",
required_if("allow-root-download", "false")
)]
root: Option<PathBuf>,
#[clap(short = 'v', long = "root-version", default_value = "1")]
root_version: NonZeroU64,
#[clap(short = 'm', long = "metadata-url")]
metadata_base_url: Url,
#[clap(short = 't', long = "targets-url", required_unless = "metadata-only")]
targets_base_url: Option<Url>,
#[clap(long)]
allow_root_download: bool,
#[clap(long)]
allow_expired_repo: bool,
#[clap(short = 'n', long = "target-names", conflicts_with = "metadata-only")]
target_names: Vec<String>,
#[clap(long, required_unless = "metadata-only")]
targets_dir: Option<PathBuf>,
#[clap(long)]
metadata_dir: PathBuf,
#[clap(long, conflicts_with_all(&["target-names", "targets-dir", "targets-base-url"]))]
metadata_only: bool,
}
#[rustfmt::skip]
fn expired_repo_warning() {
eprintln!("\
=================================================================
WARNING: repo metadata is expired, meaning the owner hasn't verified its contents lately and it could be unsafe!
=================================================================");
}
impl CloneArgs {
pub(crate) fn run(&self) -> Result<()> {
let root_path = if let Some(path) = &self.root {
PathBuf::from(path)
} else if self.allow_root_download {
let outdir = std::env::current_dir().context(error::CurrentDirSnafu)?;
download_root(&self.metadata_base_url, self.root_version, outdir)?
} else {
eprintln!("No root.json available");
std::process::exit(1);
};
let targets_base_url = self
.targets_base_url
.as_ref()
.unwrap_or(&Url::parse(UNUSED_URL).context(error::UrlParseSnafu {
url: UNUSED_URL.to_owned(),
})?)
.clone();
let expiration_enforcement = if self.allow_expired_repo {
expired_repo_warning();
ExpirationEnforcement::Unsafe
} else {
ExpirationEnforcement::Safe
};
let repository = RepositoryLoader::new(
File::open(&root_path).context(error::OpenRootSnafu { path: &root_path })?,
self.metadata_base_url.clone(),
targets_base_url,
)
.expiration_enforcement(expiration_enforcement)
.load()
.context(error::RepoLoadSnafu)?;
if self.metadata_only {
println!("Cloning repository metadata to {:?}", self.metadata_dir);
repository
.cache_metadata(&self.metadata_dir, true)
.context(error::CloneRepositorySnafu)?;
} else {
let targets_dir = self.targets_dir.as_ref().expect(
"Developer error: `targets_dir` is required unless downloading metadata only",
);
println!(
"Cloning repository:\n\tmetadata location: {:?}\n\ttargets location: {:?}",
self.metadata_dir, targets_dir
);
if self.target_names.is_empty() {
repository
.cache(&self.metadata_dir, &targets_dir, None::<&[&str]>, true)
.context(error::CloneRepositorySnafu)?;
} else {
repository
.cache(
&self.metadata_dir,
&targets_dir,
Some(self.target_names.as_slice()),
true,
)
.context(error::CloneRepositorySnafu)?;
}
};
Ok(())
}
}