diff options
author | Marcel Müller <m.mueller@ifm.com> | 2022-05-11 12:00:50 +0200 |
---|---|---|
committer | Marcel Müller <m.mueller@ifm.com> | 2022-05-12 09:08:10 +0200 |
commit | 3773563e6ca33c8340d7d4e9e384809e3d182fe9 (patch) | |
tree | 388986dbd7e4c8ef186646a17d23d6aa3212be17 /crates | |
parent | 4c2f168e8831be7334bf4c46db725df644ed6490 (diff) |
Allows untagged enums to be represented as strings
Signed-off-by: Marcel Müller <m.mueller@ifm.com>
Diffstat (limited to 'crates')
-rw-r--r-- | crates/core/tedge_api/examples/print_config.rs | 18 | ||||
-rw-r--r-- | crates/core/tedge_api/src/config.rs | 160 | ||||
-rw-r--r-- | crates/core/tedge_api/tedge_config_derive/src/lib.rs | 57 | ||||
-rw-r--r-- | crates/core/tedge_api/tests/derive_config.rs | 12 |
4 files changed, 173 insertions, 74 deletions
diff --git a/crates/core/tedge_api/examples/print_config.rs b/crates/core/tedge_api/examples/print_config.rs index 498a79b7..ba6e7a6b 100644 --- a/crates/core/tedge_api/examples/print_config.rs +++ b/crates/core/tedge_api/examples/print_config.rs @@ -97,11 +97,29 @@ fn main() { name: String, port: Port, }, + UndocumentedComplex { + num: u16, + foo: f32, + }, + } + + #[derive(Config)] + #[config(untagged)] + enum DebugLevel { + /// Enables debug output + /// + /// And info of course + Debug, + /// Only pertinent information will be logged + Info, + /// A custom debug level + Custom(String), } #[derive(Config)] struct NginxConfig { vhosts: Vec<NginxVHost>, + debug_level: DebugLevel, allow_priv_ports: bool, } diff --git a/crates/core/tedge_api/src/config.rs b/crates/core/tedge_api/src/config.rs index 2cd2ce78..90bee595 100644 --- a/crates/core/tedge_api/src/config.rs +++ b/crates/core/tedge_api/src/config.rs @@ -46,6 +46,17 @@ impl ConfigDescription { } } +/// How an enum is represented +#[derive(Debug, Serialize, PartialEq)] +pub enum EnumVariantRepresentation { + /// The enum is represented by a string + /// + /// This is the case with unit variants for example + String(&'static str), + /// The enum is represented by the value presented here + Wrapped(Box<ConfigDescription>), +} + /// The kind of enum tagging used by the [`ConfigKind`] #[derive(Debug, Serialize, PartialEq)] pub enum ConfigEnumKind { @@ -104,7 +115,11 @@ pub enum ConfigKind { /// Config represents multiple choice of configurations Enum( ConfigEnumKind, - Vec<(&'static str, Option<&'static str>, ConfigDescription)>, + Vec<( + &'static str, + Option<&'static str>, + EnumVariantRepresentation, + )>, ), } @@ -163,34 +178,37 @@ impl_config_kind!(ConfigKind::Integer; "Integer"; "An unsigned integer with 8 bi impl_config_kind!(ConfigKind::Float; "Float"; "A floating point value with 64 bits" => f64); impl_config_kind!(ConfigKind::Float; "Float"; "A floating point value with 32 bits" => f32); -impl_config_kind!(ConfigKind::Bool; "Boolean"; "A boolean representing either true or false" => bool); -impl_config_kind!(ConfigKind::String; "String"; "An UTF-8 encoded string of characters" => String); +impl_config_kind!(ConfigKind::Bool; "Boolean"; "A boolean" => bool); +impl_config_kind!(ConfigKind::String; "String"; "An UTF-8 string" => String); /******Pretty Printing of Configs******/ impl ConfigDescription { /// Get a [`RcDoc`](pretty::RcDoc) which can be used to write the documentation of this pub fn as_terminal_doc<'a>(&'a self, arena: &'a Arena<'a>) -> RefDoc<'a> { - let mut doc = arena - .nil() - .append(Color::LightBlue.bold().paint(self.name()).to_string()) - .append(arena.space()) - .append(match self.kind() { - ConfigKind::Bool - | ConfigKind::Integer - | ConfigKind::Float - | ConfigKind::String - | ConfigKind::Wrapped(_) - | ConfigKind::Array(_) - | ConfigKind::HashMap(_) => arena.nil(), - ConfigKind::Struct(_) => { - arena.text(Color::Blue.dimmed().paint("[Table]").to_string()) - } - ConfigKind::Enum(_, _) => { - arena.text(Color::Green.dimmed().paint("[Enum]").to_string()) - } - }) - .append(arena.hardline()); + let mut doc = arena.nil(); + + if !matches!(self.kind(), ConfigKind::Wrapped(_)) && self.doc().is_none() { + doc = doc + .append(Color::LightBlue.bold().paint(self.name()).to_string()) + .append(arena.space()) + .append(match self.kind() { + ConfigKind::Bool + | ConfigKind::Integer + | ConfigKind::Float + | ConfigKind::String + | ConfigKind::Wrapped(_) + | ConfigKind::Array(_) + | ConfigKind::HashMap(_) => arena.nil(), + ConfigKind::Struct(_) => { + arena.text(Color::Blue.dimmed().paint("[Table]").to_string()) + } + ConfigKind::Enum(_, _) => { + arena.text(Color::Green.dimmed().paint("[Enum]").to_string()) + } + }) + .append(arena.hardline()); + } let skin = MadSkin::default_dark(); let render_markdown = |text: &str| { @@ -238,19 +256,23 @@ impl ConfigDescription { ConfigKind::Enum(enum_kind, variants) => { doc = doc .append(arena.hardline()) - .append(Color::Green.paint("[Variants]").to_string()) + .append(Color::Green.paint("One of:").to_string()) .append(arena.space()) .append(match enum_kind { - ConfigEnumKind::Tagged(tag) => arena.text("Tagged with ").append( - arena.text( - Color::LightGreen - .italic() - .paint(format!("'{}'", tag)) - .to_string(), - ), + ConfigEnumKind::Tagged(tag) => arena.text( + Color::White + .dimmed() + .paint(format!( + "[Tagged with {}]", + Color::LightGreen + .italic() + .dimmed() + .paint(format!("'{}'", tag)) + )) + .to_string(), ), ConfigEnumKind::Untagged => { - arena.text(Color::LightBlue.paint("Untagged").to_string()) + arena.text(Color::White.dimmed().paint("[Untagged]").to_string()) } }) .append(arena.hardline()) @@ -259,28 +281,70 @@ impl ConfigDescription { variants .iter() .map(|(member_name, member_doc, member_conf)| { - let mut doc = arena.nil(); - - if let Some(member_doc) = member_doc { - doc = doc.append(render_markdown(&member_doc)); - } - doc.append( - arena.text( - Color::Green.bold().paint(*member_name).to_string(), - ), - ) - .append(": ") - .append( - Pretty::pretty(member_conf.as_terminal_doc(arena), arena) + arena.text("-").append(arena.space()).append({ + let mut doc = arena + .nil() + .append(match member_conf { + EnumVariantRepresentation::String(_) => arena.text( + Color::Green + .bold() + .paint(&format!( + "{:?}", + member_name.to_lowercase() + )) + .to_string(), + ), + EnumVariantRepresentation::Wrapped(_) => arena + .text( + Color::Green + .bold() + .paint(*member_name) + .to_string(), + ), + }) + .append(": "); + + if let Some(member_doc) = member_doc { + doc = doc.append(render_markdown(&member_doc)); + } + + doc.append( + Pretty::pretty( + match member_conf { + EnumVariantRepresentation::String(_) => { + arena.nil().into_doc() + } + + EnumVariantRepresentation::Wrapped( + member_conf, + ) => arena + .text( + Color::LightRed + .paint("Is a: ") + .to_string(), + ) + .append(member_conf.as_terminal_doc(arena)) + .into_doc(), + }, + arena, + ) .nest(4), - ) + ) + .nest(2) + }) }), Doc::hardline(), ), ); } - ConfigKind::Array(conf) | ConfigKind::HashMap(conf) | ConfigKind::Wrapped(conf) => { - doc = doc.append(Pretty::pretty(conf.as_terminal_doc(arena), arena)) + ConfigKind::Array(conf) => { + doc = doc + .append(Color::LightRed.paint("Many of:").to_string()) + .append(arena.space()) + .append(conf.as_terminal_doc(arena)); + } + ConfigKind::HashMap(conf) | ConfigKind::Wrapped(conf) => { + doc = doc.append(conf.as_terminal_doc(arena)); } }; diff --git a/crates/core/tedge_api/tedge_config_derive/src/lib.rs b/crates/core/tedge_api/tedge_config_derive/src/lib.rs index 3799e38c..5c34dbf2 100644 --- a/crates/core/tedge_api/tedge_config_derive/src/lib.rs +++ b/crates/core/tedge_api/tedge_config_derive/src/lib.rs @@ -16,6 +16,7 @@ struct ConfigField<'q> { #[derive(Debug)] enum ConfigVariantKind<'q> { + String(&'q Ident), Wrapped(&'q Ident, ConfigField<'q>), Struct(&'q Ident, Vec<ConfigField<'q>>), } @@ -143,12 +144,14 @@ impl<'q> ToTokens for ConfigQuote<'q> { ( #ident, #docs, - ::tedge_api::config::ConfigDescription::new( - ::std::string::String::from(#ident), - ::tedge_api::config::ConfigKind::Wrapped( - std::boxed::Box::new(<#ty as ::tedge_api::AsConfig>::as_config()) - ), - None, + ::tedge_api::config::EnumVariantRepresentation::Wrapped( + std::boxed::Box::new(::tedge_api::config::ConfigDescription::new( + ::std::string::String::from(#ident), + ::tedge_api::config::ConfigKind::Wrapped( + std::boxed::Box::new(<#ty as ::tedge_api::AsConfig>::as_config()) + ), + None, + )) ) ) } @@ -163,16 +166,30 @@ impl<'q> ToTokens for ConfigQuote<'q> { ( #ident, #docs, - ::tedge_api::config::ConfigDescription::new( - ::std::string::String::from(#ident), - ::tedge_api::config::ConfigKind::Struct( - vec![ - #( - (#idents, #field_docs, <#tys as ::tedge_api::AsConfig>::as_config()) - ),* - ] - ), - None + ::tedge_api::config::EnumVariantRepresentation::Wrapped( + std::boxed::Box::new(::tedge_api::config::ConfigDescription::new( + ::std::string::String::from(#ident), + ::tedge_api::config::ConfigKind::Struct( + vec![ + #( + (#idents, #field_docs, <#tys as ::tedge_api::AsConfig>::as_config()) + ),* + ] + ), + None + )) + ) + ) + } + } + ConfigVariantKind::String(ident) => { + let ident = ident.to_string(); + quote!{ + ( + #ident, + #docs, + ::tedge_api::config::EnumVariantRepresentation::String( + #ident ) ) } @@ -325,13 +342,7 @@ pub fn derive_config(input: TS) -> TS { }, ) } - syn::Fields::Unit => { - emit_error!( - var, - "Unit structs are not supported as they cannot be represented" - ); - return None; - } + syn::Fields::Unit => ConfigVariantKind::String(&var.ident), }; let docs = extract_docs_from_attributes(var.attrs.iter()); Some(ConfigVariant { kind, docs }) diff --git a/crates/core/tedge_api/tests/derive_config.rs b/crates/core/tedge_api/tests/derive_config.rs index 7e633031..67d770a4 100644 --- a/crates/core/tedge_api/tests/derive_config.rs +++ b/crates/core/tedge_api/tests/derive_config.rs @@ -1,7 +1,9 @@ #![allow(unused, dead_code)] use pretty_assertions::assert_eq; -use tedge_api::{AsConfig, Config, ConfigDescription, ConfigKind}; +use tedge_api::{ + config::EnumVariantRepresentation, AsConfig, Config, ConfigDescription, ConfigKind, +}; /// Some Config #[derive(Debug, Config)] @@ -59,8 +61,12 @@ fn check_derive_config() { assert_eq!(Port::as_config().doc(), None); if let ConfigKind::Enum(_, variants) = EnumConfig::as_config().kind() { - if let ConfigKind::Struct(fields) = variants[2].2.kind() { - assert_eq!(fields[0].1, Some("The port of the inner complex type")) + if let EnumVariantRepresentation::Wrapped(kind) = &variants[2].2 { + if let ConfigKind::Struct(fields) = kind.kind() { + assert_eq!(fields[0].1, Some("The port of the inner complex type")) + } else { + panic!() + } } else { panic!() } |