use std::path::PathBuf;
use clap::Parser;
use clap::Subcommand;
use tracing::log;
use quilt_rs::uri::Namespace;
mod benchmark;
mod browse;
mod commit;
mod install;
mod list;
mod model;
mod output;
mod package;
mod pull;
mod push;
mod status;
mod uninstall;
use model::Model;
use output::print;
fn parse_optional_namespace(namespace: Option<String>) -> Result<Option<Namespace>, Error> {
Ok(match namespace {
Some(namespace) => Some(namespace.try_into()?),
None => None,
})
}
#[derive(Parser)]
#[command(version, about, long_about = None)]
struct Args {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Browse {
#[arg(value_name = "PKG_URI")]
uri: String,
},
Commit {
#[arg(short, long)]
domain: PathBuf,
#[arg(short, long)]
message: String,
#[arg(short, long)]
user_meta: Option<String>,
#[arg(short, long)]
namespace: String,
},
Install {
#[arg(value_name = "PKG_URI")]
uri: String,
#[arg(short, long)]
domain: PathBuf,
#[arg(short, long)]
namespace: Option<String>,
#[arg(short, long)]
path: Option<Vec<PathBuf>>,
},
List {
#[arg(short, long)]
domain: PathBuf,
},
Package {
#[arg(short, long)]
message: Option<String>,
#[arg(value_name = "S3_URI")]
uri: String,
#[arg(short, long, value_name = "PKG_URI")]
target: String,
#[arg(short, long)]
user_meta: Option<String>,
},
Pull {
#[arg(short, long)]
domain: PathBuf,
#[arg(short, long)]
namespace: String,
},
Push {
#[arg(short, long)]
domain: PathBuf,
#[arg(short, long)]
namespace: String,
},
Status {
#[arg(short, long)]
domain: PathBuf,
#[arg(short, long)]
namespace: String,
},
Benchmark {
#[arg(short, long)]
number: i32,
#[arg(short, long)]
path: Option<PathBuf>,
},
Uninstall {
#[arg(short, long)]
namespace: String,
#[arg(short, long)]
domain: PathBuf,
},
}
pub async fn init() -> Result<(), Error> {
let args = Args::parse();
match args.command {
Commands::Browse { uri } => {
let (m, temp_dir) = Model::from_temp_dir()?;
let args = browse::Input { uri };
log::info!("Browsing {:?} using {:?}", args, temp_dir);
print(browse::command(m, args).await);
Ok(())
}
Commands::Commit {
domain,
namespace,
message,
user_meta,
} => {
let user_meta = match &user_meta {
Some(object) => match serde_json::from_str(object)? {
serde_json::Value::Object(object) => Some(object),
_ => {
return Err(Error::CommitMetaInvalid(object.to_string()));
}
},
None => None,
};
let args = commit::Input {
message,
namespace: namespace.try_into()?,
user_meta,
};
log::info!("Committing {:?}", args);
print(commit::command(Model::from(domain), args).await);
Ok(())
}
Commands::Install {
path,
domain,
namespace,
uri,
} => {
let args = install::Input {
namespace: parse_optional_namespace(namespace)?,
paths: path,
uri,
};
log::info!("Installing {:?}", args);
print(install::command(Model::from(domain), args).await);
Ok(())
}
Commands::List { domain } => {
if !domain.exists() {
return Err(Error::Domain(domain));
}
log::info!("Listing installed packages");
print(list::command(Model::from(domain)).await);
Ok(())
}
Commands::Package {
message,
target,
uri,
user_meta,
} => {
let user_meta = match &user_meta {
Some(object) => match serde_json::from_str(object)? {
serde_json::Value::Object(object) => Some(object),
_ => {
return Err(Error::CommitMetaInvalid(object.to_string()));
}
},
None => None,
};
let (m, temp_dir) = Model::from_temp_dir()?;
let args = package::Input {
message,
target,
uri,
user_meta,
};
log::info!("Packaging {:?} using {:?}", args, temp_dir);
print(package::command(m, args).await);
Ok(())
}
Commands::Pull { domain, namespace } => {
let args = pull::Input {
namespace: namespace.try_into()?,
};
log::info!("Pull {:?}", args);
print(pull::command(Model::from(domain), args).await);
Ok(())
}
Commands::Push { domain, namespace } => {
let args = push::Input {
namespace: namespace.try_into()?,
};
log::info!("Pushing {:?}", args);
print(push::command(Model::from(domain), args).await);
Ok(())
}
Commands::Status { domain, namespace } => {
let args = status::Input {
namespace: namespace.try_into()?,
};
log::info!("Status {:?}", args);
print(status::command(Model::from(domain), args).await);
Ok(())
}
Commands::Benchmark { number, path } => {
let (m, temp_dir) = Model::from_temp_dir()?;
let args = benchmark::Input {
number,
dest: path.unwrap_or(PathBuf::from("manifest.pq")),
};
log::info!(
"Benchmark manifest creation {:?}. Local domain in {:?}",
args,
temp_dir
);
print(benchmark::command(m, args).await);
Ok(())
}
Commands::Uninstall { domain, namespace } => {
if !domain.exists() {
return Err(Error::Domain(domain));
}
let args = uninstall::Input {
namespace: namespace.try_into()?,
};
log::info!("Uninstalling {:?}", args);
print(uninstall::command(Model::from(domain), args).await);
Ok(())
}
}
}
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Domain path doesn't exists: {0}")]
Domain(PathBuf),
#[error("quilt_rs error: {0}")]
Quilt(quilt_rs::Error),
#[error("Failed to create temp dir: {0}")]
TempDir(String),
#[error("Package {0} not found")]
NamespaceNotFound(Namespace),
#[error("Invalid JSON for user_meta object. Object is required")]
CommitMetaInvalid(String),
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
}
impl From<quilt_rs::Error> for Error {
fn from(err: quilt_rs::Error) -> Error {
Error::Quilt(err)
}
}