From 351a181eb922714ea60b74908f41844e79cadb0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raphael=20G=C3=B6tz?= <52959657+raphael-goetz@users.noreply.github.com> Date: Fri, 25 Jul 2025 15:39:07 +0200 Subject: [PATCH 01/11] feat: added action --- .github/workflows/ci.yml | 153 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..d7f6a35 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,153 @@ +name: Rust CI/CD Pipeline + +on: + push: + pull_request: + types: [opened, reopened] + +env: + RUST_BACKTRACE: 1 + +jobs: + # Job 1: Format Check + format: + name: Format Check + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + + - name: Check formatting + run: cargo fmt --all -- --check + + # Job 2: Lint Check + lint: + name: Lint Check + needs: [format] + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: clippy + + - name: Cache cargo registry + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-lint-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo- + + - name: Run clippy + run: cargo clippy --all-targets --all-features -- -D warnings + + - name: Check documentation + run: cargo doc --no-deps --document-private-items --all-features + env: + RUSTDOCFLAGS: "-D warnings" + + # Job 3: Build + build: + name: Build + needs: [lint] + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + + - name: Cache cargo registry + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo- + + - name: Build project + run: cargo build --verbose --all-features + + # Job 4: Test Suite + test: + name: Test Suite + needs: [build] + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust }} + + - name: Cache cargo registry + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-${{ matrix.rust }}-cargo-test-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.rust }}-cargo- + + - name: Run tests + run: cargo test --verbose --all-features + + - name: Run integration tests + run: cargo test --verbose --test '*' + + - name: Run doc tests + run: cargo test --doc --all-features + + # Job 5: Security Audit + security: + name: Security Audit + needs: [test] + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + + - name: Cache cargo registry + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-audit-${{ hashFiles('**/Cargo.lock') }} + + - name: Install cargo-audit + run: cargo install cargo-audit + + - name: Run security audit + run: cargo audit + + - name: Install cargo-deny + run: cargo install cargo-deny + + - name: Run cargo-deny + run: cargo deny check From 0278816a22046fb1178f59952d71a2a392b5f67c Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 25 Jul 2025 15:42:45 +0200 Subject: [PATCH 02/11] ref: applied formatting --- cli/src/main.rs | 134 +++++++++++++--------- cli/src/table.rs | 231 +++++++++++++++++++++++++++++--------- reader/rust/src/parser.rs | 18 +-- reader/rust/src/reader.rs | 2 - 4 files changed, 269 insertions(+), 116 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index 9e18f42..dbc9c84 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,10 +1,10 @@ +use crate::table::*; use clap::{Parser as ClapParser, Subcommand}; -use reader::parser::Parser; -use notify::{Watcher, RecursiveMode, Event, EventKind, recommended_watcher}; -use std::sync::mpsc::channel; use colored::*; -use crate::table::*; +use notify::{Event, EventKind, RecursiveMode, Watcher, recommended_watcher}; +use reader::parser::Parser; use serde_json; +use std::sync::mpsc::channel; mod table; @@ -71,28 +71,28 @@ fn main() { Commands::Feature { name, path } => { let dir_path = path.unwrap_or_else(|| "./definitions".to_string()); - let parser = match Parser::from_path(dir_path.as_str()) { - Some(reader) => reader, - None => { - panic!("Error reading definitions"); - } - }; - - if let Some(feature_name) = name { - let mut features_to_report = Vec::new(); - for feature in &parser.features { - if feature.name == feature_name { - feature_table(&feature); - features_to_report.push(feature.clone()); - } - } - summary_table(&features_to_report); - } else { - for feature in &parser.features { - feature_table(&feature); - } - summary_table(&parser.features); - } + let parser = match Parser::from_path(dir_path.as_str()) { + Some(reader) => reader, + None => { + panic!("Error reading definitions"); + } + }; + + if let Some(feature_name) = name { + let mut features_to_report = Vec::new(); + for feature in &parser.features { + if feature.name == feature_name { + feature_table(&feature); + features_to_report.push(feature.clone()); + } + } + summary_table(&features_to_report); + } else { + for feature in &parser.features { + feature_table(&feature); + } + summary_table(&parser.features); + } } Commands::Definition { name, path } => { let dir_path = path.unwrap_or_else(|| "./definitions".to_string()); @@ -109,7 +109,12 @@ fn main() { Commands::Watch { path } => { let dir_path = path.unwrap_or_else(|| "./definitions".to_string()); - println!("{}", format!("Watching directory: {}", dir_path).bright_yellow().bold()); + println!( + "{}", + format!("Watching directory: {}", dir_path) + .bright_yellow() + .bold() + ); println!("{}", "Press Ctrl+C to stop watching...".dimmed()); { @@ -126,29 +131,41 @@ fn main() { // Set up file watcher let (tx, rx) = channel(); let mut watcher = recommended_watcher(tx).unwrap(); - watcher.watch(std::path::Path::new(&dir_path), RecursiveMode::Recursive).unwrap(); + watcher + .watch(std::path::Path::new(&dir_path), RecursiveMode::Recursive) + .unwrap(); loop { match rx.recv() { - Ok(event) => { - match event { - Ok(Event { kind: EventKind::Create(_), .. }) | - Ok(Event { kind: EventKind::Modify(_), .. }) | - Ok(Event { kind: EventKind::Remove(_), .. }) => { - println!("\n{}", "Change detected! Regenerating report...".bright_yellow()); - - let parser = match Parser::from_path(dir_path.as_str()) { - Some(reader) => reader, - None => { - panic!("Error reading definitions"); - } - }; - - error_table(&parser.features); - } - _ => {} + Ok(event) => match event { + Ok(Event { + kind: EventKind::Create(_), + .. + }) + | Ok(Event { + kind: EventKind::Modify(_), + .. + }) + | Ok(Event { + kind: EventKind::Remove(_), + .. + }) => { + println!( + "\n{}", + "Change detected! Regenerating report...".bright_yellow() + ); + + let parser = match Parser::from_path(dir_path.as_str()) { + Some(reader) => reader, + None => { + panic!("Error reading definitions"); + } + }; + + error_table(&parser.features); } - } + _ => {} + }, Err(e) => println!("Watch error: {:?}", e), } } @@ -159,7 +176,12 @@ fn main() { fn search_and_display_definitions(search_name: &str, parser: &Parser) { let mut found_any = false; let mut total_matches = 0; - println!("{}", format!("Searching for definitions matching: '{}'", search_name).bright_yellow().bold()); + println!( + "{}", + format!("Searching for definitions matching: '{}'", search_name) + .bright_yellow() + .bold() + ); println!("{}", "─".repeat(60).dimmed()); for feature in &parser.features { @@ -217,7 +239,11 @@ fn search_and_display_definitions(search_name: &str, parser: &Parser) { let mut index = 0; for line in json.lines() { index += 1; - println!("{} {}", format!("{}:", index).bright_blue(), line.bright_green()); + println!( + "{} {}", + format!("{}:", index).bright_blue(), + line.bright_green() + ); } } Err(_) => println!("{}", "Error serializing RuntimeFunction".red()), @@ -227,9 +253,17 @@ fn search_and_display_definitions(search_name: &str, parser: &Parser) { } if !found_any { - println!("\n{}", format!("No definitions found matching '{}'", search_name).red().bold()); + println!( + "\n{}", + format!("No definitions found matching '{}'", search_name) + .red() + .bold() + ); } else { println!("\n{}", "─".repeat(60).dimmed()); - println!("{}", format!("Found {} matching definition(s)", total_matches).bright_yellow()); + println!( + "{}", + format!("Found {} matching definition(s)", total_matches).bright_yellow() + ); } } diff --git a/cli/src/table.rs b/cli/src/table.rs index 3805807..9f9f385 100644 --- a/cli/src/table.rs +++ b/cli/src/table.rs @@ -1,8 +1,11 @@ -use tabled::{Table, Tabled, settings::{Style, Width, object::Columns, Modify}}; +use colored::*; use reader::parser::DefinitionError; use reader::parser::Feature; -use tucana::shared::{RuntimeFunctionDefinition, DefinitionDataType, FlowType}; -use colored::*; +use tabled::{ + Table, Tabled, + settings::{Modify, Style, Width, object::Columns}, +}; +use tucana::shared::{DefinitionDataType, FlowType, RuntimeFunctionDefinition}; #[derive(Tabled)] struct FlowTypeRow { @@ -72,14 +75,36 @@ struct GeneralErrorRow { pub fn feature_table(feature: &Feature) { // Header - println!("\n{}", "╔══════════════════════════════════════════════════════════════════════════════╗".bright_cyan()); - println!("{} {} {}", "║".bright_cyan(), format!("FEATURE REPORT: {}", feature.name).bright_white().bold().on_blue(), "║".bright_cyan()); - println!("{}", "╚══════════════════════════════════════════════════════════════════════════════╝".bright_cyan()); + println!( + "\n{}", + "╔══════════════════════════════════════════════════════════════════════════════╗" + .bright_cyan() + ); + println!( + "{} {} {}", + "║".bright_cyan(), + format!("FEATURE REPORT: {}", feature.name) + .bright_white() + .bold() + .on_blue(), + "║".bright_cyan() + ); + println!( + "{}", + "╚══════════════════════════════════════════════════════════════════════════════╝" + .bright_cyan() + ); // Flow Types Section - println!("\n{}", format!("FLOW TYPES ({} defined)", feature.flow_types.len()).bright_blue().bold()); + println!( + "\n{}", + format!("FLOW TYPES ({} defined)", feature.flow_types.len()) + .bright_blue() + .bold() + ); if !feature.flow_types.is_empty() { - let flow_type_rows: Vec = feature.flow_types + let flow_type_rows: Vec = feature + .flow_types .iter() .enumerate() .map(|(i, FlowType { identifier, .. })| FlowTypeRow { @@ -97,9 +122,15 @@ pub fn feature_table(feature: &Feature) { } // Data Types Section - println!("\n{}", format!("DATA TYPES ({} defined)", feature.data_types.len()).bright_blue().bold()); + println!( + "\n{}", + format!("DATA TYPES ({} defined)", feature.data_types.len()) + .bright_blue() + .bold() + ); if !feature.data_types.is_empty() { - let data_type_rows: Vec = feature.data_types + let data_type_rows: Vec = feature + .data_types .iter() .enumerate() .map(|(i, DefinitionDataType { identifier, .. })| DataTypeRow { @@ -117,15 +148,26 @@ pub fn feature_table(feature: &Feature) { } // Runtime Functions Section - println!("\n{}", format!("RUNTIME FUNCTIONS ({} defined)", feature.runtime_functions.len()).bright_blue().bold()); + println!( + "\n{}", + format!( + "RUNTIME FUNCTIONS ({} defined)", + feature.runtime_functions.len() + ) + .bright_blue() + .bold() + ); if !feature.runtime_functions.is_empty() { - let runtime_function_rows: Vec = feature.runtime_functions + let runtime_function_rows: Vec = feature + .runtime_functions .iter() .enumerate() - .map(|(i, RuntimeFunctionDefinition { runtime_name, .. })| RuntimeFunctionRow { - index: i + 1, - runtime_name: runtime_name.clone(), - }) + .map( + |(i, RuntimeFunctionDefinition { runtime_name, .. })| RuntimeFunctionRow { + index: i + 1, + runtime_name: runtime_name.clone(), + }, + ) .collect(); let table = Table::new(runtime_function_rows) @@ -137,25 +179,40 @@ pub fn feature_table(feature: &Feature) { } // Errors Section - println!("\n{}", format!("DEFINITION ERRORS ({} found)", feature.errors.len()).bright_red().bold()); + println!( + "\n{}", + format!("DEFINITION ERRORS ({} found)", feature.errors.len()) + .bright_red() + .bold() + ); if !feature.errors.is_empty() { - let error_rows: Vec = feature.errors + let error_rows: Vec = feature + .errors .iter() .enumerate() - .map(|(i, DefinitionError { definition, definition_type, error })| ErrorRow { - index: i + 1, - definition_type: format!("{}", definition_type), - definition: definition.clone(), - error: error.clone(), - }) + .map( + |( + i, + DefinitionError { + definition, + definition_type, + error, + }, + )| ErrorRow { + index: i + 1, + definition_type: format!("{}", definition_type), + definition: definition.clone(), + error: error.clone(), + }, + ) .collect(); let table = Table::new(error_rows) .with(Style::rounded()) - .with(Modify::new(Columns::single(0)).with(Width::wrap(5))) // Index column - .with(Modify::new(Columns::single(1)).with(Width::wrap(15))) // Type column - .with(Modify::new(Columns::single(2)).with(Width::wrap(20))) // Definition column - .with(Modify::new(Columns::single(3)).with(Width::wrap(40))) // Error column + .with(Modify::new(Columns::single(0)).with(Width::wrap(5))) // Index column + .with(Modify::new(Columns::single(1)).with(Width::wrap(15))) // Type column + .with(Modify::new(Columns::single(2)).with(Width::wrap(20))) // Definition column + .with(Modify::new(Columns::single(3)).with(Width::wrap(40))) // Error column .to_string(); println!("{}", table.bright_red()); } else { @@ -165,11 +222,23 @@ pub fn feature_table(feature: &Feature) { println!("\n{}", "═".repeat(80).bright_cyan()); } - pub fn error_table(features: &Vec) { - println!("\n{}", "╔══════════════════════════════════════════════════════════════════════════════╗".bright_cyan()); - println!("{} {} {}", "║".bright_cyan(), "ERRORS".bright_white().bold().on_blue(), "║".bright_cyan()); - println!("{}", "╚══════════════════════════════════════════════════════════════════════════════╝".bright_cyan()); + println!( + "\n{}", + "╔══════════════════════════════════════════════════════════════════════════════╗" + .bright_cyan() + ); + println!( + "{} {} {}", + "║".bright_cyan(), + "ERRORS".bright_white().bold().on_blue(), + "║".bright_cyan() + ); + println!( + "{}", + "╚══════════════════════════════════════════════════════════════════════════════╝" + .bright_cyan() + ); // Collect all errors from all features let mut all_errors = Vec::new(); @@ -180,7 +249,16 @@ pub fn error_table(features: &Vec) { } // Display all errors table - println!("\n{}", format!("ALL DEFINITION ERRORS ({} found across {} features)", all_errors.len(), features.len()).bright_red().bold()); + println!( + "\n{}", + format!( + "ALL DEFINITION ERRORS ({} found across {} features)", + all_errors.len(), + features.len() + ) + .bright_red() + .bold() + ); if !all_errors.is_empty() { let error_rows: Vec = all_errors @@ -197,24 +275,38 @@ pub fn error_table(features: &Vec) { let table = Table::new(error_rows) .with(Style::rounded()) - .with(Modify::new(Columns::single(0)).with(Width::wrap(5))) // Index column - .with(Modify::new(Columns::single(1)).with(Width::wrap(15))) // Feature column - .with(Modify::new(Columns::single(2)).with(Width::wrap(12))) // Type column - .with(Modify::new(Columns::single(3)).with(Width::wrap(18))) // Definition column - .with(Modify::new(Columns::single(4)).with(Width::wrap(35))) // Error column + .with(Modify::new(Columns::single(0)).with(Width::wrap(5))) // Index column + .with(Modify::new(Columns::single(1)).with(Width::wrap(15))) // Feature column + .with(Modify::new(Columns::single(2)).with(Width::wrap(12))) // Type column + .with(Modify::new(Columns::single(3)).with(Width::wrap(18))) // Definition column + .with(Modify::new(Columns::single(4)).with(Width::wrap(35))) // Error column .to_string(); println!("{}", table.bright_red()); } else { - println!("{}", " No errors found across all features!".bright_green()); + println!( + "{}", + " No errors found across all features!".bright_green() + ); } } - - pub fn summary_table(features: &Vec) { - println!("\n{}", "╔══════════════════════════════════════════════════════════════════════════════╗".bright_cyan()); - println!("{} {} {}", "║".bright_cyan(), "CONCLUSION".bright_white().bold().on_blue(), "║".bright_cyan()); - println!("{}", "╚══════════════════════════════════════════════════════════════════════════════╝".bright_cyan()); + println!( + "\n{}", + "╔══════════════════════════════════════════════════════════════════════════════╗" + .bright_cyan() + ); + println!( + "{} {} {}", + "║".bright_cyan(), + "CONCLUSION".bright_white().bold().on_blue(), + "║".bright_cyan() + ); + println!( + "{}", + "╚══════════════════════════════════════════════════════════════════════════════╝" + .bright_cyan() + ); // Create summary table let summary_rows: Vec = features @@ -241,12 +333,12 @@ pub fn summary_table(features: &Vec) { if !summary_rows.is_empty() { let table = Table::new(summary_rows) .with(Style::rounded()) - .with(Modify::new(Columns::single(0)).with(Width::wrap(20))) // Feature name - .with(Modify::new(Columns::single(1)).with(Width::wrap(12))) // Status - .with(Modify::new(Columns::single(2)).with(Width::wrap(8))) // Errors - .with(Modify::new(Columns::single(3)).with(Width::wrap(12))) // Flow Types - .with(Modify::new(Columns::single(4)).with(Width::wrap(12))) // Data Types - .with(Modify::new(Columns::single(5)).with(Width::wrap(18))) // Runtime Functions + .with(Modify::new(Columns::single(0)).with(Width::wrap(20))) // Feature name + .with(Modify::new(Columns::single(1)).with(Width::wrap(12))) // Status + .with(Modify::new(Columns::single(2)).with(Width::wrap(8))) // Errors + .with(Modify::new(Columns::single(3)).with(Width::wrap(12))) // Flow Types + .with(Modify::new(Columns::single(4)).with(Width::wrap(12))) // Data Types + .with(Modify::new(Columns::single(5)).with(Width::wrap(18))) // Runtime Functions .to_string(); println!("{}", table.bright_blue()); @@ -260,14 +352,41 @@ pub fn summary_table(features: &Vec) { println!("\n{}", "OVERALL SUMMARY".bright_blue().bold()); if total_errors == 0 { - println!("{}", format!("PROCESS SUCCESSFUL! All {} feature(s) processed without errors.", total_features).bright_green().bold()); + println!( + "{}", + format!( + "PROCESS SUCCESSFUL! All {} feature(s) processed without errors.", + total_features + ) + .bright_green() + .bold() + ); } else { - println!("{}", format!("PROCESS FAILED! {} error(s) found across {} feature(s).", total_errors, total_features).bright_red().bold()); - println!(" {} {} successful, {} {} failed", + println!( + "{}", + format!( + "PROCESS FAILED! {} error(s) found across {} feature(s).", + total_errors, total_features + ) + .bright_red() + .bold() + ); + println!( + " {} {} successful, {} {} failed", successful_features.to_string().bright_green(), - if successful_features == 1 { "feature" } else { "features" }, - (total_features - successful_features).to_string().bright_red(), - if (total_features - successful_features) == 1 { "feature" } else { "features" } + if successful_features == 1 { + "feature" + } else { + "features" + }, + (total_features - successful_features) + .to_string() + .bright_red(), + if (total_features - successful_features) == 1 { + "feature" + } else { + "features" + } ); } diff --git a/reader/rust/src/parser.rs b/reader/rust/src/parser.rs index eec56de..7785fe3 100644 --- a/reader/rust/src/parser.rs +++ b/reader/rust/src/parser.rs @@ -36,7 +36,6 @@ impl Feature { } impl Parser { - pub fn from_path(path: &str) -> Option { let reader = match Reader::from_path(path) { Some(reader) => reader, @@ -100,8 +99,8 @@ impl Parser { Err(err) => feature.errors.push(DefinitionError { definition: Parser::extract_identifier(definition, MetaType::DataType), definition_type: MetaType::DataType, - error: err.to_string() - }) + error: err.to_string(), + }), } } MetaType::FlowType => match serde_json::from_str::(definition) { @@ -109,17 +108,20 @@ impl Parser { Err(err) => feature.errors.push(DefinitionError { definition: Parser::extract_identifier(definition, MetaType::FlowType), definition_type: MetaType::FlowType, - error: err.to_string() - }) + error: err.to_string(), + }), }, MetaType::RuntimeFunction => { match serde_json::from_str::(definition) { Ok(func) => feature.runtime_functions.push(func), Err(err) => feature.errors.push(DefinitionError { - definition: Parser::extract_identifier(definition, MetaType::RuntimeFunction), + definition: Parser::extract_identifier( + definition, + MetaType::RuntimeFunction, + ), definition_type: MetaType::RuntimeFunction, - error: err.to_string() - }) + error: err.to_string(), + }), } } } diff --git a/reader/rust/src/reader.rs b/reader/rust/src/reader.rs index 8d6dece..88925a4 100644 --- a/reader/rust/src/reader.rs +++ b/reader/rust/src/reader.rs @@ -109,7 +109,6 @@ impl Reader { // Reading the feature folder for type_path in fs::read_dir(feature_path_result.path()).unwrap() { - let type_path_result = match type_path { Ok(path) => path, Err(_) => continue, @@ -127,7 +126,6 @@ impl Reader { // Reading the type folder for definition_path in fs::read_dir(type_path_result.path()).unwrap() { - let definition_path_result = match definition_path { Ok(path) => path, Err(_) => continue, From b2a5c440fdfe099021b3ba07d8184a228841c3b3 Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 25 Jul 2025 15:51:15 +0200 Subject: [PATCH 03/11] ref: applied clippy changes --- cli/src/main.rs | 5 ++--- cli/src/table.rs | 2 +- reader/rust/src/parser.rs | 9 +++------ reader/rust/src/reader.rs | 13 ++++++------- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index dbc9c84..76fef59 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -3,7 +3,6 @@ use clap::{Parser as ClapParser, Subcommand}; use colored::*; use notify::{Event, EventKind, RecursiveMode, Watcher, recommended_watcher}; use reader::parser::Parser; -use serde_json; use std::sync::mpsc::channel; mod table; @@ -82,14 +81,14 @@ fn main() { let mut features_to_report = Vec::new(); for feature in &parser.features { if feature.name == feature_name { - feature_table(&feature); + feature_table(feature); features_to_report.push(feature.clone()); } } summary_table(&features_to_report); } else { for feature in &parser.features { - feature_table(&feature); + feature_table(feature); } summary_table(&parser.features); } diff --git a/cli/src/table.rs b/cli/src/table.rs index 9f9f385..0aa3ada 100644 --- a/cli/src/table.rs +++ b/cli/src/table.rs @@ -290,7 +290,7 @@ pub fn error_table(features: &Vec) { } } -pub fn summary_table(features: &Vec) { +pub fn summary_table(features: &[Feature]) { println!( "\n{}", "╔══════════════════════════════════════════════════════════════════════════════╗" diff --git a/reader/rust/src/parser.rs b/reader/rust/src/parser.rs index 7785fe3..5c37f0a 100644 --- a/reader/rust/src/parser.rs +++ b/reader/rust/src/parser.rs @@ -37,10 +37,7 @@ impl Feature { impl Parser { pub fn from_path(path: &str) -> Option { - let reader = match Reader::from_path(path) { - Some(reader) => reader, - None => return None, - }; + let reader = Reader::from_path(path)?; Some(Self::from_reader(reader)) } @@ -77,9 +74,9 @@ impl Parser { // Skip whitespace and find the opening quote let trimmed = after_colon.trim_start(); - if trimmed.starts_with('"') { + if let Some(stripped) = trimmed.strip_prefix('"') { // Find the closing quote - if let Some(end_quote) = trimmed[1..].find('"') { + if let Some(end_quote) = stripped.find('"') { return trimmed[1..end_quote + 1].to_string(); } } diff --git a/reader/rust/src/reader.rs b/reader/rust/src/reader.rs index 88925a4..1efa4ad 100644 --- a/reader/rust/src/reader.rs +++ b/reader/rust/src/reader.rs @@ -72,8 +72,8 @@ impl Meta { } Ok(Meta { - name: name, - r#type: r#type, + name, + r#type, data: code_snippets, }) } @@ -180,9 +180,8 @@ impl Reader { } fn get_file_name(entry: &DirEntry) -> Option { - if let Some(file_name) = entry.file_name().to_str() { - Some(file_name.to_string()) - } else { - None - } + entry + .file_name() + .to_str() + .map(|file_name| file_name.to_string()) } From c3b529d18152d1bf790e71c515858d5ac2a7aaa5 Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 25 Jul 2025 15:56:25 +0200 Subject: [PATCH 04/11] fix: set resolver to 3 --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 2ab1bb3..bf00a81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = ["cli", "reader/rust"] +resolver = "3" [workspace.package] version = "0.0.1" From 2bc600a6fa595f63f323a0fda68c5151371f6fdb Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 25 Jul 2025 22:33:07 +0200 Subject: [PATCH 05/11] ref: clippy fixes --- reader/rust/src/parser.rs | 2 +- reader/rust/src/reader.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/reader/rust/src/parser.rs b/reader/rust/src/parser.rs index 5c37f0a..8873243 100644 --- a/reader/rust/src/parser.rs +++ b/reader/rust/src/parser.rs @@ -67,7 +67,7 @@ impl Parser { }; // Look for the field pattern: "field_name": "value" or "field_name":"value" - if let Some(start) = definition.find(&format!("\"{}\"", field_name)) { + if let Some(start) = definition.find(&format!("\"{field_name}\"")) { // Find the colon after the field name if let Some(colon_pos) = definition[start..].find(':') { let after_colon = &definition[start + colon_pos + 1..]; diff --git a/reader/rust/src/reader.rs b/reader/rust/src/reader.rs index 1efa4ad..5523d65 100644 --- a/reader/rust/src/reader.rs +++ b/reader/rust/src/reader.rs @@ -46,7 +46,7 @@ impl Meta { let content = match fs::read_to_string(file_path) { Ok(content) => content, Err(err) => { - println!("Error reading file: {}", err); + println!("Error reading file: {err}"); return Err(err); } }; @@ -143,7 +143,7 @@ impl Reader { result.push(meta_result); } Err(err) => { - println!("Error reading meta: {:?}", err); + println!("Error reading meta: {err:?}"); } } } else { @@ -166,7 +166,7 @@ impl Reader { result.push(meta_result); } Err(err) => { - println!("Error reading meta: {:?}", err); + println!("Error reading meta: {err:?}"); } } } From e14ab495098a1c100196c8c9bf3654bc80dce88b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raphael=20G=C3=B6tz?= <52959657+raphael-goetz@users.noreply.github.com> Date: Fri, 25 Jul 2025 22:43:48 +0200 Subject: [PATCH 06/11] Update ci.yml --- .github/workflows/ci.yml | 71 +--------------------------------------- 1 file changed, 1 insertion(+), 70 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d7f6a35..7573e92 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,6 @@ jobs: # Job 2: Lint Check lint: name: Lint Check - needs: [format] runs-on: ubuntu-latest steps: - name: Checkout code @@ -61,7 +60,7 @@ jobs: # Job 3: Build build: name: Build - needs: [lint] + needs: [lint, format] runs-on: ubuntu-latest steps: - name: Checkout code @@ -83,71 +82,3 @@ jobs: - name: Build project run: cargo build --verbose --all-features - - # Job 4: Test Suite - test: - name: Test Suite - needs: [build] - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ matrix.rust }} - - - name: Cache cargo registry - uses: actions/cache@v4 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-${{ matrix.rust }}-cargo-test-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-${{ matrix.rust }}-cargo- - - - name: Run tests - run: cargo test --verbose --all-features - - - name: Run integration tests - run: cargo test --verbose --test '*' - - - name: Run doc tests - run: cargo test --doc --all-features - - # Job 5: Security Audit - security: - name: Security Audit - needs: [test] - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable - - - name: Cache cargo registry - uses: actions/cache@v4 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-audit-${{ hashFiles('**/Cargo.lock') }} - - - name: Install cargo-audit - run: cargo install cargo-audit - - - name: Run security audit - run: cargo audit - - - name: Install cargo-deny - run: cargo install cargo-deny - - - name: Run cargo-deny - run: cargo deny check From dd3dfe7e118787da142058543491355a0b81326f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raphael=20G=C3=B6tz?= <52959657+raphael-goetz@users.noreply.github.com> Date: Fri, 25 Jul 2025 22:44:00 +0200 Subject: [PATCH 07/11] Update ci.yml --- .github/workflows/ci.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7573e92..d0f9048 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,11 +52,6 @@ jobs: - name: Run clippy run: cargo clippy --all-targets --all-features -- -D warnings - - name: Check documentation - run: cargo doc --no-deps --document-private-items --all-features - env: - RUSTDOCFLAGS: "-D warnings" - # Job 3: Build build: name: Build From 40005c8a003485f8e181a6e292a02e0bb909d737 Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 25 Jul 2025 22:45:40 +0200 Subject: [PATCH 08/11] ref: more clippy changes --- cli/src/main.rs | 12 ++++++------ cli/src/table.rs | 8 +++----- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index 76fef59..cf81f58 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -110,7 +110,7 @@ fn main() { println!( "{}", - format!("Watching directory: {}", dir_path) + format!("Watching directory: {dir_path}") .bright_yellow() .bold() ); @@ -165,7 +165,7 @@ fn main() { } _ => {} }, - Err(e) => println!("Watch error: {:?}", e), + Err(e) => println!("Watch error: {e:?}"), } } } @@ -177,7 +177,7 @@ fn search_and_display_definitions(search_name: &str, parser: &Parser) { let mut total_matches = 0; println!( "{}", - format!("Searching for definitions matching: '{}'", search_name) + format!("Searching for definitions matching: '{search_name}'") .bright_yellow() .bold() ); @@ -240,7 +240,7 @@ fn search_and_display_definitions(search_name: &str, parser: &Parser) { index += 1; println!( "{} {}", - format!("{}:", index).bright_blue(), + format!("{index}:").bright_blue(), line.bright_green() ); } @@ -254,7 +254,7 @@ fn search_and_display_definitions(search_name: &str, parser: &Parser) { if !found_any { println!( "\n{}", - format!("No definitions found matching '{}'", search_name) + format!("No definitions found matching '{search_name}'") .red() .bold() ); @@ -262,7 +262,7 @@ fn search_and_display_definitions(search_name: &str, parser: &Parser) { println!("\n{}", "─".repeat(60).dimmed()); println!( "{}", - format!("Found {} matching definition(s)", total_matches).bright_yellow() + format!("Found {total_matches} matching definition(s)").bright_yellow() ); } } diff --git a/cli/src/table.rs b/cli/src/table.rs index 0aa3ada..8f02516 100644 --- a/cli/src/table.rs +++ b/cli/src/table.rs @@ -200,7 +200,7 @@ pub fn feature_table(feature: &Feature) { }, )| ErrorRow { index: i + 1, - definition_type: format!("{}", definition_type), + definition_type: format!("{definition_type}"), definition: definition.clone(), error: error.clone(), }, @@ -355,8 +355,7 @@ pub fn summary_table(features: &[Feature]) { println!( "{}", format!( - "PROCESS SUCCESSFUL! All {} feature(s) processed without errors.", - total_features + "PROCESS SUCCESSFUL! All {total_features} feature(s) processed without errors.", ) .bright_green() .bold() @@ -365,8 +364,7 @@ pub fn summary_table(features: &[Feature]) { println!( "{}", format!( - "PROCESS FAILED! {} error(s) found across {} feature(s).", - total_errors, total_features + "PROCESS FAILED! {total_errors} error(s) found across {total_features} feature(s)." ) .bright_red() .bold() From d704f292dcf701b7287546412569694451f25468 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raphael=20G=C3=B6tz?= <52959657+raphael-goetz@users.noreply.github.com> Date: Fri, 25 Jul 2025 22:55:20 +0200 Subject: [PATCH 09/11] Update ci.yml --- .github/workflows/ci.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d0f9048..a00430d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,3 +77,29 @@ jobs: - name: Build project run: cargo build --verbose --all-features + + + report: + name: Report + needs: [build] + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + + - name: Cache cargo registry + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-report-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo- + + - name: Generate report + run: cargo run report From 00fc013884d2db6efea9ba21c7b28868f813be0f Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 25 Jul 2025 22:55:50 +0200 Subject: [PATCH 10/11] feat: cli will exit on report when errors have been detected --- cli/src/table.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/src/table.rs b/cli/src/table.rs index 8f02516..9d77846 100644 --- a/cli/src/table.rs +++ b/cli/src/table.rs @@ -386,6 +386,7 @@ pub fn summary_table(features: &[Feature]) { "features" } ); + panic!("Process failed due to errors"); } println!("\n{}", "═".repeat(80).bright_cyan()); From 9e5104bf3b3a7973d4563ad67c763a6ba35e16b5 Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 25 Jul 2025 23:08:17 +0200 Subject: [PATCH 11/11] fix: correct escape character in definitions --- definitions/rest/data_type/type.md | 6 +++--- definitions/standard/data_type/primitive.md | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/definitions/rest/data_type/type.md b/definitions/rest/data_type/type.md index e1e309f..19e6a69 100644 --- a/definitions/rest/data_type/type.md +++ b/definitions/rest/data_type/type.md @@ -7,7 +7,7 @@ "name": [ { "code": "en-US", - "content": "HTTP Method", + "content": "HTTP Method" } ], "rules": [ @@ -30,13 +30,13 @@ "name": [ { "code": "en-US", - "content": "HTTP Route", + "content": "HTTP Route" } ], "rules": [ { "regex": { - "pattern": "/^\/\w+(?:[.:~-]\w+)*(?:\/\w+(?:[.:~-]\w+)*)*$/" + "pattern": "/^\/\\w+(?:[.:~-]\\w+)*(?:\/\\w+(?:[.:~-]\\w+)*)*$/" } } ], diff --git a/definitions/standard/data_type/primitive.md b/definitions/standard/data_type/primitive.md index 5366129..9cd2202 100644 --- a/definitions/standard/data_type/primitive.md +++ b/definitions/standard/data_type/primitive.md @@ -15,7 +15,7 @@ "rules": [ { "regex": { - "pattern": "/^(?:-(?:[1-9](?:\d{0,2}(?:,\d{3})+|\d*))|(?:0|(?:[1-9](?:\d{0,2}(?:,\d{3})+|\d*))))(?:.\d+|)$/" + "pattern": "/^(?:-(?:[1-9](?:\\d{0,2}(?:,\\d{3})+|\\d*))|(?:0|(?:[1-9](?:\\d{0,2}(?:,\\d{3})+|\\d*))))(?:.\\d+|)$/" } } ], @@ -39,7 +39,7 @@ "rules": [ { "regex": { - "pattern": "[\s\S]*" + "pattern": "[\\s\\S]*" } } ],