use crate::errors::*;
use crate::lockfile::Lockfile;
use crate::manifest::Manifest;
use clap::{ArgAction, CommandFactory, Parser, Subcommand};
use clap_complete::Shell;
use std::collections::HashSet;
use std::env;
use std::io;
use std::path::Path;
use std::path::PathBuf;
#[derive(Debug, Parser)]
#[command(version)]
pub struct Args {
#[arg(short, long, global = true, action(ArgAction::Count))]
pub verbose: u8,
#[arg(short = 'C', long)]
pub context: Option<PathBuf>,
#[command(subcommand)]
pub subcommand: SubCommand,
}
#[derive(Debug, Subcommand)]
pub enum SubCommand {
Build(Build),
Update(Update),
Fetch(Fetch),
Completions(Completions),
}
#[derive(Debug, Parser)]
pub struct Build {
#[arg(short, long)]
pub file: Option<PathBuf>,
#[arg(short, long)]
pub keep: bool,
#[arg(short, long)]
pub env: Vec<String>,
#[arg(required = true)]
pub cmd: Vec<String>,
}
impl Build {
pub fn validate(&self) -> Result<()> {
let mut env_keys = HashSet::new();
for env in &self.env {
let key = if let Some((key, _value)) = env.split_once('=') {
key
} else if env::var(env).is_ok() {
env
} else {
bail!("Referenced environment variables does not exist: {env:?}");
};
if !env_keys.insert(key) {
bail!("Can not set environment multiple times: {key:?}");
}
}
Ok(())
}
pub async fn load_files(&self) -> Result<(Option<Manifest>, Lockfile)> {
let path = self.file.as_deref().unwrap_or(Path::new("repro-env.lock"));
let lockfile = Lockfile::read_from_file(path).await?;
let manifest = if self.file.is_none() {
Some(Manifest::read_from_file("repro-env.toml").await?)
} else {
None
};
Ok((manifest, lockfile))
}
}
#[derive(Debug, Parser)]
pub struct Update {
#[arg(long)]
pub no_pull: bool,
#[arg(short, long)]
pub keep: bool,
}
#[derive(Debug, Parser)]
pub struct Fetch {
#[arg(short, long)]
pub file: Option<PathBuf>,
#[arg(long)]
pub no_pull: bool,
}
#[derive(Debug, Parser)]
pub struct Completions {
pub shell: Shell,
}
impl Completions {
pub fn generate<W: io::Write>(&self, mut w: W) -> Result<()> {
clap_complete::generate(self.shell, &mut Args::command(), "repro-env", &mut w);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_zsh_completions() {
Completions { shell: Shell::Zsh }
.generate(io::sink())
.unwrap();
}
}