index : license-api-rs.git

ascending towards madness

use data::LicenseDatabase;
use jealousy::FromEnv;
use serde::Deserialize;
use std::{net::Ipv4Addr, path::PathBuf, str::FromStr};
use tide::{http::mime, log, Response, Result, Server, StatusCode};

mod data;

#[macro_use]
extern crate lazy_static;

#[derive(Debug, Deserialize, FromEnv)]
#[from_env(prefix = "LICENSE_API")]
struct Config {
    #[serde(default = "Config::default_base_url")]
    base_url: String,
    database: String,
    #[serde(default = "Config::default_server_ip")]
    server_ip: Ipv4Addr, // TODO: IPv6 support
    #[serde(default = "Config::default_server_port")]
    server_port: u16,
}

impl Config {
    fn default_base_url() -> String {
        String::from("/")
    }

    fn default_server_ip() -> Ipv4Addr {
        Ipv4Addr::new(0, 0, 0, 0)
    }

    fn default_server_port() -> u16 {
        8080
    }
}

lazy_static! {
    #[derive (Debug)]
    static ref DATABASE: LicenseDatabase = {
        // TODO: error handling and use log::info when done
        // not sure if i should validate the unwrap since main won't start if there is an error...
        let config = Config::from_env().unwrap();
        let db_path: PathBuf = PathBuf::from_str(&config.database).unwrap();
        let database: LicenseDatabase = LicenseDatabase::load_file(db_path.as_path());

        database
    };
}

#[async_std::main]
async fn main() -> Result<()> {
    log::start();

    let config = Config::from_env();
    match config {
        Ok(config) => {
            let mut app = tide::new();

            create_routes(&mut app, &config);

            app.listen(format!("{}:{}", config.server_ip, config.server_port))
                .await?;
        }
        Err(_) => (), // jealousy prints its own error message so this is safe to ignore...i think
    }

    Ok(())
}

fn create_routes(server: &mut Server<()>, config: &Config) {
    let mut base_route = server.at(&config.base_url);
    base_route.get(get_welcome_message);

    base_route.at(":license").get(get_requested_license);
    base_route.at("list").get(get_license_list);
    base_route.at("list/full").get(get_detailed_license_list);

    // nyaaaaa whyyyy https://github.com/http-rs/tide/issues/205
    base_route.at("").get(get_welcome_message);
    base_route.at("list/").get(get_license_list);
    base_route.at("list/full/").get(get_detailed_license_list);
}

fn build_response(status: StatusCode, data: String) -> Response {
    // this is a plain text only service
    let response = Response::builder(status)
        .body(format!("{}\n", data))
        .content_type(mime::PLAIN)
        .build();

    response
}

async fn get_welcome_message(req: tide::Request<()>) -> tide::Result<Response> {
    log::info!("{:?}", req);

    Ok(build_response(
        StatusCode::Ok,
        format!("{}", "Welcome message goes here").to_string(),
    ))
}

async fn get_license_list(req: tide::Request<()>) -> tide::Result<Response> {
    log::info!("{:?}", req);

    Ok(build_response(
        StatusCode::Ok,
        format!("{}", "License list goes here").to_string(),
    ))
}

async fn get_detailed_license_list(req: tide::Request<()>) -> tide::Result<Response> {
    log::info!("{:?}", req);

    Ok(build_response(
        StatusCode::Ok,
        format!("{}", "License list with full names goes here").to_string(),
    ))
}

async fn get_requested_license(req: tide::Request<()>) -> tide::Result<Response> {
    log::info!("{:?}", req);

    Ok(build_response(
        StatusCode::Ok,
        format!("{}", "Content of requested license goes here").to_string(),
    ))
}