From 7c2540a18556c4028e3684d727c606db4ab00554 Mon Sep 17 00:00:00 2001 From: Steve Biedermann Date: Thu, 9 May 2024 23:14:14 +0200 Subject: [PATCH] update --- Cargo.lock | 7 +++++ Cargo.toml | 1 + req.yml | 1 + src/lib.rs | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++- src/main.rs | 51 +++++++++++++++++++++++----------- 5 files changed, 123 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index af5e57d..0118703 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -479,6 +479,7 @@ dependencies = [ "stringlit", "toml", "tui", + "version_operators", ] [[package]] @@ -780,6 +781,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "version_operators" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "effa0697dca74a72f99a5a3ee248673c19e02a915ce78667768b0d311d468340" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 02b9606..e511f19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,4 @@ serde_yaml = "0.9.34" stringlit = "2.1.0" toml = { version = "0.8.12", features = ["indexmap", "preserve_order"] } tui = "0.19.0" +version_operators = "0.0.1" diff --git a/req.yml b/req.yml index 415aad9..533d12e 100644 --- a/req.yml +++ b/req.yml @@ -1,4 +1,5 @@ name: journal-uploader +version: 1.0.0 description: |- The journal-uploader has two main functionalities. - Take a stream of log messages and filter them depending on their severity diff --git a/src/lib.rs b/src/lib.rs index 29c4225..c193dda 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,9 @@ +use std::fmt; + use indexmap::{indexmap, IndexMap}; use schemars::JsonSchema; -use serde::{Deserialize, Serialize, Serializer}; +use serde::de::{self, Unexpected, Visitor}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use stringlit::s; pub fn my_trim(v: &str, s: S) -> Result @@ -51,9 +54,79 @@ pub struct ConfigDefault { pub hint: Option, } +#[derive(JsonSchema, Deserialize, Serialize, Debug, Clone, PartialEq, Eq)] +pub struct Version { + major: u64, + minor: u64, + patch: u64, +} + +impl fmt::Display for Version { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}.{}.{}", self.major, self.minor, self.patch) + } +} + +// Serialization as before +fn serialize_version(version: &Version, serializer: S) -> Result +where + S: Serializer, +{ + serializer.serialize_str(&version.to_string()) +} + +// Custom deserialization +fn deserialize_version<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + struct VersionVisitor; + + impl<'de> Visitor<'de> for VersionVisitor { + type Value = Version; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a version string in the format 'major.minor.patch'") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + let parts: Vec<&str> = value.split('.').collect(); + if parts.len() != 3 { + return Err(E::invalid_value(Unexpected::Str(value), &self)); + } + + let major = parts[0] + .parse::() + .map_err(|_| E::invalid_value(Unexpected::Str(value), &self))?; + let minor = parts[1] + .parse::() + .map_err(|_| E::invalid_value(Unexpected::Str(value), &self))?; + let patch = parts[2] + .parse::() + .map_err(|_| E::invalid_value(Unexpected::Str(value), &self))?; + + Ok(Version { + major, + minor, + patch, + }) + } + } + + deserializer.deserialize_str(VersionVisitor) +} + #[derive(JsonSchema, Debug, Deserialize, Serialize)] pub struct Project { pub name: String, + #[serde( + serialize_with = "serialize_version", + deserialize_with = "deserialize_version" + )] + pub version: Version, #[serde(serialize_with = "my_trim")] pub description: String, #[serde(default, skip_serializing_if = "IndexMap::is_empty")] @@ -67,6 +140,11 @@ pub struct Project { pub fn demo_project() -> Project { Project { name: s!("journal-uploader"), + version: Version { + major: 1, + minor: 0, + patch: 0, + }, description: s!(r" The journal-uploader has two main functionalities. - Take a stream of log messages and filter them depending on their severity diff --git a/src/main.rs b/src/main.rs index d3c0369..b357212 100644 --- a/src/main.rs +++ b/src/main.rs @@ -110,21 +110,25 @@ fn add_requirements(output: &mut Vec, requirements: &IndexMap, topics: &IndexMap, level: usize) { for (id, topic) in topics { - output.push(format!("{} _{id}_ - {}", "#".repeat(level), topic.name)); + output.push(format!( + "{} _{id}_ - {}", + "#".repeat(level), + topic.name.trim() + )); if !topic.requirements.is_empty() { add_requirements(output, &topic.requirements); output.push(nl()); } if !topic.subtopics.is_empty() { add_topics(output, &topic.subtopics, level + 1); - output.push(nl()); } } } @@ -132,6 +136,7 @@ fn add_topics(output: &mut Vec, topics: &IndexMap, level: #[derive(Parser)] enum Command { Schema, + Demo, #[clap(alias = "md")] Markdown { requirements: PathBuf, @@ -153,6 +158,9 @@ struct Args { fn main() -> anyhow::Result<()> { let Args { command } = Args::parse(); match command { + Command::Demo => { + println!("{}", serde_yaml::to_string(&demo_project())?); + } Command::Schema => { let schema = schema_for!(Project); println!("{}", serde_json::to_string_pretty(&schema).unwrap()); @@ -161,29 +169,34 @@ fn main() -> anyhow::Result<()> { let project: Project = serde_yaml::from_str(&std::fs::read_to_string(requirements)?)?; let mut output = vec![ - format!("# Requirements for {}", project.name), + format!("# Requirements for {}", project.name.trim()), nl(), s!("[[_TOC_]]"), nl(), - WORD_DESCRIPTION.to_string(), + WORD_DESCRIPTION.trim().to_string(), + nl(), + format!("**VERSION: {}**", project.version), nl(), s!("## Description"), - project.description, + project.description.trim().to_string(), nl(), ]; if !project.topics.is_empty() { output.push(s!("## Requirements")); add_topics(&mut output, &project.topics, 3); - output.push(nl()); } if !project.definitions.is_empty() { output.push(s!("## Definitions")); for definition in project.definitions { - output.push(format!("- {}: {}", definition.name, definition.value)); + output.push(format!( + "- {}: {}", + definition.name.trim(), + definition.value.trim() + )); for info in definition.additional_info { - output.push(format!(" - {info}")) + output.push(format!(" - {}", info.trim())) } } output.push(nl()); @@ -192,24 +205,30 @@ fn main() -> anyhow::Result<()> { if !project.config_defaults.is_empty() { output.push(s!("## Config Defaults")); for default in project.config_defaults { - output.push(format!("- **{}**", default.name)); - output.push(format!(" - Type: {}", default.typ)); + output.push(format!("- **{}**", default.name.trim())); + output.push(format!(" - Type: {}", default.typ.trim())); if let Some(unit) = default.unit { - output.push(format!(" - Unit: {unit}")); + output.push(format!(" - Unit: {}", unit.trim())); } if let Some(valid_values) = default.valid_values { - output.push(format!(" - Valid Values: _{}_", valid_values.join(", "))); + output.push(format!( + " - Valid Values: _{}_", + valid_values.join(", ").trim() + )); } if let Some(default_value) = default.default_value { output.push(format!( " - Default Value: _{}_{}", - default_value, - default.hint.map(|h| format!(" {h}")).unwrap_or_default() + default_value.trim(), + default + .hint + .map(|h| format!(" {}", h.trim())) + .unwrap_or_default() )); } else { output.push(format!( " - **Required**: This value **_MUST_** be provided as a start parameter.{}", - default.hint.map(|h| format!(" {h}")).unwrap_or_default() + default.hint.map(|h| format!(" {}", h.trim())).unwrap_or_default() )); } output.push(nl());