use std::collections::HashMap; use serde::Deserialize; #[derive(Debug, Clone, PartialEq, Deserialize)] #[serde(untagged)] pub enum CuddleBase { Bool(bool), String(String), } #[derive(Debug, Clone, PartialEq, Deserialize)] pub struct CuddleShellScriptArgEnv { pub key: String, pub description: Option, } #[derive(Debug, Clone, PartialEq, Deserialize)] pub struct CuddleShellScriptArgFlag { pub name: String, pub description: Option, pub required: Option, pub default_value: Option, } #[derive(Debug, Clone, PartialEq, Deserialize)] #[serde(tag = "type")] pub enum CuddleShellScriptArg { #[serde(alias = "env")] Env(CuddleShellScriptArgEnv), #[serde(alias = "flag")] Flag(CuddleShellScriptArgFlag), } #[derive(Debug, Clone, PartialEq, Deserialize)] pub struct CuddleShellScript { pub description: Option, pub args: Option>, } #[derive(Debug, Clone, PartialEq, Deserialize)] pub struct CuddleDaggerScript { pub description: Option, } #[derive(Debug, Clone, PartialEq, Deserialize)] pub struct CuddleLuaScript { pub description: Option, } #[derive(Debug, Clone, PartialEq, Deserialize)] pub enum CuddleRustUpstream { #[serde(alias = "gitea")] Gitea { url: String }, } #[derive(Debug, Clone, PartialEq, Deserialize)] pub struct CuddleRustScript { pub description: Option, pub upstream: CuddleRustUpstream, } #[derive(Debug, Clone, PartialEq, Deserialize)] #[serde(tag = "type")] pub enum CuddleScript { #[serde(alias = "shell")] Shell(CuddleShellScript), #[serde(alias = "dagger")] Dagger(CuddleDaggerScript), #[serde(alias = "lua")] Lua(CuddleLuaScript), #[serde(alias = "rust")] Rust(CuddleRustScript), } #[derive(Debug, Clone, PartialEq, Deserialize)] pub struct CuddlePlan { pub base: CuddleBase, pub vars: Option, pub scripts: Option>, } #[derive(Deserialize, Debug, Clone, PartialEq)] pub struct CuddlePlanVariables(HashMap); impl From for Vec { fn from(value: CuddlePlanVariables) -> Self { let variables: CuddleVariables = value.0.into(); let mut vars = variables.0; vars.sort_by(|a, b| a.name.partial_cmp(&b.name).unwrap()); vars } } #[derive(Deserialize, Debug, Clone, PartialEq)] #[serde(untagged)] pub enum CuddlePlanVar { Str(String), Nested(HashMap), } #[derive(Clone, Debug, PartialEq)] struct CuddleVariables(Vec); impl From> for CuddleVariables { fn from(value: HashMap) -> Self { let mut variables = Vec::new(); for (k, v) in value { match v { CuddlePlanVar::Str(value) => variables.push(CuddleVariable::new(k, value)), CuddlePlanVar::Nested(nested) => { let nested_variables: CuddleVariables = nested.into(); let mut combined_variables: Vec<_> = nested_variables .0 .into_iter() .map(|v| CuddleVariable::new(format!("{}_{}", k, v.name), v.value)) .collect(); variables.append(&mut combined_variables); } } } CuddleVariables(variables) } } #[derive(Debug, Clone, PartialEq, Deserialize, serde::Serialize)] #[allow(dead_code)] pub struct CuddleVariable { pub name: String, pub value: String, } impl CuddleVariable { pub fn new(name: impl Into, value: impl Into) -> Self { Self { name: name.into(), value: value.into(), } } } impl From> for CuddlePlanVar { fn from(value: HashMap) -> Self { CuddlePlanVar::Nested(value) } } impl From> for CuddlePlanVar { fn from(value: HashMap<&str, CuddlePlanVar>) -> Self { CuddlePlanVar::Nested(value.into_iter().map(|(k, v)| (k.to_string(), v)).collect()) } } impl From for CuddlePlanVar { fn from(value: String) -> Self { CuddlePlanVar::Str(value) } } impl From<&str> for CuddlePlanVar { fn from(value: &str) -> Self { CuddlePlanVar::Str(value.to_string()) } } #[cfg(test)] mod test { use std::collections::HashMap; use super::{CuddlePlanVariables, CuddleVariable}; #[test] pub fn parse_cuddle_variables() { let cuddle = r#" someKey: someValue someNestedKey: someNestedNestedKey: someKey: key someKey: key "#; let cuddle_var: CuddlePlanVariables = serde_yaml::from_str(cuddle).unwrap(); let mut expected = HashMap::new(); expected.insert("someKey", "someValue".into()); let mut nested_key = HashMap::new(); nested_key.insert("someKey", "key".into()); let mut nested_nested_key = HashMap::new(); nested_nested_key.insert("someKey", "key".into()); nested_key.insert("someNestedNestedKey", nested_nested_key.into()); expected.insert("someNestedKey", nested_key.into()); assert_eq!( CuddlePlanVariables( expected .into_iter() .map(|(k, v)| (k.to_string(), v)) .collect() ), cuddle_var ); } #[test] pub fn to_cuddle_variables() { let cuddle = r#" someKey: someValue someNestedKey: someNestedNestedKey: someKey: key someKey: key "#; let cuddle_var: CuddlePlanVariables = serde_yaml::from_str(cuddle).unwrap(); let variables: Vec = cuddle_var.into(); let mut expected: Vec = vec![ CuddleVariable::new("someKey", "someValue"), CuddleVariable::new("someNestedKey_someKey", "key"), CuddleVariable::new("someNestedKey_someNestedNestedKey_someKey", "key"), ]; expected.sort_by(|a, b| a.name.partial_cmp(&b.name).unwrap()); assert_eq!(expected, variables); } }