use std::path::PathBuf;
use crate::host::listener::AppState;
use crate::shared::error::Error;
#[derive(Debug, Clone)]
pub struct HostArgs {
pub listen: String,
pub profile: ProfileChoice,
pub display: DisplayMode,
pub takeover: Takeover,
pub display_quality: u8,
pub browser: BrowserChoice,
pub browser_bin: Option<PathBuf>,
pub token: Option<String>,
pub takeover_enabled: bool,
pub health_enabled: bool,
pub health_public: HealthPublic,
pub engine_envs: Vec<(String, String)>,
pub browser_args: Vec<String>,
pub proxy: Option<String>,
pub recent_requests_cap: usize,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ProfileChoice {
Ephemeral,
Persistent(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DisplayMode {
Headless,
Headful,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Takeover {
Off,
On { provider: TakeoverProviderKind },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TakeoverProviderKind {
KasmVnc,
}
impl TakeoverProviderKind {
pub fn as_str(self) -> &'static str {
match self {
Self::KasmVnc => "kasmvnc",
}
}
}
impl std::str::FromStr for TakeoverProviderKind {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"kasmvnc" => Self::KasmVnc,
other => return Err(format!("unknown {other:?}; expected kasmvnc")),
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum BrowserChoice {
#[default]
Auto,
Chromium,
Chrome,
FingerprintChromium,
Edge,
Brave,
Lightpanda,
Camoufox,
}
impl std::str::FromStr for BrowserChoice {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"auto" => Self::Auto,
"chromium" => Self::Chromium,
"chrome" => Self::Chrome,
"fingerprint_chromium" | "fingerprint-chromium" => Self::FingerprintChromium,
"edge" => Self::Edge,
"brave" => Self::Brave,
"lightpanda" => Self::Lightpanda,
"camoufox" => Self::Camoufox,
other => return Err(format!("unknown {other:?}")),
})
}
}
impl BrowserChoice {
#[must_use]
pub fn profile_backend_key(&self) -> &'static str {
match self {
Self::Auto | Self::Chromium => "chromium",
Self::Chrome => "chrome",
Self::FingerprintChromium => "fingerprint-chromium",
Self::Edge => "edge",
Self::Brave => "brave",
Self::Lightpanda => "lightpanda",
Self::Camoufox => "camoufox",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HealthPublic {
Off,
Minimal,
}
pub fn install_rustls_provider() {
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
}
pub async fn run(args: HostArgs) -> Result<(), Error> {
install_rustls_provider();
let state = AppState::launch(&args).await?;
state.serve(&args.listen).await
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn browser_choice_default_is_auto() {
assert_eq!(BrowserChoice::default(), BrowserChoice::Auto);
}
#[test]
fn browser_choice_parses_every_variant_and_aliases() {
assert_eq!(
"auto".parse::<BrowserChoice>().unwrap(),
BrowserChoice::Auto
);
assert_eq!(
"chromium".parse::<BrowserChoice>().unwrap(),
BrowserChoice::Chromium
);
assert_eq!(
"chrome".parse::<BrowserChoice>().unwrap(),
BrowserChoice::Chrome
);
assert!("chrome-headless-shell".parse::<BrowserChoice>().is_err());
assert!("chrome_shell".parse::<BrowserChoice>().is_err());
assert_eq!(
"fingerprint_chromium".parse::<BrowserChoice>().unwrap(),
BrowserChoice::FingerprintChromium
);
assert_eq!(
"fingerprint-chromium".parse::<BrowserChoice>().unwrap(),
BrowserChoice::FingerprintChromium
);
assert_eq!(
"edge".parse::<BrowserChoice>().unwrap(),
BrowserChoice::Edge
);
assert_eq!(
"brave".parse::<BrowserChoice>().unwrap(),
BrowserChoice::Brave
);
assert_eq!(
"lightpanda".parse::<BrowserChoice>().unwrap(),
BrowserChoice::Lightpanda
);
assert_eq!(
"camoufox".parse::<BrowserChoice>().unwrap(),
BrowserChoice::Camoufox
);
}
#[test]
fn browser_choice_rejects_unknown() {
let err = "netscape".parse::<BrowserChoice>().unwrap_err();
assert!(err.contains("netscape"), "error was {err:?}");
}
}