use anyhow::{anyhow, Result};
use console::style;
use crate::registry::{Project, Registry};
use crate::runner;
use crate::state::State;
use crate::template::{Template, TemplateManifest};
pub fn run(name: String, dev: bool) -> Result<()> {
let reg = Registry::load()?;
let project = reg
.find(&name)
.ok_or_else(|| anyhow!("Project not found: {}. Run `devist projects list`.", name))?
.clone();
if !project.path.exists() {
return Err(anyhow!(
"Project directory missing: {}. Run `devist projects forget {}` to clean up.",
project.path.display(),
name
));
}
println!("{}", style("devist start").bold());
println!();
println!(
" {} {} {}",
style("[PROJ]").cyan(),
style(&project.name).bold(),
style(format!("at {}", project.path.display())).dim()
);
let mut state = State::load()?;
if let Some(active) = state.active_project.clone() {
if active != project.name {
stop_active_backend(®, &active)?;
state.set_active(None);
state.save()?;
}
}
let manifest = load_manifest_for(&project)?;
if let Some(cmd) = &manifest.commands.backend_start {
println!(
" {} {}",
style("[UP]").cyan(),
style(format!("backend: {}", cmd)).dim()
);
runner::run_in(&project.path, cmd)?;
state.set_active(Some(project.name.clone()));
state.save()?;
} else {
println!(" {} no backend command defined", style("[SKIP]").dim());
}
if dev {
if let Some(cmd) = &manifest.commands.dev {
println!(
" {} {}",
style("[DEV]").cyan(),
style(format!("dev: {}", cmd)).dim()
);
println!();
runner::run_in(&project.path, cmd)?;
} else {
println!(" {} no dev command defined", style("[SKIP]").dim());
}
} else {
println!();
println!("{}", style("Started.").green());
if manifest.commands.dev.is_some() {
println!(
" Run dev server: {}",
style(format!(
"cd {} && {}",
project.path.display(),
manifest.commands.dev.as_deref().unwrap_or("")
))
.cyan()
);
}
}
Ok(())
}
fn stop_active_backend(reg: &Registry, active_name: &str) -> Result<()> {
let other = match reg.find(active_name) {
Some(p) => p,
None => {
println!(
" {} previously active project gone: {}",
style("[WARN]").yellow(),
active_name
);
return Ok(());
}
};
if !other.path.exists() {
println!(
" {} previously active project missing on disk: {}",
style("[WARN]").yellow(),
active_name
);
return Ok(());
}
let manifest = match load_manifest_for(other) {
Ok(m) => m,
Err(e) => {
println!(
" {} cannot read template for {}: {}",
style("[WARN]").yellow(),
active_name,
e
);
return Ok(());
}
};
if let Some(cmd) = &manifest.commands.backend_stop {
println!(
" {} stopping {} ({})",
style("[DOWN]").yellow(),
style(active_name).bold(),
style(cmd).dim()
);
if let Err(e) = runner::run_in_quiet(&other.path, cmd) {
println!(
" {} stop command failed (continuing): {}",
style("[WARN]").yellow(),
e
);
}
}
Ok(())
}
fn load_manifest_for(project: &Project) -> Result<TemplateManifest> {
let templates = Template::list_all()?;
let tpl = templates
.into_iter()
.find(|t| t.manifest.meta.name == project.template)
.ok_or_else(|| {
anyhow!(
"Template `{}` not installed. Add it with `devist template add ...`",
project.template
)
})?;
Ok(tpl.manifest)
}