diff --git a/.gitignore b/.gitignore index 0754afe..2571619 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ *.DS_Store *.profraw -ssg.log +*.log /.vscode/ /target/ build diff --git a/Cargo.toml b/Cargo.toml index 4d67ead..2860baf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ name = "libmake" readme = "README.md" repository = "https://github.com/sebastienrousseau/libmake.git" rust-version = "1.75.0" -version = "0.2.1" +version = "0.2.2" include = [ "/CONTRIBUTING.md", "/LICENSE-APACHE", @@ -52,27 +52,32 @@ path = "benches/criterion.rs" debug = true [dependencies] -anyhow = "1.0.77" -assert_cmd = "2.0.12" -clap = "4.4.12" +anyhow = "1.0.81" +assert_cmd = "2.0.14" +clap = "4.5.3" csv = "1.3.0" dtt = "0.0.5" -env_logger = "0.10.1" +env_logger = "0.11.3" figlet-rs = "0.1.5" -openssl = { version = "0.10.62", features = ["vendored"] } -reqwest = { version = "0.11.23", features = ["blocking"] } -rlg = "0.0.2" -serde = { version = "1.0.193", features = ["derive"] } -serde_json = "1.0.108" -serde_yaml = "0.9.29" -tempfile = "3.9.0" -toml = "0.8.8" -vrd = "0.0.5" +configparser = "3.0.4" +reqwest = { version = "0.12.1", features = ["blocking"] } +rlg = "0.0.3" +serde = { version = "1.0.197", features = ["derive"] } +serde_json = "1.0.114" +serde_yaml = "0.9.33" +tempfile = "3.10.1" +toml = "0.8.12" +uuid = { version = "1.8.0", features = ["v4"] } +vrd = "0.0.6" xtasks = "0.0.2" +# Unix platforms use OpenSSL for now to provide SSL functionality +[target.'cfg(all(unix, not(target_os = "macos")))'.dependencies] +openssl = { version = "0.10.64", features = ["vendored"] } + [dev-dependencies] criterion = "0.5.1" -predicates = "3.0.4" +predicates = "3.1.0" [lib] crate-type = ["lib"] @@ -83,6 +88,42 @@ required-features = [] [features] default = [] +# Linting config +[lints.rust] + +## Forbid +missing_debug_implementations = "forbid" +missing_docs = "warn" +non_ascii_idents = "forbid" +unreachable_pub = "forbid" +unsafe_code = "forbid" + +## Deny +dead_code = "deny" +deprecated_in_future = "deny" +ellipsis_inclusive_range_patterns = "deny" +explicit_outlives_requirements = "deny" +future_incompatible = "deny" +keyword_idents = "deny" +macro_use_extern_crate = "deny" +meta_variable_misuse = "deny" +missing_fragment_specifier = "deny" +noop_method_call = "deny" +pointer_structural_match = "deny" +rust_2018_idioms = "deny" +rust_2021_compatibility = "deny" +single_use_lifetimes = "deny" +trivial_casts = "deny" +trivial_numeric_casts = "deny" +unused = "deny" +unused_features = "deny" +unused_import_braces = "deny" +unused_labels = "deny" +unused_lifetimes = "deny" +unused_macro_rules = "deny" +unused_qualifications = "deny" +variant_size_differences = "deny" + [profile.dev] codegen-units = 256 debug = true diff --git a/README.md b/README.md index 7912bc3..fafa7b9 100644 --- a/README.md +++ b/README.md @@ -51,10 +51,12 @@ The library is designed to be used as a command-line tool. It is available on [C - [Documentation](#documentation) - [Usage](#usage) - [Command-line interface](#command-line-interface) - - [Generate a new library using a CSV file](#generate-a-new-library-using-a-csv-file) - - [Generate a new library using a JSON file](#generate-a-new-library-using-a-json-file) - - [Generate a new library using a TOML file](#generate-a-new-library-using-a-toml-file) - - [Generate a new library using a YAML file](#generate-a-new-library-using-a-yaml-file) + - [Passing a configuration file](#passing-a-configuration-file) + - [Generate a new library using a CSV file](#generate-a-new-library-using-a-csv-file) + - [Generate a new library using an INI file](#generate-a-new-library-using-an-ini-file) + - [Generate a new library using a JSON file](#generate-a-new-library-using-a-json-file) + - [Generate a new library using a TOML file](#generate-a-new-library-using-a-toml-file) + - [Generate a new library using a YAML file](#generate-a-new-library-using-a-yaml-file) - [Generate a new library using the command-line interface (CLI) directly](#generate-a-new-library-using-the-command-line-interface-cli-directly) - [Examples](#examples) - [Semantic Versioning Policy](#semantic-versioning-policy) @@ -99,7 +101,7 @@ libmake --help ### Requirements -The minimum supported Rust toolchain version is currently Rust `1.71.1` or later (stable). +The minimum supported Rust toolchain version is currently Rust `1.75.0` or later (stable). `LibMake` is supported and has been tested on the following platforms: @@ -149,7 +151,9 @@ support additional platforms, please submit a pull request. `LibMake` provides a command-line interface to generate a new library project. There are a few options available to help you get started. -#### Generate a new library using a CSV file +#### Passing a configuration file + +##### Generate a new library using a CSV file The following command generates a library template from a CSV file. @@ -157,16 +161,33 @@ Have a look at the `tests/data/mylibrary.csv` file for an example and feel free to use it for your own library as a template. ```shell -libmake --csv tests/data/mylibrary.csv +libmake file --csv tests/data/mylibrary.csv +``` + +or locally if you have cloned the repository: + +```shell +cargo run -- file --csv tests/data/mylibrary.csv +``` + +##### Generate a new library using an INI file + +The following command generates a library template from an INI file. + +Have a look at the `tests/data/mylibrary.ini` file for an example and +feel free to use it for your own library as a template. + +```shell +libmake file --ini tests/data/mylibrary.ini ``` or locally if you have cloned the repository: ```shell -cargo run -- --csv tests/data/mylibrary.csv +cargo run -- file --ini tests/data/mylibrary.ini ``` -#### Generate a new library using a JSON file +##### Generate a new library using a JSON file The following command generates a library template from a JSON file. @@ -174,16 +195,16 @@ Have a look at the `tests/data/mylibrary.json` file for an example and feel free to use it for your own library as a template. ```shell -libmake --json tests/data/mylibrary.json +libmake file --json tests/data/mylibrary.json ``` or locally if you have cloned the repository: ```shell -cargo run -- --json tests/data/mylibrary.json +cargo run -- file --json tests/data/mylibrary.json ``` -#### Generate a new library using a TOML file +##### Generate a new library using a TOML file The following command generates a library template from a TOML file. @@ -191,16 +212,16 @@ Have a look at the `tests/data/mylibrary.toml` file for an example and feel free to use it for your own library as a template. ```shell -libmake --toml tests/data/mylibrary.toml +libmake file --toml tests/data/mylibrary.toml ``` or locally if you have cloned the repository: ```shell -cargo run -- --toml tests/data/mylibrary.toml +cargo run -- file --toml tests/data/mylibrary.toml ``` -#### Generate a new library using a YAML file +##### Generate a new library using a YAML file The following command generates a library template from a YAML file. @@ -208,13 +229,13 @@ Have a look at the `tests/data/mylibrary.yaml` file for an example and feel free to use it for your own library as a template. ```shell -libmake --yml tests/data/mylibrary.yaml +libmake file --yaml tests/data/mylibrary.yaml ``` or locally if you have cloned the repository: ```shell -cargo run -- --yml tests/data/mylibrary.yaml +cargo run -- file --yaml tests/data/mylibrary.yaml ``` #### Generate a new library using the command-line interface (CLI) directly @@ -223,7 +244,7 @@ The following command generates a library template using the command-line interface. ```shell -libmake \ +libmake manual\ --author "John Smith" \ --build "build.rs" \ --categories "['category 1', 'category 2', 'category 3']" \ @@ -238,7 +259,7 @@ libmake \ --output "my_library" \ --readme "README.md" \ --repository "https://github.com/example/my_library" \ - --rustversion "1.71.1" \ + --rustversion "1.75.0" \ --version "0.1.0" \ --website "https://example.com/john-smith" ``` @@ -246,7 +267,7 @@ libmake \ or locally if you have cloned the repository: ```shell -cargo run -- --author "John Smith" \ +cargo run -- manual --author "John Smith" \ --build "build.rs" \ --categories "['category 1', 'category 2', 'category 3']" \ --description "A Rust library for doing cool things" \ @@ -260,7 +281,7 @@ cargo run -- --author "John Smith" \ --output "my_library" \ --readme "README.md" \ --repository "https://github.com/example/my_library" \ - --rustversion "1.71.1" \ + --rustversion "1.75.0" \ --version "0.1.0" \ --website "https://example.com/john-smith" ``` @@ -276,6 +297,7 @@ To run the examples, clone the repository and run the following command in your | `generate_from_args` | Generates a library template using the command-line interface. | `cargo run --example generate_from_args` | | `generate_from_config` | Generates a library template from a configuration file. | `cargo run --example generate_from_config` | | `generate_from_csv` | Generates a library template from a CSV file. | `cargo run --example generate_from_csv` | +| `generate_from_ini` | Generates a library template from an INI file. | `cargo run --example generate_from_ini` | | `generate_from_json` | Generates a library template from a JSON file. | `cargo run --example generate_from_json` | | `generate_from_toml` | Generates a library template from a TOML file. | `cargo run --example generate_from_toml` | | `generate_from_yaml` | Generates a library template from a YAML file. | `cargo run --example generate_from_yaml` | @@ -334,6 +356,6 @@ providing a lot of useful suggestions on how to improve this project. [crates-badge]: https://img.shields.io/crates/v/libmake.svg?style=for-the-badge "Crates.io Badge" [divider]: https://kura.pro/common/images/elements/divider.svg "divider" [docs-badge]: https://img.shields.io/docsrs/libmake.svg?style=for-the-badge "Docs.rs Badge" -[libs-badge]: https://img.shields.io/badge/lib.rs-v0.2.1-orange.svg?style=for-the-badge "Lib.rs Badge" +[libs-badge]: https://img.shields.io/badge/lib.rs-v0.2.2-orange.svg?style=for-the-badge "Lib.rs Badge" [license-badge]: https://img.shields.io/crates/l/libmake.svg?style=for-the-badge "License Badge" [made-with-rust-badge]: https://img.shields.io/badge/rust-f04041?style=for-the-badge&labelColor=c0282d&logo=rust "Made With Rust Badge" diff --git a/TEMPLATE.md b/TEMPLATE.md index 8fe4e03..5616dee 100644 --- a/TEMPLATE.md +++ b/TEMPLATE.md @@ -4,7 +4,8 @@ alt="LibMake logo" width="261" align="right" /> -# LibMake v0.2.1 ๐Ÿฆ€ + +# LibMake v0.2.2 ๐Ÿฆ€ A code generator to reduce repetitive tasks and build high-quality Rust libraries. @@ -63,6 +64,6 @@ The library is designed to be used as a command-line tool. It is available on [C [codecov-badge]: https://img.shields.io/codecov/c/github/sebastienrousseau/libmake?style=for-the-badge&token=Q9KJ6XXL67 "Codecov Badge" [crates-badge]: https://img.shields.io/crates/v/libmake.svg?style=for-the-badge "Crates.io Badge" [docs-badge]: https://img.shields.io/docsrs/libmake.svg?style=for-the-badge "Docs.rs Badge" -[libs-badge]: https://img.shields.io/badge/lib.rs-v0.2.1-orange.svg?style=for-the-badge "Lib.rs Badge" +[libs-badge]: https://img.shields.io/badge/lib.rs-v0.2.2-orange.svg?style=for-the-badge "Lib.rs Badge" [license-badge]: https://img.shields.io/crates/l/libmake.svg?style=for-the-badge "License Badge" [made-with-rust-badge]: https://img.shields.io/badge/rust-f04041?style=for-the-badge&labelColor=c0282d&logo=rust "Made With Rust Badge" diff --git a/benches/criterion.rs b/benches/criterion.rs index a8e77e3..6a7e5d2 100644 --- a/benches/criterion.rs +++ b/benches/criterion.rs @@ -37,8 +37,6 @@ //! from the `libmake` crate, helping you evaluate and optimize its performance. #![allow(missing_docs)] -extern crate criterion; -extern crate libmake; use criterion::{criterion_group, criterion_main, Criterion}; use libmake::run; diff --git a/examples/generate_from_args.rs b/examples/generate_from_args.rs index 88da889..89b491b 100644 --- a/examples/generate_from_args.rs +++ b/examples/generate_from_args.rs @@ -53,11 +53,11 @@ use libmake::generator::generate_from_args; /// If successful, this program will print "Successfully generated files!". /// /// If there is an error, it will print an error message. -pub fn main() { +pub(crate) fn main() { // Simulate command line arguments let args = "--author=Me --output=my_library" .split(' ') - .map(std::string::ToString::to_string) // Directly using the method + .map(ToString::to_string) // Directly using the method .collect::>(); // Check if there are at least two arguments (program name and at least one option) diff --git a/examples/generate_from_config.rs b/examples/generate_from_config.rs index 7b6fb19..640af1b 100644 --- a/examples/generate_from_config.rs +++ b/examples/generate_from_config.rs @@ -49,7 +49,7 @@ use libmake::generator::generate_from_config; /// /// * `Ok(())` - If generation is successful. /// * `Err(String)` - If there is an error during generation. -pub fn main() { +pub(crate) fn main() { // Define the file path for the configuration file. let file_path = "./tests/data/mylibrary.yaml"; diff --git a/examples/generate_from_csv.rs b/examples/generate_from_csv.rs index 1c3ec96..3a7bcf8 100644 --- a/examples/generate_from_csv.rs +++ b/examples/generate_from_csv.rs @@ -57,7 +57,7 @@ use libmake::generator::generate_from_csv; /// generate_from_csv(csv_file_path) /// .expect("Failed to generate the template files"); /// ``` -pub fn main() { +pub(crate) fn main() { // Define the path to the CSV file to be used for testing. let csv_file_path = "./tests/data/mylibrary.csv"; diff --git a/examples/generate_from_json.rs b/examples/generate_from_json.rs index f0aac8c..b626724 100644 --- a/examples/generate_from_json.rs +++ b/examples/generate_from_json.rs @@ -46,7 +46,7 @@ use libmake::generator::generate_from_json; /// /// * `Result<(), String>` - Returns `Ok(())` if the template files are generated successfully, or returns an error message if there is an error during generation. /// -pub fn main() { +pub(crate) fn main() { // Define the path to the JSON file that contains configuration data. let json_file_path = "./tests/data/mylibrary.json"; diff --git a/examples/generate_from_toml.rs b/examples/generate_from_toml.rs index 0c92dbc..4daa182 100644 --- a/examples/generate_from_toml.rs +++ b/examples/generate_from_toml.rs @@ -41,7 +41,7 @@ use libmake::generator::generate_from_toml; /// /// * `Result<(), String>` - Returns `Ok(())` if the template files are generated successfully, or returns an error message if generation fails. /// -pub fn main() { +pub(crate) fn main() { // Define the path to the TOML file containing configuration. let toml_file_path = "./tests/data/mylibrary.toml"; diff --git a/examples/generate_from_yaml.rs b/examples/generate_from_yaml.rs index 36b6af6..ec77f7c 100644 --- a/examples/generate_from_yaml.rs +++ b/examples/generate_from_yaml.rs @@ -42,7 +42,7 @@ use libmake::generator::generate_from_yaml; /// /// * `Result<(), String>` - Returns `Ok(())` if the template files are generated successfully, or returns an error message if there is an error during generation. /// -pub fn main() { +pub(crate) fn main() { // Specify the path to the YAML file to be used for generating templates. let yaml_file_path = "./tests/data/mylibrary.yaml"; diff --git a/examples/get_csv_field.rs b/examples/get_csv_field.rs index b239988..8379776 100644 --- a/examples/get_csv_field.rs +++ b/examples/get_csv_field.rs @@ -47,7 +47,7 @@ use libmake::utils::get_csv_field; /// /// String containing the requested field /// -pub fn main() { +pub(crate) fn main() { // Retrieve CSV field let file_path = "../tests/data/mylibrary.csv"; println!( diff --git a/examples/get_json_field.rs b/examples/get_json_field.rs index 49b3665..3839da5 100644 --- a/examples/get_json_field.rs +++ b/examples/get_json_field.rs @@ -3,70 +3,84 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT indicates dual licensing under Apache 2.0 or MIT licenses. // Copyright ยฉ 2024 LibMake. All rights reserved. -//! # Test: Retrieving a Field from a JSON File +//! # Test: Retrieving a Field from a CSV File //! -//! This is a test that demonstrates how to retrieve a specific field from a JSON file -//! using the `get_json_field` function from the `libmake` crate. +//! This is a test that demonstrates how to retrieve a specific field from a CSV file +//! using the `get_csv_field` function from the `libmake` crate. //! //! ## Purpose //! -//! The purpose of this test is to show how to extract a JSON field (`field_author`) -//! from a JSON file located at the specified path (`file_path`). +//! The purpose of this test is to show how to extract a CSV field (`field_title`) +//! from a CSV file located at the specified path (`file_path`). //! //! ## Usage //! -//! To run this test, ensure that you have a valid JSON file at the specified path. -//! The test checks if the file exists and then uses the `get_json_field` function -//! to retrieve the specified JSON field. If the file exists and the field is found, -//! it prints the field's value; otherwise, it prints an empty string. +//! To run this test, ensure that you have a valid CSV file at the specified path. +//! The test checks if the file exists and then uses the `get_csv_field` function +//! to retrieve the specified CSV field. If the file exists and the field is found, +//! it prints the field's value; otherwise, it prints an error message or an empty string. //! //! ```rust -//! // Import the necessary function for retrieving a field from a JSON file. -//! use libmake::utils::get_json_field; +//! // Import the necessary function for retrieving a field from a CSV file. +//! use libmake::utils::get_csv_field; //! use std::path::Path; //! -//! // Specify the path to the JSON file. -//! let file_path = "../tests/data/mylibrary.json"; +//! // Specify the path to the CSV file. +//! let file_path = "../tests/data/mylibrary.csv"; //! -//! // Define the JSON field to retrieve. -//! let field_author = "author"; +//! // Define the CSV field to retrieve. +//! let field_title = "title"; //! -//! // Check if the JSON file exists before retrieving the field. -//! let value = if Path::new(file_path).exists() { -//! // If the file exists, use the `get_json_field` function to retrieve the field. -//! get_json_field(Some(file_path), field_author) -//! } else { -//! // If the file doesn't exist, set the value to an empty string. -//! String::new() -//! }; -// -//! // Print the result. -//! println!("๐Ÿฆ€ get_json_field, โœ… {}: {}", field_author, value); +//! // Check if the CSV file exists before retrieving the field. +//! let value = if Path::new(file_path).exists() { +//! // If the file exists, use the `get_csv_field` function to retrieve the field. +//! match get_csv_field(Some(file_path), 0) { +//! Some(values) => values.join(", "), +//! None => { +//! eprintln!("Error retrieving field: {}", field_title); +//! String::new() +//! } +//! } +//! } else { +//! // If the file doesn't exist, set the value to an empty string. +//! String::new() +//! }; +//! +//! // Print the result. +//! println!("๐Ÿฆ€ get_csv_field, โœ… {}: {}", field_title, value); //! ``` -// Title: Test: Retrieving a field from a JSON file -use libmake::utils::get_json_field; +// Title: Test: Retrieving a field from a CSV file +use libmake::utils::get_csv_field; use std::path::Path; -/// Retrieve JSON field +/// Retrieve CSV field /// /// # Arguments /// -/// * `file_path` - Path to the JSON file -/// * `field_author` - Name of the JSON field to retrieve +/// * `file_path` - Path to the CSV file +/// * `field_title` - Name of the CSV field to retrieve /// /// # Returns /// -/// The value of the JSON field, or an empty string if the file does not exist or the field cannot be found +/// The value of the CSV field, or an empty string if the file does not exist or the field cannot be found /// -pub fn main() { - // Retrieve JSON field - let file_path = "../tests/data/mylibrary.json"; - let field_author = "author"; +pub(crate) fn main() { + // Retrieve CSV field + let file_path = "../tests/data/mylibrary.csv"; + let field_title = "title"; + let value = if Path::new(file_path).exists() { - get_json_field(Some(file_path), field_author) + match get_csv_field(Some(file_path), 0) { + Some(values) => values.join(", "), + None => { + eprintln!("Error retrieving field: {field_title}"); + String::new() + } + } } else { String::new() }; - println!("๐Ÿฆ€ get_json_field, โœ… {field_author}: {value}"); + + println!("๐Ÿฆ€ get_csv_field, โœ… {field_title}: {value}"); } diff --git a/examples/get_yaml_field.rs b/examples/get_yaml_field.rs index 450cf13..9b6b91e 100644 --- a/examples/get_yaml_field.rs +++ b/examples/get_yaml_field.rs @@ -26,6 +26,7 @@ //! ```rust //! // Import the necessary function for retrieving a field from a YAML file. //! use libmake::utils::get_yaml_field; +//! use std::error::Error; //! use std::path::Path; //! //! // Specify the path to the YAML file. @@ -37,25 +38,32 @@ //! // Check if the YAML file exists before retrieving the field. //! let value = if Path::new(file_path).exists() { //! // If the file exists, use the `get_yaml_field` function to retrieve the field. -//! let keywords: Vec = -//! get_yaml_field(Some(file_path), field_keywords) -//! .split('\n') +//! let keywords: Result, Box> = get_yaml_field(Some(file_path), field_keywords) +//! .map(|s| s.split('\n') //! .map(|s| s.trim_start_matches("- ")) //! .filter(|s| !s.is_empty()) //! .map(|s| format!("\"{}\"", s)) -//! .collect(); -//! format!("[{}]", keywords.join(", ")) +//! .collect()); +//! +//! match keywords { +//! Ok(keywords) => format!("[{}]", keywords.join(", ")), +//! Err(e) => { +//! eprintln!("Error retrieving keywords: {}", e); +//! String::new() +//! } +//! } //! } else { //! // If the file doesn't exist, set the value to an empty string. //! String::new() //! }; -// +//! //! // Print the result. //! println!("๐Ÿฆ€ get_yaml_field, โœ… {}: {}", field_keywords, value); //! ``` // Title: Test: Retrieving a field from a YAML file use libmake::utils::get_yaml_field; +use std::error::Error; use std::path::Path; /// # Test: Retrieving a Field from a YAML File @@ -81,6 +89,7 @@ use std::path::Path; /// ```rust /// // Import the necessary function for retrieving a field from a YAML file. /// use libmake::utils::get_yaml_field; +/// use std::error::Error; /// use std::path::Path; /// /// // Specify the path to the YAML file. @@ -92,14 +101,20 @@ use std::path::Path; /// // Check if the YAML file exists before retrieving the field. /// let value = if Path::new(file_path).exists() { /// // If the file exists, use the `get_yaml_field` function to retrieve the field. -/// let keywords: Vec = -/// get_yaml_field(Some(file_path), field_keywords) -/// .split('\n') +/// let keywords: Result, Box> = get_yaml_field(Some(file_path), field_keywords) +/// .map(|s| s.split('\n') /// .map(|s| s.trim_start_matches("- ")) /// .filter(|s| !s.is_empty()) /// .map(|s| format!("\"{}\"", s)) -/// .collect(); -/// format!("[{}]", keywords.join(", ")) +/// .collect()); +/// +/// match keywords { +/// Ok(keywords) => format!("[{}]", keywords.join(", ")), +/// Err(e) => { +/// eprintln!("Error retrieving keywords: {}", e); +/// String::new() +/// } +/// } /// } else { /// // If the file doesn't exist, set the value to an empty string. /// String::new() @@ -109,19 +124,27 @@ use std::path::Path; /// println!("๐Ÿฆ€ get_yaml_field, โœ… {}: {}", field_keywords, value); /// ``` /// -pub fn main() { +pub(crate) fn main() { let file_path = "../tests/data/mylibrary.yaml"; let field_keywords = "keywords"; let value = if Path::new(file_path).exists() { - let keywords: Vec = - get_yaml_field(Some(file_path), field_keywords) - .split('\n') - .map(|s| s.trim_start_matches("- ")) - .filter(|s| !s.is_empty()) - .map(|s| format!("\"{s}\"")) - .collect(); - format!("[{}]", keywords.join(", ")) + let keywords: Result, Box> = + get_yaml_field(Some(file_path), field_keywords).map(|s| { + s.split('\n') + .map(|s| s.trim_start_matches("- ")) + .filter(|s| !s.is_empty()) + .map(|s| format!("\"{s}\"")) + .collect() + }); + + match keywords { + Ok(keywords) => format!("[{}]", keywords.join(", ")), + Err(e) => { + eprintln!("Error retrieving keywords: {e}"); + String::new() + } + } } else { String::new() }; diff --git a/output_dir/.deepsource.toml b/output_dir/.deepsource.toml new file mode 100644 index 0000000..d052a12 --- /dev/null +++ b/output_dir/.deepsource.toml @@ -0,0 +1,8 @@ +version = 1 + +[[analyzers]] +name = "rust" +enabled = true + + [analyzers.meta] + msrv = "stable" diff --git a/output_dir/.github/workflows/ci.yml b/output_dir/.github/workflows/ci.yml new file mode 100644 index 0000000..8930143 --- /dev/null +++ b/output_dir/.github/workflows/ci.yml @@ -0,0 +1,486 @@ +name: โฏ test_lib release + +on: + pull_request: + branches: + - main + - 'feat/*' + push: + branches: + - main + - 'feat/*' + +jobs: + # This job checks a local package and all of its dependencies for + # errors. + check: + name: โฏ Check ๐Ÿ’ต + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - rust: stable + target: x86_64-unknown-linux-gnu + os: ubuntu-latest + + steps: + # Check out the repository code + - name: Checkout sources + id: checkout + uses: actions/checkout@v4 + + # Install the stable Rust toolchain + - name: Install stable toolchain + id: install-toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + # Cache dependencies to speed up subsequent builds + - name: Cache dependencies + id: cache-dependencies + uses: actions/cache@v3 + with: + path: ~/.cargo + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: ${{ runner.os }}-cargo- + + # Run cargo check to check for errors + - uses: actions-rs/cargo@v1 + with: + command: check + args: --all-targets --workspace --all-features + + # This job runs the tests for the project. + test: + name: โฏ Test ๐Ÿงช + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - rust: stable + target: x86_64-unknown-linux-gnu + + steps: + # Check out the repository code + - name: Checkout sources + id: checkout + uses: actions/checkout@v4 + + # Install the stable Rust toolchain + - name: Install stable toolchain + id: install-toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + + # Cache dependencies to speed up subsequent builds + - name: Cache dependencies + id: cache-dependencies + uses: actions/cache@v3 + with: + path: ~/.cargo + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: ${{ runner.os }}-cargo- + + # Run cargo hack to check for errors + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack + id: install-cargo-hack + + - run: cargo test --all-targets --workspace --all-features + + # This job runs the tests for the project. + coverage: + name: โฏ Coverage ๐Ÿ“Š + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + runs-on: ubuntu-latest + strategy: + matrix: + include: + - rust: stable + target: x86_64-unknown-linux-gnu + + steps: + # Check out the repository code + - name: Checkout sources + id: checkout + uses: actions/checkout@v4 + + # Install the stable Rust toolchain + - name: Install stable toolchain + id: install-toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + override: true + components: llvm-tools-preview + + # Cache dependencies to speed up subsequent builds + - name: Cache dependencies + id: cache-dependencies + uses: actions/cache@v3 + with: + path: ~/.cargo + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: ${{ runner.os }}-cargo- + + # Install grcov + - name: Install grcov + run: | + mkdir -p "${HOME}/.local/bin" + curl -sL https://github.com/mozilla/grcov/releases/download/v0.8.18/grcov-x86_64-unknown-linux-gnu.tar.bz2 | tar jxf - -C "${HOME}/.local/bin" + echo "$HOME/.local/bin" >> $GITHUB_PATH + + # Use grcov to generate a coverage report + - name: Generate coverage report + id: coverage + uses: actions-rs/cargo@v1 + with: + command: xtask + args: coverage + + # Upload the coverage report to codecov + - name: Upload coverage report to codecov + id: codecov + uses: codecov/codecov-action@v3 + with: + files: coverage/*.lcov + + lints: + name: โฏ Lints ๐Ÿงน + runs-on: ubuntu-latest + strategy: + matrix: + include: + - rust: stable + target: x86_64-unknown-linux-gnu + steps: + # Check out the repository code + - name: Checkout sources + id: checkout + uses: actions/checkout@v4 + with: + submodules: true + + # Install the stable Rust toolchain + - name: Install stable toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + components: clippy + + # Cache dependencies to speed up subsequent builds + - name: Cache dependencies + uses: actions/cache@v3 + with: + path: ~/.cargo + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: ${{ runner.os }}-cargo- + + # Run cargo clippy to check for linting errors + - name: Run cargo clippy + if: github.ref == !github.event.check_run.conclusion + uses: actions-rs/cargo@v1 + with: + command: clippy + args: --all-targets --all-features -- -D warnings + + # Run Cargo Format for the code style + - name: Run Cargo Format + id: run-check-format + if: github.ref == !github.event.check_run.conclusion + run: | + cargo check --all --all-features --workspace --verbose + + # Run cargo clippy to check for linting errors + - name: Run Clippy + id: run-check-clippy + if: github.ref == !github.event.check_run.conclusion + run: | + cargo clippy --all-targets --all-features --workspace -- -D warnings + + + build: + # This job builds the project for all the targets and generates a + # release artifact that contains the binaries for all the targets. + name: โฏ Build ๐Ÿ›  + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + strategy: + fail-fast: false + matrix: + target: + # List of targets: https://doc.rust-lang.org/nightly/rustc/platform-support.html + + # FreeBSD targets ๐Ÿฌ + - x86_64-unknown-freebsd # 64-bit FreeBSD on x86-64 โœ… Tested + + # Linux targets ๐Ÿง + - aarch64-unknown-linux-gnu # 64-bit Linux systems on ARM architecture โœ… Tested + - aarch64-unknown-linux-musl # 64-bit Linux systems on ARM architecture โœ… Tested + - arm-unknown-linux-gnueabi # ARMv6 Linux (kernel 3.2, glibc 2.17) โœ… Tested + - armv7-unknown-linux-gnueabihf # ARMv7 Linux, hardfloat (kernel 3.2, glibc 2.17) โœ… Tested + - i686-unknown-linux-gnu # 32-bit Linux (kernel 3.2+, glibc 2.17+) โœ… Tested + - i686-unknown-linux-musl # 32-bit Linux (kernel 3.2+, musl libc) โœ… Tested + - x86_64-unknown-linux-gnu # 64-bit Linux (kernel 2.6.32+, glibc 2.11+) โœ… Tested + - x86_64-unknown-linux-musl # 64-bit Linux (kernel 2.6.32+, musl libc) โœ… Tested + + # macOS targets ๐ŸŽ + - aarch64-apple-darwin # 64-bit macOS on Apple Silicon โœ… Tested + - x86_64-apple-darwin # 64-bit macOS (10.7 Lion or later) โœ… Tested + + # Illumos targets ๐ŸŒž + - x86_64-unknown-illumos # 64-bit Illumos on x86-64 โœ… Tested + + include: + # FreeBSD targets ๐Ÿฌ + - target: x86_64-unknown-freebsd + os: ubuntu-latest + cross: true + + # Linux targets ๐Ÿง + - target: aarch64-unknown-linux-gnu + os: ubuntu-latest + cross: true + - target: aarch64-unknown-linux-musl + os: ubuntu-latest + cross: true + - target: arm-unknown-linux-gnueabi + os: ubuntu-latest + cross: true + - target: armv7-unknown-linux-gnueabihf + os: ubuntu-latest + cross: true + - target: i686-unknown-linux-gnu + os: ubuntu-latest + cross: true + - target: i686-unknown-linux-musl + os: ubuntu-latest + cross: true + - target: x86_64-unknown-linux-gnu + os: ubuntu-latest + cross: true + - target: x86_64-unknown-linux-musl + os: ubuntu-latest + cross: true + + # Illumos targets ๐ŸŒž + - target: x86_64-unknown-illumos + os: ubuntu-latest + cross: true + + # macOS targets ๐ŸŽ + - target: aarch64-apple-darwin + os: macos-latest + cross: true + - target: x86_64-apple-darwin + os: macos-latest + cross: true + + runs-on: ${{ matrix.os }} + + steps: + # Check out the repository code + - name: Checkout sources + id: checkout + uses: actions/checkout@v4 + + # Install the stable Rust toolchain + - name: Install stable toolchain + id: install-toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + # Cache dependencies to speed up subsequent builds + - name: Cache dependencies + id: cache-dependencies + uses: actions/cache@v3 + with: + path: ~/.cargo + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: ${{ runner.os }}-cargo- + + # Install the targets for the cross compilation toolchain + - name: Install target + id: install-target + run: rustup target add ${{ matrix.target }} + + # Update the version number based on the Cargo.toml file + - name: Update version number + id: update-version + run: | + NEW_VERSION=$(grep version Cargo.toml | sed -n 2p | cut -d '"' -f 2) + echo "VERSION=$NEW_VERSION" >> $GITHUB_ENV + shell: /bin/bash -e {0} + + # Install the cross compilation toolchain + - name: Install Cross + id: install-cross + run: | + # Install cross + cargo install cross + # Clean the build artifacts + cargo clean --verbose + shell: /bin/bash -e {0} + + # Build the targets + - name: Build targets + id: build-targets + uses: actions-rs/cargo@v1 + with: + use-cross: true + command: build + args: --verbose --workspace --release --target ${{ matrix.target }} + + # Package the binary for each target + - name: Package the binary + id: package-binary + run: | + mkdir -p target/package + tar czf target/package/${{ matrix.target }}.tar.gz -C target/${{ matrix.target }}/release . + echo "${{ matrix.target }}.tar.gz=target/package/${{ matrix.target }}.tar.gz" >> $GITHUB_ENV + + # Upload the binary for each target + - name: Upload the binary + id: upload-binary + uses: actions/upload-artifact@v2 + with: + name: ${{ matrix.target }}.tar.gz + path: ${{ env[format('{0}.tar.gz', matrix.target)] }} + + # Release the binary to GitHub Releases + release: + name: โฏ Release ๐Ÿš€ + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + needs: build + runs-on: ubuntu-latest + steps: + # Check out the repository code + - name: Checkout sources + uses: actions/checkout@v4 + + # Install the stable Rust toolchain + - name: Install stable toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + # Update the version number based on the Cargo.toml file + - name: Update version number + run: | + NEW_VERSION=$(grep version Cargo.toml | sed -n 2p | cut -d '"' -f 2) + echo "VERSION=$NEW_VERSION" >> $GITHUB_ENV + shell: /bin/bash -e {0} + + # Cache dependencies to speed up subsequent builds + - name: Cache dependencies + uses: actions/cache@v3 + with: + path: ~/.cargo + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: ${{ runner.os }}-cargo- + + # Download the artifacts from the build job + - name: Download artifacts + run: | + for target in x86_64-unknown-freebsd aarch64-unknown-linux-gnu aarch64-unknown-linux-musl arm-unknown-linux-gnueabi armv7-unknown-linux-gnueabihf i686-unknown-linux-gnu i686-unknown-linux-musl x86_64-unknown-linux-gnu x86_64-unknown-linux-musl aarch64-apple-darwin x86_64-apple-darwin x86_64-unknown-illumos; do + echo "Downloading $target artifact" + name="${target}.tar.gz" + echo "Artifact name: $name" + mkdir -p target/package + curl -sSL -H "Authorization: token ${GITHUB_TOKEN}" -H "Accept: application/vnd.github.v3+json" -L "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/actions/runs/${RUN_ID}/artifacts/$test_lib" -o "target/package/$test_lib" + done + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RUN_ID: ${{ github.event.workflow_run.id }} + + # Generate the changelog based on git log and template file + - name: Generate Changelog + run: | + # Append version information to CHANGELOG.md + echo "## Release v${{ env.VERSION }} - $(date +'%Y-%m-%d')" >> ${{ github.workspace }}/CHANGELOG.md + # Copy content of template file to CHANGELOG.md + cat TEMPLATE.md >> ${{ github.workspace }}/CHANGELOG.md + # Append git log to CHANGELOG.md + echo "$(git log --pretty=format:'%s' --reverse $(git describe --tags --abbrev=0)..HEAD)" >> ${{ github.workspace }}/CHANGELOG.md + # Append empty line to CHANGELOG.md + echo "" >> ${{ github.workspace }}/CHANGELOG.md + + # Append the artifact links to the changelog + - name: Append Artifact Links + run: | + echo "" >> ${{ github.workspace }}/CHANGELOG.md + echo "## Artifacts ๐ŸŽ" >> ${{ github.workspace }}/CHANGELOG.md + for filename in target/package/*.tar.gz; do + link="$(basename $filename)" + echo "* [$link](${{ github.server_url }}/${{ github.repository }}/releases/download/v${{ env.VERSION }}/$link)" >> ${{ github.workspace }}/CHANGELOG.md + done + + # Create the release on GitHub Releases + - name: Create Release + id: create-release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: v${{ env.VERSION }} + release_name: test_lib ๐Ÿฆ€ v${{ env.VERSION }} + body_path: ${{ github.workspace }}/CHANGELOG.md + draft: true + prerelease: false + + # Publish the release to Crates.io automatically + crate: + name: โฏ Crate.io ๐Ÿฆ€ + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + needs: release + runs-on: ubuntu-latest + + steps: + # Check out the repository code + - name: Checkout + uses: actions/checkout@v4 + + # Install the stable Rust toolchain + - name: Install stable toolchain + id: install-toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + # Cache dependencies to speed up subsequent builds + - name: Cache dependencies + id: cache-dependencies + uses: actions/cache@v2 + with: + path: /home/runner/.cargo/registry/index/ + key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} + restore-keys: ${{ runner.os }}-cargo-index- + + # Update the version number based on the Cargo.toml file + - name: Update version number + id: update-version + run: | + NEW_VERSION=$(grep version Cargo.toml | sed -n 2p | cut -d '"' -f 2) + echo "VERSION=$NEW_VERSION" >> $GITHUB_ENV + shell: /bin/bash -e {0} + + # Publish the Rust library to Crate.io + - name: Publish Library to Crate.io + id: publish-library + uses: actions-rs/cargo@v1 + env: + CARGO_API_TOKEN: ${{ secrets.CARGO_API_TOKEN }} + with: + use-cross: true + command: publish + args: --dry-run --verbose --token "${CARGO_API_TOKEN}" diff --git a/output_dir/.gitignore b/output_dir/.gitignore new file mode 100644 index 0000000..7b5a0e0 --- /dev/null +++ b/output_dir/.gitignore @@ -0,0 +1,10 @@ +*.DS_Store +*.profraw +/.vscode/ +/output/ +/public/ +/target/ +build +Icon? +src/.DS_Store +tarpaulin-report.html diff --git a/output_dir/AUTHORS.md b/output_dir/AUTHORS.md new file mode 100644 index 0000000..ff5d1ca --- /dev/null +++ b/output_dir/AUTHORS.md @@ -0,0 +1,3 @@ +# Authors + +* [John Doe](john@example.com) (Original Contributor) diff --git a/output_dir/CONTRIBUTING.md b/output_dir/CONTRIBUTING.md new file mode 100644 index 0000000..663298a --- /dev/null +++ b/output_dir/CONTRIBUTING.md @@ -0,0 +1,81 @@ +# Contributing to `test_lib` + +Welcome! We're thrilled that you're interested in contributing to the +`test_lib` library. Whether you're looking to evangelize, submit feedback, +or contribute code, we appreciate your involvement in making `test_lib` a +better tool for everyone. Here's how you can get started. + +## Evangelize + +One of the simplest ways to help us out is by spreading the word about +test_lib. We believe that a bigger, more involved community makes for a +better framework, and that better frameworks make the world a better +place. If you know people who might benefit from using test_lib, please +let them know! + +## How to Contribute + +If you're interested in making a more direct contribution, there are +several ways you can help us improve test_lib. Here are some guidelines +for submitting feedback, bug reports, and code contributions. + +### Feedback + +Your feedback is incredibly valuable to us, and we're always looking for +ways to make test_lib better. If you have ideas, suggestions, or questions +about test_lib, we'd love to hear them. Here's how you can provide +feedback: + +- Click [here][2] to submit a new feedback. +- Use a descriptive title that clearly summarizes your feedback. +- Provide a detailed description of the issue or suggestion. +- Be patient while we review and respond to your feedback. + +### Bug Reports + +If you encounter a bug while using test_lib, please let us know so we can +fix it. Here's how you can submit a bug report: + +- Click [here][2] to submit a new issue. +- Use a descriptive title that clearly summarizes the bug. +- Provide a detailed description of the issue, including steps to + reproduce it. +- Be patient while we review and respond to your bug report. + +### Code Contributions + +If you're interested in contributing code to test_lib, we're excited to +have your help! Here's what you need to know: + +#### Feature Requests + +If you have an idea for a new feature or improvement, we'd love to hear +it. Here's how you can contribute code for a new feature to test_lib: + +- Fork the repo. +- Clone the test_lib[1] repo by running: + `git clone https://github.com/test/test_lib` +- Edit files in the `src/` folder. The `src/` folder contains the source + code for test_lib. +- Submit a pull request, and we'll review and merge your changes if they + fit with our vision for test_lib. + +#### Submitting Code + +If you've identified a bug or have a specific code improvement in mind, +we welcome your pull requests. Here's how to submit your code changes: + +- Fork the repo. +- Clone the test_lib repo by running: + `git clone https://github.com/test/test_lib` +- Edit files in the `src/` folder. The `src/` folder contains the source + code for test_lib. +- Submit a pull request, and we'll review and merge your changes if they + fit with our vision for test_lib. + +We hope that this guide has been helpful in explaining how you can +contribute to test_lib. Thank you for your interest and involvement in our +project! + +[1]: https://github.com/test/test_lib +[2]: https://github.com/test/test_lib/issues/newHTTP/2 416 diff --git a/output_dir/Cargo.toml b/output_dir/Cargo.toml new file mode 100644 index 0000000..41e46ad --- /dev/null +++ b/output_dir/Cargo.toml @@ -0,0 +1,137 @@ +[package] +authors = ["John Doe "] +build = "script.rs" +categories = [category1,category2] +description = "A test library" +documentation = "https://docs.rs/test_lib" +edition = "2021" +exclude = [ + "/.git/*", + "/.github/*", + "/.gitignore", + "/.vscode/*" + ] +homepage = "https://example.com" +keywords = ["keyword1,keyword2"] +license = "MIT" +name = "test_lib" +readme = "{readme}" +repository = "https://github.com/test/test_lib" +rust-version = "1.60.0" +version = "0.1.0" +include = [ + "/benches/**", + "/build.rs", + "/Cargo.toml", + "/CONTRIBUTING.md", + "/examples/**", + "/LICENSE-APACHE", + "/LICENSE-MIT", + "/README.md", + "/src/**", + "/tests/**" +] + +[[bench]] +name = "benchmark" +harness = false +path = "benches/criterion.rs" + +[profile.bench] +debug = true + +[dependencies] +anyhow = "1.0.81" +dtt = "0.0.5" +env_logger = "0.11.3" +rlg = "0.0.3" +serde = { version = "1.0.197", features = ["derive"] } +serde_json = "1.0.114" +serde_yaml = "0.9.33" +toml = "0.8.12" +vrd = "0.0.6" + +[dev-dependencies] +criterion = "0.5.1" + +[lib] +crate-type = ["lib"] +name = "test_lib" +path = "src/lib.rs" + +[features] +default = [] + +[package.metadata.docs.rs] +all-features = true + +# Linting config +[lints.rust] + +## Forbid +missing_debug_implementations = "forbid" +missing_docs = "warn" +non_ascii_idents = "forbid" +unreachable_pub = "forbid" +unsafe_code = "forbid" + +## Deny +dead_code = "deny" +deprecated_in_future = "deny" +ellipsis_inclusive_range_patterns = "deny" +explicit_outlives_requirements = "deny" +future_incompatible = "deny" +keyword_idents = "deny" +macro_use_extern_crate = "deny" +meta_variable_misuse = "deny" +missing_fragment_specifier = "deny" +noop_method_call = "deny" +pointer_structural_match = "deny" +rust_2018_idioms = "deny" +rust_2021_compatibility = "deny" +single_use_lifetimes = "deny" +trivial_casts = "deny" +trivial_numeric_casts = "deny" +unused = "deny" +unused_features = "deny" +unused_import_braces = "deny" +unused_labels = "deny" +unused_lifetimes = "deny" +unused_macro_rules = "deny" +unused_qualifications = "deny" +variant_size_differences = "deny" + +[profile.dev] +codegen-units = 256 +debug = true +debug-assertions = true +incremental = true +lto = false +opt-level = 0 +overflow-checks = true +panic = 'unwind' +rpath = false +strip = false + +[profile.release] +codegen-units = 1 +debug = false +debug-assertions = false +incremental = false +lto = true +opt-level = "s" +overflow-checks = false +panic = "abort" +rpath = false +strip = "symbols" + +[profile.test] +codegen-units = 256 +debug = true +debug-assertions = true +incremental = true +lto = false +opt-level = 0 +overflow-checks = true +rpath = false +strip = false diff --git a/output_dir/README.md b/output_dir/README.md new file mode 100644 index 0000000..bc5942d --- /dev/null +++ b/output_dir/README.md @@ -0,0 +1,197 @@ + + + + + + +# test_lib + +A test library + + +
+ + +[![Made With Rust][made-with-rust-badge]][5] +[![Crates.io][crates-badge]][7] +[![Lib.rs][libs-badge]][9] +[![Docs.rs][docs-badge]][8] +[![License][license-badge]][2] + +โ€ข [Website][0] +โ€ข [Documentation][8] +โ€ข [Report Bug][3] +โ€ข [Request Feature][3] +โ€ข [Contributing Guidelines][4] + + +
+ + +![divider][divider] + +## Overview ๐Ÿ“– + +A test library + +## Features โœจ + +- Feature 1 +- Feature 2 +- Feature 3 + +## Getting Started ๐Ÿš€ + +It takes just a few minutes to get up and running with `test_lib`. + +### Installation + +To install `test_lib`, you need to have the Rust toolchain installed on +your machine. You can install the Rust toolchain by following the +instructions on the [Rust website][13]. + +Once you have the Rust toolchain installed, you can install `test_lib` +using the following command: + +```shell +cargo install test_lib +``` + +You can then run the help command to see the available options: + +```shell +test_lib --help +``` + +### Requirements + +The minimum supported Rust toolchain version is currently Rust +**1.60.0** or later (stable). + +### Platform support + +`test_lib` is supported and tested on the following platforms: + +### Tier 1 platforms ๐Ÿ† + +| | Operating System | Target | Description | +| --- | --- | --- | --- | +| โœ… | Linux | aarch64-unknown-linux-gnu | 64-bit Linux systems on ARM architecture | +| โœ… | Linux | i686-unknown-linux-gnu | 32-bit Linux (kernel 3.2+, glibc 2.17+) | +| โœ… | Linux | x86_64-unknown-linux-gnu | 64-bit Linux (kernel 2.6.32+, glibc 2.11+) | +| โœ… | macOS | x86_64-apple-darwin | 64-bit macOS (10.7 Lion or later) | +| โœ… | Windows | i686-pc-windows-gnu | 32-bit Windows (7 or later) | +| โœ… | Windows | i686-pc-windows-msvc | 32-bit Windows (7 or later) | +| โœ… | Windows | x86_64-pc-windows-gnu | 64-bit Windows (7 or later) | +| โœ… | Windows | x86_64-pc-windows-msvc | 64-bit Windows (7 or later) | + +### Tier 2 platforms ๐Ÿฅˆ + +| | Operating System | Target | Description | +| --- | --- | --- | --- | +| โœ… | Linux | aarch64-unknown-linux-musl | 64-bit Linux systems on ARM architecture | +| โœ… | Linux | arm-unknown-linux-gnueabi | ARMv6 Linux (kernel 3.2, glibc 2.17) | +| โœ… | Linux | arm-unknown-linux-gnueabihf | ARMv7 Linux, hardfloat (kernel 3.2, glibc 2.17) | +| โœ… | Linux | armv7-unknown-linux-gnueabihf | ARMv7 Linux, hardfloat (kernel 3.2, glibc 2.17) | +| โœ… | Linux | mips-unknown-linux-gnu | MIPS Linux (kernel 2.6.32+, glibc 2.11+) | +| โœ… | Linux | mips64-unknown-linux-gnuabi64 | MIPS64 Linux (kernel 2.6.32+, glibc 2.11+) | +| โœ… | Linux | mips64el-unknown-linux-gnuabi64 | MIPS64 Linux (kernel 2.6.32+, glibc 2.11+) | +| โœ… | Linux | mipsel-unknown-linux-gnu | MIPS Linux (kernel 2.6.32+, glibc 2.11+) | +| โœ… | macOS | aarch64-apple-darwin | 64-bit macOS (10.7 Lion or later) | +| โœ… | Windows | aarch64-pc-windows-msvc | 64-bit Windows (7 or later) | + +The [GitHub Actions][10] shows the platforms in which the `test_lib` +library tests are run. + +### Documentation + +**Info:** Please check out our [website][0] for more information. You can find our documentation on [docs.rs][8], [lib.rs][9] and +[crates.io][7]. + +## Usage ๐Ÿ“– + +To use the `test_lib` library in your project, add the following to your +`Cargo.toml` file: + +```toml +[dependencies] +test_lib = "0.1.0" +``` + +Add the following to your `main.rs` file: + +```rust +extern crate test_lib; +use test_lib::*; +``` + +then you can use the functions in your application code. + +### Examples + +To get started with `test_lib`, you can use the examples provided in the +`examples` directory of the project. + +To run the examples, clone the repository and run the following command +in your terminal from the project root directory. + +```shell +cargo run --example test_lib +``` + +## Semantic Versioning Policy ๐Ÿšฅ + +For transparency into our release cycle and in striving to maintain +backward compatibility, `test_lib` follows [semantic versioning][6]. + +## License ๐Ÿ“ + +The project is licensed under the terms of MIT. + +## Contribution ๐Ÿค + +We welcome all people who want to contribute. Please see the +[contributing instructions][4] for more information. + +Contributions in any form (issues, pull requests, etc.) to this project +must adhere to the [Rust's Code of Conduct][11]. + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the +Apache-2.0 license, shall be dual licensed as above, without any +additional terms or conditions. + +## Acknowledgements ๐Ÿ’™ + +A big thank you to all the awesome contributors of [test_lib][5] for their +help and support. + +A special thank you goes to the [Rust Reddit][12] community for +providing a lot of useful suggestions on how to improve this project. + +[0]: https://example.com +[2]: http://opensource.org/licenses/MIT +[3]: https://github.com/test/test_lib/test_lib/issues +[4]: https://github.com/test/test_lib/test_lib/blob/main/CONTRIBUTING.md +[5]: https://github.com/test/test_lib/test_lib/graphs/contributors +[6]: http://semver.org/ +[7]: https://crates.io/crates/test_lib +[8]: https://docs.rs/test_lib +[9]: https://lib.rs/crates/test_lib +[10]: https://github.com/test/test_lib/test_lib/actions +[11]: https://www.rust-lang.org/policies/code-of-conduct +[12]: https://www.reddit.com/r/rust/ +[13]: https://www.rust-lang.org/learn/get-started + +[crates-badge]: https://img.shields.io/crates/v/test_lib.svg?style=for-the-badge 'Crates.io badge' +[divider]: https://kura.pro/common/images/elements/divider.svg "divider" +[docs-badge]: https://img.shields.io/docsrs/test_lib.svg?style=for-the-badge 'Docs.rs badge' +[libs-badge]: https://img.shields.io/badge/lib.rs-v0.1.0-orange.svg?style=for-the-badge 'Lib.rs badge' +[license-badge]: https://img.shields.io/crates/l/test_lib.svg?style=for-the-badge 'License badge' +[made-with-rust-badge]: https://img.shields.io/badge/rust-f04041?style=for-the-badge&labelColor=c0282d&logo=rust 'Made With Rust badge' diff --git a/output_dir/TEMPLATE.md b/output_dir/TEMPLATE.md new file mode 100644 index 0000000..809c9a7 --- /dev/null +++ b/output_dir/TEMPLATE.md @@ -0,0 +1,68 @@ + + + + + + +# test_lib + +A test library + + +
+ + +[![Made With Rust][made-with-rust-badge]][5] +[![Crates.io][crates-badge]][7] +[![Lib.rs][libs-badge]][9] +[![Docs.rs][docs-badge]][8] +[![License][license-badge]][2] + +โ€ข [Website][0] +โ€ข [Documentation][8] +โ€ข [Report Bug][3] +โ€ข [Request Feature][3] +โ€ข [Contributing Guidelines][4] + + +
+ + +![divider][divider] + +## Overview ๐Ÿ“– + +A test library + +## Features โœจ + +- Feature 1 +- Feature 2 +- Feature 3 + +## Changelog ๐Ÿ“š + +- + +[0]: https://example.com +[2]: http://opensource.org/licenses/MIT +[3]: https://github.com/test/test_lib/test_lib/issues +[4]: https://github.com/test/test_lib/test_lib/blob/main/CONTRIBUTING.md +[5]: https://github.com/test/test_lib/test_lib/graphs/contributors +[7]: https://crates.io/crates/test_lib +[8]: https://docs.rs/test_lib +[9]: https://lib.rs/crates/test_lib + +[banner]: https://via.placeholder.com/1500x500.png/000000/FFFFFF?text=test_lib "test_lib's banner" +[crates-badge]: https://img.shields.io/crates/v/test_lib.svg?style=for-the-badge 'Crates.io badge' +[divider]: https://via.placeholder.com/1024x1.png/d8dee4/FFFFFF?text=โˆ’ "test_lib's divider" +[docs-badge]: https://img.shields.io/docsrs/test_lib.svg?style=for-the-badge 'Docs.rs badge' +[libs-badge]: https://img.shields.io/badge/lib.rs-v0.1.0-orange.svg?style=for-the-badge 'Lib.rs badge' +[license-badge]: https://img.shields.io/crates/l/test_lib.svg?style=for-the-badge 'License badge' +[made-with-rust-badge]: https://img.shields.io/badge/rust-f04041?style=for-the-badge&labelColor=c0282d&logo=rust 'Made With Rust badge' diff --git a/output_dir/benches/criterion.rs b/output_dir/benches/criterion.rs new file mode 100644 index 0000000..fcf3672 --- /dev/null +++ b/output_dir/benches/criterion.rs @@ -0,0 +1,52 @@ +// Copyright notice and licensing information. +// These lines indicate the copyright of the software and its licensing terms. +// Copyright ยฉ 2024 test_lib. All rights reserved. +// SPDX-License-Identifier: MIT + +extern crate criterion; + +use criterion::{criterion_group, criterion_main, Criterion}; +use test_lib::{run, test_lib_vec, test_lib_map, test_lib_join}; + +fn test_lib_vec_benchmark(c: &mut Criterion) { + c.bench_function("test_lib_vec_macro", |b| { + b.iter(|| { + test_lib_vec![1, 2, 3, 4, 5] + }) + }); +} + +fn test_lib_map_benchmark(c: &mut Criterion) { + c.bench_function("test_lib_map_macro", |b| { + b.iter(|| { + test_lib_map!["a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5] + }) + }); +} + +fn test_lib_join_benchmark(c: &mut Criterion) { + c.bench_function("test_lib_join_macro", |b| { + b.iter(|| { + test_lib_join!["a", "b", "c", "d", "e"] + }) + }); +} + +fn test_lib_benchmark(c: &mut Criterion) { + c.bench_function("test_lib", |b| { + b.iter(|| { + for _ in 0..1000 { + run().unwrap(); + } + }) + }); +} + +criterion_group!( + test_lib_macros_benchmark, + test_lib_vec_benchmark, + test_lib_map_benchmark, + test_lib_join_benchmark, + test_lib_benchmark +); +criterion_main!(test_lib_macros_benchmark); diff --git a/output_dir/build.rs b/output_dir/build.rs new file mode 100644 index 0000000..8ca0c3e --- /dev/null +++ b/output_dir/build.rs @@ -0,0 +1,12 @@ +// Copyright notice and licensing information. +// These lines indicate the copyright of the software and its licensing terms. +// Copyright ยฉ 2024 test_lib. All rights reserved. +// SPDX-License-Identifier: MIT + +//! This is the main function for the build script. +//! +//! Currently, it only instructs Cargo to re-run this build script if `build.rs` is changed. +fn main() { + // Avoid unnecessary re-building. + println!("cargo:rerun-if-changed=build.rs"); +} diff --git a/output_dir/deny.toml b/output_dir/deny.toml new file mode 100644 index 0000000..2670118 --- /dev/null +++ b/output_dir/deny.toml @@ -0,0 +1,71 @@ +[licenses] +# The lint level for crates which do not have a detectable license +unlicensed = "deny" + +# List of explicitly allowed licenses +# See https://spdx.org/licenses/ for list of possible licenses +# [possible values: any SPDX 3.7 short identifier (+ optional exception)]. +allow = [ + "Apache-2.0", + "MIT", + "CC0-1.0", + "ISC", + "0BSD", + "BSD-2-Clause", + "BSD-3-Clause", + "Unlicense", + "Unicode-DFS-2016", +] + +# List of banned licenses +[bans] +multiple-versions = "deny" + + +# The lint level for licenses considered copyleft +copyleft = "deny" + +# Blanket approval or denial for OSI-approved or FSF Free/Libre licenses +# * both - The license will only be approved if it is both OSI-approved *AND* FSF/Free +# * either - The license will be approved if it is either OSI-approved *OR* FSF/Free +# * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF/Free +# * fsf-only - The license will be approved if is FSF/Free *AND NOT* OSI-approved +# * neither - The license will be denied if is FSF/Free *OR* OSI-approved +allow-osi-fsf-free = "either" + +# The confidence threshold for detecting a license from license text. +# The higher the value, the more closely the license text must be to the +# canonical license text of a valid SPDX license file. +# [possible values: any between 0.0 and 1.0]. +confidence-threshold = 0.8 + +# The graph highlighting used when creating dotgraphs for crates +# with multiple versions +# * lowest-version - The path to the lowest versioned duplicate is highlighted +# * simplest-path - The path to the version with the fewest edges is highlighted +# * all - Both lowest-version and simplest-path are used +highlight = "all" + +# List of crates that are allowed. Use with care! +allow = [] + +# List of crates to deny +deny = [ + # Each entry the name of a crate and a version range. If version is + # not specified, all versions will be matched. +] + +# Certain crates/versions that will be skipped when doing duplicate detection. +skip = [] + +# Similarly to `skip` allows you to skip certain crates during duplicate detection, +# unlike skip, it also includes the entire tree of transitive dependencies starting at +# the specified crate, up to a certain depth, which is by default infinite +skip-tree = [] + + +[advisories] +notice = "deny" +unmaintained = "deny" +unsound = "deny" +vulnerability = "deny" diff --git a/output_dir/examples/example.rs b/output_dir/examples/example.rs new file mode 100644 index 0000000..2a778e4 --- /dev/null +++ b/output_dir/examples/example.rs @@ -0,0 +1,3 @@ +fn main() { + +} diff --git a/output_dir/rustfmt.toml b/output_dir/rustfmt.toml new file mode 100644 index 0000000..a38e84e --- /dev/null +++ b/output_dir/rustfmt.toml @@ -0,0 +1,32 @@ +# SPDX-FileCopyrightText: Copyright ยฉ 2023 test_lib. All rights reserved. +# SPDX-License-Identifier: MIT + +# See https://github.com/rust-lang/rustfmt/blob/master/Configurations.md +# for more configuration options + +comment_width = 72 # Maximum line width for comments +doc_comment_code_block_width = 72 # Maximum line width for code blocks in doc comments +edition = "2021" # Use a single edition only (Edition 2018 or Edition 2021) +empty_item_single_line = true # Put empty items on a single line +force_explicit_abi = true # Force explicit abi +format_code_in_doc_comments = true # Format code snippets in doc comments +format_macro_bodies = true # Format macro bodies +format_macro_matchers = true # Format macro matchers +group_imports = "StdExternalCrate" # Group imports by crate +hard_tabs = false # Use spaces instead of tabs +imports_granularity = "Module" # Group imports by module +imports_layout = "HorizontalVertical" # Layout imports horizontally and vertically +max_width = 72 # Maximum line width +merge_derives = true # Merge derives +newline_style = "Unix" # Prevent carriage returns from being added to the end of lines +normalize_comments = true # Normalize comments +normalize_doc_attributes = true # Normalize doc attributes +overflow_delimited_expr = true # Allow overflowing delimited expressions +remove_nested_parens = true # Remove nested parens +reorder_imports = true # Reorder imports +reorder_modules = true # Reorder modules +tab_spaces = 4 # Use 4 spaces for indentation +use_field_init_shorthand = true # Use field initialization shorthand when possible +use_small_heuristics = "Max" # Use max heuristics +use_try_shorthand = true # Use try shorthand when possible +wrap_comments = true # Wrap comments when line width exceeds max max_width diff --git a/output_dir/src/lib.rs b/output_dir/src/lib.rs new file mode 100644 index 0000000..2a0cd83 --- /dev/null +++ b/output_dir/src/lib.rs @@ -0,0 +1,119 @@ +// Copyright notice and licensing information. +// These lines indicate the copyright of the software and its licensing terms. +// Copyright ยฉ 2024 test_lib. All rights reserved. +// SPDX-License-Identifier: MIT +//! +//! # `test_lib` ๐Ÿฆ€ +//! +//! [![test_lib](https://via.placeholder.com/1500x500.png/000000/FFFFFF?text=test_lib)](https://example.com "test_lib - A test library") +//! +//! A test library +//! +//! [![Crates.io](https://img.shields.io/crates/v/test_lib.svg?style=for-the-badge&color=success&labelColor=27A006)](https://crates.io/crates/test_lib "Crates.io") +//! [![Lib.rs](https://img.shields.io/badge/lib.rs-v0.1.0-success.svg?style=for-the-badge&color=8A48FF&labelColor=6F36E4)](https://lib.rs/crates/test_lib "Lib.rs") +//! [![License](https://img.shields.io/crates/l/test_lib.svg?style=for-the-badge&color=007EC6&labelColor=03589B)](MIT "MIT") +//! [![Rust](https://img.shields.io/badge/rust-f04041?style=for-the-badge&labelColor=c0282d&logo=rust)](https://www.rust-lang.org "Rust") +//! +//! ## Overview +//! +//! A test library +//! +//! ## Features +//! +//! - ... +//! - ... +//! - ... +//! +//! ## Usage +//! +//! Add the following to your `Cargo.toml` file: +//! +//! ```toml +//! [dependencies] +//! test_lib = "0.1.0" +//! serde = { version = "1.0", features = ["derive"] } +//! serde_json = "1.0" +//! ``` +//! +//! ## Examples +//! +//! Check out the examples folder for helpful snippets of code that +//! demonstrate how to use the `test_lib` library. You can also check out +//! the [documentation](https://docs.rs/test_lib) for more information on +//! how to use the library. +//! +//! ```rust +//! use test_lib::test_lib; +//! +//! ``` +//! +//! ## License +//! +//! The project is licensed under the terms of the MIT license. +//! +#![cfg_attr(feature = "bench", feature(test))] +#![deny(dead_code)] +#![deny(missing_debug_implementations)] +#![deny(missing_docs)] +#![forbid(unsafe_code)] +#![warn(unreachable_pub)] +#![doc( + html_favicon_url = "", + html_logo_url = "", + html_root_url = "https://docs.rs/test_lib" +)] +#![crate_name = "test_lib"] +#![crate_type = "lib"] + +/// The `loggers` module contains the loggers for the library. +pub mod loggers; + +/// The `macros` module contains functions for generating macros. +pub mod macros; + +use serde::{Deserialize, Serialize}; +use std::error::Error; + +#[non_exhaustive] +#[derive( + Clone, + Debug, + Deserialize, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize, +)] + +#[allow(non_camel_case_types)] +/// test_lib is a data structure that ... +pub struct test_lib { + // Add any data fields needed here +} + +/// This is the main entry point for the test_lib library. +pub fn run() -> Result<(), Box> { + // Add your code here + let name = "test_lib"; + println!("Hello, {}!", { name }.to_uppercase()); + Ok(()) +} + + +impl test_lib { + /// Creates a new instance of test_lib + pub fn new() -> Self { + Self { + // Initialize any data fields here + } + } +} + +impl Default for test_lib { + /// Creates a new instance of test_lib with default values + fn default() -> Self { + Self::new() + } +} diff --git a/src/loggers.rs b/output_dir/src/loggers.rs similarity index 86% rename from src/loggers.rs rename to output_dir/src/loggers.rs index 632b660..1e5965b 100644 --- a/src/loggers.rs +++ b/output_dir/src/loggers.rs @@ -1,7 +1,7 @@ // Copyright notice and licensing information. // These lines indicate the copyright of the software and its licensing terms. -// SPDX-License-Identifier: Apache-2.0 OR MIT indicates dual licensing under Apache 2.0 or MIT licenses. -// Copyright ยฉ 2024 LibMake. All rights reserved. +// Copyright ยฉ 2024 test_lib. All rights reserved. +// SPDX-License-Identifier: MIT //! Application logging functionality //! @@ -19,7 +19,7 @@ use std::io::Write; /// /// ``` /// use rlg::LogLevel; -/// use libmake::loggers::init_logger; +/// use test_lib::loggers::init_logger; /// /// // Initialize the logging system with a default log level of `info` /// init_logger(Some(LogLevel::INFO)).unwrap(); diff --git a/output_dir/src/macros.rs b/output_dir/src/macros.rs new file mode 100644 index 0000000..e30f2be --- /dev/null +++ b/output_dir/src/macros.rs @@ -0,0 +1,120 @@ +//! # Macros for the `test_lib` crate. +//! +//! This crate provides the following macros: +//! +//! - `test_lib`: The main macro for the `test_lib` crate. +//! - `test_lib_print`: Prints the arguments to the console. +//! - `test_lib_vec`: Creates a new vector of the given elements. +//! - `test_lib_map`: Creates a new map of the given key-value pairs. +//! - `test_lib_assert`: Checks if the given expression is true. +//! - `test_lib_min`: Returns the minimum of the given values. +//! - `test_lib_max`: Returns the maximum of the given values. +//! - `test_lib_split`: Splits a string into a vector of words. +//! - `test_lib_join`: Joins a vector of strings into a single string. +//! - `test_lib_print_vec`: Prints a vector of elements to the console. +//! + +/// This macro takes any number of arguments and parses them into a +/// Rust value. +#[macro_export] +macro_rules! test_lib { + ($($tt:tt)*) => { + // Parse the arguments into a Rust value. + $crate::parse!($($tt)*) + }; +} + +/// This macro prints the arguments to the console. +#[macro_export] +macro_rules! test_lib_print { + ($($arg:tt)*) => { + println!("{}", format_args!("{}", $($arg)*)); + }; +} + +/// This macro creates a new vector of the given elements. +#[macro_export] +macro_rules! test_lib_vec { + ($($elem:expr),*) => {{ + let mut v = Vec::new(); + $(v.push($elem);)* + v + }}; +} + +/// This macro creates a new map of the given key-value pairs. +#[macro_export] +macro_rules! test_lib_map { + ($($key:expr => $value:expr),*) => {{ + use std::collections::HashMap; + let mut m = HashMap::new(); + $(m.insert($key, $value);)* + m + }}; +} + +/// This macro checks if the given expression is true. +#[macro_export] +macro_rules! test_lib_assert { + ($($arg:tt)*) => { + if !$($arg)* { + panic!("Assertion failed!"); + } + }; +} + +/// This macro returns the minimum of the given values. +#[macro_export] +macro_rules! test_lib_min { + ($($x:expr),*) => {{ + let mut min = $($x)*; + $(if min > $x { min = $x; })* + min + }}; +} + +/// This macro returns the maximum of the given values. +#[macro_export] +macro_rules! test_lib_max { + ($($x:expr),*) => {{ + let mut max = $($x)*; + $(if max < $x { max = $x; })* + max + }}; +} + +/// This macro takes a string and splits it into a vector of words. +#[macro_export] +macro_rules! test_lib_split { + ($s:expr) => {{ + let mut v = Vec::new(); + for w in $s.split_whitespace() { + v.push(w.to_string()); + } + v + }}; +} + +/// This macro takes a vector of strings and joins them together into a +/// single string. +#[macro_export] +macro_rules! test_lib_join { + ($($s:expr),*) => {{ + let mut s = String::new(); + $( + s += &$s; + )* + s + }}; +} + +/// This macro takes a vector of elements and prints them to the +/// console. +#[macro_export] +macro_rules! test_lib_print_vec { + ($($v:expr),*) => {{ + for v in $($v),* { + println!("{}", v); + } + }}; +} diff --git a/output_dir/src/main.rs b/output_dir/src/main.rs new file mode 100644 index 0000000..38c0789 --- /dev/null +++ b/output_dir/src/main.rs @@ -0,0 +1,13 @@ +// Copyright notice and licensing information. +// These lines indicate the copyright of the software and its licensing terms. +// Copyright ยฉ 2024 test_lib. All rights reserved. +// SPDX-License-Identifier: MIT + +/// This is the main entry point for the test_lib application. +fn main() { + // Call the `run()` function from the `test_lib` module. + if let Err(err) = test_lib::run() { + eprintln!("Error running test_lib: {}", err); + std::process::exit(1); + } +} diff --git a/output_dir/tests/test.rs b/output_dir/tests/test.rs new file mode 100644 index 0000000..5cf298a --- /dev/null +++ b/output_dir/tests/test.rs @@ -0,0 +1,11 @@ +#[cfg(test)] +mod tests { + + use test_lib::test_lib; + + #[test] + fn test_test_lib() { + let test_lib = test_lib::new(); + assert_eq!(test_lib, test_lib::default()); + } +} diff --git a/output_dir/tests/test_loggers.rs b/output_dir/tests/test_loggers.rs new file mode 100644 index 0000000..7a75912 --- /dev/null +++ b/output_dir/tests/test_loggers.rs @@ -0,0 +1,41 @@ +// Copyright notice and licensing information. +// These lines indicate the copyright of the software and its licensing terms. +// Copyright ยฉ 2024 test_lib. All rights reserved. +// SPDX-License-Identifier: MIT + +#[cfg(test)] +mod tests { + + use rlg::macro_log; + use rlg::{LogFormat, LogLevel}; + + #[test] + fn test_logging() { + // Create a log entry + let log_entry = + macro_log!( + "session_id", + "time", + &LogLevel::INFO, + "component", + "Log message", + &LogFormat::CLF + ); + + // Define expected values + let expected_session_id = "session_id"; + let expected_time = "time"; + let expected_level = LogLevel::INFO; + let expected_component = "component"; + let expected_description = "Log message"; + let expected_format = LogFormat::CLF; + + // Assert that individual fields match the expected values + assert_eq!(log_entry.session_id, expected_session_id); + assert_eq!(log_entry.time, expected_time); + assert_eq!(log_entry.level, expected_level); + assert_eq!(log_entry.component, expected_component); + assert_eq!(log_entry.description, expected_description); + assert_eq!(log_entry.format, expected_format); + } +} diff --git a/src/args.rs b/src/args.rs index cf83691..e8cb07f 100644 --- a/src/args.rs +++ b/src/args.rs @@ -3,17 +3,21 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT indicates dual licensing under Apache 2.0 or MIT licenses. // Copyright ยฉ 2024 LibMake. All rights reserved. -use super::generator::{ - generate_files, generate_from_csv, FileGenerationParams, -}; -use crate::generator::{ - generate_from_json, generate_from_toml, generate_from_yaml, +use super::{ + extract_param, + generator::{ + generate_files, generate_from_csv, generate_from_ini, + generate_from_json, generate_from_toml, generate_from_yaml, + FileGenerationParams, + }, }; use clap::ArgMatches; use std::error::Error; /// Processes the command line arguments provided to the program. /// +/// This function takes in command-line arguments parsed by `clap` and performs actions based on the arguments provided. +/// /// # Arguments /// /// * `matches` - An instance of `clap::ArgMatches` containing the @@ -21,83 +25,139 @@ use std::error::Error; /// /// # Errors /// -/// This function will return an error in the following situations: -/// -/// - If a specified file path for a subcommand (CSV, YAML, JSON, TOML) is invalid or the file cannot be read. -/// - If there is an error in parsing the file contents into the respective file format (CSV, YAML, JSON, TOML). -/// - If there is an error in generating files based on the parameters derived from the command line arguments. +/// This function will return an error if there is an issue with processing the command line arguments or generating files. /// /// # Panics /// -/// This function will panic if a required command line argument is not provided. -/// For example, it will panic if the `csv` subcommand is used without specifying -/// a CSV file path. -/// +/// This function may panic if a required command line argument is not provided. pub fn process_arguments( matches: &ArgMatches, ) -> Result<(), Box> { - // Extracting optional argument values from the parsed matches. - let author = matches.get_one::("author").cloned(); - let build = matches.get_one::("build").cloned(); - let categories = matches.get_one::("categories").cloned(); - let description = matches.get_one::("description").cloned(); - let documentation = - matches.get_one::("documentation").cloned(); - let edition = matches.get_one::("edition").cloned(); - let email = matches.get_one::("email").cloned(); - let homepage = matches.get_one::("homepage").cloned(); - let keywords = matches.get_one::("keywords").cloned(); - let license = matches.get_one::("license").cloned(); - let name = matches.get_one::("name").cloned(); - let output = matches.get_one::("output").cloned(); - let readme = matches.get_one::("readme").cloned(); - let repository = matches.get_one::("repository").cloned(); - let rustversion = matches.get_one::("rustversion").cloned(); - let version = matches.get_one::("version").cloned(); - let website = matches.get_one::("website").cloned(); + match matches.subcommand() { + Some(("file", file_matches)) => { + let file_types = ["csv", "ini", "json", "yaml", "toml"]; + + for file_type in file_types.iter() { + if let Some(value) = + file_matches.get_one::(file_type) + { + match *file_type { + "csv" if !value.trim().is_empty() => { + generate_from_csv(value)? + } + "ini" if !value.trim().is_empty() => { + generate_from_ini(value)? + } + "json" if !value.trim().is_empty() => { + generate_from_json(value)? + } + "yaml" if !value.trim().is_empty() => { + generate_from_yaml(value)? + } + "toml" if !value.trim().is_empty() => { + generate_from_toml(value)? + } + _ => {} + } + } + } + } + Some(("manual", manual_matches)) => { + let params = extract_manual_params(manual_matches)?; + generate_files(params)?; + println!("Template files generated successfully!"); + } + _ => { + eprintln!("No valid subcommand was used. Please use '--help' for usage information."); + std::process::exit(1); + } + } + + Ok(()) +} + +/// Extracts the parameters for manual generation from command line arguments. +pub fn extract_manual_params( + matches: &ArgMatches, +) -> Result> { + let params = FileGenerationParams { + author: extract_param!(matches, "author"), + build: extract_param!(matches, "build"), + categories: extract_param!(matches, "categories"), + description: extract_param!(matches, "description"), + documentation: extract_param!(matches, "documentation"), + edition: extract_param!(matches, "edition"), + email: extract_param!(matches, "email"), + homepage: extract_param!(matches, "homepage"), + keywords: extract_param!(matches, "keywords"), + license: extract_param!(matches, "license"), + name: extract_param!(matches, "name"), + output: extract_param!(matches, "output"), + readme: extract_param!(matches, "readme"), + repository: extract_param!(matches, "repository"), + rustversion: extract_param!(matches, "rustversion"), + version: extract_param!(matches, "version"), + website: extract_param!(matches, "website"), + }; + validate_params(¶ms)?; + Ok(params) +} + +/// Validates the manual generation parameters. +pub fn validate_params( + params: &FileGenerationParams, +) -> Result<(), Box> { + if params.name.is_none() { + return Err("The name of the library is required for manual generation.".into()); + } + + if params.output.is_none() { + return Err( + "The output directory is required for manual generation." + .into(), + ); + } + + if let Some(edition) = ¶ms.edition { + if edition != "2015" && edition != "2018" && edition != "2021" { + return Err(format!("Invalid edition: {}. Supported editions are 2015, 2018, and 2021.", edition).into()); + } + } + + if let Some(rustversion) = ¶ms.rustversion { + if !rustversion.starts_with("1.") { + return Err(format!("Invalid Rust version: {}. Rust version should start with '1.'.", rustversion).into()); + } + } + + if let Some(email) = ¶ms.email { + if !email.contains('@') { + return Err(format!("Invalid email address: {}. Email address should contain '@'.", email).into()); + } + } + + if let Some(repository) = ¶ms.repository { + if !repository.starts_with("https://") + && !repository.starts_with("git://") + { + return Err(format!("Invalid repository URL: {}. Repository URL should start with 'https://' or 'git://'.", repository).into()); + } + } + + if let Some(homepage) = ¶ms.homepage { + if !homepage.starts_with("http://") + && !homepage.starts_with("https://") + { + return Err(format!("Invalid homepage URL: {}. Homepage URL should start with 'http://' or 'https://'.", homepage).into()); + } + } - // Check which subcommand was used and perform the corresponding action. - if matches.contains_id("csv") { - let csv_file_path = matches.get_one::("csv").unwrap(); - generate_from_csv(csv_file_path)?; - } else if let Some(yaml_file_path) = - matches.get_one::("yml") - { - generate_from_yaml(yaml_file_path)?; - } else if let Some(json_file_path) = - matches.get_one::("json") - { - generate_from_json(json_file_path)?; - } else if let Some(toml_file_path) = - matches.get_one::("toml") - { - generate_from_toml(toml_file_path)?; - } else if !matches.args_present() { - // If no subcommand is used and there are additional arguments, - // create a parameter struct and generate files. - let params = FileGenerationParams { - author, - build, - categories, - description, - documentation, - edition, - email, - homepage, - keywords, - license, - name, - output, - readme, - repository, - rustversion, - version, - website, - }; - generate_files(params)?; - println!("\n\nTemplate files generated successfully!"); - } else { - println!("โŒ No arguments provided. Please provide the required arguments to generate the template files."); + if let Some(documentation) = ¶ms.documentation { + if !documentation.starts_with("http://") + && !documentation.starts_with("https://") + { + return Err(format!("Invalid documentation URL: {}. Documentation URL should start with 'http://' or 'https://'.", documentation).into()); + } } Ok(()) diff --git a/src/ascii.rs b/src/ascii.rs index 9791fd6..e850e7d 100644 --- a/src/ascii.rs +++ b/src/ascii.rs @@ -3,6 +3,8 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT indicates dual licensing under Apache 2.0 or MIT licenses. // Copyright ยฉ 2024 LibMake. All rights reserved. +//! This module provides functionality for generating ASCII art from text using the FIGlet library. + use figlet_rs::FIGfont; use std::error::Error; use std::fmt; @@ -19,12 +21,8 @@ pub enum ArtError { impl fmt::Display for ArtError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - Self::FontLoadError => { - write!(f, "Failed to load FIGfont") - } - Self::ConversionError => { - write!(f, "Failed to convert text to ASCII art") - } + Self::FontLoadError => write!(f, "Failed to load FIGfont"), + Self::ConversionError => write!(f, "Failed to convert text to ASCII art"), } } } @@ -41,14 +39,45 @@ impl Error for ArtError {} /// /// This function returns an `Err` in the following situations: /// +/// - If the input `text` is empty (`ConversionError`). /// - If the standard `FIGfont` fails to load (`FontLoadError`). /// - If the text cannot be converted to ASCII art (`ConversionError`). /// +/// # Examples +/// +/// ``` +/// use libmake::ascii::generate_ascii_art; +/// +/// let text = "Hello, world!"; +/// let result = generate_ascii_art(text); +/// assert!(result.is_ok()); +/// ``` pub fn generate_ascii_art(text: &str) -> Result { - let standard_font = - FIGfont::standard().map_err(|_| ArtError::FontLoadError)?; + if text.is_empty() { + return Err(ArtError::ConversionError); + } + + let standard_font = load_standard_font()?; let figure = standard_font .convert(text) .ok_or(ArtError::ConversionError)?; Ok(figure.to_string()) } + +/// Loads the standard FIGfont. +/// +/// # Errors +/// +/// This function returns an `Err` if the standard `FIGfont` fails to load (`FontLoadError`). +/// +/// # Examples +/// +/// ``` +/// use libmake::ascii::load_standard_font; +/// +/// let result = load_standard_font(); +/// assert!(result.is_ok()); +/// ``` +pub fn load_standard_font() -> Result { + FIGfont::standard().map_err(|_| ArtError::FontLoadError) +} diff --git a/src/cli.rs b/src/cli.rs index 8352901..d4c58bc 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -3,7 +3,9 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT indicates dual licensing under Apache 2.0 or MIT licenses. // Copyright ยฉ 2024 LibMake. All rights reserved. -use clap::{Arg, ArgMatches, Command, Error}; +// use std::path::Path; + +use clap::{error::Error, Arg, ArgMatches, Command}; /// Constructs the command-line interface for the application using Clap, /// including all necessary arguments. @@ -20,44 +22,88 @@ use clap::{Arg, ArgMatches, Command, Error}; /// This function will return an error if the command-line argument parsing fails. /// pub fn build() -> Result { - let mut command = Command::new("My Library") + let manual_args = vec![ + create_arg_info("author", Some("Me"), "Sets the author of the library", 'a', "author", "AUTHOR"), + create_arg_info("build", Some("build.rs"), "Sets the build script that is used to perform additional build-time operations.", 'b', "build", "BUILD"), + create_arg_info("categories", Some("['category 1', 'category 2']"), "Sets the categories of the library", 'c', "categories", "CATEGORIES"), + create_arg_info("description", Some("A library for doing things"), "Sets the description of the library", 'd', "description", "DESCRIPTION"), + create_arg_info("documentation", Some("https://lib.rs/crates/my_library"), "Sets the documentation URL of the library", 'u', "documentation", "DOCUMENTATION"), + create_arg_info("edition", Some("2021"), "Sets the edition of the library", 'e', "edition", "EDITION"), + create_arg_info("email", Some("test@test.com"), "Sets the email of the library author", '@', "email", "EMAIL"), + create_arg_info("homepage", Some("https://test.com"), "Sets the homepage of the library", 'p', "homepage", "HOMEPAGE"), + create_arg_info("keywords", Some("['keyword1', 'keyword2']"), "Sets the keywords of the library", 'k', "keywords", "KEYWORDS"), + create_arg_info("license", Some("MIT OR Apache-2.0"), "Sets the license of the library", 'l', "license", "LICENSE"), + create_arg_info("name", Some("my_library"), "Sets the name of the library", 'n', "name", "NAME"), + create_arg_info("output", Some("my_library"), "Sets the output directory for the library", 'o', "output", "OUTPUT"), + create_arg_info("readme", Some("README.md"), "Sets the README file for the library", 'm', "readme", "README"), + create_arg_info("repository", Some("https://github.com/example/my_library"), "Sets the repository URL of the library", 'g', "repository", "REPOSITORY"), + create_arg_info("rustversion", Some("1.75.0"), "Sets the Rust version of the library", 'r', "rustversion", "RUSTVERSION"), + create_arg_info("version", Some("0.2.2"), "Sets the version of the library", 'v', "version", "VERSION"), + create_arg_info("website", Some("https://test.com"), "Sets the website of the library author", 'w', "website", "WEBSITE"), + ]; + + let file_args = vec![ + create_arg_info( + "csv", + Some(""), + "Sets the CSV file to use for generating the library", + 'x', + "csv", + "CSV", + ), + create_arg_info( + "ini", + Some(""), + "Sets the INI file to use for generating the library", + 'i', + "ini", + "INI", + ), + create_arg_info( + "json", + Some(""), + "Sets the JSON file to use for generating the library", + 'j', + "json", + "JSON", + ), + create_arg_info( + "yaml", + Some(""), + "Sets the YAML file to use for generating the library", + 'y', + "yaml", + "YAML", + ), + create_arg_info( + "toml", + Some(""), + "Sets the TOML file to use for generating the library", + 't', + "toml", + "TOML", + ), + ]; + + let command = Command::new("My Library") .author("Sebastien Rousseau") - .about( - "A Rust library generator that helps create high-quality Rust libraries quickly and easily.", + .about("A Rust library generator that helps create high-quality Rust libraries quickly and easily.") + .after_help("By default, if no arguments are passed in, the CLI will throw an error. To see a list of available actions, run `--help`.") + .subcommand( + Command::new("manual") + .about("Set library information manually") + .args(manual_args.into_iter().map(create_arg).collect::>()), ) - .after_help( - "By default, if no arguments are passed in, the CLI will \ - throw an error. To see a list of available actions, run \ - `--help`.", + .subcommand( + Command::new("file") + .about("Set library information from a file") + .args(file_args.into_iter().map(create_arg).collect::>()), ); - let args = vec![ - create_arg_info("author", Some("Me"), "Sets the author of the library", 'a', "author", "AUTHOR"), - create_arg_info("build", Some("build.rs"), "Sets the build script that is used to perform additional build-time operations.", 'b', "build", "BUILD"), - create_arg_info("categories", Some("['category 1', 'category 2']"), "Sets the categories of the library", 'c', "categories", "CATEGORIES"), - create_arg_info("description", Some("A library for doing things"), "Sets the description of the library", 'd', "description", "DESCRIPTION"), - create_arg_info("documentation", Some("https://lib.rs/crates/my_library"), "Sets the documentation URL of the library", 'u', "documentation", "DOCUMENTATION"), - create_arg_info("edition", Some("2021"), "Sets the edition of the library", 'e', "edition", "EDITION"), - create_arg_info("email", Some("test@test.com"), "Sets the email of the library author", '@', "email", "EMAIL"), - create_arg_info("homepage", Some("https://test.com"), "Sets the homepage of the library", 'p', "homepage", "HOMEPAGE"), - create_arg_info("keywords", Some("['keyword1', 'keyword2']"), "Sets the keywords of the library", 'k', "keywords", "KEYWORDS"), - create_arg_info("license", Some("MIT OR Apache-2.0"), "Sets the license of the library", 'l', "license", "LICENSE"), - create_arg_info("name", Some("my_library"), "Sets the name of the library", 'n', "name", "NAME"), - create_arg_info("output", Some("my_library"), "Sets the output directory for the library", 'o', "output", "OUTPUT"), - create_arg_info("readme", Some("README.md"), "Sets the README file for the library", 'm', "readme", "README"), - create_arg_info("repository", Some("https://github.com/example/my_library"), "Sets the repository URL of the library", 'g', "repository", "REPOSITORY"), - create_arg_info("rustversion", Some("1.71.1"), "Sets the Rust version of the library", 'r', "rustversion", "RUSTVERSION"), - create_arg_info("version", Some("0.2.1"), "Sets the version of the library", 'v', "version", "VERSION"), - create_arg_info("website", Some("https://test.com"), "Sets the website of the library author", 'w', "website", "WEBSITE"), - create_arg_info("csv", Some(""), "Sets the CSV file to use for generating the library", 'x', "csv", "CSV"), - create_arg_info("json", Some(""), "Sets the JSON file to use for generating the library", 'j', "json", "JSON"), - create_arg_info("yaml", Some(""), "Sets the YAML file to use for generating the library", 'y', "yaml", "YAML"), - create_arg_info("toml", Some(""), "Sets the TOML file to use for generating the library", 't', "toml", "TOML"), - ]; - for arg in args { - command = command.arg(create_arg(arg)); - } - Ok(command.get_matches()) + // Assuming validate_args is a custom function that you have implemented + let matches = command.clone().try_get_matches()?; + + Ok(matches) } /// Helper function to create a command-line argument. @@ -80,7 +126,7 @@ const fn create_arg_info( } /// Creates an argument based on provided information. -fn create_arg( +pub fn create_arg( arg_info: ( &'static str, Option<&'static str>, diff --git a/src/generator.rs b/src/generator.rs index 4b167cf..8cf741d 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -5,8 +5,6 @@ use super::interface::replace_placeholders; use serde::{Deserialize, Serialize}; -use serde_json; -use serde_yaml; use std::{ fs, io, path::{Path, PathBuf}, @@ -100,7 +98,7 @@ impl FileGenerationParams { repository: Some( "https://github.com/example/my_library".to_string(), ), - rustversion: Some("1.71.1".to_string()), + rustversion: Some("1.75.0".to_string()), version: Some("0.1.0".to_string()), website: Some("https://example.com/john-smith".to_string()), } @@ -223,28 +221,27 @@ pub fn create_template_folder() -> io::Result<()> { // println!("Creating template directory: {:?}", template_dir_path); create_directory(&template_dir_path)?; let url = "https://raw.githubusercontent.com/sebastienrousseau/libmake/main/template/"; - let files = - [ - "AUTHORS.tpl", - "build.tpl", - "Cargo.tpl", - "ci.tpl", - "CONTRIBUTING.tpl", - "criterion.tpl", - "deepsource.tpl", - "deny.tpl", - "example.tpl", - "gitignore.tpl", - "lib.tpl", - "loggers.tpl", - "macros.tpl", - "main.tpl", - "README.tpl", - "rustfmt.tpl", - "TEMPLATE.tpl", - "test.tpl", - "test_loggers.tpl", - ]; + let files = [ + "AUTHORS.tpl", + "build.tpl", + "Cargo.tpl", + "ci.tpl", + "CONTRIBUTING.tpl", + "criterion.tpl", + "deepsource.tpl", + "deny.tpl", + "example.tpl", + "gitignore.tpl", + "lib.tpl", + "loggers.tpl", + "macros.tpl", + "main.tpl", + "README.tpl", + "rustfmt.tpl", + "TEMPLATE.tpl", + "test.tpl", + "test_loggers.tpl", + ]; for file in &files { let file_path = template_dir_path.join(file); // Check if the file already exists @@ -268,7 +265,7 @@ pub fn create_template_folder() -> io::Result<()> { })?; // Write the file contents, trimming any leading or trailing newline characters - std::fs::write( + fs::write( &file_path, file_contents .trim_start_matches('\n') @@ -351,15 +348,14 @@ pub fn generate_files(params: FileGenerationParams) -> io::Result<()> { create_template_folder()?; // Define the subdirectories to be created within the project directory - let subdirectories = - [ - "src", - "benches", - "examples", - "tests", - ".github/", - ".github/workflows", - ]; + let subdirectories = [ + "src", + "benches", + "examples", + "tests", + ".github/", + ".github/workflows", + ]; // Iterate over the subdirectories and create them for subdir in &subdirectories { @@ -471,12 +467,13 @@ pub fn generate_from_config( ) -> io::Result<()> { match file_type { "csv" => generate_from_csv(path), + "ini" => generate_from_ini(path), "json" => generate_from_json(path), - "yaml" | "yml" => generate_from_yaml(path), + "yaml" => generate_from_yaml(path), "toml" => generate_from_toml(path), _ => Err(io::Error::new( io::ErrorKind::InvalidInput, - "Invalid configuration file format. Supported formats: CSV, JSON, TOML, YAML.", + "Invalid configuration file format. Supported formats: CSV, INI, JSON, TOML, YAML.", )), } } @@ -513,55 +510,29 @@ pub fn generate_from_config( /// - If an error occurs while parsing the CSV data into the `FileGenerationParams` struct. /// - If there is an error in generating files based on the parameters from each CSV record. /// -pub fn generate_from_csv(csv_path: &str) -> io::Result<()> { - let mut reader = csv::Reader::from_path(csv_path)?; +pub fn generate_from_csv(path: &str) -> io::Result<()> { + let mut reader = csv::Reader::from_path(path)?; for result in reader.records() { let record = result?; // println!("{:?}", record); let params = FileGenerationParams { - author: record.get(0).map(std::string::ToString::to_string), - build: record.get(1).map(std::string::ToString::to_string), - categories: record - .get(2) - .map(std::string::ToString::to_string), - description: record - .get(3) - .map(std::string::ToString::to_string), - documentation: record - .get(4) - .map(std::string::ToString::to_string), - edition: record - .get(5) - .map(std::string::ToString::to_string), - email: record.get(6).map(std::string::ToString::to_string), - homepage: record - .get(7) - .map(std::string::ToString::to_string), - keywords: record - .get(8) - .map(std::string::ToString::to_string), - license: record - .get(9) - .map(std::string::ToString::to_string), - name: record.get(10).map(std::string::ToString::to_string), - output: record - .get(11) - .map(std::string::ToString::to_string), - readme: record - .get(12) - .map(std::string::ToString::to_string), - repository: record - .get(13) - .map(std::string::ToString::to_string), - rustversion: record - .get(14) - .map(std::string::ToString::to_string), - version: record - .get(15) - .map(std::string::ToString::to_string), - website: record - .get(16) - .map(std::string::ToString::to_string), + author: record.get(0).map(ToString::to_string), + build: record.get(1).map(ToString::to_string), + categories: record.get(2).map(ToString::to_string), + description: record.get(3).map(ToString::to_string), + documentation: record.get(4).map(ToString::to_string), + edition: record.get(5).map(ToString::to_string), + email: record.get(6).map(ToString::to_string), + homepage: record.get(7).map(ToString::to_string), + keywords: record.get(8).map(ToString::to_string), + license: record.get(9).map(ToString::to_string), + name: record.get(10).map(ToString::to_string), + output: record.get(11).map(ToString::to_string), + readme: record.get(12).map(ToString::to_string), + repository: record.get(13).map(ToString::to_string), + rustversion: record.get(14).map(ToString::to_string), + version: record.get(15).map(ToString::to_string), + website: record.get(16).map(ToString::to_string), }; // println!("Params: {:?}", params); generate_files(params)?; @@ -602,7 +573,7 @@ pub fn generate_from_csv(csv_path: &str) -> io::Result<()> { /// - If the JSON data cannot be deserialized into the `FileGenerationParams` struct. /// - If there is an error in generating files based on the parameters. /// -pub fn generate_from_json(path: &str) -> std::io::Result<()> { +pub fn generate_from_json(path: &str) -> io::Result<()> { let contents = fs::read_to_string(path)?; let params: FileGenerationParams = serde_json::from_str(&contents)?; generate_files(params)?; @@ -640,12 +611,48 @@ pub fn generate_from_json(path: &str) -> std::io::Result<()> { /// - If the YAML data cannot be deserialized into the `FileGenerationParams` struct. /// - If there is an error in generating files based on the parameters. /// -pub fn generate_from_yaml(path: &str) -> std::io::Result<()> { +pub fn generate_from_yaml(path: &str) -> io::Result<()> { let contents = fs::read_to_string(path)?; let params: FileGenerationParams = serde_yaml::from_str(&contents) - .map_err( - |e| std::io::Error::new(std::io::ErrorKind::Other, e) - )?; + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + generate_files(params)?; + Ok(()) +} + +/// Generates files for a new Rust project based on an INI file. +/// +/// The INI file must contain a single section with the following +/// keys: +/// - `author` - the author of the project (optional). +/// - `build` - the build command to be used for building the project (optional). +/// - `categories` - the categories that the project belongs to (optional). +/// - `description` - a short description of the project (optional). +/// - `documentation` - the documentation URL of the project (optional). +/// - `edition` - the edition of the project (optional). +/// - `email` - the email address of the author (optional). +/// - `homepage` - the homepage of the project (optional). +/// - `keywords` - keywords that describe the project (optional). +/// - `license` - the license under which the project is released (optional). +/// - `name` - the name of the project (optional). +/// - `output` - the output directory where the project files will be created (required). +/// - `readme` - the name of the readme file (optional). +/// - `repository` - the url of the project's repository (optional). +/// - `rustversion` - the minimum Rust version required by the project (optional). +/// - `version` - the initial version of the project (optional). +/// - `website` - the website of the project (optional). +/// +/// # Errors +/// +/// This function will return an error in the following situations: +/// +/// - If the specified INI file cannot be found, read, or is not valid UTF-8. +/// - If the INI data cannot be parsed into the `FileGenerationParams` struct. +/// - If there is an error in generating files based on the parameters. +/// +pub fn generate_from_ini(path: &str) -> io::Result<()> { + let contents = fs::read_to_string(path)?; + let params: FileGenerationParams = toml::from_str(&contents) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; generate_files(params)?; Ok(()) } @@ -681,12 +688,10 @@ pub fn generate_from_yaml(path: &str) -> std::io::Result<()> { /// - If the TOML data cannot be deserialized into the `FileGenerationParams` struct. /// - If there is an error in generating files based on the parameters. /// -pub fn generate_from_toml(path: &str) -> std::io::Result<()> { +pub fn generate_from_toml(path: &str) -> io::Result<()> { let contents = fs::read_to_string(path)?; let params: FileGenerationParams = toml::from_str(&contents) - .map_err( - |e| std::io::Error::new(std::io::ErrorKind::Other, e) - )?; + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; generate_files(params)?; Ok(()) } @@ -719,7 +724,7 @@ pub fn generate_from_toml(path: &str) -> std::io::Result<()> { /// - If an invalid argument is provided. Each argument must be in the form `--name=value`. /// - If there is an error in generating files based on the parameters derived from the arguments. /// -pub fn generate_from_args(args_str: &str) -> std::io::Result<()> { +pub fn generate_from_args(args_str: &str) -> io::Result<()> { let args = args_str.split_whitespace(); let mut params = FileGenerationParams::default(); for arg in args { @@ -755,8 +760,8 @@ pub fn generate_from_args(args_str: &str) -> std::io::Result<()> { "--version" => params.version = Some(value.to_string()), "--website" => params.website = Some(value.to_string()), _ => { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, + return Err(io::Error::new( + io::ErrorKind::Other, format!("Invalid argument: {name}"), )) } diff --git a/src/lib.rs b/src/lib.rs index 4c8d1ed..46db36f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,7 +15,7 @@ //! //! [![Rust](https://img.shields.io/badge/rust-f04041?style=for-the-badge&labelColor=c0282d&logo=rust)](https://www.rust-lang.org) //! [![Crates.io](https://img.shields.io/crates/v/libmake.svg?style=for-the-badge&color=success&labelColor=27A006)](https://crates.io/crates/libmake) -//! [![Lib.rs](https://img.shields.io/badge/lib.rs-v0.2.1-success.svg?style=for-the-badge&color=8A48FF&labelColor=6F36E4)](https://lib.rs/crates/libmake) +//! [![Lib.rs](https://img.shields.io/badge/lib.rs-v0.2.2-success.svg?style=for-the-badge&color=8A48FF&labelColor=6F36E4)](https://lib.rs/crates/libmake) //! [![GitHub](https://img.shields.io/badge/github-555555?style=for-the-badge&labelColor=000000&logo=github)](https://github.com/sebastienrousseau/libmake) //! [![License](https://img.shields.io/crates/l/libmake.svg?style=for-the-badge&color=007EC6&labelColor=03589B)](http://opensource.org/licenses/MIT) //! @@ -82,12 +82,10 @@ use crate::args::process_arguments; use crate::ascii::generate_ascii_art; use crate::cli::build; -use crate::loggers::init_logger; use dtt::DateTime; -use rlg::{macro_log, LogFormat, LogLevel}; -use std::error::Error; -use std::fs::File; -use std::io::Write; +use rlg::{log_format::LogFormat, log_level::LogLevel, macro_log}; +use std::{error::Error, fs::File, io::Write}; +use uuid::Uuid; /// The `args` module contains functions for processing command-line /// arguments. @@ -103,8 +101,6 @@ pub mod generator; /// The `interface` module contains functions for displaying the /// interface. pub mod interface; -/// The `loggers` module contains the loggers for the library. -pub mod loggers; /// The `macros` module contains functions for generating macros. pub mod macros; /// The `utils` module contains a function for reading a CSV file at the @@ -114,16 +110,6 @@ pub mod utils; /// Initializes the logger with a file logger and a terminal logger and processes /// command-line arguments to generate the new library. /// -/// # Examples -/// -/// ``` -/// use libmake::run; -/// -/// if let Err(e) = run() { -/// eprintln!("Application error: {}", e); -/// } -/// ``` -/// /// # Errors /// /// This function will return an error in the following situations: @@ -135,16 +121,14 @@ pub mod utils; pub fn run() -> Result<(), Box> { let date = DateTime::new(); let iso = date.iso_8601; - - // Initialize the logger using the `env_logger` crate - init_logger(None)?; + let uuid = Uuid::new_v4().to_string(); // Open the log file for appending - let mut log_file = File::create("./ssg.log")?; + let mut log_file = File::create("./libmake.log")?; // Generate ASCII art for the tool's CLI let log = macro_log!( - "id", + &uuid, &iso, &LogLevel::INFO, "deps", @@ -159,7 +143,7 @@ pub fn run() -> Result<(), Box> { Err(e) => eprintln!("Error generating ASCII art: {:?}", e), } let log = macro_log!( - "id", + &uuid, &iso, &LogLevel::INFO, "deps", @@ -174,11 +158,17 @@ pub fn run() -> Result<(), Box> { process_arguments(&matches)?; // Check the number of arguments, provide a welcome message if no arguments were passed - if std::env::args().len() == 1 { - eprintln!( - "\n\nWelcome to LibMake! ๐Ÿ‘‹\n\nLet's get started! Please, run `libmake --help` for more information.\n" - ); - } + macro_log!( + &uuid, + &iso, + &LogLevel::INFO, + "cli", + "Welcome to LibMake! ๐Ÿ‘‹\n\nLet's get started! Please, run `libmake --help` for more information.", + &LogFormat::CLF + ); + eprintln!( + "\n\nWelcome to LibMake! ๐Ÿ‘‹\n\nLet's get started! Please, run `libmake --help` for more information.\n" + ); Ok(()) } diff --git a/src/macros.rs b/src/macros.rs index 270538f..4bb0a47 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -122,7 +122,7 @@ macro_rules! assert_generate_from_yaml { /// # Arguments /// /// * `$path` - The path to the configuration file. -/// * `$file_type` - The type of the configuration file: `json`, `yaml`, `yml`, or `csv`. +/// * `$file_type` - The type of the configuration file: `json`, `yaml` or `csv`. /// /// # Panics /// @@ -236,3 +236,25 @@ macro_rules! macro_execute_and_log { Ok(()) }}; } + +/// Extracts a parameter from a `Matches` object. +/// +/// This macro takes two arguments: `$matches` and `$name`. It attempts to retrieve the value +/// associated with `$name` from the `$matches` object. If the value is found and is of type `String`, +/// it returns a `Some` variant containing a cloned copy of the value. Otherwise, it returns `None`. +/// +/// # Arguments +/// +/// * `$matches` - A `Matches` object that contains the parameter values. +/// * `$name` - The name of the parameter to extract. +/// +/// # Returns +/// +/// A `Option` containing the extracted parameter value, or `None` if the parameter is not found +/// or is not of type `String`. +#[macro_export] +macro_rules! extract_param { + ($matches:expr, $name:expr) => { + $matches.get_one::($name).map(|s| s.to_owned()) + }; +} diff --git a/src/main.rs b/src/main.rs index 6e61a0c..307b780 100644 --- a/src/main.rs +++ b/src/main.rs @@ -44,11 +44,10 @@ fn main() { std::process::exit(1); } } - - // Handle errors from `run()` - if let Err(ref err) = run() { - // Renamed error variable for clarity - eprintln!("Error running libmake: {err}"); - std::process::exit(1); + else { + match run() { + Ok(_) => println!("Program completed successfully."), + Err(e) => eprintln!("Program encountered an error: {}", e), + } } } diff --git a/src/utils.rs b/src/utils.rs index ec72866..23c99e8 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -3,35 +3,71 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT indicates dual licensing under Apache 2.0 or MIT licenses. // Copyright ยฉ 2024 LibMake. All rights reserved. +use serde_json::Value; +use serde_yaml::from_reader; +use serde_yaml::{from_str, to_string, Value as YamlValue}; use std::env; +use std::fs; use std::fs::File; +use std::io::BufReader; use std::path::Path; +/// Reads a file and deserializes its content using the specified deserializer function. +/// +/// # Arguments +/// +/// * `file_path` - The path of the file to read. +/// * `deserializer` - A function that takes a `File` and returns a deserialized value of type `T`. +/// +/// # Returns +/// +/// Returns a `Result>` containing the deserialized value, or an error if one occurs. +/// +fn read_file( + file_path: &Path, + deserializer: F, +) -> Result> +where + F: FnOnce(File) -> Result>, +{ + let file = File::open(file_path)?; + deserializer(file) +} + /// Reads a CSV file at the given file path and returns the value of /// the given field. /// /// # Arguments /// /// * `file_path` - An optional string slice that holds the file path of the CSV file to read. -/// * `field_name` - A string slice that holds the name of the field to retrieve. +/// * `field_index` - The index of the field to retrieve. /// pub fn get_csv_field( - file: Option<&str>, + file_path: Option<&str>, field_index: usize, ) -> Option> { - let current_dir = env::current_dir().ok()?; - let file_path = Path::new(¤t_dir).join(file?); - let file = File::open(file_path).ok()?; - let mut rdr = csv::Reader::from_reader(file); - let mut values = Vec::new(); - for result in rdr.records() { - let record = result.ok()?; - let field_value = record.get(field_index).unwrap_or(""); - values.push(field_value.to_string()); - println!("Value: {field_value}"); - } - println!("Values: {values:?}"); - Some(values) + file_path.and_then(|file_path| { + let current_dir = env::current_dir().ok()?; + let file_path = Path::new(¤t_dir).join(file_path); + let file = File::open(file_path).ok()?; + let mut rdr = csv::Reader::from_reader(file); + + let mut values = Vec::new(); + for result in rdr.records() { + let record = result.ok()?; + if let Some(field_value) = record.get(field_index) { + values.push(field_value.to_string()); + } else { + // Field index is out of range + return None; + } + } + if values.is_empty() { + None + } else { + Some(values) + } + }) } /// Retrieves a specific field's value from a JSON file. @@ -43,24 +79,33 @@ pub fn get_csv_field( /// /// # Returns /// -/// Returns a `String` containing the value of the specified field. -/// -/// # Panics -/// -/// Panics if the file specified by `file_path` cannot be opened or if the content of the file -/// is not valid JSON. This can happen due to various reasons like the file not existing, -/// being unreadable, or JSON parsing failures. +/// Returns a `Result>` containing the value of the specified field, or an error if one occurs. /// pub fn get_json_field( file_path: Option<&str>, field_name: &str, -) -> String { - file_path.map_or_else(String::new, |file_path| { - let file = File::open(Path::new(file_path)).unwrap(); - let json: serde_json::Value = - serde_json::from_reader(file).unwrap(); - json[field_name].to_string() - }) +) -> Result> { + file_path.map_or_else( + || Ok(String::new()), + |file_path| { + let current_dir = env::current_dir()?; + let file_path = Path::new(¤t_dir).join(file_path); + read_file(&file_path, |file| { + let json: Value = serde_json::from_reader(file)?; + let json_value = json[field_name].clone(); + let field_value = match json_value.as_str() { + Some(s) => s.to_string(), + None => { + let binding = json_value.to_string(); + let trimmed_binding = + binding.trim_matches('"').to_string(); + trimmed_binding + } + }; + Ok(field_value) + }) + }, + ) } /// Retrieves a specific field's value from a YAML file. @@ -72,75 +117,81 @@ pub fn get_json_field( /// /// # Returns /// -/// Returns a `String` containing the value of the specified field. -/// -/// # Panics -/// -/// Panics if: -/// -/// - The file specified by `file_path` cannot be opened. -/// - The file content is not valid YAML or does not contain the specified `field_name`. -/// - Fails to serialize the YAML content to a string. +/// Returns a `Result>` containing the value of the specified field, or an error if one occurs. /// pub fn get_yaml_field( file_path: Option<&str>, field_name: &str, -) -> String { - file_path.map_or_else(String::new, |file_path| { - let file = - match File::open(Path::new(file_path)) { - Ok(file) => file, - Err(e) => { - eprintln!("Error opening file: {e}"); - return String::new(); // Return a default value on error - } - }; - - let yaml: serde_yaml::Value = - match serde_yaml::from_reader(file) { - Ok(data) => data, - Err(e) => { - eprintln!("Error reading YAML: {e}"); - return String::new(); // or handle the error as appropriate - } - }; - let field_value = &yaml[field_name]; - let field_value_str = - serde_yaml::to_string(&field_value).unwrap(); - field_value_str.trim().to_string() - }) +) -> Result> { + file_path.map_or_else( + || Ok(String::new()), + |file_path| { + let current_dir = env::current_dir()?; + let file_path = Path::new(¤t_dir).join(file_path); + read_file(&file_path, |file| { + let yaml: serde_yaml::Value = from_reader(file)?; + println!("YAML Value: {:?}", yaml); + let field_value = &yaml[field_name]; + println!("Field Value: {:?}", field_value); + let field_value_str = to_string(&field_value)?; + Ok(field_value_str.trim().to_string()) + }) + }, + ) } -/// Retrieves a specific field's value from a JSON file. +/// Retrieves a specific field's value from a configuration file. /// /// # Arguments /// -/// * `file_path` - An optional reference to the path of the JSON file. +/// * `file_path` - An optional reference to the path of the configuration file. +/// * `file_format` - The format of the configuration file ("json" or "yaml"). /// * `field_name` - The name of the field to retrieve the value from. /// /// # Returns /// -/// Returns a `String` containing the value of the specified field. -/// -/// # Panics -/// -/// Panics if: -/// -/// - The file specified by `file_path` cannot be opened. -/// - The file content is not valid JSON or does not contain the specified `field_name`. +/// Returns a `Result>` containing the value of the specified field, or an error if one occurs. /// pub fn get_config_field( file_path: Option<&str>, - file_type: Option<&str>, + file_format: Option<&str>, field_name: &str, -) -> String { - file_type.map_or_else( - String::new, // Provide a default value for the None case - |file_type| match file_type { - // Transform the value in the Some case - "json" => get_json_field(file_path, field_name), - "yaml" => get_yaml_field(file_path, field_name), - _ => String::new(), +) -> Result> { + // Ensure file_path is provided + let file_path = file_path.ok_or("File path is not provided")?; + + // Ensure file_format is provided and is either 'json' or 'yaml' + let format = file_format.ok_or("File format is not provided")?; + match format { + "json" => { + // Read JSON file and extract field value + let file = File::open(file_path)?; + let reader = BufReader::new(file); + let json: Value = serde_json::from_reader(reader)?; + let field_value = json.get(field_name).ok_or("Field not found in JSON")?; + + // Handle JSON string values to remove surrounding quotes + match field_value.as_str() { + Some(s) => Ok(s.to_string()), + None => { + let s = field_value.to_string(); + Ok(s.trim_matches('"').to_string()) + } + } }, - ) + "yaml" => { + let yaml_str = fs::read_to_string(file_path)?; + let yaml_value: YamlValue = from_str(&yaml_str)?; + let field_value = yaml_value.get(field_name).ok_or("Field not found in YAML")?; + + // Convert the field_value to a string + let field_value_str = match field_value { + YamlValue::String(s) => s.clone(), + _ => to_string(field_value).unwrap(), + }; + + Ok(field_value_str) + } + _ => Err(format!("Unsupported file format: {}. Supported formats are 'json' and 'yaml'.", format).into()) + } } diff --git a/template/Cargo.tpl b/template/Cargo.tpl index c3e6ebb..71ddc5c 100644 --- a/template/Cargo.tpl +++ b/template/Cargo.tpl @@ -41,15 +41,15 @@ path = "benches/criterion.rs" debug = true [dependencies] -anyhow = "1.0.79" +anyhow = "1.0.81" dtt = "0.0.5" -env_logger = "0.10.1" -serde = { version = "1.0.195", features = ["derive"] } -serde_json = "1.0.111" -serde_yaml = "0.9.30" -toml = "0.8.8" -vrd = "0.0.5" -rlg = "0.0.2" +env_logger = "0.11.3" +rlg = "0.0.3" +serde = { version = "1.0.197", features = ["derive"] } +serde_json = "1.0.114" +serde_yaml = "0.9.33" +toml = "0.8.12" +vrd = "0.0.6" [dev-dependencies] criterion = "0.5.1" @@ -65,6 +65,42 @@ default = [] [package.metadata.docs.rs] all-features = true +# Linting config +[lints.rust] + +## Forbid +missing_debug_implementations = "forbid" +missing_docs = "warn" +non_ascii_idents = "forbid" +unreachable_pub = "forbid" +unsafe_code = "forbid" + +## Deny +dead_code = "deny" +deprecated_in_future = "deny" +ellipsis_inclusive_range_patterns = "deny" +explicit_outlives_requirements = "deny" +future_incompatible = "deny" +keyword_idents = "deny" +macro_use_extern_crate = "deny" +meta_variable_misuse = "deny" +missing_fragment_specifier = "deny" +noop_method_call = "deny" +pointer_structural_match = "deny" +rust_2018_idioms = "deny" +rust_2021_compatibility = "deny" +single_use_lifetimes = "deny" +trivial_casts = "deny" +trivial_numeric_casts = "deny" +unused = "deny" +unused_features = "deny" +unused_import_braces = "deny" +unused_labels = "deny" +unused_lifetimes = "deny" +unused_macro_rules = "deny" +unused_qualifications = "deny" +variant_size_differences = "deny" + [profile.dev] codegen-units = 256 debug = true diff --git a/tests/data/mylibrary.csv b/tests/data/mylibrary.csv index 1a127f6..eaf2260 100644 --- a/tests/data/mylibrary.csv +++ b/tests/data/mylibrary.csv @@ -1,2 +1,2 @@ author,build,categories,description,documentation,edition,email,homepage,keywords,license,name,output,readme,repository,rustversion,version,website -Me,build.rs,"['category 1', 'category 2']",A library for doing things,https://lib.rs/crates/my_library,2021,test@test.com,https://test.com,"['keyword1', 'keyword2']",MIT OR Apache-2.0,my_library,my_library,README.md,https://github.com/test/test,1.71.1,0.2.1,https://test.com +Me,build.rs,"['category 1', 'category 2']",A library for doing things,https://lib.rs/crates/my_library,2021,test@test.com,https://test.com,"['keyword1', 'keyword2']",MIT OR Apache-2.0,my_library,my_library,README.md,https://github.com/test/test,1.75.0,0.2.2,https://test.com diff --git a/tests/data/mylibrary.ini b/tests/data/mylibrary.ini new file mode 100644 index 0000000..1c1b568 --- /dev/null +++ b/tests/data/mylibrary.ini @@ -0,0 +1,17 @@ +author = "Me" +build = "build.rs" +categories = "'category 1','category 2','category 3'" +description = "A library for doing things" +documentation = "https://lib.rs/crates/my_library" +edition = "2021" +email = "test@test.com" +homepage = "https://test.com" +keywords = "'keyword 1','keyword 2','keyword 3'" +license = "MIT OR Apache-2.0" +name = "my_library" +output = "my_library" +readme = "README.md" +repository = "https://github.com/test/test" +rustversion = "1.75.0" +version = "0.2.2" +website = "https://test.com" diff --git a/tests/data/mylibrary.json b/tests/data/mylibrary.json index 4bb1a1e..41f29db 100644 --- a/tests/data/mylibrary.json +++ b/tests/data/mylibrary.json @@ -13,7 +13,7 @@ "output": "my_library", "readme": "README.md", "repository": "https://github.com/test/test", - "rustversion": "1.71.1", - "version": "0.2.1", + "rustversion": "1.75.0", + "version": "0.2.2", "website": "https://test.com" } diff --git a/tests/data/mylibrary.toml b/tests/data/mylibrary.toml index 988e105..1c1b568 100644 --- a/tests/data/mylibrary.toml +++ b/tests/data/mylibrary.toml @@ -12,6 +12,6 @@ name = "my_library" output = "my_library" readme = "README.md" repository = "https://github.com/test/test" -rustversion = "1.71.1" -version = "0.2.1" +rustversion = "1.75.0" +version = "0.2.2" website = "https://test.com" diff --git a/tests/data/mylibrary.yaml b/tests/data/mylibrary.yaml index 4fda192..8072147 100644 --- a/tests/data/mylibrary.yaml +++ b/tests/data/mylibrary.yaml @@ -7,11 +7,11 @@ edition: '2021' email: test@test.com homepage: https://test.com keywords: "'keyword 1','keyword 2','keyword 3'" -license: MIT OR Apache-2.0 +license: 'MIT OR Apache-2.0' name: my_library output: my_library readme: README.md repository: https://github.com/test/test -rustversion: '1.71.1' -version: '0.2.1' +rustversion: '1.75.0' +version: '0.2.2' website: https://test.com diff --git a/tests/test_args.rs b/tests/test_args.rs index 51e440f..ca52ce8 100644 --- a/tests/test_args.rs +++ b/tests/test_args.rs @@ -1,263 +1,527 @@ -#[cfg(test)] -mod tests { - use std::{fs, path::Path}; +use clap::{Arg, Command}; +use libmake::args::extract_manual_params; +use libmake::{ + args::{process_arguments, validate_params}, + generator::FileGenerationParams, +}; - use clap::{Arg, Command}; - use libmake::{ - args::process_arguments, - generator::{ - generate_from_csv, generate_from_json, generate_from_toml, - generate_from_yaml, FileGenerationParams, - }, +// Tests the process_arguments function with valid arguments +#[test] +fn test_process_arguments() { + let matches = Command::new("libmake") + .subcommand( + Command::new("file") + .arg( + Arg::new("csv") + .long("csv") + .value_name("FILE") + .default_value("tests/data/mylibrary.csv"), + ) + .arg( + Arg::new("ini") + .long("ini") + .value_name("FILE") + .default_value("tests/data/mylibrary.ini"), + ) + .arg( + Arg::new("json") + .long("json") + .value_name("FILE") + .default_value("tests/data/mylibrary.json"), + ) + .arg( + Arg::new("yaml") + .long("yaml") + .value_name("FILE") + .default_value("tests/data/mylibrary.yaml"), + ) + .arg( + Arg::new("toml") + .long("toml") + .value_name("FILE") + .default_value("tests/data/mylibrary.toml"), + ), + ) + .get_matches_from(vec![ + "libmake", + "file", + "--csv", + "tests/data/mylibrary.csv", + "--ini", + "tests/data/mylibrary.ini", + "--json", + "tests/data/mylibrary.json", + "--yaml", + "tests/data/mylibrary.yaml", + "--toml", + "tests/data/mylibrary.toml", + ]); + + let result = process_arguments(&matches); + assert!(result.is_ok()); +} + +// Tests the validation of parameters with an invalid edition +#[test] +fn test_validate_params_invalid_edition() { + let params = FileGenerationParams { + name: Some("test_lib".to_string()), + output: Some("output_dir".to_string()), + author: None, + build: None, + categories: None, + description: None, + documentation: None, + edition: Some("2023".to_string()), // Invalid edition + email: None, + homepage: None, + keywords: None, + license: None, + readme: None, + repository: None, + rustversion: None, + version: None, + website: None, }; - // Tests the `generate_from_csv` function by passing a valid CSV - // file path as input and asserting that the result is an Ok value, - // indicating that the template files were generated successfully - // from the CSV file. - #[allow(clippy::uninlined_format_args)] - #[test] - fn test_generate_from_csv() { - let csv_file = "./tests/data/mylibrary.csv"; - let result = generate_from_csv(csv_file); - assert!( - result.is_ok(), - "generate_from_csv was expected to return an Ok result, but it returned an error" - ); - } + let result = validate_params(¶ms); - // Tests the `generate_from_json` function by passing a valid JSON - // file path as input and asserting that the result is an Ok value, - // indicating that the template files were generated successfully - // from the JSON file. - #[test] - fn test_generate_from_json() { - let json_file = "./tests/data/mylibrary.json"; - let result = generate_from_json(json_file); - assert!( - result.is_ok(), - "generate_from_json was expected to return an Ok result, but it returned an error" - ); - } + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().to_string(), + "Invalid edition: 2023. Supported editions are 2015, 2018, and 2021.".to_string() + ); +} - // Tests the `generate_from_toml` function by passing a valid TOML - // file path as input and asserting that the result is an Ok value, - // indicating that the template files were generated successfully - // from the TOML file. - #[test] - fn test_generate_from_toml() { - let toml_file = "./tests/data/mylibrary.toml"; - let result = generate_from_toml(toml_file); - assert!( - result.is_ok(), - "generate_from_toml was expected to return an Ok result, but it returned an error" - ); - } +// Tests the validation of parameters with an invalid documentation URL +#[test] +fn test_validate_params_invalid_documentation() { + let params = FileGenerationParams { + name: Some("test_lib".to_string()), + output: Some("output_dir".to_string()), + author: None, + build: None, + categories: None, + description: None, + documentation: Some("123".to_string()), // Invalid documentation + edition: None, + email: None, + homepage: None, + keywords: None, + license: None, + readme: None, + repository: None, + rustversion: None, + version: None, + website: None, + }; + let result = validate_params(¶ms); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().to_string(), + "Invalid documentation URL: 123. Documentation URL should start with 'http://' or 'https://'.".to_string() + ); +} - // Tests the `generate_from_yaml` function by passing a valid YAML - // file path as input and asserting that the result is an Ok value, - // indicating that the template files were generated successfully - // from the YAML file. - #[test] - fn test_generate_from_yaml() { - let yaml_file = "./tests/data/mylibrary.yaml"; - let result = generate_from_yaml(yaml_file); - assert!( - result.is_ok(), - "generate_from_yaml was expected to return an Ok result, but it returned an error" - ); - } +// Tests the validation of parameters with an invalid email address +#[test] +fn test_validate_params_invalid_email() { + let params = FileGenerationParams { + name: Some("test_lib".to_string()), + output: Some("output_dir".to_string()), + author: None, + build: None, + categories: None, + description: None, + documentation: None, + edition: None, + email: Some("".to_string()), // Invalid email + homepage: None, + keywords: None, + license: None, + readme: None, + repository: None, + rustversion: None, + version: None, + website: None, + }; + let result = validate_params(¶ms); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().to_string(), + "Invalid email address: . Email address should contain '@'.".to_string() + ); +} - // Tests to verify that the process_arguments function correctly - // handles the arguments when "csv" argument is passed. It should - // extract the file path and call generate_from_csv function with - // the file path. Finally, it should generate the expected files in - // the current directory. - #[allow(clippy::manual_assert)] - #[test] - fn test_process_arguments_with_csv() { - let file_path = "./tests/data/mylibrary.csv"; - let matches = Command::new("myapp") - .arg(Arg::new("author").short('a').long("author")) - .arg(Arg::new("build").short('b').long("build")) - .arg(Arg::new("categories").short('C').long("categories")) - .arg(Arg::new("description").short('d').long("description")) - .arg( - Arg::new("documentation") - .short('D') - .long("documentation"), - ) - .arg(Arg::new("edition").short('e').long("edition")) - .arg(Arg::new("email").short('E').long("email")) - .arg(Arg::new("homepage").short('p').long("homepage")) - .arg(Arg::new("keywords").short('k').long("keywords")) - .arg(Arg::new("license").short('l').long("license")) - .arg(Arg::new("name").short('n').long("name")) - .arg(Arg::new("output").short('o').long("output")) - .arg(Arg::new("readme").short('r').long("readme")) - .arg(Arg::new("repository").short('R').long("repository")) - .arg(Arg::new("rustversion").short('V').long("rustversion")) - .arg(Arg::new("version").short('v').long("version")) - .arg(Arg::new("website").short('w').long("website")) - .arg(Arg::new("csv").short('c').long("csv")) - .get_matches_from(vec!["myapp", "-c", file_path]); - assert!(matches.contains_id("csv")); - assert_eq!( - matches.get_one::("csv"), - Some(&file_path.to_string()) - ); +// Tests the validation of parameters with an invalid homepage URL +#[test] +fn test_validate_params_invalid_homepage() { + let params = FileGenerationParams { + name: Some("test_lib".to_string()), + output: Some("output_dir".to_string()), + author: None, + build: None, + categories: None, + description: None, + documentation: None, + edition: None, + email: None, + homepage: Some("123".to_string()), // Invalid homepage + keywords: None, + license: None, + readme: None, + repository: None, + rustversion: None, + version: None, + website: None, + }; + let result = validate_params(¶ms); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().to_string(), + "Invalid homepage URL: 123. Homepage URL should start with 'http://' or 'https://'.".to_string() + ); +} - process_arguments(&matches).unwrap(); - // Check that the files were generated - let expected_files = vec![ - "Cargo.toml", - "src/lib.rs", - "CONTRIBUTING.md", - "README.md", - ".gitignore", - ]; - for file in &expected_files { - let path = Path::new(file); - if !path.exists() { - println!("Expected file not found: {path:?}"); - } - assert!(path.exists()); - } - } +// Tests the validation of parameters with an invalid repository URL +#[test] +fn test_validate_params_invalid_repository() { + let params = FileGenerationParams { + name: Some("test_lib".to_string()), + output: Some("output_dir".to_string()), + author: None, + build: None, + categories: None, + description: None, + documentation: None, + edition: None, + email: None, + homepage: None, + keywords: None, + license: None, + readme: None, + repository: Some("123".to_string()), // Invalid repository + rustversion: None, + version: None, + website: None, + }; + let result = validate_params(¶ms); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().to_string(), + "Invalid repository URL: 123. Repository URL should start with 'https://' or 'git://'.".to_string() + ); +} - // Tests to verify that the process_arguments function correctly - // handles the arguments when "json" argument is passed. It should - // extract the file path and call generate_from_json function with - // the file path. Finally, it should generate the expected files in - // the current directory. - #[test] - fn test_process_arguments_with_json() { - let json_file_path = "./tests/data/mylibrary.json"; - let path = Path::new(json_file_path); - assert!(path.exists(), "File {json_file_path} does not exist"); +// Tests the validation of parameters with an invalid Rust version +#[test] +fn test_validate_params_invalid_rustversion() { + let params = FileGenerationParams { + name: Some("test_lib".to_string()), + output: Some("output_dir".to_string()), + author: None, + build: None, + categories: None, + description: None, + documentation: None, + edition: None, + email: None, + homepage: None, + keywords: None, + license: None, + readme: None, + repository: None, + rustversion: Some("2.0".to_string()), // Invalid Rust version + version: None, + website: None, + }; - let matches = Command::new("myapp") - .arg(Arg::new("json").short('j').long("json")) - .get_matches_from(vec!["myapp", "-j", json_file_path]); + let result = validate_params(¶ms); - assert!(matches.contains_id("json")); - assert_eq!( - matches.get_one::("json"), - Some(&json_file_path.to_string()) - ); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().to_string(), + "Invalid Rust version: 2.0. Rust version should start with '1.'.".to_string() + ); +} - // Generate the files - let result = generate_from_json(json_file_path); - assert!( - result.is_ok(), - "Failed to generate the template files: {}", - result.unwrap_err() - ); +// Tests the validation of parameters with missing name +#[test] +fn test_validate_params_missing_name() { + let params = FileGenerationParams { + name: None, + author: None, + build: None, + categories: None, + description: None, + documentation: None, + edition: None, + email: None, + homepage: None, + keywords: None, + license: None, + output: None, + readme: None, + repository: None, + rustversion: None, + version: None, + website: None, + }; - // Check that the files were generated - let expected_files = vec![ - "Cargo.toml", - "src/lib.rs", - "CONTRIBUTING.md", - "README.md", - ".gitignore", - ]; - for file in &expected_files { - let path = Path::new(file); - assert!( - path.exists(), - "Failed to generate the template files" - ); - } - } + let result = validate_params(¶ms); + + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().to_string(), + "The name of the library is required for manual generation." + .to_string() + ); +} + +// Tests the validation of parameters with missing output +#[test] +fn test_validate_params_missing_output() { + let params = FileGenerationParams { + name: Some("test_lib".to_string()), + output: None, + author: None, + build: None, + categories: None, + description: None, + documentation: None, + edition: None, + email: None, + homepage: None, + keywords: None, + license: None, + readme: None, + repository: None, + rustversion: None, + version: None, + website: None, + }; + + let result = validate_params(¶ms); - // Tests to verify that the process_arguments function correctly - // handles the arguments when "toml" argument is passed. It should - // extract the file path and call generate_from_toml function with - // the file path. Finally, it should generate the expected files in - // the current directory. - #[test] - fn test_process_arguments_with_toml() { - let file_path = "./tests/data/mylibrary.toml"; - let contents = fs::read_to_string(file_path); - let mut params: FileGenerationParams = - toml::from_str(&contents.unwrap()).unwrap(); - params.output = Some(".".to_string()); - let matches = Command::new("myapp") - .arg(Arg::new("author").short('a').long("author")) - .arg(Arg::new("build").short('b').long("build")) - .arg(Arg::new("categories").short('C').long("categories")) - .arg(Arg::new("description").short('d').long("description")) - .arg( - Arg::new("documentation") - .short('D') - .long("documentation"), - ) - .arg(Arg::new("edition").short('e').long("edition")) - .arg(Arg::new("email").short('E').long("email")) - .arg(Arg::new("homepage").short('p').long("homepage")) - .arg(Arg::new("keywords").short('k').long("keywords")) - .arg(Arg::new("license").short('l').long("license")) - .arg(Arg::new("name").short('n').long("name")) - .arg(Arg::new("output").short('o').long("output")) - .arg(Arg::new("readme").short('r').long("readme")) - .arg(Arg::new("repository").short('R').long("repository")) - .arg(Arg::new("rustversion").short('V').long("rustversion")) - .arg(Arg::new("version").short('v').long("version")) - .arg(Arg::new("website").short('w').long("website")) - .arg(Arg::new("toml").short('t').long("toml")) - .get_matches_from(vec!["myapp", "-t", file_path]); - assert!(matches.contains_id("toml")); - assert_eq!( - matches.get_one::("toml"), - Some(&file_path.to_string()) - ); - } + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().to_string(), + "The output directory is required for manual generation." + .to_string() + ); +} + +// Tests the validation of parameters with valid inputs +#[test] +fn test_validate_params_valid() { + let params = FileGenerationParams { + name: Some("test_lib".to_string()), + output: Some("output_dir".to_string()), + author: None, + build: None, + categories: None, + description: None, + documentation: None, + edition: None, + email: None, + homepage: None, + keywords: None, + license: None, + readme: None, + repository: None, + rustversion: None, + version: None, + website: None, + }; + + let result = validate_params(¶ms); - // Tests to verify that the process_arguments function correctly - // handles the arguments when "yaml" argument is passed. It should - // extract the file path and call generate_from_yaml function with - // the file path. Finally, it should generate the expected files in - // the current directory. - #[test] - fn test_process_arguments_with_yaml() { - let file_path = "./tests/data/mylibrary.yaml"; - let path = Path::new(file_path); - assert!(path.exists(), "File {file_path} does not exist"); + assert!(result.is_ok()); +} + +// Tests extract manual parameters +#[test] +fn test_extract_manual_params_all_fields() { + let matches = Command::new("libmake") + .subcommand( + Command::new("manual") + .arg( + Arg::new("name") + .long("name") + .value_name("NAME") + .default_value("test_lib"), + ) + .arg( + Arg::new("output") + .long("output") + .value_name("OUTPUT") + .default_value("output_dir"), + ) + .arg( + Arg::new("author") + .long("author") + .value_name("AUTHOR") + .default_value("John Doe"), + ) + .arg( + Arg::new("build") + .long("build") + .value_name("BUILD") + .default_value("script.rs"), + ) + .arg( + Arg::new("categories") + .long("categories") + .value_name("CATEGORIES") + .default_value("category1,category2"), + ) + .arg( + Arg::new("description") + .long("description") + .value_name("DESCRIPTION") + .default_value("A test library"), + ) + .arg( + Arg::new("documentation") + .long("documentation") + .value_name("DOCUMENTATION") + .default_value("https://docs.rs/test_lib"), + ) + .arg( + Arg::new("edition") + .long("edition") + .value_name("EDITION") + .default_value("2021"), + ) + .arg( + Arg::new("email") + .long("email") + .value_name("EMAIL") + .default_value("john@example.com"), + ) + .arg( + Arg::new("homepage") + .long("homepage") + .value_name("HOMEPAGE") + .default_value("https://example.com"), + ) + .arg( + Arg::new("keywords") + .long("keywords") + .value_name("KEYWORDS") + .default_value("keyword1,keyword2"), + ) + .arg( + Arg::new("license") + .long("license") + .value_name("LICENSE") + .default_value("MIT"), + ) + .arg( + Arg::new("readme") + .long("readme") + .value_name("README") + .default_value("README.md"), + ) + .arg( + Arg::new("repository") + .long("repository") + .value_name("REPOSITORY") + .default_value( + "https://github.com/test/test_lib", + ), + ) + .arg( + Arg::new("rustversion") + .long("rustversion") + .value_name("RUSTVERSION") + .default_value("1.60.0"), + ) + .arg( + Arg::new("version") + .long("version") + .value_name("VERSION") + .default_value("0.1.0"), + ) + .arg( + Arg::new("website") + .long("website") + .value_name("WEBSITE") + .default_value("https://example.com"), + ), + ) + .get_matches_from(vec![ + "libmake", + "manual", + "--name", + "test_lib", + "--output", + "output_dir", + "--author", + "John Doe", + "--build", + "script.rs", + "--categories", + "category1,category2", + "--description", + "A test library", + "--documentation", + "https://docs.rs/test_lib", + "--edition", + "2021", + "--email", + "john@example.com", + "--homepage", + "https://example.com", + "--keywords", + "keyword1,keyword2", + "--license", + "MIT", + "--readme", + "README.md", + "--repository", + "https://github.com/test/test_lib", + "--rustversion", + "1.60.0", + "--version", + "0.1.0", + "--website", + "https://example.com", + ]); - let contents = fs::read_to_string(file_path).unwrap(); - let mut params: FileGenerationParams = - serde_yaml::from_str(&contents).unwrap(); - params.output = Some(".".to_string()); + let result = extract_manual_params( + matches.subcommand_matches("manual").unwrap() + ); + assert!(result.is_ok()); - let matches = Command::new("myapp") - .arg(Arg::new("author").short('a').long("author")) - .arg(Arg::new("build").short('b').long("build")) - .arg(Arg::new("categories").short('C').long("categories")) - .arg(Arg::new("description").short('d').long("description")) - .arg( - Arg::new("documentation") - .short('D') - .long("documentation"), - ) - .arg(Arg::new("edition").short('e').long("edition")) - .arg(Arg::new("email").short('E').long("email")) - .arg(Arg::new("homepage").short('p').long("homepage")) - .arg(Arg::new("keywords").short('k').long("keywords")) - .arg(Arg::new("license").short('l').long("license")) - .arg(Arg::new("name").short('n').long("name")) - .arg(Arg::new("output").short('o').long("output")) - .arg(Arg::new("readme").short('r').long("readme")) - .arg(Arg::new("repository").short('R').long("repository")) - .arg(Arg::new("rustversion").short('V').long("rustversion")) - .arg(Arg::new("version").short('v').long("version")) - .arg(Arg::new("website").short('w').long("website")) - .arg(Arg::new("yaml").short('y').long("yaml")) - .get_matches_from(vec!["myapp", "-y", file_path]); - assert!(matches.contains_id("yaml")); - assert_eq!( - matches.get_one::("yaml"), - Some(&file_path.to_string()) - ); - } + let params = result.unwrap(); + assert_eq!(params.name, Some("test_lib".to_string())); + assert_eq!(params.output, Some("output_dir".to_string())); + assert_eq!(params.author, Some("John Doe".to_string())); + assert_eq!(params.build, Some("script.rs".to_string())); + assert_eq!( + params.categories, + Some("category1,category2".to_string()) + ); + assert_eq!(params.description, Some("A test library".to_string())); + assert_eq!( + params.documentation, + Some("https://docs.rs/test_lib".to_string()) + ); + assert_eq!(params.edition, Some("2021".to_string())); + assert_eq!(params.email, Some("john@example.com".to_string())); + assert_eq!( + params.homepage, + Some("https://example.com".to_string()) + ); + assert_eq!(params.keywords, Some("keyword1,keyword2".to_string())); + assert_eq!(params.license, Some("MIT".to_string())); + assert_eq!(params.readme, Some("README.md".to_string())); + assert_eq!( + params.repository, + Some("https://github.com/test/test_lib".to_string()) + ); + assert_eq!(params.rustversion, Some("1.60.0".to_string())); + assert_eq!(params.version, Some("0.1.0".to_string())); + assert_eq!(params.website, Some("https://example.com".to_string())); } diff --git a/tests/test_ascii.rs b/tests/test_ascii.rs index 75fc088..fad81ff 100644 --- a/tests/test_ascii.rs +++ b/tests/test_ascii.rs @@ -1,18 +1,35 @@ #[cfg(test)] mod tests { - // Import the generate_ascii_art function from the libmake::ascii module - use libmake::ascii::generate_ascii_art; + use libmake::ascii::{generate_ascii_art, load_standard_font, ArtError}; - // Define a unit test named test_generate_ascii_art #[test] - fn test_generate_ascii_art() { - // Create a test string + fn test_generate_ascii_art_success() { let text = "Hello, world!"; + let result = generate_ascii_art(text); + assert!(result.is_ok()); + let ascii_art = result.unwrap(); + assert!(!ascii_art.is_empty()); + } + + #[test] + fn test_generate_ascii_art_empty_text() { + let text = ""; + let result = generate_ascii_art(text); + assert!(result.is_err()); + assert!(matches!(result.unwrap_err(), ArtError::ConversionError)); + } - // Call the generate_ascii_art function with the test string and assert that no panic occurs - assert!(std::panic::catch_unwind(|| { - generate_ascii_art(text).unwrap(); - }) - .is_ok()); + #[test] + fn test_load_standard_font_success() { + let result = load_standard_font(); + assert!(result.is_ok()); + } + + #[test] + fn test_generate_ascii_art_conversion_error() { + let text = "\u{1F600}"; // Emoji character + let result = generate_ascii_art(text); + assert!(result.is_err()); + assert!(matches!(result.unwrap_err(), ArtError::ConversionError)); } } diff --git a/tests/test_cli.rs b/tests/test_cli.rs index a8f84f8..8536d6f 100644 --- a/tests/test_cli.rs +++ b/tests/test_cli.rs @@ -1,45 +1,76 @@ #[cfg(test)] mod tests { - use libmake::cli::build; + use libmake::cli::{build, create_arg}; #[test] - // Test that the arguments for the build CLI are correctly set - fn test_build_cli_args() { - // Define the expected argument values - let arg_specs = [ - ("author", "Me"), - ("build", "build.rs"), - ("categories", "['category 1', 'category 2']"), - ("description", "A library for doing things"), - ("documentation", "https://lib.rs/crates/my_library"), - ("edition", "2021"), - ("email", "test@test.com"), - ("homepage", "https://test.com"), - ("keywords", "['keyword1', 'keyword2']"), - ("license", "MIT OR Apache-2.0"), - ("name", "my_library"), - ("output", "my_library"), - ("readme", "README.md"), - ("repository", "https://github.com/example/my_library"), - ("rustversion", "1.71.1"), - ("version", "0.2.1"), - ("website", "https://test.com"), - ]; - - // Call the build_cli function to get the command-line arguments - let args = build().unwrap(); - - // Iterate through the expected argument values - for (arg_name, expected_value) in &arg_specs { - // Get the actual value for the argument - let arg_value: Option<&String> = args.get_one(arg_name); - - // Compare the actual and expected values - assert_eq!( - Some(&(*expected_value).to_string()), - arg_value, - "Incorrect value for argument {arg_name}", - ); - } + fn test_build_manual_subcommand() { + let matches = build(); + assert!(matches.is_ok()); } + + #[test] + fn test_create_arg() { + // Test case 1: Create an argument with all fields + let arg_info = ( + "name", + Some("default"), + "help message", + 'n', + "name", + "NAME", + ); + let arg = create_arg(arg_info); + assert_eq!(arg.get_id(), "name"); + assert_eq!(arg.get_help().unwrap().to_string(), "help message"); + assert_eq!(arg.get_short().unwrap(), 'n'); + assert_eq!(arg.get_long().unwrap(), "name"); + + // Test case 2: Create an argument without a default value + let arg_info = + ("name", None, "help message", 'n', "name", "NAME"); + let arg = create_arg(arg_info); + assert_eq!(arg.get_id(), "name"); + assert_eq!(arg.get_help().unwrap().to_string(), "help message"); + assert_eq!(arg.get_short().unwrap(), 'n'); + assert_eq!(arg.get_long().unwrap(), "name"); + + // Test case 3: Create an argument with only required fields + let arg_info = ("name", None, "", 'n', "name", "NAME"); + let arg = create_arg(arg_info); + assert_eq!(arg.get_id(), "name"); + assert_eq!(arg.get_short().unwrap(), 'n'); + assert_eq!(arg.get_long().unwrap(), "name"); + + // Test case 4: Create an argument with a multi-word long flag + let arg_info = + ("name", None, "help message", 'n', "long-flag", "NAME"); + let arg = create_arg(arg_info); + assert_eq!(arg.get_long().unwrap(), "long-flag"); + + // Test case 5: Argument with an empty help message + let arg_info = ( + "name", None, "", // Empty help message + 'n', "name", "NAME", + ); + let arg = create_arg(arg_info); + + // Updated assertion + assert!(arg.get_help().is_some()); // Help should be set, even if empty + + // Optionally, depending on the implementation of StyledStr: + assert_eq!(arg.get_help().unwrap().to_string(), ""); // Check if the help string is indeed empty + + // Test case 6: Long argument name + let arg_info = ( + "very-long-argument-name", + None, + "help", + 'v', + "very-long-argument-name", + "NAME", + ); + let arg = create_arg(arg_info); + assert_eq!(arg.get_id(), "very-long-argument-name"); + } + } diff --git a/tests/test_generator.rs b/tests/test_generator.rs index f20a727..8ad4345 100644 --- a/tests/test_generator.rs +++ b/tests/test_generator.rs @@ -29,13 +29,12 @@ fn test_get_csv_field() { fn test_get_json_field() { let file_path = "./tests/data/mylibrary.json"; let field_name = "mylibrary"; - let value = - if Path::new(file_path).exists() { - get_json_field(Some(file_path), field_name) - } else { - String::new() - }; - assert_eq!(value, "null"); + let value = if Path::new(file_path).exists() { + get_json_field(Some(file_path), field_name) + } else { + Ok(String::new()) // Wrap the String in Ok + }; + assert_eq!(value.unwrap(), "null".to_string()); // Unwrap the value and compare with "null" } /// Tests the `get_yaml_field` function by passing a YAML file path and @@ -44,13 +43,12 @@ fn test_get_json_field() { fn test_get_yaml_field() { let file_path = "./tests/data/mylibrary.yaml"; let field_name = "mylibrary"; - let value = - if Path::new(file_path).exists() { - get_yaml_field(Some(file_path), field_name) - } else { - String::new() - }; - assert_eq!(value, "null"); + let value = if Path::new(file_path).exists() { + get_yaml_field(Some(file_path), field_name) + } else { + Ok(String::new()) // Wrapping the String in Ok to match the expected type + }; + assert_eq!(value.unwrap(), "null".to_string()); // Unwrap the value and compare with "null" } /// Tests the `generate_from_config` function by passing a YAML file @@ -105,7 +103,7 @@ fn generate_from_toml() { fn test_generate_from_args() { let args = "--author=Me --output=my_library" .split(' ') - .map(std::string::ToString::to_string) + .map(ToString::to_string) .collect::>(); let args_str = args[1..].join(" "); @@ -135,7 +133,7 @@ fn test_from_args() { --version= \ --website=" .split(' ') - .map(std::string::ToString::to_string) // Replaced the closure with the method directly + .map(ToString::to_string) // Replaced the closure with the method directly .collect::>(); let args_str = args[1..].join(" "); @@ -149,7 +147,7 @@ fn test_from_args() { /// without errors. #[test] fn test_assert_generate_files() { - let temp_dir = std::env::temp_dir().join("my_library"); + let temp_dir = env::temp_dir().join("my_library"); let mut params = FileGenerationParams::new(); params.output = Some(temp_dir.as_path().to_str().unwrap().to_owned()); diff --git a/tests/test_loggers.rs b/tests/test_loggers.rs index 7c7984e..58fa5cf 100644 --- a/tests/test_loggers.rs +++ b/tests/test_loggers.rs @@ -6,21 +6,19 @@ #[cfg(test)] mod tests { - use rlg::macro_log; - use rlg::{LogFormat, LogLevel}; + use rlg::{log_format::LogFormat, log_level::LogLevel, macro_log}; #[test] fn test_logging() { // Create a log entry - let log_entry = - macro_log!( - "session_id", - "time", - &LogLevel::INFO, - "component", - "Log message", - &LogFormat::CLF - ); + let log_entry = macro_log!( + "session_id", + "time", + &LogLevel::INFO, + "component", + "Log message", + &LogFormat::CLF + ); // Define expected values let expected_session_id = "session_id"; diff --git a/tests/test_macros.rs b/tests/test_macros.rs index 40bd4f2..6b6df8c 100644 --- a/tests/test_macros.rs +++ b/tests/test_macros.rs @@ -1,7 +1,6 @@ #[cfg(test)] mod tests { - extern crate libmake; use libmake::generator::{ generate_files, generate_from_csv, generate_from_json, generate_from_yaml, diff --git a/tests/test_utils.rs b/tests/test_utils.rs index 5bf38e8..0c4a86c 100644 --- a/tests/test_utils.rs +++ b/tests/test_utils.rs @@ -8,6 +8,8 @@ mod tests { #[test] fn test_get_csv_field() { let file_path = "./tests/data/mylibrary.csv"; + + // Test with valid field index let field_index = 0; let expected_value = Some(vec!["Me".to_string()]); let actual_value = get_csv_field(Some(file_path), field_index); @@ -15,21 +17,22 @@ mod tests { // Test with invalid field index let field_index = 100; - let expected_value = Some(vec![String::new()]); + let expected_value = None; let actual_value = get_csv_field(Some(file_path), field_index); assert_eq!(expected_value, actual_value); } + // Unit test for the `get_csv_fields()` function. #[test] fn test_get_csv_fields() { let file_path = "./tests/data/mylibrary.csv"; // Test the function with various input values - assert_eq!(file_path, "./tests/data/mylibrary.csv"); assert_eq!( get_csv_field(Some(file_path), 0), Some(vec!["Me".to_string()]) ); + print!("{:?}", get_csv_field(Some(file_path), 0)); assert_eq!( get_csv_field(Some(file_path), 1), Some(vec!["build.rs".to_string()]) @@ -84,56 +87,78 @@ mod tests { ); assert_eq!( get_csv_field(Some(file_path), 14), - Some(vec!["1.71.1".to_string()]) + Some(vec!["1.75.0".to_string()]) ); assert_eq!( get_csv_field(Some(file_path), 15), - Some(vec!["0.2.1".to_string()]) + Some(vec!["0.2.2".to_string()]) ); assert_eq!( get_csv_field(Some(file_path), 16), Some(vec!["https://test.com".to_string()]) ); } + // Unit test for the `get_json_field()` function. #[test] fn test_get_json_field_existing() { - let file_path = None; - let field_name = "null"; - let expected_value = ""; - let actual_value = get_json_field(file_path, field_name); + let file_path = Some("./tests/data/mylibrary.json"); + let field_name = "name"; + let expected_value = Ok("my_library".to_string()); + let actual_value = get_json_field(file_path, field_name) + .map_err(|err| err.to_string()); assert_eq!(expected_value, actual_value); } + // Unit test for the `get_yaml_field()` function. #[test] fn test_get_yaml_field_existing() { - let file_path = None; - let field_name = "null"; - let expected_value = ""; - let actual_value = get_yaml_field(file_path, field_name); + let file_path = Some("./tests/data/mylibrary.yaml"); + let field_name = "description"; + let expected_value = + Ok("A library for doing things".to_string()); + let actual_value = get_yaml_field(file_path, field_name) + .map_err(|err| err.to_string()); assert_eq!(expected_value, actual_value); } + + // Unit test for the `get_config_field()` function. // Unit test for the `get_config_field()` function. #[test] fn test_get_config_field_existing() { - let file_path = None; - let field_name = "nonexistent"; - let expected_value = ""; - + let file_path = Some("./tests/data/mylibrary.yaml"); + let field_name = "license"; + let expected_value = "MIT OR Apache-2.0"; let actual_yaml_value = - get_config_field(file_path, Some("yaml"), field_name); + get_config_field(file_path, Some("yaml"), field_name) + .unwrap_or_else(|_| { + panic!("Failed to get config field: {}", field_name) + }); + assert_eq!(expected_value, actual_yaml_value); + let json_file_path = Some("./tests/data/mylibrary.json"); let actual_json_value = - get_config_field(file_path, Some("json"), field_name); + get_config_field(json_file_path, Some("json"), field_name) + .map_err(|err| err.to_string()) + .unwrap(); assert_eq!(expected_value, actual_json_value); - let actual_empty_value = - get_config_field(None, None, field_name); - assert_eq!(expected_value, actual_empty_value); + // Test with an empty file format + let actual_empty_format_value = + get_config_field(file_path, None, field_name) + .map_err(|err| err.to_string()) + .unwrap_err(); + assert_eq!( + actual_empty_format_value, + "File format is not provided" + ); - let actual_unknown_value = - get_config_field(file_path, Some("unknown"), field_name); - assert_eq!(expected_value, actual_unknown_value); + // Test with an unknown file format + let actual_unknown_format_value = + get_config_field(file_path, Some("unknown"), field_name) + .map_err(|err| err.to_string()) + .unwrap_err(); + assert_eq!(actual_unknown_format_value, "Unsupported file format: unknown. Supported formats are 'json' and 'yaml'."); } } diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 5f02a40..159c84f 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -anyhow = "1.0.79" +anyhow = "1.0.81" xtaskops = "0.4.2" [[bin]]