From e32208fec87cc4f3ea17f1af2dbb4e3c6215aa6a Mon Sep 17 00:00:00 2001 From: Jonas Grawe Date: Fri, 28 Jul 2023 22:00:09 +0200 Subject: [PATCH 01/12] feat: started new lib replacement crate --- crates/punktf-lib-gsgh/Cargo.toml | 18 ++ crates/punktf-lib-gsgh/profile.yaml | 1 + crates/punktf-lib-gsgh/src/lib.rs | 250 ++++++++++++++++++++++++++++ 3 files changed, 269 insertions(+) create mode 100644 crates/punktf-lib-gsgh/Cargo.toml create mode 100644 crates/punktf-lib-gsgh/profile.yaml create mode 100644 crates/punktf-lib-gsgh/src/lib.rs diff --git a/crates/punktf-lib-gsgh/Cargo.toml b/crates/punktf-lib-gsgh/Cargo.toml new file mode 100644 index 0000000..7ccc5ff --- /dev/null +++ b/crates/punktf-lib-gsgh/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "punktf-lib-gsgh" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +keywords.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +log.workspace = true +semver = { version = "1.0.18", features = ["serde"] } +serde = { workspace = true, features = ["derive"] } +serde_yaml = "0.9.25" +thiserror.workspace = true +version-compare = "0.1.1" +versions = { version = "5.0.0", features = ["serde"] } diff --git a/crates/punktf-lib-gsgh/profile.yaml b/crates/punktf-lib-gsgh/profile.yaml new file mode 100644 index 0000000..cb28714 --- /dev/null +++ b/crates/punktf-lib-gsgh/profile.yaml @@ -0,0 +1 @@ +version: "3.2" diff --git a/crates/punktf-lib-gsgh/src/lib.rs b/crates/punktf-lib-gsgh/src/lib.rs new file mode 100644 index 0000000..71c3b54 --- /dev/null +++ b/crates/punktf-lib-gsgh/src/lib.rs @@ -0,0 +1,250 @@ +use std::str::FromStr; + +use serde::Deserialize; + +pub mod version { + use serde::{de::Visitor, Deserialize, Deserializer}; + use std::{fmt, num::ParseIntError, str::FromStr}; + use thiserror::Error; + + #[derive(Debug, Clone, Copy, PartialEq, Eq, Error)] + pub enum ParseVersionError { + #[error("trailing characters")] + TrailingCharacters, + #[error("invalid number")] + InvalidNumber, + #[error("empty")] + Empty, + #[error("invalid separator")] + InvalidSeparator, + } + + impl From for ParseVersionError { + fn from(value: ParseIntError) -> Self { + Self::InvalidNumber + } + } + + pub type Result = std::result::Result; + + #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + pub struct Version { + pub major: u8, + pub minor: u8, + pub patch: u8, + } + + impl Version { + pub const ZERO: Self = Version { + major: 0, + minor: 0, + patch: 0, + }; + + pub fn with_major(mut self, major: u8) -> Self { + self.major = major; + self + } + + pub fn with_minor(mut self, minor: u8) -> Self { + self.minor = minor; + self + } + + pub fn with_patch(mut self, patch: u8) -> Self { + self.patch = patch; + self + } + } + + fn parse_u8(s: &str) -> Result> { + fn check_digit(bytes: &[u8], idx: usize) -> bool { + bytes.get(idx).map(u8::is_ascii_digit).unwrap_or(false) + } + + if s.is_empty() { + return Ok(None); + } + + let bytes = s.as_bytes(); + + // u8 can be max 3 digits (255) + let eat = if !check_digit(bytes, 0) { + // To short + return Err(ParseVersionError::InvalidNumber); + } else if !check_digit(bytes, 1) { + 1 + } else if !check_digit(bytes, 2) { + 2 + } else if !check_digit(bytes, 3) { + 3 + } else { + // To long + return Err(ParseVersionError::InvalidNumber); + }; + + Ok(Some((&s[eat..], s[..eat].parse::()?))) + } + + fn parse_dot(s: &str) -> Result> { + if s.is_empty() { + return Ok(None); + } + + if s.as_bytes()[0] == b'.' { + Ok(Some(&s[1..])) + } else { + Err(ParseVersionError::InvalidSeparator) + } + } + + impl FromStr for Version { + type Err = ParseVersionError; + + fn from_str(s: &str) -> Result { + let Some((s, major)) = parse_u8(s)? else { + return Err(ParseVersionError::Empty); + }; + + let Some(s) = parse_dot(s)? else { + return Ok(Version { major, ..Default::default() }); + }; + + let Some((s, minor)) = parse_u8(s)? else { + // The parse `.` is trailing + return Err(ParseVersionError::TrailingCharacters); + }; + + let Some(s) = parse_dot(s)? else { + return Ok(Version { major, minor, ..Default::default() }); + }; + + let Some((s, patch)) = parse_u8(s)? else { + // The parse `.` is trailing + return Err(ParseVersionError::TrailingCharacters); + }; + + if s.is_empty() { + return Ok(Version { + major, + minor, + patch, + }); + } else { + return Err(ParseVersionError::TrailingCharacters); + } + } + } + + impl<'de> Deserialize<'de> for Version { + fn deserialize(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("semver version") + } + + fn visit_str(self, string: &str) -> Result + where + E: serde::de::Error, + { + string.parse().map_err(serde::de::Error::custom) + } + } + + deserializer.deserialize_str(VersionVisitor) + } + } + + #[cfg(test)] + mod tests { + use super::*; + + #[test] + fn version_parse_ok() -> Result<(), Box> { + assert_eq!("1".parse::()?, Version::ZERO.with_major(1)); + Ok(()) + } + + #[test] + fn version_parse_err() -> Result<(), Box> { + assert_eq!( + "1.".parse::(), + Err(ParseVersionError::TrailingCharacters) + ); + + Ok(()) + } + } +} + +pub mod profile { + use super::version; + use std::str::FromStr; + + use serde::Deserialize; + use thiserror::Error; + + #[derive(Debug, Error)] + pub enum Error { + #[error("invalid profile: {0}")] + InvalidProfile(#[from] serde_yaml::Error), + #[error("unsupported version: {0}")] + UnsupportedVersion(versions::Versioning), + } + + pub type Result = std::result::Result; + + #[derive(Debug, Deserialize)] + #[serde(default)] + pub struct Version { + version: version::Version, + } + + impl Default for Version { + fn default() -> Self { + Self { + version: version::Version::ZERO, + } + } + } + + impl AsRef for Version { + fn as_ref(&self) -> &version::Version { + &self.version + } + } + + #[derive(Debug, Deserialize)] + pub struct Profile { + #[serde(flatten)] + version: Version, + } + + impl FromStr for Profile { + type Err = Error; + + fn from_str(s: &str) -> Result { + let version: Version = serde_yaml::from_str(s)?; + + println!("Read version: {version:?}"); + + todo!() + } + } +} + +#[test] +fn main() -> Result<(), Box> { + let profile = std::fs::read_to_string("profile.yaml")?; + println!("Parsing profile:\n{profile}"); + let p = profile::Profile::from_str(&profile)?; + + Ok(()) +} From 6f02c9729b9314f982a749e5fc8e1b7a977f6ea5 Mon Sep 17 00:00:00 2001 From: Jonas Grawe Date: Fri, 28 Jul 2023 22:23:42 +0200 Subject: [PATCH 02/12] feat: improved version parsing --- crates/punktf-lib-gsgh/src/lib.rs | 52 +++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/crates/punktf-lib-gsgh/src/lib.rs b/crates/punktf-lib-gsgh/src/lib.rs index 71c3b54..4d428ad 100644 --- a/crates/punktf-lib-gsgh/src/lib.rs +++ b/crates/punktf-lib-gsgh/src/lib.rs @@ -34,6 +34,18 @@ pub mod version { pub patch: u8, } + impl fmt::Display for Version { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + major, + minor, + patch, + } = self; + + write!(f, "{major}.{minor}.{patch}") + } + } + impl Version { pub const ZERO: Self = Version { major: 0, @@ -69,18 +81,15 @@ pub mod version { let bytes = s.as_bytes(); // u8 can be max 3 digits (255) - let eat = if !check_digit(bytes, 0) { - // To short - return Err(ParseVersionError::InvalidNumber); - } else if !check_digit(bytes, 1) { - 1 - } else if !check_digit(bytes, 2) { - 2 - } else if !check_digit(bytes, 3) { - 3 - } else { - // To long - return Err(ParseVersionError::InvalidNumber); + let eat = match ( + check_digit(bytes, 0), + check_digit(bytes, 1), + check_digit(bytes, 2), + ) { + (true, true, true) => 3, + (true, true, _) => 2, + (true, _, _) => 1, + _ => return Err(ParseVersionError::InvalidNumber), }; Ok(Some((&s[eat..], s[..eat].parse::()?))) @@ -178,6 +187,7 @@ pub mod version { "1.".parse::(), Err(ParseVersionError::TrailingCharacters) ); + assert_eq!("".parse::(), Err(ParseVersionError::Empty)); Ok(()) } @@ -196,7 +206,7 @@ pub mod profile { #[error("invalid profile: {0}")] InvalidProfile(#[from] serde_yaml::Error), #[error("unsupported version: {0}")] - UnsupportedVersion(versions::Versioning), + UnsupportedVersion(version::Version), } pub type Result = std::result::Result; @@ -204,7 +214,7 @@ pub mod profile { #[derive(Debug, Deserialize)] #[serde(default)] pub struct Version { - version: version::Version, + pub version: version::Version, } impl Default for Version { @@ -215,6 +225,12 @@ pub mod profile { } } + impl From for version::Version { + fn from(value: Version) -> Self { + value.version + } + } + impl AsRef for Version { fn as_ref(&self) -> &version::Version { &self.version @@ -224,7 +240,9 @@ pub mod profile { #[derive(Debug, Deserialize)] pub struct Profile { #[serde(flatten)] - version: Version, + pub version: Version, + + pub aliases: Vec, } impl FromStr for Profile { @@ -235,7 +253,7 @@ pub mod profile { println!("Read version: {version:?}"); - todo!() + Err(Error::UnsupportedVersion(version.into())) } } } @@ -244,7 +262,7 @@ pub mod profile { fn main() -> Result<(), Box> { let profile = std::fs::read_to_string("profile.yaml")?; println!("Parsing profile:\n{profile}"); - let p = profile::Profile::from_str(&profile)?; + let p = profile::Profile::from_str(&profile); Ok(()) } From 3c57ede6b751a41c452c7c0afd1b861ae44c337c Mon Sep 17 00:00:00 2001 From: Jonas Grawe Date: Sat, 29 Jul 2023 12:15:23 +0200 Subject: [PATCH 03/12] feat: more tests --- crates/punktf-lib-gsgh/src/lib.rs | 41 +++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/crates/punktf-lib-gsgh/src/lib.rs b/crates/punktf-lib-gsgh/src/lib.rs index 4d428ad..a576844 100644 --- a/crates/punktf-lib-gsgh/src/lib.rs +++ b/crates/punktf-lib-gsgh/src/lib.rs @@ -188,9 +188,48 @@ pub mod version { Err(ParseVersionError::TrailingCharacters) ); assert_eq!("".parse::(), Err(ParseVersionError::Empty)); + assert_eq!( + "1.1.1 ".parse::(), + Err(ParseVersionError::TrailingCharacters) + ); + assert_eq!( + "1.1.1.".parse::(), + Err(ParseVersionError::TrailingCharacters) + ); + assert_eq!( + "1.1.1.1".parse::(), + Err(ParseVersionError::TrailingCharacters) + ); + assert_eq!( + "256".parse::(), + Err(ParseVersionError::InvalidNumber) + ); Ok(()) } + + #[test] + fn version_cmp() { + assert!(Version::ZERO.with_major(1) == Version::ZERO.with_major(1)); + assert!(Version::ZERO.with_minor(2) == Version::ZERO.with_minor(2)); + assert!(Version::ZERO.with_patch(3) == Version::ZERO.with_patch(3)); + + assert!(Version::ZERO.with_major(1) < Version::ZERO.with_major(2)); + assert!(Version::ZERO.with_minor(2) < Version::ZERO.with_major(2)); + assert!(Version::ZERO.with_patch(3) < Version::ZERO.with_major(2)); + + assert!( + Version { + major: 2, + minor: 3, + patch: 1 + } > Version { + major: 2, + minor: 1, + patch: 10 + } + ); + } } } @@ -211,6 +250,8 @@ pub mod profile { pub type Result = std::result::Result; + /// Wrapper struct to be able to first parse only the version and then choose + /// the appropiate profile struct for it to do version compatible parsing. #[derive(Debug, Deserialize)] #[serde(default)] pub struct Version { From 049bfd4bb3d0edb387490473c0a1acb5b7537515 Mon Sep 17 00:00:00 2001 From: Jonas Grawe Date: Thu, 3 Aug 2023 00:38:51 +0200 Subject: [PATCH 04/12] work work work --- crates/punktf-lib-gsgh/profile.yaml | 46 +++- crates/punktf-lib-gsgh/src/lib.rs | 350 +++++++++++++++++++++++++--- 2 files changed, 362 insertions(+), 34 deletions(-) diff --git a/crates/punktf-lib-gsgh/profile.yaml b/crates/punktf-lib-gsgh/profile.yaml index cb28714..d46fbcb 100644 --- a/crates/punktf-lib-gsgh/profile.yaml +++ b/crates/punktf-lib-gsgh/profile.yaml @@ -1 +1,45 @@ -version: "3.2" +version: 1.0.0 + +aliases: + - Foo + - Bar + +extends: + - Parent + +env: + Foo: Bar + Bool: true + Number: 2.4 + Map: + This: Should + Not: be + Allowed: null + +transformers: + - type: LineTerminator + with: LF + +target: /tmp + +pre_hooks: | + set -eoux pipefail + echo 'Foo' + +items: + - priority: 5 + env: + Foo: Bar + Bool: true + pre_hook: |- + set -eoux pipefail + echo 'Foo' + path: /dev/null + merge: + type: Hook + with: | + #!/usr/bin/env bash + + set -eoux pipefail + + echo "test" diff --git a/crates/punktf-lib-gsgh/src/lib.rs b/crates/punktf-lib-gsgh/src/lib.rs index a576844..d045f98 100644 --- a/crates/punktf-lib-gsgh/src/lib.rs +++ b/crates/punktf-lib-gsgh/src/lib.rs @@ -1,9 +1,5 @@ -use std::str::FromStr; - -use serde::Deserialize; - pub mod version { - use serde::{de::Visitor, Deserialize, Deserializer}; + use serde::{de::Visitor, Deserialize, Deserializer, Serialize}; use std::{fmt, num::ParseIntError, str::FromStr}; use thiserror::Error; @@ -20,7 +16,7 @@ pub mod version { } impl From for ParseVersionError { - fn from(value: ParseIntError) -> Self { + fn from(_: ParseIntError) -> Self { Self::InvalidNumber } } @@ -53,20 +49,32 @@ pub mod version { patch: 0, }; - pub fn with_major(mut self, major: u8) -> Self { + pub const fn new(major: u8, minor: u8, patch: u8) -> Self { + Self { + major, + minor, + patch, + } + } + + pub const fn with_major(mut self, major: u8) -> Self { self.major = major; self } - pub fn with_minor(mut self, minor: u8) -> Self { + pub const fn with_minor(mut self, minor: u8) -> Self { self.minor = minor; self } - pub fn with_patch(mut self, patch: u8) -> Self { + pub const fn with_patch(mut self, patch: u8) -> Self { self.patch = patch; self } + + pub const fn compatible(self, other: Self) -> bool { + self.major == other.major + } } fn parse_u8(s: &str) -> Result> { @@ -134,17 +142,26 @@ pub mod version { }; if s.is_empty() { - return Ok(Version { + Ok(Version { major, minor, patch, - }); + }) } else { - return Err(ParseVersionError::TrailingCharacters); + Err(ParseVersionError::TrailingCharacters) } } } + impl Serialize for Version { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } + } + impl<'de> Deserialize<'de> for Version { fn deserialize(deserializer: D) -> Result where @@ -178,6 +195,14 @@ pub mod version { #[test] fn version_parse_ok() -> Result<(), Box> { assert_eq!("1".parse::()?, Version::ZERO.with_major(1)); + assert_eq!( + "22.12".parse::()?, + Version::ZERO.with_major(22).with_minor(12) + ); + assert_eq!( + "0.12.55".parse::()?, + Version::ZERO.with_minor(12).with_patch(55) + ); Ok(()) } @@ -233,11 +258,126 @@ pub mod version { } } +pub mod env { + use std::collections::HashMap; + + use serde::{Deserialize, Serialize}; + + #[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] + pub struct Environment(pub HashMap); + + impl Environment { + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + } +} + +pub mod transform { + use serde::{Deserialize, Serialize}; + + pub trait Transform { + fn apply(&self, content: String) -> Result>; + } + + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] + #[serde(tag = "type", content = "with")] + pub enum Transformer { + /// Transformer which replaces line termination characters with either unix + /// style (`\n`) or windows style (`\r\b`). + LineTerminator(LineTerminator), + } + + /// Transformer which replaces line termination characters with either unix + /// style (`\n`) or windows style (`\r\b`). + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] + pub enum LineTerminator { + /// Replaces all occurrences of `\r\n` with `\n` (unix style). + LF, + + /// Replaces all occurrences of `\n` with `\r\n` (windows style). + CRLF, + } +} + +pub mod hook { + use serde::{Deserialize, Serialize}; + + // Have special syntax for skipping deployment on pre_hook + // Analog: + // e.g. punktf:skip_deployment + + #[repr(transparent)] + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] + pub struct Hook(pub String); +} + +pub mod merge { + use serde::{Deserialize, Serialize}; + + use crate::hook::Hook; + + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] + #[serde(tag = "type", content = "with")] + pub enum MergeMode { + Hook(Hook), + } +} + +pub mod item { + use std::path::PathBuf; + + use serde::{Deserialize, Serialize}; + + use crate::{merge::MergeMode, profile::Shared}; + + #[derive(Debug, Serialize, Deserialize)] + pub struct Item { + #[serde(flatten)] + pub shared: Shared, + + pub path: PathBuf, + + #[serde(skip_serializing_if = "Option::is_none", default)] + pub rename: Option, + + #[serde(skip_serializing_if = "Option::is_none", default)] + pub overwrite_target: Option, + + #[serde(skip_serializing_if = "Option::is_none", default)] + pub merge: Option, + } +} + +pub mod prio { + use serde::{Deserialize, Serialize}; + + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] + pub struct Priority(pub u32); + + impl PartialOrd for Priority { + fn partial_cmp(&self, other: &Self) -> Option { + // Reverse sort ordering (smaller = higher) + other.0.partial_cmp(&self.0) + } + } + + impl Ord for Priority { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + // Reverse sort ordering (smaller = higher) + other.0.cmp(&self.0) + } + } +} + pub mod profile { - use super::version; - use std::str::FromStr; + use crate::{ + env::Environment, hook::Hook, item::Item, prio::Priority, transform::Transformer, + version::Version, + }; + use std::{path::PathBuf, str::FromStr}; - use serde::Deserialize; + use serde::{Deserialize, Serialize}; use thiserror::Error; #[derive(Debug, Error)] @@ -245,65 +385,209 @@ pub mod profile { #[error("invalid profile: {0}")] InvalidProfile(#[from] serde_yaml::Error), #[error("unsupported version: {0}")] - UnsupportedVersion(version::Version), + UnsupportedVersion(Version), } pub type Result = std::result::Result; /// Wrapper struct to be able to first parse only the version and then choose - /// the appropiate profile struct for it to do version compatible parsing. - #[derive(Debug, Deserialize)] + /// the appropriate profile struct for it to do version compatible parsing. + #[repr(transparent)] + #[derive(Debug, Deserialize, Serialize)] #[serde(default)] - pub struct Version { - pub version: version::Version, + pub struct ProfileVersion { + pub version: Version, } - impl Default for Version { + impl Default for ProfileVersion { fn default() -> Self { Self { - version: version::Version::ZERO, + version: Version::ZERO, } } } - impl From for version::Version { - fn from(value: Version) -> Self { + impl From for Version { + fn from(value: ProfileVersion) -> Self { value.version } } - impl AsRef for Version { - fn as_ref(&self) -> &version::Version { + impl AsRef for ProfileVersion { + fn as_ref(&self) -> &Version { &self.version } } - #[derive(Debug, Deserialize)] + #[derive(Debug, Deserialize, Serialize)] + pub struct Shared { + #[serde(skip_serializing_if = "Option::is_none", default)] + pub priority: Option, + + #[serde(rename = "env", skip_serializing_if = "Environment::is_empty", default)] + pub environment: Environment, + + #[serde(skip_serializing_if = "Vec::is_empty", default)] + pub transformers: Vec, + + #[serde(skip_serializing_if = "Option::is_none", default)] + pub pre_hook: Option, + + #[serde(skip_serializing_if = "Option::is_none", default)] + pub post_hook: Option, + } + + #[derive(Debug, Deserialize, Serialize)] pub struct Profile { #[serde(flatten)] - pub version: Version, + pub version: ProfileVersion, + + #[serde(flatten)] + pub shared: Shared, + #[serde(skip_serializing_if = "Vec::is_empty", default)] pub aliases: Vec, + + #[serde(skip_serializing_if = "Vec::is_empty", default)] + pub extends: Vec, + + #[serde(skip_serializing_if = "Option::is_none", default)] + pub target: Option, + + #[serde(skip_serializing_if = "Vec::is_empty", default)] + pub items: Vec, + } + + impl Profile { + pub const VERSION: Version = Version::new(1, 0, 0); } impl FromStr for Profile { type Err = Error; fn from_str(s: &str) -> Result { - let version: Version = serde_yaml::from_str(s)?; + let version: Version = serde_yaml::from_str::(s)?.version; - println!("Read version: {version:?}"); + // No version or explicit zero version + if version == Version::ZERO { + return Err(Error::UnsupportedVersion(version)); + } - Err(Error::UnsupportedVersion(version.into())) + // Version matching + if Self::VERSION.compatible(version) { + serde_yaml::from_str(s).map_err(Into::into) + } else { + Err(Error::UnsupportedVersion(version)) + } } } } #[test] +#[ignore = "debugging"] fn main() -> Result<(), Box> { + use std::str::FromStr; + let profile = std::fs::read_to_string("profile.yaml")?; - println!("Parsing profile:\n{profile}"); - let p = profile::Profile::from_str(&profile); + let p = profile::Profile::from_str(&profile)?; + + println!("{p:#?}"); Ok(()) } + +#[test] +#[ignore = "debugging"] +fn prnp() { + use crate::hook::Hook; + use crate::{item::Item, prio::Priority}; + use env::Environment; + use profile::{Profile, ProfileVersion}; + use serde_yaml::Value; + use std::path::PathBuf; + use transform::Transformer; + + use crate::profile::Shared; + + let p = Profile { + version: ProfileVersion { + version: Profile::VERSION, + }, + aliases: vec!["Foo".into(), "Bar".into()], + extends: vec!["Parent".into()], + target: Some(PathBuf::from("Test")), + shared: Shared { + environment: Environment( + [ + ("Foo".into(), Value::String("Bar".into())), + ("Bool".into(), Value::Bool(true)), + ] + .into_iter() + .collect(), + ), + transformers: vec![Transformer::LineTerminator(transform::LineTerminator::LF)], + pre_hook: Some(Hook("set -eoux pipefail\necho 'Foo'".into())), + post_hook: None, + priority: Some(Priority(5)), + }, + + items: vec![Item { + shared: Shared { + environment: Environment( + [ + ("Foo".into(), Value::String("Bar".into())), + ("Bool".into(), Value::Bool(true)), + ] + .into_iter() + .collect(), + ), + transformers: vec![Transformer::LineTerminator(transform::LineTerminator::LF)], + pre_hook: Some(Hook("set -eoux pipefail\necho 'Foo'".into())), + post_hook: None, + priority: Some(Priority(5)), + }, + path: PathBuf::from("/dev/null"), + rename: None, + overwrite_target: None, + merge: Some(merge::MergeMode::Hook(Hook("Test\nasdf".into()))), + }], + }; + + serde_yaml::to_writer(std::io::stdout(), &p).unwrap(); +} + +#[test] +#[ignore = "debugging"] +fn prni() { + use crate::hook::Hook; + use crate::{item::Item, prio::Priority}; + use env::Environment; + use serde_yaml::Value; + use std::path::PathBuf; + use transform::Transformer; + + use crate::profile::Shared; + + let i = Item { + shared: Shared { + environment: Environment( + [ + ("Foo".into(), Value::String("Bar".into())), + ("Bool".into(), Value::Bool(true)), + ] + .into_iter() + .collect(), + ), + transformers: vec![Transformer::LineTerminator(transform::LineTerminator::LF)], + pre_hook: Some(Hook("set -eoux pipefail\necho 'Foo'".into())), + post_hook: None, + priority: Some(Priority(5)), + }, + path: PathBuf::from("/dev/null"), + rename: None, + overwrite_target: None, + merge: Some(merge::MergeMode::Hook(Hook("Test".into()))), + }; + + serde_yaml::to_writer(std::io::stdout(), &i).unwrap(); +} From f0cf5badb09eccc5f919d5d34707c508ee590ec1 Mon Sep 17 00:00:00 2001 From: Jonas Grawe Date: Thu, 3 Aug 2023 23:37:15 +0200 Subject: [PATCH 05/12] work work work --- crates/punktf-lib-gsgh/profile.yaml | 30 +++++++++++++++++------------ crates/punktf-lib-gsgh/src/lib.rs | 29 ++++++++++++++++++---------- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/crates/punktf-lib-gsgh/profile.yaml b/crates/punktf-lib-gsgh/profile.yaml index d46fbcb..415daa7 100644 --- a/crates/punktf-lib-gsgh/profile.yaml +++ b/crates/punktf-lib-gsgh/profile.yaml @@ -17,29 +17,35 @@ env: Allowed: null transformers: - - type: LineTerminator + - type: line_terminator with: LF target: /tmp -pre_hooks: | - set -eoux pipefail - echo 'Foo' +pre_hooks: + type: inline + with: | + set -eoux pipefail + echo 'Foo' items: - priority: 5 env: Foo: Bar Bool: true - pre_hook: |- - set -eoux pipefail - echo 'Foo' + pre_hook: + type: inline + with: |- + set -eoux pipefail + echo 'Foo' path: /dev/null merge: - type: Hook - with: | - #!/usr/bin/env bash + type: hook + with: + type: inline + with: | + #!/usr/bin/env bash - set -eoux pipefail + set -eoux pipefail - echo "test" + echo "test" diff --git a/crates/punktf-lib-gsgh/src/lib.rs b/crates/punktf-lib-gsgh/src/lib.rs index d045f98..f4c3175 100644 --- a/crates/punktf-lib-gsgh/src/lib.rs +++ b/crates/punktf-lib-gsgh/src/lib.rs @@ -281,7 +281,7 @@ pub mod transform { } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] - #[serde(tag = "type", content = "with")] + #[serde(tag = "type", content = "with", rename_all = "snake_case")] pub enum Transformer { /// Transformer which replaces line termination characters with either unix /// style (`\n`) or windows style (`\r\b`). @@ -301,15 +301,24 @@ pub mod transform { } pub mod hook { + use std::{path::PathBuf, process::Command}; + use serde::{Deserialize, Serialize}; // Have special syntax for skipping deployment on pre_hook // Analog: // e.g. punktf:skip_deployment - #[repr(transparent)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] - pub struct Hook(pub String); + #[serde(tag = "type", content = "with", rename_all = "snake_case")] + pub enum Hook { + Inline(String), + File(PathBuf), + } + + impl Hook { + pub fn run(self) {} + } } pub mod merge { @@ -318,7 +327,7 @@ pub mod merge { use crate::hook::Hook; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] - #[serde(tag = "type", content = "with")] + #[serde(tag = "type", content = "with", rename_all = "snake_case")] pub enum MergeMode { Hook(Hook), } @@ -526,8 +535,8 @@ fn prnp() { .collect(), ), transformers: vec![Transformer::LineTerminator(transform::LineTerminator::LF)], - pre_hook: Some(Hook("set -eoux pipefail\necho 'Foo'".into())), - post_hook: None, + pre_hook: Some(Hook::Inline("set -eoux pipefail\necho 'Foo'".into())), + post_hook: Some(Hook::File("Test".into())), priority: Some(Priority(5)), }, @@ -542,14 +551,14 @@ fn prnp() { .collect(), ), transformers: vec![Transformer::LineTerminator(transform::LineTerminator::LF)], - pre_hook: Some(Hook("set -eoux pipefail\necho 'Foo'".into())), + pre_hook: None, post_hook: None, priority: Some(Priority(5)), }, path: PathBuf::from("/dev/null"), rename: None, overwrite_target: None, - merge: Some(merge::MergeMode::Hook(Hook("Test\nasdf".into()))), + merge: None, }], }; @@ -579,14 +588,14 @@ fn prni() { .collect(), ), transformers: vec![Transformer::LineTerminator(transform::LineTerminator::LF)], - pre_hook: Some(Hook("set -eoux pipefail\necho 'Foo'".into())), + pre_hook: Some(Hook::Inline("set -eoux pipefail\necho 'Foo'".into())), post_hook: None, priority: Some(Priority(5)), }, path: PathBuf::from("/dev/null"), rename: None, overwrite_target: None, - merge: Some(merge::MergeMode::Hook(Hook("Test".into()))), + merge: None, }; serde_yaml::to_writer(std::io::stdout(), &i).unwrap(); From ecc36aa6ea237c2af15df04132f1e0e0e96a4c9f Mon Sep 17 00:00:00 2001 From: Jonas Grawe Date: Mon, 14 Aug 2023 23:35:28 +0200 Subject: [PATCH 06/12] feat: implemeneted hook --- crates/punktf-lib-gsgh/Cargo.toml | 1 + crates/punktf-lib-gsgh/src/lib.rs | 226 +++++++++++++++++++++++++++++- 2 files changed, 223 insertions(+), 4 deletions(-) diff --git a/crates/punktf-lib-gsgh/Cargo.toml b/crates/punktf-lib-gsgh/Cargo.toml index 7ccc5ff..930e66a 100644 --- a/crates/punktf-lib-gsgh/Cargo.toml +++ b/crates/punktf-lib-gsgh/Cargo.toml @@ -9,6 +9,7 @@ keywords.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +cfg-if = "1.0.0" log.workspace = true semver = { version = "1.0.18", features = ["serde"] } serde = { workspace = true, features = ["derive"] } diff --git a/crates/punktf-lib-gsgh/src/lib.rs b/crates/punktf-lib-gsgh/src/lib.rs index f4c3175..da02a41 100644 --- a/crates/punktf-lib-gsgh/src/lib.rs +++ b/crates/punktf-lib-gsgh/src/lib.rs @@ -259,18 +259,85 @@ pub mod version { } pub mod env { - use std::collections::HashMap; + use std::{ + collections::{btree_set, BTreeMap, BTreeSet, HashSet}, + ops::Deref, + }; use serde::{Deserialize, Serialize}; #[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] - pub struct Environment(pub HashMap); + pub struct Environment(pub BTreeMap); impl Environment { pub fn is_empty(&self) -> bool { self.0.is_empty() } } + + #[derive(Default, Debug, Clone, PartialEq, Eq)] + pub struct LayeredEnvironment(Vec<(&'static str, Environment)>); + + impl LayeredEnvironment { + pub fn push(&mut self, name: &'static str, env: Environment) { + self.0.push((name, env)); + } + + pub fn pop(&mut self) -> Option<(&'static str, Environment)> { + self.0.pop() + } + + pub fn keys(&self) -> BTreeSet<&str> { + self.0 + .iter() + .flat_map(|(_, layer)| layer.0.keys()) + .map(|key| key.as_str()) + .collect() + } + + pub fn get(&self, key: &str) -> Option<&serde_yaml::Value> { + for (_, layer) in self.0.iter() { + if let Some(value) = layer.0.get(key) { + return Some(value); + } + } + + return None; + } + + pub fn iter(&self) -> LayeredIter<'_> { + LayeredIter::new(self) + } + + pub fn as_str_map(&self) -> BTreeMap<&str, String> { + self.iter() + // TODO: Optimize + // `trim` to remove trailing `\n` + .map(|(k, v)| (k, serde_yaml::to_string(v).unwrap().trim().into())) + .collect() + } + } + + pub struct LayeredIter<'a> { + env: &'a LayeredEnvironment, + keys: btree_set::IntoIter<&'a str>, + } + + impl<'a> LayeredIter<'a> { + pub fn new(env: &'a LayeredEnvironment) -> Self { + let keys = env.keys().into_iter(); + Self { env, keys } + } + } + + impl<'a> Iterator for LayeredIter<'a> { + type Item = (&'a str, &'a serde_yaml::Value); + + fn next(&mut self) -> Option { + let key = self.keys.next()?; + Some((key, self.env.get(key)?)) + } + } } pub mod transform { @@ -301,14 +368,57 @@ pub mod transform { } pub mod hook { - use std::{path::PathBuf, process::Command}; + use std::{ + io::{BufRead, BufReader}, + path::{Path, PathBuf}, + process::{Command, Stdio}, + }; use serde::{Deserialize, Serialize}; + use thiserror::Error; + + use crate::env::LayeredEnvironment; // Have special syntax for skipping deployment on pre_hook // Analog: // e.g. punktf:skip_deployment + #[derive(Error, Debug)] + pub enum HookError { + #[error("IO Error")] + IoError(#[from] std::io::Error), + + #[error("Process failed with status `{0}`")] + ExitStatusError(std::process::ExitStatus), + } + + impl From for HookError { + fn from(value: std::process::ExitStatus) -> Self { + Self::ExitStatusError(value) + } + } + + pub type Result = std::result::Result; + + // TODO: Replace once `exit_ok` becomes stable + trait ExitOk { + type Error; + + fn exit_ok(self) -> Result<(), Self::Error>; + } + + impl ExitOk for std::process::ExitStatus { + type Error = HookError; + + fn exit_ok(self) -> Result<(), ::Error> { + if self.success() { + Ok(()) + } else { + Err(self.into()) + } + } + } + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(tag = "type", content = "with", rename_all = "snake_case")] pub enum Hook { @@ -317,7 +427,115 @@ pub mod hook { } impl Hook { - pub fn run(self) {} + pub fn run(self, cwd: &Path, env: LayeredEnvironment) -> Result<()> { + let mut child = self + .prepare_command()? + .current_dir(cwd) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .envs(env.as_str_map()) + .spawn()?; + + // No need to call kill here as the program will immediately exit + // and thereby kill all spawned children + let stdout = child.stdout.take().expect("Failed to get stdout from hook"); + + for line in BufReader::new(stdout).lines() { + match line { + Ok(line) => println!("hook::stdout > {}", line), + Err(err) => { + // Result is explicitly ignored as an error was already + // encountered + let _ = child.kill(); + return Err(err.into()); + } + } + } + + // No need to call kill here as the program will immediately exit + // and thereby kill all spawned children + let stderr = child.stderr.take().expect("Failed to get stderr from hook"); + + for line in BufReader::new(stderr).lines() { + match line { + Ok(line) => println!("hook::stderr > {}", line), + Err(err) => { + // Result is explicitly ignored as an error was already + // encountered + let _ = child.kill(); + return Err(err.into()); + } + } + } + + child + .wait_with_output()? + .status + .exit_ok() + .map_err(Into::into) + } + + fn prepare_command(&self) -> Result { + let mut cmd = None; + + #[cfg(target_family = "windows")] + { + let mut c = Command::new("cmd"); + c.arg("/C"); + cmd = Some(c); + } + + #[cfg(target_family = "unix")] + { + let mut c = Command::new("sh"); + c.arg("-c"); + cmd = Some(c) + } + + let Some(mut cmd) = cmd else { + return Err(HookError::IoError(std::io::Error::new(std::io::ErrorKind::Other, "Hooks are only supported on Windows and Unix-based systems"))); + }; + + match self { + Self::Inline(s) => { + cmd.arg(s); + } + Self::File(path) => { + let s = std::fs::read_to_string(path)?; + cmd.arg(s); + } + } + + Ok(cmd) + } + } + + #[cfg(test)] + mod tests { + use crate::env::Environment; + + use super::*; + + #[test] + fn echo_hello_world() { + let env = Environment( + [ + ("TEST", serde_yaml::Value::Bool(true)), + ("FOO", serde_yaml::Value::String(" BAR Test".into())), + ] + .into_iter() + .map(|(k, v)| (k.to_string(), v)) + .collect(), + ); + + let mut lenv = LayeredEnvironment::default(); + lenv.push("test", env); + + println!("{:#?}", lenv.as_str_map()); + + let hook = Hook::Inline(r#"echo "Hello World""#.to_string()); + hook.run(Path::new("/tmp"), lenv).unwrap(); + } } } From f3c67388237e6d70d6a9cf5a2d38e5edfc71b7c8 Mon Sep 17 00:00:00 2001 From: Jonas Grawe Date: Tue, 15 Aug 2023 10:55:31 +0200 Subject: [PATCH 07/12] feat: file modules --- crates/punktf-lib-gsgh/src/env.rs | 79 +++ crates/punktf-lib-gsgh/src/hook.rs | 170 ++++++ crates/punktf-lib-gsgh/src/item.rs | 22 + crates/punktf-lib-gsgh/src/lib.rs | 719 +----------------------- crates/punktf-lib-gsgh/src/merge.rs | 9 + crates/punktf-lib-gsgh/src/prio.rs | 18 + crates/punktf-lib-gsgh/src/profile.rs | 110 ++++ crates/punktf-lib-gsgh/src/transform.rs | 24 + crates/punktf-lib-gsgh/src/version.rs | 257 +++++++++ 9 files changed, 697 insertions(+), 711 deletions(-) create mode 100644 crates/punktf-lib-gsgh/src/env.rs create mode 100644 crates/punktf-lib-gsgh/src/hook.rs create mode 100644 crates/punktf-lib-gsgh/src/item.rs create mode 100644 crates/punktf-lib-gsgh/src/merge.rs create mode 100644 crates/punktf-lib-gsgh/src/prio.rs create mode 100644 crates/punktf-lib-gsgh/src/profile.rs create mode 100644 crates/punktf-lib-gsgh/src/transform.rs create mode 100644 crates/punktf-lib-gsgh/src/version.rs diff --git a/crates/punktf-lib-gsgh/src/env.rs b/crates/punktf-lib-gsgh/src/env.rs new file mode 100644 index 0000000..98e86ec --- /dev/null +++ b/crates/punktf-lib-gsgh/src/env.rs @@ -0,0 +1,79 @@ +use std::{ + collections::{btree_set, BTreeMap, BTreeSet, HashSet}, + ops::Deref, +}; + +use serde::{Deserialize, Serialize}; + +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Environment(pub BTreeMap); + +impl Environment { + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } +} + +#[derive(Default, Debug, Clone, PartialEq, Eq)] +pub struct LayeredEnvironment(Vec<(&'static str, Environment)>); + +impl LayeredEnvironment { + pub fn push(&mut self, name: &'static str, env: Environment) { + self.0.push((name, env)); + } + + pub fn pop(&mut self) -> Option<(&'static str, Environment)> { + self.0.pop() + } + + pub fn keys(&self) -> BTreeSet<&str> { + self.0 + .iter() + .flat_map(|(_, layer)| layer.0.keys()) + .map(|key| key.as_str()) + .collect() + } + + pub fn get(&self, key: &str) -> Option<&serde_yaml::Value> { + for (_, layer) in self.0.iter() { + if let Some(value) = layer.0.get(key) { + return Some(value); + } + } + + return None; + } + + pub fn iter(&self) -> LayeredIter<'_> { + LayeredIter::new(self) + } + + pub fn as_str_map(&self) -> BTreeMap<&str, String> { + self.iter() + // TODO: Optimize + // `trim` to remove trailing `\n` + .map(|(k, v)| (k, serde_yaml::to_string(v).unwrap().trim().into())) + .collect() + } +} + +pub struct LayeredIter<'a> { + env: &'a LayeredEnvironment, + keys: btree_set::IntoIter<&'a str>, +} + +impl<'a> LayeredIter<'a> { + pub fn new(env: &'a LayeredEnvironment) -> Self { + let keys = env.keys().into_iter(); + Self { env, keys } + } +} + +impl<'a> Iterator for LayeredIter<'a> { + type Item = (&'a str, &'a serde_yaml::Value); + + fn next(&mut self) -> Option { + let key = self.keys.next()?; + Some((key, self.env.get(key)?)) + } +} diff --git a/crates/punktf-lib-gsgh/src/hook.rs b/crates/punktf-lib-gsgh/src/hook.rs new file mode 100644 index 0000000..b8b35de --- /dev/null +++ b/crates/punktf-lib-gsgh/src/hook.rs @@ -0,0 +1,170 @@ +use std::{ + io::{BufRead, BufReader}, + path::{Path, PathBuf}, + process::{Command, Stdio}, +}; + +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +use crate::env::LayeredEnvironment; + +// Have special syntax for skipping deployment on pre_hook +// Analog: +// e.g. punktf:skip_deployment + +#[derive(Error, Debug)] +pub enum HookError { + #[error("IO Error")] + IoError(#[from] std::io::Error), + + #[error("Process failed with status `{0}`")] + ExitStatusError(std::process::ExitStatus), +} + +impl From for HookError { + fn from(value: std::process::ExitStatus) -> Self { + Self::ExitStatusError(value) + } +} + +pub type Result = std::result::Result; + +// TODO: Replace once `exit_ok` becomes stable +trait ExitOk { + type Error; + + fn exit_ok(self) -> Result<(), Self::Error>; +} + +impl ExitOk for std::process::ExitStatus { + type Error = HookError; + + fn exit_ok(self) -> Result<(), ::Error> { + if self.success() { + Ok(()) + } else { + Err(self.into()) + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(tag = "type", content = "with", rename_all = "snake_case")] +pub enum Hook { + Inline(String), + File(PathBuf), +} + +impl Hook { + pub fn run(self, cwd: &Path, env: LayeredEnvironment) -> Result<()> { + let mut child = self + .prepare_command()? + .current_dir(cwd) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .envs(env.as_str_map()) + .spawn()?; + + // No need to call kill here as the program will immediately exit + // and thereby kill all spawned children + let stdout = child.stdout.take().expect("Failed to get stdout from hook"); + + for line in BufReader::new(stdout).lines() { + match line { + Ok(line) => println!("hook::stdout > {}", line), + Err(err) => { + // Result is explicitly ignored as an error was already + // encountered + let _ = child.kill(); + return Err(err.into()); + } + } + } + + // No need to call kill here as the program will immediately exit + // and thereby kill all spawned children + let stderr = child.stderr.take().expect("Failed to get stderr from hook"); + + for line in BufReader::new(stderr).lines() { + match line { + Ok(line) => println!("hook::stderr > {}", line), + Err(err) => { + // Result is explicitly ignored as an error was already + // encountered + let _ = child.kill(); + return Err(err.into()); + } + } + } + + child + .wait_with_output()? + .status + .exit_ok() + .map_err(Into::into) + } + + fn prepare_command(&self) -> Result { + #[allow(unused_assignments)] + let mut cmd = None; + + #[cfg(target_family = "windows")] + { + let mut c = Command::new("cmd"); + c.arg("/C"); + cmd = Some(c); + } + + #[cfg(target_family = "unix")] + { + let mut c = Command::new("sh"); + c.arg("-c"); + cmd = Some(c) + } + + let Some(mut cmd) = cmd else { + return Err(HookError::IoError(std::io::Error::new(std::io::ErrorKind::Other, "Hooks are only supported on Windows and Unix-based systems"))); + }; + + match self { + Self::Inline(s) => { + cmd.arg(s); + } + Self::File(path) => { + let s = std::fs::read_to_string(path)?; + cmd.arg(s); + } + } + + Ok(cmd) + } +} + +#[cfg(test)] +mod tests { + use crate::env::Environment; + + use super::*; + + #[test] + fn echo_hello_world() { + let env = Environment( + [ + ("TEST", serde_yaml::Value::Bool(true)), + ("FOO", serde_yaml::Value::String(" BAR Test".into())), + ] + .into_iter() + .map(|(k, v)| (k.to_string(), v)) + .collect(), + ); + + let mut lenv = LayeredEnvironment::default(); + lenv.push("test", env); + + println!("{:#?}", lenv.as_str_map()); + + let hook = Hook::Inline(r#"echo "Hello World""#.to_string()); + hook.run(Path::new("/tmp"), lenv).unwrap(); + } +} diff --git a/crates/punktf-lib-gsgh/src/item.rs b/crates/punktf-lib-gsgh/src/item.rs new file mode 100644 index 0000000..32e7d30 --- /dev/null +++ b/crates/punktf-lib-gsgh/src/item.rs @@ -0,0 +1,22 @@ +use std::path::PathBuf; + +use serde::{Deserialize, Serialize}; + +use crate::{merge::MergeMode, profile::Shared}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Item { + #[serde(flatten)] + pub shared: Shared, + + pub path: PathBuf, + + #[serde(skip_serializing_if = "Option::is_none", default)] + pub rename: Option, + + #[serde(skip_serializing_if = "Option::is_none", default)] + pub overwrite_target: Option, + + #[serde(skip_serializing_if = "Option::is_none", default)] + pub merge: Option, +} diff --git a/crates/punktf-lib-gsgh/src/lib.rs b/crates/punktf-lib-gsgh/src/lib.rs index da02a41..1f7de57 100644 --- a/crates/punktf-lib-gsgh/src/lib.rs +++ b/crates/punktf-lib-gsgh/src/lib.rs @@ -1,714 +1,11 @@ -pub mod version { - use serde::{de::Visitor, Deserialize, Deserializer, Serialize}; - use std::{fmt, num::ParseIntError, str::FromStr}; - use thiserror::Error; - - #[derive(Debug, Clone, Copy, PartialEq, Eq, Error)] - pub enum ParseVersionError { - #[error("trailing characters")] - TrailingCharacters, - #[error("invalid number")] - InvalidNumber, - #[error("empty")] - Empty, - #[error("invalid separator")] - InvalidSeparator, - } - - impl From for ParseVersionError { - fn from(_: ParseIntError) -> Self { - Self::InvalidNumber - } - } - - pub type Result = std::result::Result; - - #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] - pub struct Version { - pub major: u8, - pub minor: u8, - pub patch: u8, - } - - impl fmt::Display for Version { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { - major, - minor, - patch, - } = self; - - write!(f, "{major}.{minor}.{patch}") - } - } - - impl Version { - pub const ZERO: Self = Version { - major: 0, - minor: 0, - patch: 0, - }; - - pub const fn new(major: u8, minor: u8, patch: u8) -> Self { - Self { - major, - minor, - patch, - } - } - - pub const fn with_major(mut self, major: u8) -> Self { - self.major = major; - self - } - - pub const fn with_minor(mut self, minor: u8) -> Self { - self.minor = minor; - self - } - - pub const fn with_patch(mut self, patch: u8) -> Self { - self.patch = patch; - self - } - - pub const fn compatible(self, other: Self) -> bool { - self.major == other.major - } - } - - fn parse_u8(s: &str) -> Result> { - fn check_digit(bytes: &[u8], idx: usize) -> bool { - bytes.get(idx).map(u8::is_ascii_digit).unwrap_or(false) - } - - if s.is_empty() { - return Ok(None); - } - - let bytes = s.as_bytes(); - - // u8 can be max 3 digits (255) - let eat = match ( - check_digit(bytes, 0), - check_digit(bytes, 1), - check_digit(bytes, 2), - ) { - (true, true, true) => 3, - (true, true, _) => 2, - (true, _, _) => 1, - _ => return Err(ParseVersionError::InvalidNumber), - }; - - Ok(Some((&s[eat..], s[..eat].parse::()?))) - } - - fn parse_dot(s: &str) -> Result> { - if s.is_empty() { - return Ok(None); - } - - if s.as_bytes()[0] == b'.' { - Ok(Some(&s[1..])) - } else { - Err(ParseVersionError::InvalidSeparator) - } - } - - impl FromStr for Version { - type Err = ParseVersionError; - - fn from_str(s: &str) -> Result { - let Some((s, major)) = parse_u8(s)? else { - return Err(ParseVersionError::Empty); - }; - - let Some(s) = parse_dot(s)? else { - return Ok(Version { major, ..Default::default() }); - }; - - let Some((s, minor)) = parse_u8(s)? else { - // The parse `.` is trailing - return Err(ParseVersionError::TrailingCharacters); - }; - - let Some(s) = parse_dot(s)? else { - return Ok(Version { major, minor, ..Default::default() }); - }; - - let Some((s, patch)) = parse_u8(s)? else { - // The parse `.` is trailing - return Err(ParseVersionError::TrailingCharacters); - }; - - if s.is_empty() { - Ok(Version { - major, - minor, - patch, - }) - } else { - Err(ParseVersionError::TrailingCharacters) - } - } - } - - impl Serialize for Version { - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - serializer.serialize_str(&self.to_string()) - } - } - - impl<'de> Deserialize<'de> for Version { - fn deserialize(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("semver version") - } - - fn visit_str(self, string: &str) -> Result - where - E: serde::de::Error, - { - string.parse().map_err(serde::de::Error::custom) - } - } - - deserializer.deserialize_str(VersionVisitor) - } - } - - #[cfg(test)] - mod tests { - use super::*; - - #[test] - fn version_parse_ok() -> Result<(), Box> { - assert_eq!("1".parse::()?, Version::ZERO.with_major(1)); - assert_eq!( - "22.12".parse::()?, - Version::ZERO.with_major(22).with_minor(12) - ); - assert_eq!( - "0.12.55".parse::()?, - Version::ZERO.with_minor(12).with_patch(55) - ); - Ok(()) - } - - #[test] - fn version_parse_err() -> Result<(), Box> { - assert_eq!( - "1.".parse::(), - Err(ParseVersionError::TrailingCharacters) - ); - assert_eq!("".parse::(), Err(ParseVersionError::Empty)); - assert_eq!( - "1.1.1 ".parse::(), - Err(ParseVersionError::TrailingCharacters) - ); - assert_eq!( - "1.1.1.".parse::(), - Err(ParseVersionError::TrailingCharacters) - ); - assert_eq!( - "1.1.1.1".parse::(), - Err(ParseVersionError::TrailingCharacters) - ); - assert_eq!( - "256".parse::(), - Err(ParseVersionError::InvalidNumber) - ); - - Ok(()) - } - - #[test] - fn version_cmp() { - assert!(Version::ZERO.with_major(1) == Version::ZERO.with_major(1)); - assert!(Version::ZERO.with_minor(2) == Version::ZERO.with_minor(2)); - assert!(Version::ZERO.with_patch(3) == Version::ZERO.with_patch(3)); - - assert!(Version::ZERO.with_major(1) < Version::ZERO.with_major(2)); - assert!(Version::ZERO.with_minor(2) < Version::ZERO.with_major(2)); - assert!(Version::ZERO.with_patch(3) < Version::ZERO.with_major(2)); - - assert!( - Version { - major: 2, - minor: 3, - patch: 1 - } > Version { - major: 2, - minor: 1, - patch: 10 - } - ); - } - } -} - -pub mod env { - use std::{ - collections::{btree_set, BTreeMap, BTreeSet, HashSet}, - ops::Deref, - }; - - use serde::{Deserialize, Serialize}; - - #[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] - pub struct Environment(pub BTreeMap); - - impl Environment { - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - } - - #[derive(Default, Debug, Clone, PartialEq, Eq)] - pub struct LayeredEnvironment(Vec<(&'static str, Environment)>); - - impl LayeredEnvironment { - pub fn push(&mut self, name: &'static str, env: Environment) { - self.0.push((name, env)); - } - - pub fn pop(&mut self) -> Option<(&'static str, Environment)> { - self.0.pop() - } - - pub fn keys(&self) -> BTreeSet<&str> { - self.0 - .iter() - .flat_map(|(_, layer)| layer.0.keys()) - .map(|key| key.as_str()) - .collect() - } - - pub fn get(&self, key: &str) -> Option<&serde_yaml::Value> { - for (_, layer) in self.0.iter() { - if let Some(value) = layer.0.get(key) { - return Some(value); - } - } - - return None; - } - - pub fn iter(&self) -> LayeredIter<'_> { - LayeredIter::new(self) - } - - pub fn as_str_map(&self) -> BTreeMap<&str, String> { - self.iter() - // TODO: Optimize - // `trim` to remove trailing `\n` - .map(|(k, v)| (k, serde_yaml::to_string(v).unwrap().trim().into())) - .collect() - } - } - - pub struct LayeredIter<'a> { - env: &'a LayeredEnvironment, - keys: btree_set::IntoIter<&'a str>, - } - - impl<'a> LayeredIter<'a> { - pub fn new(env: &'a LayeredEnvironment) -> Self { - let keys = env.keys().into_iter(); - Self { env, keys } - } - } - - impl<'a> Iterator for LayeredIter<'a> { - type Item = (&'a str, &'a serde_yaml::Value); - - fn next(&mut self) -> Option { - let key = self.keys.next()?; - Some((key, self.env.get(key)?)) - } - } -} - -pub mod transform { - use serde::{Deserialize, Serialize}; - - pub trait Transform { - fn apply(&self, content: String) -> Result>; - } - - #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] - #[serde(tag = "type", content = "with", rename_all = "snake_case")] - pub enum Transformer { - /// Transformer which replaces line termination characters with either unix - /// style (`\n`) or windows style (`\r\b`). - LineTerminator(LineTerminator), - } - - /// Transformer which replaces line termination characters with either unix - /// style (`\n`) or windows style (`\r\b`). - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] - pub enum LineTerminator { - /// Replaces all occurrences of `\r\n` with `\n` (unix style). - LF, - - /// Replaces all occurrences of `\n` with `\r\n` (windows style). - CRLF, - } -} - -pub mod hook { - use std::{ - io::{BufRead, BufReader}, - path::{Path, PathBuf}, - process::{Command, Stdio}, - }; - - use serde::{Deserialize, Serialize}; - use thiserror::Error; - - use crate::env::LayeredEnvironment; - - // Have special syntax for skipping deployment on pre_hook - // Analog: - // e.g. punktf:skip_deployment - - #[derive(Error, Debug)] - pub enum HookError { - #[error("IO Error")] - IoError(#[from] std::io::Error), - - #[error("Process failed with status `{0}`")] - ExitStatusError(std::process::ExitStatus), - } - - impl From for HookError { - fn from(value: std::process::ExitStatus) -> Self { - Self::ExitStatusError(value) - } - } - - pub type Result = std::result::Result; - - // TODO: Replace once `exit_ok` becomes stable - trait ExitOk { - type Error; - - fn exit_ok(self) -> Result<(), Self::Error>; - } - - impl ExitOk for std::process::ExitStatus { - type Error = HookError; - - fn exit_ok(self) -> Result<(), ::Error> { - if self.success() { - Ok(()) - } else { - Err(self.into()) - } - } - } - - #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] - #[serde(tag = "type", content = "with", rename_all = "snake_case")] - pub enum Hook { - Inline(String), - File(PathBuf), - } - - impl Hook { - pub fn run(self, cwd: &Path, env: LayeredEnvironment) -> Result<()> { - let mut child = self - .prepare_command()? - .current_dir(cwd) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .envs(env.as_str_map()) - .spawn()?; - - // No need to call kill here as the program will immediately exit - // and thereby kill all spawned children - let stdout = child.stdout.take().expect("Failed to get stdout from hook"); - - for line in BufReader::new(stdout).lines() { - match line { - Ok(line) => println!("hook::stdout > {}", line), - Err(err) => { - // Result is explicitly ignored as an error was already - // encountered - let _ = child.kill(); - return Err(err.into()); - } - } - } - - // No need to call kill here as the program will immediately exit - // and thereby kill all spawned children - let stderr = child.stderr.take().expect("Failed to get stderr from hook"); - - for line in BufReader::new(stderr).lines() { - match line { - Ok(line) => println!("hook::stderr > {}", line), - Err(err) => { - // Result is explicitly ignored as an error was already - // encountered - let _ = child.kill(); - return Err(err.into()); - } - } - } - - child - .wait_with_output()? - .status - .exit_ok() - .map_err(Into::into) - } - - fn prepare_command(&self) -> Result { - let mut cmd = None; - - #[cfg(target_family = "windows")] - { - let mut c = Command::new("cmd"); - c.arg("/C"); - cmd = Some(c); - } - - #[cfg(target_family = "unix")] - { - let mut c = Command::new("sh"); - c.arg("-c"); - cmd = Some(c) - } - - let Some(mut cmd) = cmd else { - return Err(HookError::IoError(std::io::Error::new(std::io::ErrorKind::Other, "Hooks are only supported on Windows and Unix-based systems"))); - }; - - match self { - Self::Inline(s) => { - cmd.arg(s); - } - Self::File(path) => { - let s = std::fs::read_to_string(path)?; - cmd.arg(s); - } - } - - Ok(cmd) - } - } - - #[cfg(test)] - mod tests { - use crate::env::Environment; - - use super::*; - - #[test] - fn echo_hello_world() { - let env = Environment( - [ - ("TEST", serde_yaml::Value::Bool(true)), - ("FOO", serde_yaml::Value::String(" BAR Test".into())), - ] - .into_iter() - .map(|(k, v)| (k.to_string(), v)) - .collect(), - ); - - let mut lenv = LayeredEnvironment::default(); - lenv.push("test", env); - - println!("{:#?}", lenv.as_str_map()); - - let hook = Hook::Inline(r#"echo "Hello World""#.to_string()); - hook.run(Path::new("/tmp"), lenv).unwrap(); - } - } -} - -pub mod merge { - use serde::{Deserialize, Serialize}; - - use crate::hook::Hook; - - #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] - #[serde(tag = "type", content = "with", rename_all = "snake_case")] - pub enum MergeMode { - Hook(Hook), - } -} - -pub mod item { - use std::path::PathBuf; - - use serde::{Deserialize, Serialize}; - - use crate::{merge::MergeMode, profile::Shared}; - - #[derive(Debug, Serialize, Deserialize)] - pub struct Item { - #[serde(flatten)] - pub shared: Shared, - - pub path: PathBuf, - - #[serde(skip_serializing_if = "Option::is_none", default)] - pub rename: Option, - - #[serde(skip_serializing_if = "Option::is_none", default)] - pub overwrite_target: Option, - - #[serde(skip_serializing_if = "Option::is_none", default)] - pub merge: Option, - } -} - -pub mod prio { - use serde::{Deserialize, Serialize}; - - #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] - pub struct Priority(pub u32); - - impl PartialOrd for Priority { - fn partial_cmp(&self, other: &Self) -> Option { - // Reverse sort ordering (smaller = higher) - other.0.partial_cmp(&self.0) - } - } - - impl Ord for Priority { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - // Reverse sort ordering (smaller = higher) - other.0.cmp(&self.0) - } - } -} - -pub mod profile { - use crate::{ - env::Environment, hook::Hook, item::Item, prio::Priority, transform::Transformer, - version::Version, - }; - use std::{path::PathBuf, str::FromStr}; - - use serde::{Deserialize, Serialize}; - use thiserror::Error; - - #[derive(Debug, Error)] - pub enum Error { - #[error("invalid profile: {0}")] - InvalidProfile(#[from] serde_yaml::Error), - #[error("unsupported version: {0}")] - UnsupportedVersion(Version), - } - - pub type Result = std::result::Result; - - /// Wrapper struct to be able to first parse only the version and then choose - /// the appropriate profile struct for it to do version compatible parsing. - #[repr(transparent)] - #[derive(Debug, Deserialize, Serialize)] - #[serde(default)] - pub struct ProfileVersion { - pub version: Version, - } - - impl Default for ProfileVersion { - fn default() -> Self { - Self { - version: Version::ZERO, - } - } - } - - impl From for Version { - fn from(value: ProfileVersion) -> Self { - value.version - } - } - - impl AsRef for ProfileVersion { - fn as_ref(&self) -> &Version { - &self.version - } - } - - #[derive(Debug, Deserialize, Serialize)] - pub struct Shared { - #[serde(skip_serializing_if = "Option::is_none", default)] - pub priority: Option, - - #[serde(rename = "env", skip_serializing_if = "Environment::is_empty", default)] - pub environment: Environment, - - #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub transformers: Vec, - - #[serde(skip_serializing_if = "Option::is_none", default)] - pub pre_hook: Option, - - #[serde(skip_serializing_if = "Option::is_none", default)] - pub post_hook: Option, - } - - #[derive(Debug, Deserialize, Serialize)] - pub struct Profile { - #[serde(flatten)] - pub version: ProfileVersion, - - #[serde(flatten)] - pub shared: Shared, - - #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub aliases: Vec, - - #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub extends: Vec, - - #[serde(skip_serializing_if = "Option::is_none", default)] - pub target: Option, - - #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub items: Vec, - } - - impl Profile { - pub const VERSION: Version = Version::new(1, 0, 0); - } - - impl FromStr for Profile { - type Err = Error; - - fn from_str(s: &str) -> Result { - let version: Version = serde_yaml::from_str::(s)?.version; - - // No version or explicit zero version - if version == Version::ZERO { - return Err(Error::UnsupportedVersion(version)); - } - - // Version matching - if Self::VERSION.compatible(version) { - serde_yaml::from_str(s).map_err(Into::into) - } else { - Err(Error::UnsupportedVersion(version)) - } - } - } -} +pub mod env; +pub mod hook; +pub mod item; +pub mod merge; +pub mod prio; +pub mod profile; +pub mod transform; +pub mod version; #[test] #[ignore = "debugging"] diff --git a/crates/punktf-lib-gsgh/src/merge.rs b/crates/punktf-lib-gsgh/src/merge.rs new file mode 100644 index 0000000..7200df4 --- /dev/null +++ b/crates/punktf-lib-gsgh/src/merge.rs @@ -0,0 +1,9 @@ +use serde::{Deserialize, Serialize}; + +use crate::hook::Hook; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(tag = "type", content = "with", rename_all = "snake_case")] +pub enum MergeMode { + Hook(Hook), +} diff --git a/crates/punktf-lib-gsgh/src/prio.rs b/crates/punktf-lib-gsgh/src/prio.rs new file mode 100644 index 0000000..648f01f --- /dev/null +++ b/crates/punktf-lib-gsgh/src/prio.rs @@ -0,0 +1,18 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Priority(pub u32); + +impl PartialOrd for Priority { + fn partial_cmp(&self, other: &Self) -> Option { + // Reverse sort ordering (smaller = higher) + other.0.partial_cmp(&self.0) + } +} + +impl Ord for Priority { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + // Reverse sort ordering (smaller = higher) + other.0.cmp(&self.0) + } +} diff --git a/crates/punktf-lib-gsgh/src/profile.rs b/crates/punktf-lib-gsgh/src/profile.rs new file mode 100644 index 0000000..b4c895a --- /dev/null +++ b/crates/punktf-lib-gsgh/src/profile.rs @@ -0,0 +1,110 @@ +use crate::{ + env::Environment, hook::Hook, item::Item, prio::Priority, transform::Transformer, + version::Version, +}; +use std::{path::PathBuf, str::FromStr}; + +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum Error { + #[error("invalid profile: {0}")] + InvalidProfile(#[from] serde_yaml::Error), + #[error("unsupported version: {0}")] + UnsupportedVersion(Version), +} + +pub type Result = std::result::Result; + +/// Wrapper struct to be able to first parse only the version and then choose +/// the appropriate profile struct for it to do version compatible parsing. +#[repr(transparent)] +#[derive(Debug, Deserialize, Serialize)] +#[serde(default)] +pub struct ProfileVersion { + pub version: Version, +} + +impl Default for ProfileVersion { + fn default() -> Self { + Self { + version: Version::ZERO, + } + } +} + +impl From for Version { + fn from(value: ProfileVersion) -> Self { + value.version + } +} + +impl AsRef for ProfileVersion { + fn as_ref(&self) -> &Version { + &self.version + } +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct Shared { + #[serde(skip_serializing_if = "Option::is_none", default)] + pub priority: Option, + + #[serde(rename = "env", skip_serializing_if = "Environment::is_empty", default)] + pub environment: Environment, + + #[serde(skip_serializing_if = "Vec::is_empty", default)] + pub transformers: Vec, + + #[serde(skip_serializing_if = "Option::is_none", default)] + pub pre_hook: Option, + + #[serde(skip_serializing_if = "Option::is_none", default)] + pub post_hook: Option, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct Profile { + #[serde(flatten)] + pub version: ProfileVersion, + + #[serde(flatten)] + pub shared: Shared, + + #[serde(skip_serializing_if = "Vec::is_empty", default)] + pub aliases: Vec, + + #[serde(skip_serializing_if = "Vec::is_empty", default)] + pub extends: Vec, + + #[serde(skip_serializing_if = "Option::is_none", default)] + pub target: Option, + + #[serde(skip_serializing_if = "Vec::is_empty", default)] + pub items: Vec, +} + +impl Profile { + pub const VERSION: Version = Version::new(1, 0, 0); +} + +impl FromStr for Profile { + type Err = Error; + + fn from_str(s: &str) -> Result { + let version: Version = serde_yaml::from_str::(s)?.version; + + // No version or explicit zero version + if version == Version::ZERO { + return Err(Error::UnsupportedVersion(version)); + } + + // Version matching + if Self::VERSION.compatible(version) { + serde_yaml::from_str(s).map_err(Into::into) + } else { + Err(Error::UnsupportedVersion(version)) + } + } +} diff --git a/crates/punktf-lib-gsgh/src/transform.rs b/crates/punktf-lib-gsgh/src/transform.rs new file mode 100644 index 0000000..c7322c9 --- /dev/null +++ b/crates/punktf-lib-gsgh/src/transform.rs @@ -0,0 +1,24 @@ +use serde::{Deserialize, Serialize}; + +pub trait Transform { + fn apply(&self, content: String) -> Result>; +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(tag = "type", content = "with", rename_all = "snake_case")] +pub enum Transformer { + /// Transformer which replaces line termination characters with either unix + /// style (`\n`) or windows style (`\r\b`). + LineTerminator(LineTerminator), +} + +/// Transformer which replaces line termination characters with either unix +/// style (`\n`) or windows style (`\r\b`). +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] +pub enum LineTerminator { + /// Replaces all occurrences of `\r\n` with `\n` (unix style). + LF, + + /// Replaces all occurrences of `\n` with `\r\n` (windows style). + CRLF, +} diff --git a/crates/punktf-lib-gsgh/src/version.rs b/crates/punktf-lib-gsgh/src/version.rs new file mode 100644 index 0000000..a38b4cb --- /dev/null +++ b/crates/punktf-lib-gsgh/src/version.rs @@ -0,0 +1,257 @@ +use serde::{de::Visitor, Deserialize, Deserializer, Serialize}; +use std::{fmt, num::ParseIntError, str::FromStr}; +use thiserror::Error; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)] +pub enum ParseVersionError { + #[error("trailing characters")] + TrailingCharacters, + #[error("invalid number")] + InvalidNumber, + #[error("empty")] + Empty, + #[error("invalid separator")] + InvalidSeparator, +} + +impl From for ParseVersionError { + fn from(_: ParseIntError) -> Self { + Self::InvalidNumber + } +} + +pub type Result = std::result::Result; + +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct Version { + pub major: u8, + pub minor: u8, + pub patch: u8, +} + +impl fmt::Display for Version { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + major, + minor, + patch, + } = self; + + write!(f, "{major}.{minor}.{patch}") + } +} + +impl Version { + pub const ZERO: Self = Version { + major: 0, + minor: 0, + patch: 0, + }; + + pub const fn new(major: u8, minor: u8, patch: u8) -> Self { + Self { + major, + minor, + patch, + } + } + + pub const fn with_major(mut self, major: u8) -> Self { + self.major = major; + self + } + + pub const fn with_minor(mut self, minor: u8) -> Self { + self.minor = minor; + self + } + + pub const fn with_patch(mut self, patch: u8) -> Self { + self.patch = patch; + self + } + + pub const fn compatible(self, other: Self) -> bool { + self.major == other.major + } +} + +fn parse_u8(s: &str) -> Result> { + fn check_digit(bytes: &[u8], idx: usize) -> bool { + bytes.get(idx).map(u8::is_ascii_digit).unwrap_or(false) + } + + if s.is_empty() { + return Ok(None); + } + + let bytes = s.as_bytes(); + + // u8 can be max 3 digits (255) + let eat = match ( + check_digit(bytes, 0), + check_digit(bytes, 1), + check_digit(bytes, 2), + ) { + (true, true, true) => 3, + (true, true, _) => 2, + (true, _, _) => 1, + _ => return Err(ParseVersionError::InvalidNumber), + }; + + Ok(Some((&s[eat..], s[..eat].parse::()?))) +} + +fn parse_dot(s: &str) -> Result> { + if s.is_empty() { + return Ok(None); + } + + if s.as_bytes()[0] == b'.' { + Ok(Some(&s[1..])) + } else { + Err(ParseVersionError::InvalidSeparator) + } +} + +impl FromStr for Version { + type Err = ParseVersionError; + + fn from_str(s: &str) -> Result { + let Some((s, major)) = parse_u8(s)? else { + return Err(ParseVersionError::Empty); + }; + + let Some(s) = parse_dot(s)? else { + return Ok(Version { major, ..Default::default() }); + }; + + let Some((s, minor)) = parse_u8(s)? else { + // The parse `.` is trailing + return Err(ParseVersionError::TrailingCharacters); + }; + + let Some(s) = parse_dot(s)? else { + return Ok(Version { major, minor, ..Default::default() }); + }; + + let Some((s, patch)) = parse_u8(s)? else { + // The parse `.` is trailing + return Err(ParseVersionError::TrailingCharacters); + }; + + if s.is_empty() { + Ok(Version { + major, + minor, + patch, + }) + } else { + Err(ParseVersionError::TrailingCharacters) + } + } +} + +impl Serialize for Version { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for Version { + fn deserialize(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("semver version") + } + + fn visit_str(self, string: &str) -> Result + where + E: serde::de::Error, + { + string.parse().map_err(serde::de::Error::custom) + } + } + + deserializer.deserialize_str(VersionVisitor) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn version_parse_ok() -> Result<(), Box> { + assert_eq!("1".parse::()?, Version::ZERO.with_major(1)); + assert_eq!( + "22.12".parse::()?, + Version::ZERO.with_major(22).with_minor(12) + ); + assert_eq!( + "0.12.55".parse::()?, + Version::ZERO.with_minor(12).with_patch(55) + ); + Ok(()) + } + + #[test] + fn version_parse_err() -> Result<(), Box> { + assert_eq!( + "1.".parse::(), + Err(ParseVersionError::TrailingCharacters) + ); + assert_eq!("".parse::(), Err(ParseVersionError::Empty)); + assert_eq!( + "1.1.1 ".parse::(), + Err(ParseVersionError::TrailingCharacters) + ); + assert_eq!( + "1.1.1.".parse::(), + Err(ParseVersionError::TrailingCharacters) + ); + assert_eq!( + "1.1.1.1".parse::(), + Err(ParseVersionError::TrailingCharacters) + ); + assert_eq!( + "256".parse::(), + Err(ParseVersionError::InvalidNumber) + ); + + Ok(()) + } + + #[test] + fn version_cmp() { + assert!(Version::ZERO.with_major(1) == Version::ZERO.with_major(1)); + assert!(Version::ZERO.with_minor(2) == Version::ZERO.with_minor(2)); + assert!(Version::ZERO.with_patch(3) == Version::ZERO.with_patch(3)); + + assert!(Version::ZERO.with_major(1) < Version::ZERO.with_major(2)); + assert!(Version::ZERO.with_minor(2) < Version::ZERO.with_major(2)); + assert!(Version::ZERO.with_patch(3) < Version::ZERO.with_major(2)); + + assert!( + Version { + major: 2, + minor: 3, + patch: 1 + } > Version { + major: 2, + minor: 1, + patch: 10 + } + ); + } +} From 04a26ea2fd3e08130e222c4c9ed2fe751c84dfea Mon Sep 17 00:00:00 2001 From: Jonas Grawe Date: Tue, 15 Aug 2023 12:41:20 +0200 Subject: [PATCH 08/12] feat: values and templates --- crates/punktf-lib-gsgh/Cargo.toml | 1 + crates/punktf-lib-gsgh/profile.yaml | 4 - crates/punktf-lib-gsgh/src/env.rs | 12 +- crates/punktf-lib-gsgh/src/hook.rs | 6 +- crates/punktf-lib-gsgh/src/lib.rs | 6 +- crates/punktf-lib-gsgh/src/template.rs | 71 +++++++++ crates/punktf-lib-gsgh/src/value.rs | 197 +++++++++++++++++++++++++ 7 files changed, 283 insertions(+), 14 deletions(-) create mode 100644 crates/punktf-lib-gsgh/src/template.rs create mode 100644 crates/punktf-lib-gsgh/src/value.rs diff --git a/crates/punktf-lib-gsgh/Cargo.toml b/crates/punktf-lib-gsgh/Cargo.toml index 930e66a..0bd4258 100644 --- a/crates/punktf-lib-gsgh/Cargo.toml +++ b/crates/punktf-lib-gsgh/Cargo.toml @@ -11,6 +11,7 @@ keywords.workspace = true [dependencies] cfg-if = "1.0.0" log.workspace = true +minijinja = { version = "1.0.6", default-features = false, features = ["builtins", "adjacent_loop_items", "debug", "deserialization"] } semver = { version = "1.0.18", features = ["serde"] } serde = { workspace = true, features = ["derive"] } serde_yaml = "0.9.25" diff --git a/crates/punktf-lib-gsgh/profile.yaml b/crates/punktf-lib-gsgh/profile.yaml index 415daa7..794f095 100644 --- a/crates/punktf-lib-gsgh/profile.yaml +++ b/crates/punktf-lib-gsgh/profile.yaml @@ -11,10 +11,6 @@ env: Foo: Bar Bool: true Number: 2.4 - Map: - This: Should - Not: be - Allowed: null transformers: - type: line_terminator diff --git a/crates/punktf-lib-gsgh/src/env.rs b/crates/punktf-lib-gsgh/src/env.rs index 98e86ec..7914394 100644 --- a/crates/punktf-lib-gsgh/src/env.rs +++ b/crates/punktf-lib-gsgh/src/env.rs @@ -5,8 +5,10 @@ use std::{ use serde::{Deserialize, Serialize}; -#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct Environment(pub BTreeMap); +use crate::value::Value; + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct Environment(pub BTreeMap); impl Environment { pub fn is_empty(&self) -> bool { @@ -14,7 +16,7 @@ impl Environment { } } -#[derive(Default, Debug, Clone, PartialEq, Eq)] +#[derive(Default, Debug, Clone, PartialEq)] pub struct LayeredEnvironment(Vec<(&'static str, Environment)>); impl LayeredEnvironment { @@ -34,7 +36,7 @@ impl LayeredEnvironment { .collect() } - pub fn get(&self, key: &str) -> Option<&serde_yaml::Value> { + pub fn get(&self, key: &str) -> Option<&Value> { for (_, layer) in self.0.iter() { if let Some(value) = layer.0.get(key) { return Some(value); @@ -70,7 +72,7 @@ impl<'a> LayeredIter<'a> { } impl<'a> Iterator for LayeredIter<'a> { - type Item = (&'a str, &'a serde_yaml::Value); + type Item = (&'a str, &'a Value); fn next(&mut self) -> Option { let key = self.keys.next()?; diff --git a/crates/punktf-lib-gsgh/src/hook.rs b/crates/punktf-lib-gsgh/src/hook.rs index b8b35de..e04746f 100644 --- a/crates/punktf-lib-gsgh/src/hook.rs +++ b/crates/punktf-lib-gsgh/src/hook.rs @@ -143,7 +143,7 @@ impl Hook { #[cfg(test)] mod tests { - use crate::env::Environment; + use crate::{env::Environment, value::Value}; use super::*; @@ -151,8 +151,8 @@ mod tests { fn echo_hello_world() { let env = Environment( [ - ("TEST", serde_yaml::Value::Bool(true)), - ("FOO", serde_yaml::Value::String(" BAR Test".into())), + ("TEST", Value::Bool(true)), + ("FOO", Value::String(" BAR Test".into())), ] .into_iter() .map(|(k, v)| (k.to_string(), v)) diff --git a/crates/punktf-lib-gsgh/src/lib.rs b/crates/punktf-lib-gsgh/src/lib.rs index 1f7de57..49c3b40 100644 --- a/crates/punktf-lib-gsgh/src/lib.rs +++ b/crates/punktf-lib-gsgh/src/lib.rs @@ -4,7 +4,9 @@ pub mod item; pub mod merge; pub mod prio; pub mod profile; +pub mod template; pub mod transform; +pub mod value; pub mod version; #[test] @@ -27,9 +29,9 @@ fn prnp() { use crate::{item::Item, prio::Priority}; use env::Environment; use profile::{Profile, ProfileVersion}; - use serde_yaml::Value; use std::path::PathBuf; use transform::Transformer; + use value::Value; use crate::profile::Shared; @@ -86,9 +88,9 @@ fn prni() { use crate::hook::Hook; use crate::{item::Item, prio::Priority}; use env::Environment; - use serde_yaml::Value; use std::path::PathBuf; use transform::Transformer; + use value::Value; use crate::profile::Shared; diff --git a/crates/punktf-lib-gsgh/src/template.rs b/crates/punktf-lib-gsgh/src/template.rs new file mode 100644 index 0000000..2acfacc --- /dev/null +++ b/crates/punktf-lib-gsgh/src/template.rs @@ -0,0 +1,71 @@ +use std::io::Write; + +use crate::env::LayeredEnvironment; + +pub type Result> = std::result::Result; + +pub trait TemplateEngine { + fn render_to_write( + &mut self, + w: W, + name: &str, + env: &LayeredEnvironment, + content: &str, + ) -> Result<()>; + + fn render(&mut self, name: &str, env: &LayeredEnvironment, content: &str) -> Result { + let mut buf = Vec::new(); + self.render_to_write(&mut buf, name, env, content)?; + Ok(String::from_utf8(buf)?) + } +} + +pub mod mj { + use std::io::Write; + + use crate::{env::LayeredEnvironment, value}; + + use super::{Result, TemplateEngine}; + use minijinja::{value::StructObject, Environment, UndefinedBehavior, Value}; + + impl From for Value { + fn from(value: value::Value) -> Self { + match value { + value::Value::Null => Value::from(()), + value::Value::String(v) => Value::from(v), + value::Value::Bool(v) => Value::from(v), + value::Value::Float(v) => Value::from(v), + value::Value::Int(v) => Value::from(v), + } + } + } + + impl StructObject for LayeredEnvironment { + fn get_field(&self, name: &str) -> Option { + self.get(name).map(|v| v.clone().into()) + } + } + + pub struct MiniJinja; + + impl TemplateEngine for MiniJinja { + fn render_to_write( + &mut self, + w: W, + name: &str, + ctx: &LayeredEnvironment, + content: &str, + ) -> Result<()> { + let mut env = Environment::new(); + // Error on undefined variables + env.set_undefined_behavior(UndefinedBehavior::Strict); + env.add_template(name, content)?; + + let tmpl = env.get_template(name)?; + let val = Value::from_struct_object(ctx.clone()); + tmpl.render_to_write(val, w)?; + + Ok(()) + } + } +} diff --git a/crates/punktf-lib-gsgh/src/value.rs b/crates/punktf-lib-gsgh/src/value.rs new file mode 100644 index 0000000..25aca23 --- /dev/null +++ b/crates/punktf-lib-gsgh/src/value.rs @@ -0,0 +1,197 @@ +#[derive(Debug, Clone, PartialEq, PartialOrd)] +pub enum Value { + Null, + String(String), + Bool(bool), + Float(f64), + Int(i64), +} + +impl From for Value { + fn from(value: String) -> Self { + Self::String(value) + } +} + +impl From<&str> for Value { + fn from(value: &str) -> Self { + Self::String(value.to_owned()) + } +} + +impl From for Value { + fn from(value: bool) -> Self { + Self::Bool(value) + } +} + +impl From<&bool> for Value { + fn from(value: &bool) -> Self { + Self::Bool(*value) + } +} + +impl> From> for Value { + fn from(value: Option) -> Self { + let Some(v) = value else { + return Value::Null; + }; + v.into() + } +} + +impl From<()> for Value { + fn from(_: ()) -> Self { + Self::Null + } +} + +pub mod ser { + use super::Value; + use serde::ser; + + impl ser::Serialize for Value { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + match self { + Value::Null => serializer.serialize_unit(), + Value::String(v) => serializer.serialize_str(v), + Value::Bool(v) => serializer.serialize_bool(*v), + Value::Float(v) => serializer.serialize_f64(*v), + Value::Int(v) => serializer.serialize_i64(*v), + } + } + } +} + +pub mod de { + use serde::de; + + use super::Value; + + impl<'de> de::Deserialize<'de> for Value { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct ValueVisitor; + + impl<'de> de::Visitor<'de> for ValueVisitor { + type Value = Value; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("any literal value") + } + + fn visit_i8(self, v: i8) -> Result + where + E: de::Error, + { + Ok(Value::Int(v as i64)) + } + + fn visit_i16(self, v: i16) -> Result + where + E: de::Error, + { + Ok(Value::Int(v as i64)) + } + + fn visit_i32(self, v: i32) -> Result + where + E: de::Error, + { + Ok(Value::Int(v as i64)) + } + + fn visit_i64(self, v: i64) -> Result + where + E: de::Error, + { + Ok(Value::Int(v as i64)) + } + + fn visit_u8(self, v: u8) -> Result + where + E: de::Error, + { + Ok(Value::Int(v as i64)) + } + + fn visit_u16(self, v: u16) -> Result + where + E: de::Error, + { + Ok(Value::Int(v as i64)) + } + + fn visit_u32(self, v: u32) -> Result + where + E: de::Error, + { + Ok(Value::Int(v as i64)) + } + + fn visit_u64(self, v: u64) -> Result + where + E: de::Error, + { + Ok(Value::Int(v as i64)) + } + + fn visit_f32(self, v: f32) -> Result + where + E: de::Error, + { + Ok(Value::Float(v as f64)) + } + + fn visit_f64(self, v: f64) -> Result + where + E: de::Error, + { + Ok(Value::Float(v as f64)) + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + Ok(Value::String(v.to_owned())) + } + + fn visit_string(self, v: String) -> Result + where + E: de::Error, + { + Ok(Value::String(v)) + } + + fn visit_bool(self, v: bool) -> Result + where + E: de::Error, + { + Ok(Value::Bool(v)) + } + + fn visit_none(self) -> Result + where + E: de::Error, + { + Ok(Value::Null) + } + + fn visit_unit(self) -> Result + where + E: de::Error, + { + Ok(Value::Null) + } + } + + deserializer.deserialize_any(ValueVisitor) + } + } +} From 9c8557938d8c8a0f9869c927da3d28e6ae96c03a Mon Sep 17 00:00:00 2001 From: Jonas Grawe Date: Tue, 15 Aug 2023 13:31:21 +0200 Subject: [PATCH 09/12] feat: template registry --- crates/punktf-lib-gsgh/src/template.rs | 40 ++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/crates/punktf-lib-gsgh/src/template.rs b/crates/punktf-lib-gsgh/src/template.rs index 2acfacc..b22cde0 100644 --- a/crates/punktf-lib-gsgh/src/template.rs +++ b/crates/punktf-lib-gsgh/src/template.rs @@ -1,13 +1,13 @@ -use std::io::Write; +use std::{collections::BTreeMap, io::Write, path::Path}; use crate::env::LayeredEnvironment; pub type Result> = std::result::Result; pub trait TemplateEngine { - fn render_to_write( + fn render_to_write( &mut self, - w: W, + w: &mut dyn Write, name: &str, env: &LayeredEnvironment, content: &str, @@ -20,6 +20,21 @@ pub trait TemplateEngine { } } +#[derive(Default)] +pub struct Registry(BTreeMap<&'static str, Box>); + +impl Registry { + pub fn register(&mut self, extension: &'static str, engine: E) { + self.0.insert(extension, Box::new(engine)); + } + + pub fn get_for_path(&mut self, path: &Path) -> Option<&mut dyn TemplateEngine> { + let ext = path.extension()?.to_str()?; + let r = self.0.get_mut(ext)?; + Some(r.as_mut()) + } +} + pub mod mj { use std::io::Write; @@ -49,9 +64,9 @@ pub mod mj { pub struct MiniJinja; impl TemplateEngine for MiniJinja { - fn render_to_write( + fn render_to_write( &mut self, - w: W, + w: &mut dyn Write, name: &str, ctx: &LayeredEnvironment, content: &str, @@ -69,3 +84,18 @@ pub mod mj { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn registry() { + let mut reg = Registry::default(); + reg.register("mjinja", mj::MiniJinja); + + let eng = reg.get_for_path(Path::new("/test/path/123/file.txt.mjinja")); + + assert!(eng.is_some()) + } +} From 14a45275f659f25f31a1ea70a28ee970d9f3a051 Mon Sep 17 00:00:00 2001 From: Jonas Grawe Date: Sat, 26 Aug 2023 11:24:53 +0200 Subject: [PATCH 10/12] chore: cargo update --- Cargo.lock | 562 +++++++++++++++++++++++++++++++---------------------- Cargo.toml | 5 +- 2 files changed, 327 insertions(+), 240 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f68a9c7..acfe2a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.17.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] @@ -17,28 +17,70 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "anstream" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi 0.1.19", + "hermit-abi", "libc", "winapi", ] -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - [[package]] name = "backtrace" -version = "0.3.66" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", @@ -68,9 +110,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.76" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -80,36 +125,43 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.1.2" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e638668a62aced2c9fb72b5135a33b4a500485ccf2a0e402e09aa04ab2fc115" +checksum = "1d5f1946157a96594eb2d2c10eb7ad9a2b27518cb3000209dec700c35df9197d" dependencies = [ - "bitflags", + "clap_builder", "clap_derive", - "clap_lex", - "is-terminal", "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78116e32a042dd73c2901f0dc30790d20ff3447f3e3472fad359e8c3d282bcd6" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", "strsim", - "termcolor", ] [[package]] name = "clap_complete" -version = "4.1.1" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6540eedc41f8a5a76cf3d8d458057dcdf817be4158a55b5f861f7a5483de75" +checksum = "586a385f7ef2f8b4d86bddaa0c094794e7ccbfe5ffef1f434fe928143fc783a5" dependencies = [ "clap", ] [[package]] name = "clap_derive" -version = "4.1.0" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" +checksum = "c9fd1a5729c4548118d7d70ff234a44868d00489a4b6597b0b020918a0e91a1a" dependencies = [ "heck", - "proc-macro-error", "proc-macro2", "quote", "syn", @@ -117,18 +169,15 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.3.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" -dependencies = [ - "os_str_bytes", -] +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" [[package]] name = "clap_mangen" -version = "0.2.7" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb258c6232b4d728d13d6072656627924c16707aae6267cd5a1ea05abff9a25c" +checksum = "cf8e5f34d85d9e0bbe2491d100a7a7c1007bb2467b518080bfe311e8947197a9" dependencies = [ "clap", "roff", @@ -147,34 +196,30 @@ dependencies = [ "owo-colors", ] +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "console" -version = "0.15.5" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60" +checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" dependencies = [ "encode_unicode", "lazy_static", "libc", "unicode-width", - "windows-sys", -] - -[[package]] -name = "ctor" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb" -dependencies = [ - "quote", - "syn", + "windows-sys 0.45.0", ] [[package]] name = "diff" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "dirs" @@ -196,6 +241,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + [[package]] name = "encode_unicode" version = "0.3.6" @@ -214,25 +265,10 @@ dependencies = [ ] [[package]] -name = "errno" -version = "0.2.8" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "eyre" @@ -246,9 +282,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", @@ -257,21 +293,21 @@ dependencies = [ [[package]] name = "gimli" -version = "0.26.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" [[package]] name = "hashbrown" -version = "0.12.3" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -282,15 +318,6 @@ dependencies = [ "libc", ] -[[package]] -name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - [[package]] name = "indenter" version = "0.3.3" @@ -299,41 +326,28 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "1.9.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" dependencies = [ - "autocfg", + "equivalent", "hashbrown", ] [[package]] -name = "io-lifetimes" -version = "1.0.3" +name = "itertools" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" dependencies = [ - "libc", - "windows-sys", -] - -[[package]] -name = "is-terminal" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330" -dependencies = [ - "hermit-abi 0.2.6", - "io-lifetimes", - "rustix", - "windows-sys", + "either", ] [[package]] name = "itoa" -version = "1.0.4" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "lazy_static" @@ -343,69 +357,70 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.137" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] -name = "linux-raw-sys" -version = "0.1.3" +name = "log" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] -name = "log" -version = "0.4.17" +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "minijinja" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "50c43c912b380856deeb78d826e3b77df13a90e69aef6223e3ad28c02d2ca857" dependencies = [ - "cfg-if", + "serde", ] [[package]] -name = "memchr" -version = "2.5.0" +name = "minimal-lexical" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.5.4" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] [[package]] -name = "object" -version = "0.29.0" +name = "nom" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", + "minimal-lexical", ] [[package]] -name = "once_cell" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" - -[[package]] -name = "os_str_bytes" -version = "6.4.0" +name = "object" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5bf27447411e9ee3ff51186bf7a08e16c341efdde93f4d823e8844429bed7e" +checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe" +dependencies = [ + "memchr", +] [[package]] -name = "output_vt100" -version = "0.1.2" +name = "once_cell" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" -dependencies = [ - "winapi", -] +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "owo-colors" @@ -415,45 +430,19 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" [[package]] name = "pretty_assertions" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" dependencies = [ - "ctor", "diff", - "output_vt100", "yansi", ] -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro2" -version = "1.0.47" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] @@ -494,11 +483,26 @@ dependencies = [ "walkdir", ] +[[package]] +name = "punktf-lib-gsgh" +version = "0.1.0" +dependencies = [ + "cfg-if", + "log", + "minijinja", + "semver", + "serde", + "serde_yaml", + "thiserror", + "version-compare", + "versions", +] + [[package]] name = "quote" -version = "1.0.21" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -537,29 +541,15 @@ checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316" [[package]] name = "rustc-demangle" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" - -[[package]] -name = "rustix" -version = "0.36.4" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb93e85278e08bb5788653183213d3a60fc242b10cb9be96586f5a73dcb67c23" -dependencies = [ - "bitflags", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys", -] +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "same-file" @@ -570,20 +560,29 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "semver" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +dependencies = [ + "serde", +] + [[package]] name = "serde" -version = "1.0.152" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", @@ -592,9 +591,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.91" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" dependencies = [ "itoa", "ryu", @@ -603,9 +602,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.16" +version = "0.9.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92b5b431e8907b50339b51223b97d102db8d987ced36f6e4d03621db9316c834" +checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574" dependencies = [ "indexmap", "itoa", @@ -641,9 +640,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.104" +version = "2.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae548ec36cf198c0ef7710d3c230987c2d6d7bd98ad6edc0274462724c585ce" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" dependencies = [ "proc-macro2", "quote", @@ -652,27 +651,27 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" dependencies = [ "proc-macro2", "quote", @@ -681,15 +680,15 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.5" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-segmentation" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-width" @@ -699,24 +698,40 @@ checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "unsafe-libyaml" -version = "0.2.4" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" + +[[package]] +name = "utf8parse" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e5fa573d8ac5f1a856f8d7be41d390ee973daf97c806b2c1a465e4e1406e68" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] -name = "version_check" -version = "0.9.4" +name = "version-compare" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" + +[[package]] +name = "versions" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c73a36bc44e3039f51fbee93e39f41225f6b17b380eb70cc2aab942df06b34dd" +dependencies = [ + "itertools", + "nom", + "serde", +] [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi", "winapi-util", ] @@ -759,60 +774,135 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.42.0" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "yansi" diff --git a/Cargo.toml b/Cargo.toml index eda2ba5..c4fe9e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = ["crates/*"] exclude = ["guide/linkchecker"] +resolver = "2" [workspace.package] authors = ["Michael Lohr ", "Shemnei"] @@ -35,10 +36,6 @@ opt-level = 0 # and is only used when debugging. debug = 1 -[profile.dev.package.backtrace] -# color-eyre: Improves performance for debug builds -opt-level = 3 - [profile.release] lto = "thin" # Optimize for binary size. In this case also turns out to be the fastest to From 9cf16d67d9957c76d7b9feea2339944e38fac3f3 Mon Sep 17 00:00:00 2001 From: Jonas Grawe Date: Sat, 20 Jan 2024 23:24:38 +0100 Subject: [PATCH 11/12] chore: fmt --- crates/punktf-lib-gsgh/src/hook.rs | 7 ++++-- crates/punktf-lib-gsgh/src/version.rs | 31 ++++++++++++++++----------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/crates/punktf-lib-gsgh/src/hook.rs b/crates/punktf-lib-gsgh/src/hook.rs index e04746f..8d5825c 100644 --- a/crates/punktf-lib-gsgh/src/hook.rs +++ b/crates/punktf-lib-gsgh/src/hook.rs @@ -124,8 +124,11 @@ impl Hook { } let Some(mut cmd) = cmd else { - return Err(HookError::IoError(std::io::Error::new(std::io::ErrorKind::Other, "Hooks are only supported on Windows and Unix-based systems"))); - }; + return Err(HookError::IoError(std::io::Error::new( + std::io::ErrorKind::Other, + "Hooks are only supported on Windows and Unix-based systems", + ))); + }; match self { Self::Inline(s) => { diff --git a/crates/punktf-lib-gsgh/src/version.rs b/crates/punktf-lib-gsgh/src/version.rs index a38b4cb..17c63c3 100644 --- a/crates/punktf-lib-gsgh/src/version.rs +++ b/crates/punktf-lib-gsgh/src/version.rs @@ -119,26 +119,33 @@ impl FromStr for Version { fn from_str(s: &str) -> Result { let Some((s, major)) = parse_u8(s)? else { - return Err(ParseVersionError::Empty); - }; + return Err(ParseVersionError::Empty); + }; let Some(s) = parse_dot(s)? else { - return Ok(Version { major, ..Default::default() }); - }; + return Ok(Version { + major, + ..Default::default() + }); + }; let Some((s, minor)) = parse_u8(s)? else { - // The parse `.` is trailing - return Err(ParseVersionError::TrailingCharacters); - }; + // The parse `.` is trailing + return Err(ParseVersionError::TrailingCharacters); + }; let Some(s) = parse_dot(s)? else { - return Ok(Version { major, minor, ..Default::default() }); - }; + return Ok(Version { + major, + minor, + ..Default::default() + }); + }; let Some((s, patch)) = parse_u8(s)? else { - // The parse `.` is trailing - return Err(ParseVersionError::TrailingCharacters); - }; + // The parse `.` is trailing + return Err(ParseVersionError::TrailingCharacters); + }; if s.is_empty() { Ok(Version { From eddd814d3a5c6284717371ffe7778c1274368e2b Mon Sep 17 00:00:00 2001 From: Jonas Grawe Date: Sat, 20 Jan 2024 23:26:57 +0100 Subject: [PATCH 12/12] chore: clippy --- crates/punktf-lib-gsgh/src/env.rs | 5 ++--- crates/punktf-lib-gsgh/src/prio.rs | 3 +-- crates/punktf-lib-gsgh/src/value.rs | 4 ++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/crates/punktf-lib-gsgh/src/env.rs b/crates/punktf-lib-gsgh/src/env.rs index 7914394..22d32d1 100644 --- a/crates/punktf-lib-gsgh/src/env.rs +++ b/crates/punktf-lib-gsgh/src/env.rs @@ -1,6 +1,5 @@ use std::{ - collections::{btree_set, BTreeMap, BTreeSet, HashSet}, - ops::Deref, + collections::{btree_set, BTreeMap, BTreeSet}, }; use serde::{Deserialize, Serialize}; @@ -43,7 +42,7 @@ impl LayeredEnvironment { } } - return None; + None } pub fn iter(&self) -> LayeredIter<'_> { diff --git a/crates/punktf-lib-gsgh/src/prio.rs b/crates/punktf-lib-gsgh/src/prio.rs index 648f01f..952469a 100644 --- a/crates/punktf-lib-gsgh/src/prio.rs +++ b/crates/punktf-lib-gsgh/src/prio.rs @@ -5,8 +5,7 @@ pub struct Priority(pub u32); impl PartialOrd for Priority { fn partial_cmp(&self, other: &Self) -> Option { - // Reverse sort ordering (smaller = higher) - other.0.partial_cmp(&self.0) + Some(self.cmp(other)) } } diff --git a/crates/punktf-lib-gsgh/src/value.rs b/crates/punktf-lib-gsgh/src/value.rs index 25aca23..d7b3504 100644 --- a/crates/punktf-lib-gsgh/src/value.rs +++ b/crates/punktf-lib-gsgh/src/value.rs @@ -110,7 +110,7 @@ pub mod de { where E: de::Error, { - Ok(Value::Int(v as i64)) + Ok(Value::Int(v)) } fn visit_u8(self, v: u8) -> Result @@ -152,7 +152,7 @@ pub mod de { where E: de::Error, { - Ok(Value::Float(v as f64)) + Ok(Value::Float(v)) } fn visit_str(self, v: &str) -> Result