index : license-api-rs.git

ascending towards madness

author holly sparkles <sparkles@holly.sh> 2023-10-04 8:50:17.0 +00:00:00
committer holly sparkles <sparkles@holly.sh> 2023-10-04 8:50:17.0 +00:00:00
commit
4156bc42fae04732ff39adc9baff45e98fe32f8b [patch]
tree
10d28e798c900a3c2c0c1d9c0a1a4cc4e3855239
parent
1e43f7cfe8eca1f10cea7e68e112864940f533bb
download
4156bc42fae04732ff39adc9baff45e98fe32f8b.tar.gz

refactor!: remove lazy_static dep, use rustfmt on data module



Diff

 Cargo.toml  |   2 +-
 src/data.rs | 121 ++++++++++++++++++++++++++++++-------------------------------
 src/main.rs |  71 +++++++++++++++++-------------------
 3 files changed, 94 insertions(+), 100 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index cb79530..5cdf648 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,9 +7,7 @@ edition = "2021"

[dependencies]
async-std = { version = "1.12.0", features = ["attributes"] }
envy = "0.4.2"
jealousy = { version = "0.1.5", features = ["derive"] }
lazy_static = "1.4.0"
serde = { version = "1.0.188", features = ["derive"] }
serde_yaml = "0.9.25"
tide = "0.16.0"
diff --git a/src/data.rs b/src/data.rs
index 345d07d..2447ca3 100644
--- a/src/data.rs
+++ b/src/data.rs
@@ -1,82 +1,81 @@
use std::path::Path;

use serde::Deserialize;
use std::str::FromStr;
use tide::log;

#[derive(Debug, Deserialize)]
#[derive(Debug, Deserialize, Clone)]
pub struct LicenseDatabase {
    pub licenses: Vec<License>,
}

pub trait Queryable {
	fn get_licenses(self: &Self, chunk_size: usize) -> Option<String>;
	fn get_detailed_licenses(self: &Self) -> Option<String>;
	fn get_license(self: &Self, id: &str) -> Option<License>;
    fn get_licenses(self: &Self, chunk_size: usize) -> Option<String>;
    fn get_detailed_licenses(self: &Self) -> Option<String>;
    fn get_license(self: &Self, id: &str) -> Option<License>;
}

impl Queryable for LicenseDatabase {
	fn get_licenses(self: &Self, chunk_size: usize) -> Option<String> {
		let result = self
			.licenses
			.iter()
			.map(|license| license.id.clone())
			.collect::<Vec<String>>()
			.chunks(chunk_size)
			.map(|chunk| chunk.join(", "))
			.collect::<Vec<String>>()
			.join("\n");
		
		if result.is_empty() {
			log::warn!("License database not found!");
			None
		}
		else {
			Some(result)
		}
	}
	
	fn get_detailed_licenses(self: &Self) -> Option<String> {
		const PADDING: usize = 20;
    fn get_licenses(self: &Self, chunk_size: usize) -> Option<String> {
        let result = self
            .licenses
            .iter()
            .map(|license| license.id.clone())
            .collect::<Vec<String>>()
            .chunks(chunk_size)
            .map(|chunk| chunk.join(", "))
            .collect::<Vec<String>>()
            .join("\n");

		let result: String = self
			.licenses
			.iter()
			.map(|license| {
				format!(
					"{:padding$}{}",
					license.id,
					license.description,
					padding = PADDING
				)
			})
			.collect::<Vec<String>>()
			.join("\n");
        if result.is_empty() {
            log::warn!("License database not found!");
            None
        } else {
            Some(result)
        }
    }

		if result.is_empty() {
			log::warn!("License database not found!");
			None
		}
		else {
			Some(result)
		}
	}
    fn get_detailed_licenses(self: &Self) -> Option<String> {
        const PADDING: usize = 20;

	fn get_license(self: &Self, id: &str) -> Option<License> {
		let query: Option<License> = self
			.licenses
			.iter()
			.map(|license| license.clone())
			.find(|license| license.id.eq(id));
	
		query
	}
        let result: String = self
            .licenses
            .iter()
            .map(|license| {
                format!(
                    "{:padding$}{}",
                    license.id,
                    license.description,
                    padding = PADDING
                )
            })
            .collect::<Vec<String>>()
            .join("\n");

        if result.is_empty() {
            log::warn!("License database not found!");
            None
        } else {
            Some(result)
        }
    }

    fn get_license(self: &Self, id: &str) -> Option<License> {
        let query: Option<License> = self
            .licenses
            .iter()
            .map(|license| license.clone())
            .find(|license| license.id.eq(id));

        query
    }
}

impl LicenseDatabase {
    pub fn load_file(file: &Path) -> LicenseDatabase {
        let file_contents = std::fs::read_to_string(file).unwrap();
    pub fn load(file: String) -> 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 db_path: std::path::PathBuf = std::path::PathBuf::from_str(file.as_str()).unwrap();
        let file_contents = std::fs::read_to_string(db_path.as_path()).unwrap();
        let database: LicenseDatabase = serde_yaml::from_str(&file_contents).unwrap();

        database
    }
}
diff --git a/src/main.rs b/src/main.rs
index 6fd0f5e..fa6b0c4 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -5,29 +5,26 @@ use async_std::{
use data::LicenseDatabase;
use jealousy::FromEnv;
use serde::Deserialize;
use std::{net::Ipv4Addr, str::FromStr};
use std::net::Ipv4Addr;
use tide::{http::mime, log, Response, Result, Server, StatusCode};

use crate::data::{License, Queryable};
use crate::data::Queryable;

mod data;

#[macro_use]
extern crate lazy_static;

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

impl Config {
impl ServerConfig {
    fn default_base_url() -> String {
        String::from("/")
    }
@@ -41,16 +38,10 @@ impl Config {
    }
}

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: std::path::PathBuf = std::path::PathBuf::from_str(&config.database).unwrap();
        let database: LicenseDatabase = LicenseDatabase::load_file(db_path.as_path());
        database
    };
#[derive(Debug, Clone)]
struct RunningConfig {
    config: ServerConfig,
    database: LicenseDatabase,
}

const ERROR_LICENSE_NOT_FOUND: &str =
@@ -60,11 +51,17 @@ const ERROR_LICENSE_NOT_FOUND: &str =
async fn main() -> Result<()> {
    log::start();

    match Config::from_env() {
    match ServerConfig::from_env() {
        Ok(config) => {
            let mut app = tide::new();
            // TODO: fix the borrow issue here
            let run_config = RunningConfig {
                config: config.clone(),
                database: LicenseDatabase::load(config.database.clone()),
            };

            create_routes(&mut app, &config);
            let mut app = tide::with_state(run_config);

            create_routes(&mut app);

            app.listen(format!("{}:{}", config.server_ip, config.server_port))
                .await?;
@@ -75,8 +72,10 @@ async fn main() -> Result<()> {
    Ok(())
}

fn create_routes(server: &mut Server<()>, config: &Config) {
    let mut base_route = server.at(&config.base_url);
fn create_routes(server: &mut Server<RunningConfig>) {
    // 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_route.get(get_welcome_message);

    base_route.at(":license").get(get_requested_license);
@@ -99,7 +98,7 @@ fn build_response(status: StatusCode, data: String) -> Response {
    response
}

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

    Ok(build_response(
@@ -108,11 +107,11 @@ async fn get_welcome_message(req: tide::Request<()>) -> tide::Result<Response> {
    ))
}

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

    // TODO: env var this
    match DATABASE.get_licenses(5) {
    // 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,
@@ -121,10 +120,10 @@ async fn get_license_list(req: tide::Request<()>) -> tide::Result<Response> {
    }
}

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

    match DATABASE.get_detailed_licenses() {
    match req.state().database.get_detailed_licenses() {
        Some(licenses) => Ok(build_response(StatusCode::Ok, format!("{}", licenses))),
        None => Ok(build_response(
            StatusCode::NotFound,
@@ -133,19 +132,17 @@ async fn get_detailed_license_list(req: tide::Request<()>) -> tide::Result<Respo
    }
}

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

    let config = Config::from_env().unwrap();

    // remove prefix from requested route
    // TODO: make this better
    let request = req.url().path().strip_prefix("/").unwrap_or("");

    let response = match DATABASE.get_license(request) {
    let response = match req.state().database.get_license(request) {
        Some(license) => {
            // is there a better way to do this?
            let source_dir = PathBuf::from(&config.database);
            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);