use crate::repl::runner::Runner;
use anyhow::{Context, Result};
use clap::{Args, Subcommand};
use crabhub::manifest::Manifest;
use futures_util::StreamExt;
use std::path::{Path, PathBuf};
use wcore::protocol::message::hub_event;
#[derive(Args, Debug)]
pub struct Hub {
#[arg(long)]
pub branch: Option<String>,
#[arg(long)]
pub path: Option<PathBuf>,
#[command(subcommand)]
pub command: HubCommand,
}
#[derive(Subcommand, Debug)]
pub enum HubCommand {
Install(HubInstall),
Uninstall(HubPackage),
Test(HubTest),
}
#[derive(Args, Debug)]
pub struct HubTest {
pub path: PathBuf,
}
#[derive(Args, Debug)]
pub struct HubInstall {
pub package: String,
#[arg(long)]
pub force: bool,
}
#[derive(Args, Debug)]
pub struct HubPackage {
pub package: String,
}
impl Hub {
pub async fn run(self, runner: &mut Runner) -> Result<()> {
let branch = self.branch.as_deref().unwrap_or("");
let path = self
.path
.as_deref()
.map(|p| p.to_string_lossy())
.unwrap_or_default();
match self.command {
HubCommand::Test(t) => return test_manifest(&t.path),
HubCommand::Install(p) => {
let mut stream =
std::pin::pin!(runner.install_package(&p.package, branch, &path, p.force));
while let Some(event) = stream.next().await {
match event? {
hub_event::Event::Step(s) => println!(" {}", s.message),
hub_event::Event::Warning(w) => eprintln!(" warning: {}", w.message),
hub_event::Event::SetupOutput(o) => print!("{}", o.content),
hub_event::Event::Done(d) => {
if !d.error.is_empty() {
anyhow::bail!("{}", d.error);
}
}
}
}
println!("Done: {}", p.package);
}
HubCommand::Uninstall(p) => {
let mut stream = std::pin::pin!(runner.uninstall_package(&p.package));
while let Some(event) = stream.next().await {
match event? {
hub_event::Event::Step(s) => println!(" {}", s.message),
hub_event::Event::Warning(w) => eprintln!(" warning: {}", w.message),
hub_event::Event::SetupOutput(o) => print!("{}", o.content),
hub_event::Event::Done(d) => {
if !d.error.is_empty() {
anyhow::bail!("{}", d.error);
}
}
}
}
println!("Done: {}", p.package);
}
}
Ok(())
}
}
fn test_manifest(path: &Path) -> Result<()> {
let content =
std::fs::read_to_string(path).with_context(|| format!("cannot read {}", path.display()))?;
let manifest: Manifest =
toml::from_str(&content).with_context(|| format!("failed to parse {}", path.display()))?;
println!("ok {}", manifest.package.name);
Ok(())
}