lenso-cli 0.1.28

Lenso command-line interface for scaffolding and operating Lenso backend projects.
use std::path::{Path, PathBuf};
use std::process::Command;

use anyhow::{Context, Result, bail};

#[derive(Debug, Clone)]
pub struct ConsoleDevOptions {
    pub cwd: Option<PathBuf>,
    pub host: Option<String>,
    pub open: bool,
    pub package: Option<PathBuf>,
    pub port: u16,
    pub runtime_console_root: Option<PathBuf>,
}

pub fn run_console_dev(options: ConsoleDevOptions) -> Result<()> {
    let runtime_console_root =
        resolve_runtime_console_root(options.runtime_console_root.as_deref())?;
    let script = runtime_console_root.join("scripts/console-package-dev.mjs");
    if !script.exists() {
        bail!("Runtime Console dev runner not found: {}", script.display());
    }

    let mut command = Command::new("node");
    command.arg(script);
    if let Some(cwd) = options.cwd {
        command.arg("--cwd").arg(cwd);
    }
    if let Some(host) = options.host {
        command.arg("--host").arg(host);
    }
    if let Some(package) = options.package {
        command.arg("--package").arg(package);
    }
    command.arg("--port").arg(options.port.to_string());
    if options.open {
        command.env("LENSO_CONSOLE_DEV_OPEN", "1");
    }

    let status = command.status().with_context(|| {
        format!(
            "run Runtime Console dev runner in {}",
            runtime_console_root.display()
        )
    })?;
    if !status.success() {
        bail!("Runtime Console dev runner exited with {status}");
    }
    Ok(())
}

fn resolve_runtime_console_root(explicit: Option<&Path>) -> Result<PathBuf> {
    if let Some(root) = explicit {
        return Ok(root.to_path_buf());
    }
    if let Ok(root) = std::env::var("LENSO_RUNTIME_CONSOLE_ROOT") {
        return Ok(PathBuf::from(root));
    }

    let cwd = std::env::current_dir().context("read current directory")?;
    if let Some(root) = find_runtime_console_root_from(&cwd) {
        return Ok(root);
    }

    let sibling = cwd
        .join("../lenso-runtime-console")
        .canonicalize()
        .context(
            "resolve sibling lenso-runtime-console; set LENSO_RUNTIME_CONSOLE_ROOT if it is elsewhere",
        )?;
    Ok(sibling)
}

fn find_runtime_console_root_from(start: &Path) -> Option<PathBuf> {
    start
        .ancestors()
        .find(|candidate| is_runtime_console_root(candidate))
        .map(Path::to_path_buf)
}

fn is_runtime_console_root(candidate: &Path) -> bool {
    candidate
        .join("scripts")
        .join("console-package-dev.mjs")
        .is_file()
}