Skip to main content

wyvern/
browsers_cmd.rs

1//! `wyvern browsers list|refresh` — inspect / rebuild the local browser registry.
2
3use wyvern_host::{
4    browser_registry_path, list_browser_entries, refresh_browser_registry, BrowserRegistryEntry,
5    HostError,
6};
7
8use crate::error::{emit_host_error, EmitError};
9
10/// Run a `browsers` subcommand; returns stdout text on success.
11///
12/// # Errors
13///
14/// Returns structured stderr + exit code on registry failure.
15pub fn run_browsers_command(args: &[String]) -> Result<String, BrowsersError> {
16    let sub = args.first().map(String::as_str).unwrap_or("list");
17    match sub {
18        "list" => list(),
19        "refresh" => refresh(),
20        other => Err(BrowsersError::Usage {
21            message: format!(
22                "unknown browsers subcommand '{other}'\nUsage: wyvern browsers list|refresh"
23            ),
24        }),
25    }
26}
27
28/// CLI browsers subcommand failure.
29#[derive(Debug)]
30pub enum BrowsersError {
31    /// Bad argv.
32    Usage {
33        /// Plain-text usage.
34        message: String,
35    },
36    /// Registry / host failure with stderr JSON.
37    Stage {
38        /// Stderr JSON.
39        stderr: String,
40        /// Process exit code.
41        exit_code: i32,
42    },
43    /// Emit-boundary serialize failure.
44    Emit(EmitError),
45}
46
47fn list() -> Result<String, BrowsersError> {
48    let path = browser_registry_path();
49    let entries = list_browser_entries(&path).map_err(map_host)?;
50    Ok(format_entries(&entries))
51}
52
53fn refresh() -> Result<String, BrowsersError> {
54    let path = browser_registry_path();
55    let file = refresh_browser_registry(&path).map_err(map_host)?;
56    Ok(format!(
57        "Refreshed {} ({} entries)\n{}",
58        path.display(),
59        file.entries.len(),
60        format_entries(&file.entries)
61    ))
62}
63
64fn format_entries(entries: &[BrowserRegistryEntry]) -> String {
65    if entries.is_empty() {
66        return "No browsers found in registry.\nRun: wyvern browsers refresh".into();
67    }
68    let mut out = String::new();
69    for e in entries {
70        out.push_str(&format!(
71            "{:<10}  {:<20}  {}\n",
72            e.id,
73            e.name,
74            e.executable.display()
75        ));
76    }
77    out
78}
79
80fn map_host(err: HostError) -> BrowsersError {
81    match emit_host_error(&err) {
82        Ok(stderr) => {
83            let exit_code = match &err {
84                HostError::Bind { .. } => wyvern_schema::ErrorCode::HostBindError.exit_code(),
85                HostError::ViewerNotFound { .. } | HostError::ViewerUnsupported { .. } => {
86                    wyvern_schema::ErrorCode::HostViewerError.exit_code()
87                }
88                _ => wyvern_schema::ErrorCode::HostError.exit_code(),
89            };
90            BrowsersError::Stage { stderr, exit_code }
91        }
92        Err(e) => BrowsersError::Emit(e),
93    }
94}