use std::convert::Infallible;
use std::fs::File;
use std::path::PathBuf;
use clap::ValueHint;
use hypersonic::{AuthToken, CallParams, IssueParams, Issuer};
use sonic_persist_fs::LedgerDir;
use crate::dump::dump_ledger;
#[derive(Parser)]
pub enum Cmd {
Issue {
issuer: PathBuf,
params: PathBuf,
output: Option<PathBuf>,
},
State {
dir: PathBuf,
},
Call {
dir: PathBuf,
call: PathBuf,
},
Export {
dir: PathBuf,
#[clap(short, long)]
terminals: Vec<AuthToken>,
output: PathBuf,
},
Accept {
dir: PathBuf,
input: PathBuf,
},
Dump {
#[clap(short, long, global = true)]
force: bool,
#[clap(value_hint = ValueHint::FilePath)]
src: PathBuf,
#[clap(value_hint = ValueHint::FilePath)]
dst: Option<PathBuf>,
},
}
impl Cmd {
pub fn exec(self) -> anyhow::Result<()> {
match self {
Cmd::Issue { issuer, params, output } => issue(issuer, params, output)?,
Cmd::State { dir } => state(dir)?,
Cmd::Call { dir, call: path } => call(dir, path)?,
Cmd::Export { dir, terminals, output } => export(dir, terminals, output)?,
Cmd::Accept { dir, input } => accept(dir, input)?,
Cmd::Dump { force, src, dst } => dump(force, src, dst)?,
}
Ok(())
}
}
fn issue(issuer_file: PathBuf, form: PathBuf, output: Option<PathBuf>) -> anyhow::Result<()> {
let issuer = Issuer::load(issuer_file, |_, _, _| -> Result<_, Infallible> { todo!("signature validation") })?;
let file = File::open(&form)?;
let params = serde_yaml::from_reader::<_, IssueParams>(file)?;
let path = output.unwrap_or(form);
let output = path
.with_file_name(params.name.as_str())
.with_extension("contract");
let articles = issuer.issue(params);
LedgerDir::new(articles, output)?;
Ok(())
}
fn state(path: PathBuf) -> anyhow::Result<()> {
let ledger = LedgerDir::load(path)?;
let val = serde_yaml::to_string(&ledger.state().main)?;
println!("{val}");
Ok(())
}
fn call(dir: PathBuf, form: PathBuf) -> anyhow::Result<()> {
let mut ledger = LedgerDir::load(dir)?;
let file = File::open(form)?;
let call = serde_yaml::from_reader::<_, CallParams>(file)?;
let opid = ledger.call(call)?;
println!("Operation ID: {opid}");
Ok(())
}
fn export(dir: PathBuf, terminals: impl IntoIterator<Item = AuthToken>, output: PathBuf) -> anyhow::Result<()> {
let ledger = LedgerDir::load(dir)?;
ledger.export_to_file(terminals, output)?;
Ok(())
}
fn accept(dir: PathBuf, input: PathBuf) -> anyhow::Result<()> {
let mut ledger = LedgerDir::load(dir)?;
ledger.accept_from_file(input, |_, _, _| Err("signature validation is not implemented yet"))?;
Ok(())
}
fn dump(force: bool, src: PathBuf, dst: Option<PathBuf>) -> anyhow::Result<()> {
match src.extension() {
Some(ext) if ext == "contract" => {
let dst = dst
.as_ref()
.map(|p| p.to_owned())
.unwrap_or_else(|| src.join("dump"));
dump_ledger(&src, dst, force).inspect_err(|_| println!())?;
Ok(())
}
Some(_) => Err(anyhow!("Can't detect the type for '{}': the extension is not recognized", src.display())),
None => Err(anyhow!("The path '{}' can't be recognized as known data", src.display())),
}
}