update
This commit is contained in:
parent
d9e9ce628c
commit
7c2540a185
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
1
req.yml
1
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
|
||||
|
|
|
|||
80
src/lib.rs
80
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<S>(v: &str, s: S) -> Result<S::Ok, S::Error>
|
||||
|
|
@ -51,9 +54,79 @@ pub struct ConfigDefault {
|
|||
pub hint: Option<String>,
|
||||
}
|
||||
|
||||
#[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<S>(version: &Version, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(&version.to_string())
|
||||
}
|
||||
|
||||
// Custom deserialization
|
||||
fn deserialize_version<'de, D>(deserializer: D) -> Result<Version, D::Error>
|
||||
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<E>(self, value: &str) -> Result<Version, E>
|
||||
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::<u64>()
|
||||
.map_err(|_| E::invalid_value(Unexpected::Str(value), &self))?;
|
||||
let minor = parts[1]
|
||||
.parse::<u64>()
|
||||
.map_err(|_| E::invalid_value(Unexpected::Str(value), &self))?;
|
||||
let patch = parts[2]
|
||||
.parse::<u64>()
|
||||
.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
|
||||
|
|
|
|||
51
src/main.rs
51
src/main.rs
|
|
@ -110,21 +110,25 @@ fn add_requirements(output: &mut Vec<String>, requirements: &IndexMap<String, Re
|
|||
for (id, requirement) in requirements {
|
||||
output.push(format!(
|
||||
"- **_{id}_ - {}:** {}",
|
||||
requirement.name, requirement.description
|
||||
requirement.name.trim(),
|
||||
requirement.description.trim()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn add_topics(output: &mut Vec<String>, topics: &IndexMap<String, Topic>, 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<String>, topics: &IndexMap<String, Topic>, 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());
|
||||
|
|
|
|||
Loading…
Reference in New Issue