From 4b6608fd4f1da0c4efe1128b1ad8be4a3384721f Mon Sep 17 00:00:00 2001 From: Martin Berg Alstad Date: Sat, 22 Jun 2024 17:33:33 +0200 Subject: [PATCH] Using library and removed common functions (#3) * Added lib from git and removed code which is in lib now * NormalizePathLayer to remove trailing slash --- Cargo.lock | 34 ++++------ Cargo.toml | 8 +-- Dockerfile | 1 - derive/Cargo.lock | 46 ------------- derive/Cargo.toml | 11 ---- derive/src/lib.rs | 25 -------- src/config.rs | 5 ++ src/expressions/truth_table.rs | 7 +- src/main.rs | 3 + src/parsing/expression_parser.rs | 3 +- src/parsing/mod.rs | 3 +- src/parsing/utils.rs | 107 ------------------------------- src/routing/error.rs | 2 - src/routing/response.rs | 30 +-------- src/routing/routes/index.rs | 3 +- src/routing/routes/simplify.rs | 2 +- src/routing/routes/table.rs | 2 +- src/utils/array.rs | 74 --------------------- src/utils/axum.rs | 100 ++--------------------------- src/utils/mod.rs | 1 - 20 files changed, 41 insertions(+), 426 deletions(-) delete mode 100644 derive/Cargo.lock delete mode 100644 derive/Cargo.toml delete mode 100644 derive/src/lib.rs delete mode 100644 src/parsing/utils.rs delete mode 100644 src/utils/array.rs diff --git a/Cargo.lock b/Cargo.lock index 5107e36..6e54fae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -138,6 +138,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "derive" version = "1.0.0" +source = "git+https://github.com/emberal/rust-lib.git?tag=1.0.0#d5974dda2071355115090e26175b657ee82b719b" dependencies = [ "quote", "syn", @@ -173,12 +174,6 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" -[[package]] -name = "futures-sink" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" - [[package]] name = "futures-task" version = "0.3.30" @@ -307,6 +302,17 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lib" +version = "1.0.0" +source = "git+https://github.com/emberal/rust-lib.git?tag=1.0.0#d5974dda2071355115090e26175b657ee82b719b" +dependencies = [ + "axum", + "derive", + "nom", + "serde", +] + [[package]] name = "libc" version = "0.2.155" @@ -555,11 +561,10 @@ name = "simplify_truths" version = "2.0.0" dependencies = [ "axum", - "derive", + "lib", "nom", "serde", "tokio", - "tokio-util", "tower-http", "tracing", "tracing-subscriber", @@ -641,19 +646,6 @@ dependencies = [ "syn", ] -[[package]] -name = "tokio-util" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - [[package]] name = "tower" version = "0.4.13" diff --git a/Cargo.toml b/Cargo.toml index 93f4eda..7eb3caf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,16 +8,14 @@ authors = ["Martin Berg Alstad"] # Parsing nom = "7.1.3" # Async -tokio = { version = "1.38.0", features = ["macros", "rt-multi-thread", "fs"] } -tokio-util = { version = "0.7.11", features = ["io"] } +tokio = { version = "1.38.0", features = ["macros", "rt-multi-thread"] } # Serialization / Deserialization serde = { version = "1.0.203", features = ["derive", "rc"] } # API axum = { version = "0.7.5", features = ["macros"] } -tower-http = { version = "0.5.2", features = ["cors", "trace"] } +tower-http = { version = "0.5.2", features = ["cors", "trace", "normalize-path"] } # Logging tracing = "0.1.40" tracing-subscriber = "0.3.18" -# Derive macros -derive = { path = "derive" } +lib = { git = "https://github.com/emberal/rust-lib.git", tag = "1.0.0", features = ["axum", "vec", "nom", "serde", "derive"] } diff --git a/Dockerfile b/Dockerfile index 75de611..a4b297c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,7 +21,6 @@ WORKDIR /simplify_truths COPY ./Cargo.lock ./Cargo.lock COPY ./Cargo.toml ./Cargo.toml -COPY ./derive ./derive # this build step will cache your dependencies RUN cargo build --release diff --git a/derive/Cargo.lock b/derive/Cargo.lock deleted file mode 100644 index 25477dc..0000000 --- a/derive/Cargo.lock +++ /dev/null @@ -1,46 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "derive" -version = "1.0.0" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "proc-macro2" -version = "1.0.85" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "syn" -version = "2.0.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/derive/Cargo.toml b/derive/Cargo.toml deleted file mode 100644 index f7aed7f..0000000 --- a/derive/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "derive" -version = "1.0.0" -edition = "2021" - -[lib] -proc-macro = true - -[dependencies] -syn = "2.0.66" -quote = "1.0.36" diff --git a/derive/src/lib.rs b/derive/src/lib.rs deleted file mode 100644 index 3dbc4b4..0000000 --- a/derive/src/lib.rs +++ /dev/null @@ -1,25 +0,0 @@ -extern crate proc_macro; - -use proc_macro::TokenStream; -use quote::quote; -use syn::{DeriveInput, parse_macro_input}; - -#[proc_macro_derive(IntoResponse)] -pub fn into_response_derive(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - into_response_derive_impl(input) -} - -fn into_response_derive_impl(input: DeriveInput) -> TokenStream { - let name = &input.ident; - - let expanded = quote! { - impl IntoResponse for #name { - fn into_response(self) -> Response { - BaseResponse::create(self) - } - } - }; - - TokenStream::from(expanded) -} diff --git a/src/config.rs b/src/config.rs index 2354ecf..b6620fd 100644 --- a/src/config.rs +++ b/src/config.rs @@ -11,3 +11,8 @@ pub const RESOURCE_DIR: &str = if IS_DEV { } else { "./static" }; +pub const OPENAPI_PATH: &str = if IS_DEV { + "/openapi" +} else { + "/simplify-truths/v2/openapi" +}; diff --git a/src/expressions/truth_table.rs b/src/expressions/truth_table.rs index dc40b87..b50660d 100644 --- a/src/expressions/truth_table.rs +++ b/src/expressions/truth_table.rs @@ -1,12 +1,12 @@ use std::cmp::Ordering; use std::collections::HashMap; +use lib::map; +use lib::vector::distinct::Distinct; use serde::{Deserialize, Serialize}; use crate::expressions::expression::Expression; -use crate::map; use crate::routing::options::TruthTableOptions; -use crate::utils::array::Distinct; type TruthMatrix = Vec>; @@ -197,8 +197,9 @@ impl TruthTable { #[cfg(test)] mod tests { + use lib::matrix; + use crate::expressions::helpers::{and, atomic, implies, not, or}; - use crate::matrix; use super::*; diff --git a/src/main.rs b/src/main.rs index 5d7ff56..54cb131 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,9 @@ use std::net::SocketAddr; +use lib::{create_app, join_routes}; use tokio::net::TcpListener; use tower_http::cors::{Any, CorsLayer}; +use tower_http::normalize_path::NormalizePathLayer; use tower_http::trace; use tower_http::trace::TraceLayer; use tracing::Level; @@ -34,6 +36,7 @@ async fn main() { let app = create_app!(routes, CorsLayer::new().allow_origin(Any), + NormalizePathLayer::trim_trailing_slash(), TraceLayer::new_for_http() .make_span_with(trace::DefaultMakeSpan::new().level(Level::INFO)) .on_response(trace::DefaultOnResponse::new().level(Level::INFO)) diff --git a/src/parsing/expression_parser.rs b/src/parsing/expression_parser.rs index 5e67745..95eb8fa 100644 --- a/src/parsing/expression_parser.rs +++ b/src/parsing/expression_parser.rs @@ -1,3 +1,5 @@ +use lib::nom::combinators::{exhausted, parenthesized, trim}; +use lib::nom::util::IntoResult; use nom::branch::alt; use nom::bytes::complete::{tag, take_while, take_while1}; use nom::character::complete::char; @@ -8,7 +10,6 @@ use nom::sequence::{pair, preceded}; use crate::expressions::expression::Expression; use crate::expressions::helpers::{and, atomic, implies, not, or}; -use crate::parsing::utils::{exhausted, IntoResult, parenthesized, trim}; pub fn parse_expression(input: &str) -> Result>> { exhausted(_parse_expression)(input).into_result() diff --git a/src/parsing/mod.rs b/src/parsing/mod.rs index c3e1d28..74eee6a 100644 --- a/src/parsing/mod.rs +++ b/src/parsing/mod.rs @@ -1,2 +1 @@ -pub(crate) mod expression_parser; -mod utils; \ No newline at end of file +pub(crate) mod expression_parser; \ No newline at end of file diff --git a/src/parsing/utils.rs b/src/parsing/utils.rs deleted file mode 100644 index 820f92a..0000000 --- a/src/parsing/utils.rs +++ /dev/null @@ -1,107 +0,0 @@ -use std::ops::RangeFrom; - -use nom::{InputIter, InputLength, InputTake, IResult, Slice}; -use nom::bytes::complete::take_while_m_n; -use nom::character::complete::{char, multispace0}; -use nom::combinator::eof; -use nom::error::{Error, ParseError}; -use nom::sequence::{delimited, terminated}; - -/// Trim leading and trailing whitespace from the input Parser -/// - Parameters -/// - `inner`: The parser to trim -/// - Returns: A parser that trims leading and trailing whitespace from the input and then runs the value from the inner parser -pub fn trim<'a, Parser, R>(inner: Parser) -> impl FnMut(&'a str) -> IResult<&'a str, R> -where - Parser: Fn(&'a str) -> IResult<&'a str, R>, -{ - delimited( - multispace0, - inner, - multispace0, - ) -} - -/// Parse a parenthesized expression. This parser will parse an expression that is surrounded by parentheses -/// and will trim the whitespace surrounding the expression. -/// - Parameters -/// - `inner`: The parser to run inside the parentheses -/// - Returns: A parser that parses a parenthesized expression -pub fn parenthesized<'a, Parser, R>(inner: Parser) -> impl FnMut(&'a str) -> IResult<&'a str, R> -where - Parser: Fn(&'a str) -> IResult<&'a str, R>, -{ - delimited( - char('('), - trim(inner), - char(')'), - ) -} - -/// Take where the predicate is true and the length is exactly `n` -/// - Parameters -/// - `n`: The length of the string to take -/// - `predicate`: The predicate to call to validate the input -/// - Returns: A parser that takes `n` characters from the input -pub fn take_where>(n: usize, predicate: F) -> impl Fn(Input) -> IResult -where - Input: InputTake + InputIter + InputLength + Slice>, - F: Fn(::Item) -> bool, -{ - move |input: Input| { - take_while_m_n(n, n, |it| predicate(it))(input) - } -} - -pub fn exhausted<'a, Parser, R>(inner: Parser) -> impl FnMut(&'a str) -> IResult<&'a str, R> -where - Parser: Fn(&'a str) -> IResult<&'a str, R>, -{ - terminated(inner, eof) -} - -pub trait IntoResult { - type Error; - fn into_result(self) -> Result; -} - -impl IntoResult for IResult { - type Error = nom::Err>; - fn into_result(self) -> Result { - self.map(|(_remaining, value)| value) - } -} - -#[cfg(test)] -mod tests { - use nom::bytes::streaming::take_while; - use crate::parsing::utils::{exhausted, parenthesized, take_where}; - - #[test] - fn test_parenthesized() { - let input = "(test)"; - let (remaining, result) = parenthesized(take_where(4, |c: char| c.is_ascii_alphabetic()))(input).unwrap(); - assert_eq!(remaining, ""); - assert_eq!(result, "test"); - } - - #[test] - fn test_parenthesized_parse_until_end() { - let input = "(test)"; - assert!(parenthesized(take_while(|_| true))(input).is_err()); - } - - #[test] - fn test_exhausted() { - let input = "test"; - let (remaining, result) = exhausted(take_where(4, |c: char| c.is_ascii_alphabetic()))(input).unwrap(); - assert_eq!(remaining, ""); - assert_eq!(result, "test"); - } - - #[test] - fn test_exhausted_not_exhausted() { - let input = "test "; - assert!(exhausted(take_where(4, |c: char| c.is_ascii_alphabetic()))(input).is_err()); - } -} diff --git a/src/routing/error.rs b/src/routing/error.rs index d15d2de..b41a17f 100644 --- a/src/routing/error.rs +++ b/src/routing/error.rs @@ -9,8 +9,6 @@ pub enum ErrorKind { InvalidExpression, /// The expression is too long. LimitExceeded, - /// The expression is missing a character to be considered valid. - MissingCharacter, /// Unexpected error. #[default] Unexpected, diff --git a/src/routing/response.rs b/src/routing/response.rs index e9b4461..c7bc7df 100644 --- a/src/routing/response.rs +++ b/src/routing/response.rs @@ -1,34 +1,11 @@ -use axum::Json; use axum::response::{IntoResponse, Response}; +use lib::derive::IntoResponse; use serde::Serialize; -use derive::IntoResponse; use crate::expressions::expression::Expression; use crate::expressions::simplify::Law; use crate::expressions::truth_table::TruthTable; -#[derive(Serialize)] -struct BaseResponse { - version: String, - #[serde(flatten)] - result: T, -} - -impl BaseResponse { - fn create(result: T) -> Response { - Self { - version: env!("CARGO_PKG_VERSION").to_string(), - result, - }.into_response() - } -} - -impl IntoResponse for BaseResponse { - fn into_response(self) -> Response { - Json(self).into_response() - } -} - #[derive(Debug, PartialEq, Serialize)] pub struct Operation { pub before: String, @@ -38,7 +15,7 @@ pub struct Operation { impl Operation { pub fn new(before: &Expression, after: &Expression, law: Law) -> Option { - if *before != *after { + if before != after { Some(Self { before: before.to_string(), after: after.to_string(), law }) } else { None @@ -67,9 +44,6 @@ impl IsValidResponse { pub const fn valid() -> Self { Self { is_valid: true } } - pub const fn invalid() -> Self { - Self { is_valid: false } - } } #[derive(Serialize, IntoResponse)] diff --git a/src/routing/routes/index.rs b/src/routing/routes/index.rs index 1313cfd..677acae 100644 --- a/src/routing/routes/index.rs +++ b/src/routing/routes/index.rs @@ -2,10 +2,11 @@ use axum::extract::Path; use axum::http::StatusCode; use axum::Json; use axum::response::{IntoResponse, Response}; +use lib::router; use serde::Serialize; -use crate::{load_html, router}; use crate::expressions::expression::Expression; +use crate::load_html; use crate::routing::error::{Error, ErrorKind}; use crate::routing::response::IsValidResponse; diff --git a/src/routing/routes/simplify.rs b/src/routing/routes/simplify.rs index 5836a9b..ab2d73c 100644 --- a/src/routing/routes/simplify.rs +++ b/src/routing/routes/simplify.rs @@ -1,10 +1,10 @@ use axum::extract::{Path, Query}; use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; +use lib::{router, routes}; use crate::expressions::expression::Expression; use crate::expressions::truth_table::TruthTable; -use crate::{router, routes}; use crate::routing::error::{Error, ErrorKind}; use crate::routing::options::{SimplifyAndTableOptions, SimplifyOptions}; use crate::routing::response::SimplifyResponse; diff --git a/src/routing/routes/table.rs b/src/routing/routes/table.rs index 87dbc62..17cda97 100644 --- a/src/routing/routes/table.rs +++ b/src/routing/routes/table.rs @@ -1,8 +1,8 @@ use axum::extract::{Path, Query}; use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; +use lib::{router, routes}; -use crate::{router, routes}; use crate::expressions::expression::Expression; use crate::expressions::truth_table::TruthTable; use crate::routing::error::{Error, ErrorKind}; diff --git a/src/utils/array.rs b/src/utils/array.rs deleted file mode 100644 index ca84a10..0000000 --- a/src/utils/array.rs +++ /dev/null @@ -1,74 +0,0 @@ -#[macro_export] -macro_rules! set { - () => { std::collections::HashSet::new() }; - ($($x:expr),*) => { - { - let mut temp_set = std::collections::HashSet::new(); - $( - temp_set.insert($x); - )* - temp_set - } - }; -} - -#[macro_export] -macro_rules! matrix { - ($($($x:expr),*);*) => { - { - let mut temp_vec = vec![]; - {} // Needed to avoid clippy warning - $( - temp_vec.push(vec![$($x),*]); - )* - temp_vec - } - }; - ($x:expr; $m:expr => $n:expr) => { - vec![vec![$x; $m]; $n] - }; -} - -#[macro_export] -macro_rules! map { - () => { std::collections::HashMap::new() }; - ($($k:expr => $v:expr),*) => { - { - let mut temp_map = std::collections::HashMap::new(); - $( - temp_map.insert($k, $v); - )* - temp_map - } - }; -} - - -pub trait Distinct { - fn distinct(&mut self); -} - -impl Distinct for Vec { - fn distinct(&mut self) { - *self = self.iter() - .fold(vec![], |mut acc, x| { - if !acc.contains(x) { - acc.push(x.clone()); - } - acc - }); - } -} - - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_distinct() { - let mut vec = vec![1, 2, 3, 1, 2, 3]; - vec.distinct(); - assert_eq!(vec, vec![1, 2, 3]); - } -} diff --git a/src/utils/axum.rs b/src/utils/axum.rs index 599096e..f0e2798 100644 --- a/src/utils/axum.rs +++ b/src/utils/axum.rs @@ -1,89 +1,3 @@ -use axum::body::Body; -use axum::response::Html; -use tokio::fs::File; -use tokio_util::io::ReaderStream; - -use crate::config::RESOURCE_DIR; - -/// Create an axum router function with the given body or routes. -/// # Examples -/// ``` -/// router!( -/// get "/" => index, -/// get "/openapi" => open_api -/// ); -/// router!("/simplify", routes!( -/// get "/:exp" => simplify, -/// get "/table/:exp" => simplify_and_table -/// )); -/// ``` -#[macro_export] -macro_rules! router { - ($body:expr) => { - pub(crate) fn router() -> axum::Router<()> { - $body - } - }; - ($route:expr, $router:expr) => { - router!(axum::Router::new().nest($route, $router)); - }; - ($($method:ident $route:expr => $func:expr),* $(,)?) => { - router!($crate::routes!($($method $route => $func),*)); - }; -} - -/// Create a router with the given routes. -/// # Examples -/// ``` -/// routes!( -/// get "/" => index, -/// post "/" => create -/// ); -/// ``` -#[macro_export] -macro_rules! routes { - ($($method:ident $route:expr => $func:expr),* $(,)?) => { - axum::Router::new() - $(.route($route, axum::routing::$method($func)))* - }; -} - -#[macro_export] -macro_rules! join_routes { - ($($route:expr),* $(,)?) => { - axum::Router::new()$(.merge($route))* - }; -} - -#[macro_export] -macro_rules! create_app { - ($router:expr, $($layer:expr),* $(,)?) => { - $router$(.layer($layer))* - }; -} - -/// Load an HTML file from the given file path, relative to the resource directory. -/// # Arguments -/// * `file_path` - The path to the HTML file. -/// # Returns -/// The HTML file as a `Html` object containing the content-type 'text/html' or an error message if the file is not found or cannot be read. -/// # Examples -/// ``` -/// let html = load_html("openapi.html").await.unwrap(); -/// ``` -pub async fn load_html(file_path: &str) -> Result, String> { - load_file(file_path).await.map(Html) -} - -pub async fn load_file(file_path: &str) -> Result { - let file = match File::open(format!("{}/{}", RESOURCE_DIR, file_path)).await { - Ok(file) => file, - Err(err) => return Err(format!("File not found: {err}")), - }; - let stream = ReaderStream::new(file); - Ok(Body::from_stream(stream)) -} - /// Load an HTML file from the given file path, relative to the resource directory. /// The file is loading on compile time as a string literal. /// # Arguments @@ -96,12 +10,8 @@ pub async fn load_file(file_path: &str) -> Result { /// ``` #[macro_export] macro_rules! load_html { - ($filename:literal) => { - axum::response::Html( - axum::body::Body::new( - $crate::absolute_path!($filename) - ) - ) + ($filename:expr) => { + lib::load_html!($crate::absolute_path!($filename), "{{docs}}" => $crate::config::OPENAPI_PATH) }; } @@ -109,8 +19,7 @@ macro_rules! load_html { #[cfg(debug_assertions)] macro_rules! absolute_path { ($filename:literal) => { - include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/resources/static/", $filename)) - .replace("{{docs}}", "/openapi") + concat!(env!("CARGO_MANIFEST_DIR"), "/src/resources/static/", $filename) }; } @@ -118,7 +27,6 @@ macro_rules! absolute_path { #[cfg(not(debug_assertions))] macro_rules! absolute_path { ($filename:literal) => { - include_str!(concat!("/static/", $filename)) - .replace("{{docs}}", "/simplify-truths/v2/openapi") + concat!("/static/", $filename) }; } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 4ac8329..d7435b3 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,3 +1,2 @@ -pub mod array; pub mod serialize; pub mod axum; \ No newline at end of file