browser-control 0.2.1

CLI that manages browsers and exposes them over CDP/BiDi for agent-driven development. Includes an optional MCP server.
Documentation
//! Linux browser detection.

use super::{Installed, Kind, Probe};
use std::path::PathBuf;

enum Cand<'a> {
    Which(&'a str),
    Abs(&'a str),
}

fn candidates_for(kind: Kind) -> &'static [Cand<'static>] {
    match kind {
        Kind::Chrome => &[
            Cand::Which("google-chrome"),
            Cand::Which("google-chrome-stable"),
            Cand::Abs("/usr/bin/google-chrome"),
            Cand::Abs("/opt/google/chrome/chrome"),
        ],
        Kind::Edge => &[
            Cand::Which("microsoft-edge"),
            Cand::Which("microsoft-edge-stable"),
            Cand::Abs("/usr/bin/microsoft-edge"),
        ],
        Kind::Chromium => &[
            Cand::Which("chromium"),
            Cand::Which("chromium-browser"),
            Cand::Abs("/usr/bin/chromium"),
            Cand::Abs("/snap/bin/chromium"),
        ],
        Kind::Brave => &[
            Cand::Which("brave-browser"),
            Cand::Which("brave"),
            Cand::Abs("/usr/bin/brave-browser"),
        ],
        Kind::Firefox => &[
            Cand::Which("firefox"),
            Cand::Abs("/usr/bin/firefox"),
            Cand::Abs("/snap/bin/firefox"),
        ],
    }
}

fn find_one<P: Probe>(probe: &P, kind: Kind) -> Option<PathBuf> {
    for c in candidates_for(kind) {
        match c {
            Cand::Which(name) => {
                if let Some(p) = probe.which(name) {
                    return Some(p);
                }
            }
            Cand::Abs(path) => {
                let p = PathBuf::from(path);
                if probe.exists(&p) {
                    return Some(p);
                }
            }
        }
    }
    None
}

pub(super) fn detect<P: Probe>(probe: &P) -> Vec<Installed> {
    let mut out = Vec::new();
    for kind in [
        Kind::Chrome,
        Kind::Edge,
        Kind::Chromium,
        Kind::Brave,
        Kind::Firefox,
    ] {
        if let Some(exe) = find_one(probe, kind) {
            let version = probe
                .run_version(&exe)
                .unwrap_or_else(|| "unknown".to_string());
            out.push(Installed {
                kind,
                executable: exe,
                version,
                engine: kind.engine(),
            });
        }
    }
    out
}