use async_std::{ fs, path::{Path, PathBuf}, }; use data::LicenseDatabase; use tide::{http::mime, log, Response, Result, Server, StatusCode}; use crate::{ config::{RunningConfig, ServerConfig}, data::Queryable, messages::ErrorMsg, }; mod config; mod data; mod messages; #[async_std::main] async fn main() -> Result<()> { log::start(); match ::from_env() { Ok(config) => { // TODO: fix the borrow issue here let run_config = RunningConfig { config: config.clone(), database: LicenseDatabase::load(config.database.clone()).unwrap_or_default(), }; let mut app = tide::with_state(run_config); create_routes(&mut app); 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) { // TODO: figure out how to not need clone let url = server.clone().state().config.base_url.clone(); let mut base_route = server.at(&url); // BASE_URL/ base_route.at(":license").get(get_requested_license); /* BASE_URL/list BASE_URL/full BASE_URL/list/ BASE_URL/full/ */ 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 let mut fallback_routes = base_route.at(""); fallback_routes.get(get_welcome_message); fallback_routes.at("list/").get(get_license_list); fallback_routes .at("list/full/") .get(get_detailed_license_list); fallback_routes.at("list/*").get(get_fallback_response); fallback_routes.at("list/full/*").get(get_fallback_response); } 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 { match &req.state().config.welcome_message { Some(file) => match async_std::fs::read_to_string(file).await { Ok(data) => return Ok(build_response(StatusCode::Ok, data)), Err(_) => { log::error!("Unable to read welcome message file."); return Ok(build_response(StatusCode::Ok, build_welcome_message(req))); } }, None => return Ok(build_response(StatusCode::Ok, build_welcome_message(req))), } } fn build_welcome_message(req: tide::Request) -> String { // TODO: make this better let base_url = req.state().config.base_url.clone(); let header = String::from("Available endpoints:"); let base = format!("{}", base_url); let list = format!("{}list", base_url); let all = format!("{}list/full", base_url); let license = format!("{}", base_url); format!( "{}\n {}\n {}\n {}\n {}", header, base, list, all, license ) } async fn get_fallback_response(req: tide::Request) -> tide::Result { Ok(error_response(req.url().path())) } async fn get_license_list(req: tide::Request) -> tide::Result { // TODO: env var the chunks match req.state().database.get_licenses(5) { Some(licenses) => Ok(build_response(StatusCode::Ok, format!("{}", licenses))), None => Ok(build_response( StatusCode::NotFound, ErrorMsg::NoLicensesAvailable.to_string(), )), } } async fn get_detailed_license_list(req: tide::Request) -> tide::Result { match req.state().database.get_detailed_licenses() { Some(licenses) => Ok(build_response(StatusCode::Ok, format!("{}", licenses))), None => Ok(build_response( StatusCode::NotFound, ErrorMsg::NoLicensesAvailable.to_string(), )), } } async fn get_requested_license(req: tide::Request) -> tide::Result { // remove prefix from requested route // TODO: make this better let request = req.url().path().strip_prefix("/").unwrap_or(""); // TODO: break this up let response = match req.state().database.get_license(request) { Some(license) => { // is there a better way to do this? let source_dir = PathBuf::from(req.state().config.database.clone()); let source_dir = source_dir.parent().unwrap_or(Path::new("")); let mut license_file_path = PathBuf::from(source_dir); license_file_path.push(license.filename); match fs::read_to_string(&license_file_path).await { Ok(contents) => { build_response(StatusCode::Accepted, contents.trim_end().to_string()) } Err(_) => { log::error!( "{} '{}'.", ErrorMsg::LicenseReadError, license_file_path.to_string_lossy().to_string() ); error_response(request) } } } None => { log::error!("{} '{}'", ErrorMsg::InvalidLicense, request); error_response(request) } }; Ok(response) } fn error_response(_req: &str) -> Response { build_response( StatusCode::NotFound, format!("{}", ErrorMsg::InvalidLicenseRequest), ) }