use crate::build_targets;
use crate::datetime::parse_datetime;
use crate::error::{self, Result};
use crate::source::parse_key_source;
use chrono::{DateTime, Utc};
use clap::Parser;
use snafu::ResultExt;
use std::num::{NonZeroU64, NonZeroUsize};
use std::path::PathBuf;
use tough::editor::signed::PathExists;
use tough::editor::RepositoryEditor;
use tough::key_source::KeySource;
#[derive(Debug, Parser)]
pub(crate) struct CreateArgs {
#[clap(short = 'k', long = "key", required = true, parse(try_from_str = parse_key_source))]
keys: Vec<Box<dyn KeySource>>,
#[clap(long = "snapshot-version")]
snapshot_version: NonZeroU64,
#[clap(long = "snapshot-expires", parse(try_from_str = parse_datetime))]
snapshot_expires: DateTime<Utc>,
#[clap(long = "targets-version")]
targets_version: NonZeroU64,
#[clap(long = "targets-expires", parse(try_from_str = parse_datetime))]
targets_expires: DateTime<Utc>,
#[clap(long = "timestamp-version")]
timestamp_version: NonZeroU64,
#[clap(long = "timestamp-expires", parse(try_from_str = parse_datetime))]
timestamp_expires: DateTime<Utc>,
#[clap(short = 'r', long = "root")]
root: PathBuf,
#[clap(short = 't', long = "add-targets")]
targets_indir: PathBuf,
#[clap(long = "target-path-exists", default_value = "skip")]
target_path_exists: PathExists,
#[clap(short = 'f', long = "follow")]
follow: bool,
#[clap(short = 'j', long = "jobs")]
jobs: Option<NonZeroUsize>,
#[clap(short = 'o', long = "outdir")]
outdir: PathBuf,
}
impl CreateArgs {
pub(crate) fn run(&self) -> Result<()> {
if let Some(jobs) = self.jobs {
rayon::ThreadPoolBuilder::new()
.num_threads(usize::from(jobs))
.build_global()
.context(error::InitializeThreadPoolSnafu)?;
}
let targets = build_targets(&self.targets_indir, self.follow)?;
let mut editor = RepositoryEditor::new(&self.root)
.context(error::EditorCreateSnafu { path: &self.root })?;
editor
.targets_version(self.targets_version)
.context(error::DelegationStructureSnafu)?
.targets_expires(self.targets_expires)
.context(error::DelegationStructureSnafu)?
.snapshot_version(self.snapshot_version)
.snapshot_expires(self.snapshot_expires)
.timestamp_version(self.timestamp_version)
.timestamp_expires(self.timestamp_expires);
for (target_name, target) in targets {
editor
.add_target(target_name, target)
.context(error::DelegationStructureSnafu)?;
}
let signed_repo = editor.sign(&self.keys).context(error::SignRepoSnafu)?;
let metadata_dir = &self.outdir.join("metadata");
let targets_outdir = &self.outdir.join("targets");
signed_repo
.link_targets(&self.targets_indir, targets_outdir, self.target_path_exists)
.context(error::LinkTargetsSnafu {
indir: &self.targets_indir,
outdir: targets_outdir,
})?;
signed_repo
.write(metadata_dir)
.context(error::WriteRepoSnafu {
directory: metadata_dir,
})?;
Ok(())
}
}