diff --git a/Cargo.lock b/Cargo.lock index 19ce770..c920a57 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -675,7 +675,6 @@ dependencies = [ "http-body", "http-body-util", "pin-project-lite", - "tower", "tower-layer", "tower-service", "tracing", diff --git a/Cargo.toml b/Cargo.toml index 10600bf..017de3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ tokio = { version = "1.38.0", features = ["macros", "rt-multi-thread"] } 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", "normalize-path", "tower"] } +tower-http = { version = "0.5.2", features = ["cors", "trace", "normalize-path"] } tower = "0.4.13" # Logging tracing = "0.1.40" diff --git a/spec/dist/@typespec/openapi3/openapi.v2.yaml b/spec/dist/@typespec/openapi3/openapi.v2.yaml index 0945dc2..d1ba2c9 100644 --- a/spec/dist/@typespec/openapi3/openapi.v2.yaml +++ b/spec/dist/@typespec/openapi3/openapi.v2.yaml @@ -1,12 +1,71 @@ openapi: 3.0.0 info: - title: Simplify Truth Expressions + title: Simplify Truth API + description: A service to simplify truth expressions, and generate truth tables. version: v2 -tags: [] +tags: + - name: Common + - name: Expression + - name: Table paths: - /simplify/{exp}: + /: get: - operationId: Simplify_simplify + tags: + - Common + operationId: Index_index + summary: Information + description: Information about this API. + parameters: [] + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Responses.InfoResponse' + /is-valid/{exp}: + get: + tags: + - Common + - Expression + operationId: Index_isValid + summary: Check if an expression is valid + description: Check if an expression is valid. + parameters: + - name: exp + in: path + required: true + schema: + type: string + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Responses.IsValidResponse' + /openapi: + get: + tags: + - Common + operationId: Index_openAPI + summary: The OpenAPI specification + description: The OpenAPI specification for this API. + parameters: [] + responses: + '200': + description: Returns a response as HTML, with the content type set to "text/html". + content: + text/html: + schema: + type: string + /simplify/table/{exp}: + get: + tags: + - Expression + - Table + operationId: Simplify_simplifyTable + summary: Simplify and generate a truth table parameters: - name: exp in: path @@ -17,14 +76,62 @@ paths: in: query required: false schema: - $ref: '#/components/schemas/SimplifyOptions' + $ref: '#/components/schemas/Options.SimplifyTableOptions' responses: '200': description: The request has succeeded. content: application/json: schema: - $ref: '#/components/schemas/SimplifyResponse' + $ref: '#/components/schemas/Responses.SimplifyTableResponse' + /simplify/{exp}: + get: + tags: + - Expression + operationId: Simplify_simplify + summary: Simplify a truth expression + parameters: + - name: exp + in: path + required: true + schema: + type: string + - name: query + in: query + required: false + schema: + $ref: '#/components/schemas/Options.SimplifyOptions' + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Responses.SimplifyResponse' + /table/{exp}: + get: + tags: + - Table + operationId: TruthTable_simplify + summary: Generate a truth table + parameters: + - name: exp + in: path + required: true + schema: + type: string + - name: query + in: query + required: false + schema: + $ref: '#/components/schemas/Options.TableOptions' + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/Responses.TruthTableResponse' components: schemas: Expression: @@ -32,12 +139,14 @@ components: - $ref: '#/components/schemas/Models.ExpressionNot' - $ref: '#/components/schemas/Models.ExpressionBinary' - $ref: '#/components/schemas/Models.ExpressionAtomic' + title: A truth expression Models.BinaryOperator: type: string enum: - AND - OR - IMPLICATION + title: A binary operator Models.ExpressionAtomic: type: object required: @@ -45,6 +154,8 @@ components: properties: atomic: type: string + title: The atomic value + title: An atomic expression Models.ExpressionBinary: type: object required: @@ -53,52 +164,214 @@ components: - right properties: left: - $ref: '#/components/schemas/Expression' + allOf: + - $ref: '#/components/schemas/Expression' + title: The left expression operator: - $ref: '#/components/schemas/Models.BinaryOperator' + allOf: + - $ref: '#/components/schemas/Models.BinaryOperator' + title: The binary operator right: - $ref: '#/components/schemas/Expression' + allOf: + - $ref: '#/components/schemas/Expression' + title: The right expression + title: A binary expression Models.ExpressionNot: type: object required: - not properties: not: - $ref: '#/components/schemas/Expression' - SimplifyOptions: + allOf: + - $ref: '#/components/schemas/Expression' + title: The expression to negate + title: The inverse of an expression + Models.TruthTable: type: object required: - - lang - - simplify - - caseSensitive + - header + - truthMatrix + properties: + header: + type: array + items: + type: string + title: The header of the truth table + truthMatrix: + type: array + items: + type: array + items: + type: boolean + title: The rows and columns of the truth table + title: A truth table + Options.Hide: + type: string + enum: + - NONE + - 'TRUE' + - 'FALSE' + title: Whether to hide specific rows in a truth table + Options.SimplifyOptions: + type: object properties: - lang: - type: string - enum: - - en - - nb - default: en simplify: type: boolean + title: Whether to simplify the expression default: true - caseSensitive: + ignoreCase: type: boolean + title: Whether to ignore case when simplifying default: false - SimplifyResponse: + title: Options for simplifying an expression + Options.SimplifyTableOptions: + type: object + properties: + simplify: + type: boolean + title: Whether to simplify the expression + default: true + ignoreCase: + type: boolean + title: Whether to ignore case when simplifying + default: false + sort: + allOf: + - $ref: '#/components/schemas/Options.Sort' + title: Sort order for the truth table + default: DEFAULT + hide: + allOf: + - $ref: '#/components/schemas/Options.Hide' + title: Hide specific rows in the truth table + default: NONE + hideIntermediateSteps: + type: boolean + description: |- + Hide intermediate steps when generating the truth table + For example in the expression "A and B or C", the intermediate step is: A and B + title: Hide intermediate steps when generating the truth table + default: false + title: Options for simplifying an expression and generating a truth table + Options.Sort: + type: string + enum: + - DEFAULT + - TRUE_FIRST + - FALSE_FIRST + title: Sort order for a truth table + Options.TableOptions: + type: object + properties: + sort: + allOf: + - $ref: '#/components/schemas/Options.Sort' + title: Sort order for the truth table + default: DEFAULT + hide: + allOf: + - $ref: '#/components/schemas/Options.Hide' + title: Hide specific rows in the truth table + default: NONE + hideIntermediateSteps: + type: boolean + description: |- + Hide intermediate steps when generating the truth table + For example in the expression "A and B or C", the intermediate step is: A and B + title: Hide intermediate steps when generating the truth table + default: false + title: Options for generating a truth table + Responses.InfoResponse: + type: object + required: + - message + - docs + - createdBy + properties: + message: + type: string + docs: + type: string + createdBy: + type: string + description: Information about this API. + title: Information + Responses.IsValidResponse: + type: object + required: + - isValid + properties: + isValid: + type: boolean + description: If an expression is valid. + title: If an expression is valid + Responses.SimplifyResponse: type: object required: - before - after + - operations - expression properties: before: type: string + title: Before simplification after: type: string - orderOfOperations: + title: After simplification + operations: type: array items: type: string - default: [] + title: Steps taken to simplify expression: - $ref: '#/components/schemas/Expression' + allOf: + - $ref: '#/components/schemas/Expression' + title: The simplified expression + description: Response after simplifying an expression. + title: Simplify Response + Responses.SimplifyTableResponse: + type: object + required: + - before + - after + - operations + - expression + - truthTable + properties: + before: + type: string + title: Before simplification + after: + type: string + title: After simplification + operations: + type: array + items: + type: string + title: Steps taken to simplify + expression: + allOf: + - $ref: '#/components/schemas/Expression' + title: The simplified expression + truthTable: + allOf: + - $ref: '#/components/schemas/Models.TruthTable' + title: The truth table + description: Response after simplifying an expression and generating a truth table. + title: Simplify and Table Response + Responses.TruthTableResponse: + type: object + required: + - truthTable + properties: + truthTable: + allOf: + - $ref: '#/components/schemas/Models.TruthTable' + title: The truth table + description: Response after generating a truth table. + title: Truth Table Response + Version: + type: string + enum: + - v2 diff --git a/spec/main.tsp b/spec/main.tsp index 39d300f..215ccd5 100644 --- a/spec/main.tsp +++ b/spec/main.tsp @@ -1,38 +1,75 @@ import "@typespec/http"; import "@typespec/versioning"; import "./models.tsp"; +import "./response.tsp"; +import "./options.tsp"; using TypeSpec.Http; using TypeSpec.Versioning; using Models; +using Responses; +using Options; + +/** + * A service to simplify truth expressions, and generate truth tables. + */ +@service({ + title: "Simplify Truth API", +}) +@versioned(Version) +namespace SimplifyTruths; enum Version { - v2 + v2, } -@versioned(Version) -@service({ - title: "Simplify Truth Expressions", - description: "Simplify truth expressions", -}) -namespace Simplify { - model SimplifyResponse { - before: string; - after: string; - orderOfOperations?: string[] = []; - expression: Expression; - } +@tag("Common") +interface Index { + /** + * Information about this API. + */ + @get + @summary("Information") + index(): InfoResponse; - model SimplifyOptions { - lang: "en" | "nb" = "en"; - simplify: boolean = true; - caseSensitive: boolean = false; - } + /** + * The OpenAPI specification for this API. + */ + @get + @route("/openapi") + @summary("The OpenAPI specification") + openAPI(): HTML; - @route("/simplify") - interface Simplify { - @get op simplify( - @path exp: string, - @query query?: SimplifyOptions - ): SimplifyResponse; - } + /** + * Check if an expression is valid. + */ + @get + @tag("Expression") + @route("/is-valid") + @summary("Check if an expression is valid") + isValid(@path exp: string): IsValidResponse; +} + +@tag("Expression") +@route("/simplify") +interface Simplify { + @get + @summary("Simplify a truth expression") + simplify(@path exp: string, @query query?: SimplifyOptions): SimplifyResponse; + + @get + @tag("Table") + @route("/table") + @summary("Simplify and generate a truth table") + simplifyTable( + @path exp: string, + @query query?: SimplifyTableOptions, + ): SimplifyTableResponse; +} + +@tag("Table") +@route("/table") +interface TruthTable { + @get + @summary("Generate a truth table") + simplify(@path exp: string, @query query?: TableOptions): TruthTableResponse; } diff --git a/spec/models.tsp b/spec/models.tsp index 7fbdba0..c2cd76d 100644 --- a/spec/models.tsp +++ b/spec/models.tsp @@ -3,29 +3,50 @@ using TypeSpec.OpenAPI; namespace Models; +@summary("A binary operator") enum BinaryOperator { - AND, - OR, - IMPLICATION + AND, + OR, + IMPLICATION, } +@summary("The inverse of an expression") model ExpressionNot { - not: Expression; + @summary("The expression to negate") + not: Expression; } +@summary("A binary expression") model ExpressionBinary { - left: Expression; - operator: BinaryOperator; - right: Expression; + @summary("The left expression") + left: Expression; + + @summary("The binary operator") + operator: BinaryOperator; + + @summary("The right expression") + right: Expression; } +@summary("An atomic expression") model ExpressionAtomic { - atomic: string; + @summary("The atomic value") + atomic: string; } @oneOf +@summary("A truth expression") union Expression { - ExpressionNot; - ExpressionBinary; - ExpressionAtomic; -} \ No newline at end of file + ExpressionNot, + ExpressionBinary, + ExpressionAtomic, +} + +@summary("A truth table") +model TruthTable { + @summary("The header of the truth table") + header: string[]; + + @summary("The rows and columns of the truth table") + truthMatrix: boolean[][]; +} diff --git a/spec/options.tsp b/spec/options.tsp new file mode 100644 index 0000000..72ea0ec --- /dev/null +++ b/spec/options.tsp @@ -0,0 +1,46 @@ +namespace Options; + +@summary("Options for simplifying an expression") +model SimplifyOptions { + @summary("Whether to simplify the expression") + simplify?: boolean = true; + + @summary("Whether to ignore case when simplifying") + ignoreCase?: boolean = false; +} + +@summary("Sort order for a truth table") +enum Sort { + DEFAULT, + TRUE_FIRST, + FALSE_FIRST, +} + +@summary("Whether to hide specific rows in a truth table") +enum Hide { + NONE, + TRUE, + FALSE, +} + +@summary("Options for generating a truth table") +model TableOptions { + @summary("Sort order for the truth table") + sort?: Sort = Sort.DEFAULT; + + @summary("Hide specific rows in the truth table") + hide?: Hide = Hide.NONE; + + /** + * Hide intermediate steps when generating the truth table + * For example in the expression "A and B or C", the intermediate step is: A and B + */ + @summary("Hide intermediate steps when generating the truth table") + hideIntermediateSteps?: boolean = false; +} + +@summary("Options for simplifying an expression and generating a truth table") +model SimplifyTableOptions { + ...SimplifyOptions; + ...TableOptions; +} diff --git a/spec/package.json b/spec/package.json index 18f7f77..b894d0e 100644 --- a/spec/package.json +++ b/spec/package.json @@ -5,7 +5,8 @@ "author": "Martin Berg Alstad", "scripts": { "tsp-compile": "tsp compile . --output-dir dist", - "redoc-build": "redocly build-docs dist/@typespec/openapi3/openapi.v2.yaml --output ../src/resources/static/openapi.html" + "redoc-build": "redocly build-docs dist/@typespec/openapi3/openapi.v2.yaml --output ../src/resources/static/openapi.html", + "tsp-format": "tsp format \"**/*.tsp\"" }, "dependencies": { "@typespec/compiler": "latest", diff --git a/spec/response.tsp b/spec/response.tsp new file mode 100644 index 0000000..123ef89 --- /dev/null +++ b/spec/response.tsp @@ -0,0 +1,69 @@ +import "@typespec/http"; +import "./models.tsp"; +using TypeSpec.Http; +using Models; + +namespace Responses; + +/** + * Returns a response as HTML, with the content type set to "text/html". + */ +@summary("Returns a response as HTML") +model HTML { + @header contentType: "text/html"; + @body _: string; +} + +/** + * Information about this API. + */ +@summary("Information") +model InfoResponse { + message: string; + docs: string; + createdBy: string; +} + +/** + * If an expression is valid. + */ +@summary("If an expression is valid") +model IsValidResponse { + isValid: boolean; +} + +/** + * Response after simplifying an expression. + */ +@summary("Simplify Response") +model SimplifyResponse { + @summary("Before simplification") + before: string; + + @summary("After simplification") + after: string; + + @summary("Steps taken to simplify") + operations: string[]; + + @summary("The simplified expression") + expression: Expression; +} + +/** + * Response after generating a truth table. + */ +@summary("Truth Table Response") +model TruthTableResponse { + @summary("The truth table") + truthTable: Models.TruthTable; +} + +/** + * Response after simplifying an expression and generating a truth table. + */ +@summary("Simplify and Table Response") +model SimplifyTableResponse { + ...SimplifyResponse; + ...TruthTableResponse; +} diff --git a/src/expressions/truth_table.rs b/src/expressions/truth_table.rs index b50660d..4c09a6a 100644 --- a/src/expressions/truth_table.rs +++ b/src/expressions/truth_table.rs @@ -112,7 +112,7 @@ impl TruthTable { return vec![]; } atomics.sort(); - Self::truth_combinations(atomics.len()).iter() + Self::truth_combinations(atomics.len() as u32).iter() .filter_map(|combo| { let expression = Self::resolve_expression(expression, &atomics.iter() .enumerate() @@ -127,12 +127,13 @@ impl TruthTable { }).collect() } - fn truth_combinations(count: usize) -> TruthMatrix { - (0..2usize.pow(count as u32)) - .map(|i| (0..count).rev() - // Just trust me bro - .map(|j| (i >> j) & 1 == 0).collect() - ).collect() + fn truth_combinations(count: u32) -> TruthMatrix { + let row_len = 2usize.pow(count); + let rows = 0..row_len; + rows.map(|index| (0..count).rev() + // Just trust me bro + .map(|shift| (index >> shift) & 1 == 0).collect() + ).collect() } fn resolve_expression(expression: &Expression, booleans: &HashMap, header: &[String], hide_intermediate: bool) -> Vec { diff --git a/src/main.rs b/src/main.rs index b541660..6afe346 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,14 @@ use std::net::SocketAddr; -use axum::{ServiceExt}; -use axum::extract::Request; -use lib::{create_app, join_routes}; +use axum::extract::Request; +use axum::ServiceExt; +use lib::{create_app, join_routes}; use tokio::net::TcpListener; +use tower::Layer; use tower_http::cors::{Any, CorsLayer}; use tower_http::normalize_path::NormalizePathLayer; use tower_http::trace; use tower_http::trace::TraceLayer; -use tower::Layer; - use tracing::Level; use crate::routing::routes::*; diff --git a/src/resources/static/openapi.html b/src/resources/static/openapi.html index d53a584..bd61799 100644 --- a/src/resources/static/openapi.html +++ b/src/resources/static/openapi.html @@ -3,7 +3,7 @@ - Simplify Truth Expressions + Simplify Truth API