summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcel Müller <m.mueller@ifm.com>2022-05-11 12:00:50 +0200
committerMarcel Müller <m.mueller@ifm.com>2022-05-12 09:08:10 +0200
commit3773563e6ca33c8340d7d4e9e384809e3d182fe9 (patch)
tree388986dbd7e4c8ef186646a17d23d6aa3212be17
parent4c2f168e8831be7334bf4c46db725df644ed6490 (diff)
Allows untagged enums to be represented as strings
Signed-off-by: Marcel Müller <m.mueller@ifm.com>
-rw-r--r--crates/core/tedge_api/examples/print_config.rs18
-rw-r--r--crates/core/tedge_api/src/config.rs160
-rw-r--r--crates/core/tedge_api/tedge_config_derive/src/lib.rs57
-rw-r--r--crates/core/tedge_api/tests/derive_config.rs12
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!()
}