Login
4 branches 0 tags
Ben (Desktop/Arch) Added super basic integration tests 8ffc43a 1 month ago 108 Commits
rubhub / src / state.rs
use std::{
    env, fs,
    net::{IpAddr, Ipv4Addr, SocketAddr},
    path::PathBuf,
    sync::Arc,
    time::Instant,
};

use anyhow::Result;

#[derive(Debug, Clone)]
pub struct AppConfig {
    pub git_root: PathBuf,
    pub session_root: PathBuf,
    pub http_bind_addr: SocketAddr,
    pub ssh_bind_addr: SocketAddr,
    pub ssh_public_host: String,
    pub base_url: String,
    pub reuse_port: bool,
}

impl Default for AppConfig {
    fn default() -> Self {
        let dir_root = PathBuf::from("./data/");
        let git_root = dir_root.join("git");
        let session_root = dir_root.join("sessions");

        let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
        let port = 3000;
        let http_bind_addr = SocketAddr::new(ip, port);

        let base_url = format!("http://{http_bind_addr}");

        let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
        let port = 2222;
        let ssh_bind_addr = SocketAddr::new(ip, port);

        let ssh_public_host = if port == 22 {
            format!("{ssh_bind_addr}")
        } else {
            format!("{ip}")
        };

        Self {
            git_root,
            session_root,
            http_bind_addr,
            ssh_bind_addr,
            ssh_public_host,
            base_url,
            reuse_port: false,
        }
    }
}

impl AppConfig {
    pub fn set_dir_root(mut self, dir_root: &str) -> Self {
        let dir_root = PathBuf::from(dir_root);

        let git_root = dir_root.join("git");
        let session_root = dir_root.join("sessions");

        self.git_root = git_root;
        self.session_root = session_root;

        self
    }

    pub fn set_http_bind_addr(mut self, bind_addr: &str) -> Result<Self> {
        let addr = bind_addr.parse::<SocketAddr>()?;

        self.http_bind_addr = addr;
        self.base_url = format!("http://{addr}");

        Ok(self)
    }

    pub fn set_ssh_bind_addr(mut self, bind_addr: &str) -> Result<Self> {
        let addr = bind_addr.parse::<SocketAddr>()?;

        self.ssh_bind_addr = addr;
        self.ssh_public_host = if addr.port() == 22 {
            format!("{addr}")
        } else {
            format!("{}", addr.ip())
        };

        Ok(self)
    }

    pub fn set_base_url(mut self, base_url: &str) -> Self {
        self.base_url = base_url.to_string();
        self
    }

    pub fn set_ssh_public_host(mut self, host: &str) -> Self {
        self.ssh_public_host = host.to_string();
        self
    }

    pub fn set_reuse_port(mut self, reuse_port: bool) -> Self {
        self.reuse_port = reuse_port;
        self
    }

    pub fn load_env(self) -> Result<Self> {
        let config = self;

        let config = match env::var("DIR_ROOT") {
            Ok(dir_root) => config.set_dir_root(&dir_root),
            _ => config,
        };
        let config = match env::var("HTTP_BIND_ADDRESS") {
            Ok(addr) => config.set_http_bind_addr(&addr)?,
            _ => config,
        };
        let config = match env::var("BASE_URL") {
            Ok(uri) => config.set_base_url(&uri),
            _ => config,
        };
        let config = match env::var("SSH_BIND_ADDRESS") {
            Ok(addr) => config.set_ssh_bind_addr(&addr)?,
            _ => config,
        };
        let config = match env::var("SSH_PUBLIC_HOST") {
            Ok(uri) => config.set_ssh_public_host(&uri),
            _ => config,
        };
        let config = match env::var("REUSE_PORT") {
            Ok(b) => config.set_reuse_port(if b.to_lowercase() == "true" {
                true
            } else {
                false
            }),
            _ => config,
        };

        Ok(config)
    }

    pub fn new() -> Result<Self> {
        let config: Self = Self::default();
        Ok(config.load_env()?)
    }

    pub fn build(self, process_start: Instant) -> Result<GlobalState> {
        GlobalState::new(self, process_start)
    }
}

#[derive(Debug, Clone)]
pub struct GlobalState {
    pub config: Arc<AppConfig>,
    pub process_start: Instant,
}

impl GlobalState {
    pub fn uri(&self, path: &str) -> String {
        format!("{}{}", self.config.base_url, path)
    }

    pub fn new(config: AppConfig, process_start: Instant) -> Result<Self> {
        fs::create_dir_all(&config.git_root)?;
        fs::create_dir_all(&config.session_root)?;

        let state = Self {
            process_start,
            config: Arc::new(config),
        };

        Ok(state)
    }
}