discord-proxy 0.1.0

Windows-first Discord process-local proxy launcher
Documentation
use anyhow::Result;
use std::{fmt, path::Path, str::FromStr};

#[cfg(windows)]
mod windows;

#[cfg(windows)]
pub use windows::{
    DiscordInstall, InstallCandidateReport, LaunchPlan, discover_install, inspect_candidates,
    is_process_running,
};

#[cfg(not(windows))]
mod unsupported {
    use super::DiscordChannel;
    use anyhow::{Result, bail};
    use std::path::Path;
    use tokio::process::Child;

    #[derive(Debug)]
    pub struct DiscordInstall;

    #[derive(Debug)]
    pub struct InstallCandidateReport;

    pub struct LaunchPlan;

    impl DiscordInstall {
        pub fn source(&self) -> &str {
            ""
        }

        pub fn channel(&self) -> DiscordChannel {
            DiscordChannel::Stable
        }

        pub fn root(&self) -> &Path {
            Path::new("")
        }

        pub fn app_dir(&self) -> &Path {
            Path::new("")
        }

        pub fn update_exe(&self) -> Option<&Path> {
            None
        }

        pub fn exe_path(&self) -> &Path {
            Path::new("")
        }

        pub fn exe_name(&self) -> &str {
            ""
        }
    }

    impl LaunchPlan {
        pub fn new(_install: DiscordInstall, _proxy_url: String) -> Self {
            Self
        }

        pub fn describe(&self) -> String {
            "this MVP currently supports Windows launch plans only".to_string()
        }

        pub fn spawn(&self) -> Result<Child> {
            bail!("this MVP currently supports Windows launch plans only")
        }
    }

    pub fn discover_install(
        _channel: DiscordChannel,
        _override_dir: Option<&Path>,
    ) -> Result<DiscordInstall> {
        bail!("this MVP currently supports Windows Discord discovery only")
    }

    pub fn inspect_candidates(
        _channel: DiscordChannel,
        _override_dir: Option<&Path>,
    ) -> Vec<InstallCandidateReport> {
        Vec::new()
    }

    pub fn is_process_running(_exe_name: &str) -> bool {
        false
    }
}

#[cfg(not(windows))]
pub use unsupported::{
    DiscordInstall, InstallCandidateReport, LaunchPlan, discover_install, inspect_candidates,
    is_process_running,
};

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum DiscordChannel {
    Stable,
    Canary,
    Ptb,
    Development,
}

impl DiscordChannel {
    pub fn valid_values() -> &'static [&'static str] {
        &["stable", "canary", "ptb", "development"]
    }

    pub fn install_dir_name(self) -> &'static str {
        match self {
            Self::Stable => "Discord",
            Self::Canary => "DiscordCanary",
            Self::Ptb => "DiscordPTB",
            Self::Development => "DiscordDevelopment",
        }
    }

    pub fn exe_candidates(self) -> &'static [&'static str] {
        match self {
            Self::Stable => &["Discord.exe"],
            Self::Canary => &["DiscordCanary.exe"],
            Self::Ptb => &["DiscordPTB.exe"],
            Self::Development => &["DiscordDevelopment.exe"],
        }
    }
}

impl FromStr for DiscordChannel {
    type Err = anyhow::Error;

    fn from_str(value: &str) -> Result<Self> {
        match value.trim().to_ascii_lowercase().as_str() {
            "stable" | "discord" => Ok(Self::Stable),
            "canary" | "discordcanary" => Ok(Self::Canary),
            "ptb" | "discordptb" => Ok(Self::Ptb),
            "development" | "dev" | "discorddevelopment" => Ok(Self::Development),
            other => anyhow::bail!("unsupported Discord channel: {other}"),
        }
    }
}

impl fmt::Display for DiscordChannel {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        let value = match self {
            Self::Stable => "stable",
            Self::Canary => "canary",
            Self::Ptb => "ptb",
            Self::Development => "development",
        };
        formatter.write_str(value)
    }
}

#[allow(dead_code)]
pub fn discover(_channel: DiscordChannel, _override_dir: Option<&Path>) -> Result<DiscordInstall> {
    discover_install(_channel, _override_dir)
}