diff options
author | Marcel Müller <m.mueller@ifm.com> | 2022-05-12 09:14:42 +0200 |
---|---|---|
committer | Marcel Müller <m.mueller@ifm.com> | 2022-05-12 09:14:42 +0200 |
commit | 85abb91f4fa91c0f3b0991f5d59d2c5ecc68b8d8 (patch) | |
tree | 388986dbd7e4c8ef186646a17d23d6aa3212be17 | |
parent | 473801d5dd64ea9c2e669afe650ee210cd9edfa2 (diff) | |
parent | 3773563e6ca33c8340d7d4e9e384809e3d182fe9 (diff) |
Merge branch 'feature/add_config_exploration' into feature/add_tedge_api_only
-rw-r--r-- | Cargo.lock | 148 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | crates/core/tedge_api/Cargo.toml | 2 | ||||
-rw-r--r-- | crates/core/tedge_api/examples/print_config.rs | 85 | ||||
-rw-r--r-- | crates/core/tedge_api/src/config.rs | 229 | ||||
-rw-r--r-- | crates/core/tedge_api/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/core/tedge_api/tedge_config_derive/Cargo.toml | 15 | ||||
-rw-r--r-- | crates/core/tedge_api/tedge_config_derive/src/lib.rs | 382 | ||||
-rw-r--r-- | crates/core/tedge_api/tests/derive_config.rs | 76 |
9 files changed, 835 insertions, 107 deletions
@@ -72,9 +72,9 @@ checksum = "48ad219abc0c06ca788aface2e3a1970587e3413ab70acd20e54b6ec524c1f8f" dependencies = [ "argh_shared", "heck", - "proc-macro2 1.0.32", - "quote 1.0.10", - "syn 1.0.82", + "proc-macro2 1.0.38", + "quote 1.0.18", + "syn 1.0.93", ] [[package]] @@ -168,9 +168,9 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "648ed8c8d2ce5409ccd57453d9d1b214b342a0d69376a6feda1fd6cae3299308" dependencies = [ - "proc-macro2 1.0.32", - "quote 1.0.10", - "syn 1.0.82", + "proc-macro2 1.0.38", + "quote 1.0.18", + "syn 1.0.93", ] [[package]] @@ -179,9 +179,9 @@ version = "0.1.52" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3" dependencies = [ - "proc-macro2 1.0.32", - "quote 1.0.10", - "syn 1.0.82", + "proc-macro2 1.0.38", + "quote 1.0.18", + "syn 1.0.93", ] [[package]] @@ -675,8 +675,8 @@ version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa" dependencies = [ - "quote 1.0.10", - "syn 1.0.82", + "quote 1.0.18", + "syn 1.0.93", ] [[package]] @@ -693,7 +693,7 @@ checksum = "c73af209b6a5dc8ca7cbaba720732304792cddc933cfea3d74509c2b1ef2f436" dependencies = [ "num-bigint 0.4.3", "num-traits", - "syn 1.0.82", + "syn 1.0.93", ] [[package]] @@ -976,9 +976,9 @@ version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" dependencies = [ - "proc-macro2 1.0.32", - "quote 1.0.10", - "syn 1.0.82", + "proc-macro2 1.0.38", + "quote 1.0.18", + "syn 1.0.93", ] [[package]] @@ -1404,9 +1404,9 @@ version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45a95a48d0bc28f9af628286e8a4da09f96f34a97744a2e9a5a4db9814ad527d" dependencies = [ - "proc-macro2 1.0.32", - "quote 1.0.10", - "syn 1.0.82", + "proc-macro2 1.0.38", + "quote 1.0.18", + "syn 1.0.93", ] [[package]] @@ -1508,9 +1508,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7e25b214433f669161f414959594216d8e6ba83b6679d3db96899c0b4639033" dependencies = [ "cfg-if 1.0.0", - "proc-macro2 1.0.32", - "quote 1.0.10", - "syn 1.0.82", + "proc-macro2 1.0.38", + "quote 1.0.18", + "syn 1.0.93", ] [[package]] @@ -1874,9 +1874,9 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389" dependencies = [ - "proc-macro2 1.0.32", - "quote 1.0.10", - "syn 1.0.82", + "proc-macro2 1.0.38", + "quote 1.0.18", + "syn 1.0.93", ] [[package]] @@ -2018,9 +2018,9 @@ dependencies = [ [[package]] name = "pretty_assertions" -version = "1.0.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0cfe1b2403f172ba0f234e500906ee0a3e493fb81092dac23ebefe129301cc" +checksum = "c89f989ac94207d048d92db058e4f6ec7342b0971fc58d1271ca148b799b3563" dependencies = [ "ansi_term", "ctor", @@ -2045,9 +2045,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.32", - "quote 1.0.10", - "syn 1.0.82", + "proc-macro2 1.0.38", + "quote 1.0.18", + "syn 1.0.93", "version_check", ] @@ -2057,8 +2057,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.32", - "quote 1.0.10", + "proc-macro2 1.0.38", + "quote 1.0.18", "version_check", ] @@ -2073,9 +2073,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.32" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" +checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa" dependencies = [ "unicode-xid 0.2.2", ] @@ -2123,11 +2123,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.10" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" dependencies = [ - "proc-macro2 1.0.32", + "proc-macro2 1.0.38", ] [[package]] @@ -2580,9 +2580,9 @@ version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" dependencies = [ - "proc-macro2 1.0.32", - "quote 1.0.10", - "syn 1.0.82", + "proc-macro2 1.0.38", + "quote 1.0.18", + "syn 1.0.93", ] [[package]] @@ -2625,9 +2625,9 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2acd6defeddb41eb60bb468f8825d0cfd0c2a76bc03bfd235b6a1dc4f6a1ad5" dependencies = [ - "proc-macro2 1.0.32", - "quote 1.0.10", - "syn 1.0.82", + "proc-macro2 1.0.38", + "quote 1.0.18", + "syn 1.0.93", ] [[package]] @@ -2747,9 +2747,9 @@ checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ "heck", "proc-macro-error", - "proc-macro2 1.0.32", - "quote 1.0.10", - "syn 1.0.82", + "proc-macro2 1.0.38", + "quote 1.0.18", + "syn 1.0.93", ] [[package]] @@ -2765,12 +2765,12 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.82" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" +checksum = "04066589568b72ec65f42d65a1a52436e954b168773148893c020269563decf2" dependencies = [ - "proc-macro2 1.0.32", - "quote 1.0.10", + "proc-macro2 1.0.38", + "quote 1.0.18", "unicode-xid 0.2.2", ] @@ -2861,8 +2861,10 @@ dependencies = [ "miette", "nu-ansi-term", "pretty", + "pretty_assertions", "serde", "static_assertions", + "tedge_config_derive", "termcolor", "termimad", "thiserror", @@ -2903,6 +2905,16 @@ dependencies = [ ] [[package]] +name = "tedge_config_derive" +version = "0.1.0" +dependencies = [ + "proc-macro-error", + "proc-macro2 1.0.38", + "quote 1.0.18", + "syn 1.0.93", +] + +[[package]] name = "tedge_dummy_plugin" version = "0.5.2" dependencies = [ @@ -3021,9 +3033,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7cad0a06f9a61e94355aa3b3dc92d85ab9c83406722b1ca5e918d4297c12c23" dependencies = [ "cfg-if 1.0.0", - "proc-macro2 1.0.32", - "quote 1.0.10", - "syn 1.0.82", + "proc-macro2 1.0.38", + "quote 1.0.18", + "syn 1.0.93", "version_check", ] @@ -3072,9 +3084,9 @@ version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ - "proc-macro2 1.0.32", - "quote 1.0.10", - "syn 1.0.82", + "proc-macro2 1.0.38", + "quote 1.0.18", + "syn 1.0.93", ] [[package]] @@ -3165,9 +3177,9 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" dependencies = [ - "proc-macro2 1.0.32", - "quote 1.0.10", - "syn 1.0.82", + "proc-macro2 1.0.38", + "quote 1.0.18", + "syn 1.0.93", ] [[package]] @@ -3291,9 +3303,9 @@ version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" dependencies = [ - "proc-macro2 1.0.32", - "quote 1.0.10", - "syn 1.0.82", + "proc-macro2 1.0.38", + "quote 1.0.18", + "syn 1.0.93", ] [[package]] @@ -3589,9 +3601,9 @@ dependencies = [ "bumpalo", "lazy_static", "log", - "proc-macro2 1.0.32", - "quote 1.0.10", - "syn 1.0.82", + "proc-macro2 1.0.38", + "quote 1.0.18", + "syn 1.0.93", "wasm-bindgen-shared", ] @@ -3613,7 +3625,7 @@ version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" dependencies = [ - "quote 1.0.10", + "quote 1.0.18", "wasm-bindgen-macro-support", ] @@ -3623,9 +3635,9 @@ version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" dependencies = [ - "proc-macro2 1.0.32", - "quote 1.0.10", - "syn 1.0.82", + "proc-macro2 1.0.38", + "quote 1.0.18", + "syn 1.0.93", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3,6 +3,7 @@ members = [ "crates/common/*", "crates/core/*", + "crates/core/tedge_api/tedge_config_derive", "crates/tests/*", "plugins/tedge_apt_plugin", "plugins/tedge_dummy_plugin", diff --git a/crates/core/tedge_api/Cargo.toml b/crates/core/tedge_api/Cargo.toml index 7fdf5508..619e769e 100644 --- a/crates/core/tedge_api/Cargo.toml +++ b/crates/core/tedge_api/Cargo.toml @@ -19,7 +19,9 @@ pretty = { version = "0.11.3", features = ["termcolor"] } termcolor = "1.1.3" termimad = "0.20.1" nu-ansi-term = "0.45.1" +tedge_config_derive = { version = "0.1.0", path = "tedge_config_derive" } [dev-dependencies] +pretty_assertions = "1.2.1" static_assertions = "1.1.0" tokio = { version = "1.16.1", features = ["full"] } diff --git a/crates/core/tedge_api/examples/print_config.rs b/crates/core/tedge_api/examples/print_config.rs index 5ee6458e..ba6e7a6b 100644 --- a/crates/core/tedge_api/examples/print_config.rs +++ b/crates/core/tedge_api/examples/print_config.rs @@ -2,7 +2,10 @@ use std::collections::HashMap; use nu_ansi_term::Color; use pretty::Arena; -use tedge_api::config::{AsConfig, ConfigDescription, ConfigKind}; +use tedge_api::{ + config::{AsConfig, ConfigDescription, ConfigKind}, + Config, +}; struct Port(u64); impl AsConfig for Port { @@ -21,7 +24,7 @@ impl AsConfig for VHost { fn as_config() -> ConfigDescription { ConfigDescription::new( String::from("VHost"), - ConfigKind::Struct(HashMap::from([(String::from("name"), String::as_config())])), + ConfigKind::Struct(vec![("name", None, String::as_config())]), Some("A virtual host definition"), ) } @@ -47,12 +50,12 @@ fn main() { let doc = ConfigDescription::new( String::from("ServerConfig"), - ConfigKind::Struct(HashMap::from([ - (String::from("port"), Port::as_config()), - (String::from("interface"), String::as_config()), - (String::from("virtual_hosts"), Vec::<VHost>::as_config()), - (String::from("headers"), HashMap::<String, String>::as_config()), - ])), + ConfigKind::Struct(vec![ + ("port", None, Port::as_config()), + ("interface", None, String::as_config()), + ("virtual_hosts", None, Vec::<VHost>::as_config()), + ("headers", None, HashMap::<String, String>::as_config()), + ]), Some("Specify how the server should be started\n\n## Note\n\nThis is a reallly really loooooooooooooooooong loooooooooooooooooooong new *line*."), ); let rendered_doc = doc.as_terminal_doc(&arena); @@ -76,5 +79,71 @@ fn main() { ); println!("------- Output for ServerConfig"); println!("{}", output); + let arena = Arena::new(); + + #[derive(Config)] + #[config(tag = "type")] + /// An Nginx virtual host + /// + /// # Note + /// + /// This is an example and as such is nonsense + enum NginxVHost { + /// A simple host consisting of a string + Simple(String), + /// A more complex host that can also specify its port + Complex { + /// the name of the VHost + 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, + } + + let doc = NginxConfig::as_config(); + let rendered_doc = doc.as_terminal_doc(&arena); + + let mut output = String::new(); + + rendered_doc.render_fmt(80, &mut output).unwrap(); + + println!("------- Output for NginxConfig"); + println!( + "Configuration for {} plugin kinds", + Color::White.bold().paint(doc.name()) + ); + println!( + "{}", + Color::White.dimmed().bold().paint(format!( + "=================={}=============", + std::iter::repeat('=') + .take(doc.name().len()) + .collect::<String>() + )) + ); + println!("{}", output); println!("-------"); } diff --git a/crates/core/tedge_api/src/config.rs b/crates/core/tedge_api/src/config.rs index 7357b497..90bee595 100644 --- a/crates/core/tedge_api/src/config.rs +++ b/crates/core/tedge_api/src/config.rs @@ -6,7 +6,7 @@ use serde::Serialize; use termimad::MadSkin; /// Generic config that represents what kind of config a plugin wishes to accept -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, PartialEq)] pub struct ConfigDescription { name: String, kind: ConfigKind, @@ -46,8 +46,28 @@ 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 { + /// An internal tag with the given tag name + Tagged(&'static str), + /// An untagged enum variant + Untagged, +} + /// The specific kind a [`Config`] represents -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, PartialEq)] pub enum ConfigKind { /// Config represents a boolean `true`/`false` Bool, @@ -70,18 +90,37 @@ pub enum ConfigKind { /// Config represents a string String, + /// Wrap another config + /// + /// This is particularly useful if you want to restrict another kind. The common example is a + /// `Port` config object which is represented as a `u16` but with an explanation of what it is + /// meant to represent. + Wrapped(Box<ConfigDescription>), + /// Config represents an array of values of the given [`ConfigKind`] Array(Box<ConfigDescription>), - /// Config represents a map of different configurations - Struct(HashMap<String, ConfigDescription>), - /// Config represents a hashmap of named configurations of the same type /// /// # Note /// /// The key is always a [`String`] so this only holds the value config HashMap(Box<ConfigDescription>), + + /// Config represents a map of different configurations + /// + /// The tuple represent `(field_name, documentation, config_description)` + Struct(Vec<(&'static str, Option<&'static str>, ConfigDescription)>), + + /// Config represents multiple choice of configurations + Enum( + ConfigEnumKind, + Vec<( + &'static str, + Option<&'static str>, + EnumVariantRepresentation, + )>, + ), } /// Turn a plugin configuration into a [`Config`] object @@ -124,25 +163,57 @@ macro_rules! impl_config_kind { }; } -impl_config_kind!(ConfigKind::Integer; "Integer"; "A signed integer with 64 bits" => u64, i64); +impl_config_kind!(ConfigKind::Integer; "Integer"; "A signed integer with 64 bits" => i64); +impl_config_kind!(ConfigKind::Integer; "Integer"; "An unsigned integer with 64 bits" => u64); + +impl_config_kind!(ConfigKind::Integer; "Integer"; "A signed integer with 32 bits" => i32); +impl_config_kind!(ConfigKind::Integer; "Integer"; "An unsigned integer with 32 bits" => u32); + +impl_config_kind!(ConfigKind::Integer; "Integer"; "A signed integer with 16 bits" => i16); +impl_config_kind!(ConfigKind::Integer; "Integer"; "An unsigned integer with 16 bits" => u16); + +impl_config_kind!(ConfigKind::Integer; "Integer"; "A signed integer with 8 bits" => i8); +impl_config_kind!(ConfigKind::Integer; "Integer"; "An unsigned integer with 8 bits" => u8); + impl_config_kind!(ConfigKind::Float; "Float"; "A floating point value with 64 bits" => f64); -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::Float; "Float"; "A floating point value with 32 bits" => f32); + +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.hardline()); + let mut doc = arena.nil(); - if let Some(conf_doc) = self.doc() { - let skin = MadSkin::default_dark(); - let rendered = skin.text(&conf_doc, None).to_string(); - doc = doc.append(arena.intersperse( + 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| { + let rendered = skin.text(text, None).to_string(); + arena.intersperse( rendered.split("\n").map(|t| { arena.intersperse( t.split(char::is_whitespace).map(|t| t.to_string()), @@ -150,35 +221,131 @@ impl ConfigDescription { ) }), arena.hardline(), - )); + ) + }; + + if let Some(conf_doc) = self.doc() { + doc = doc.append(render_markdown(&conf_doc)); } match self.kind() { - ConfigKind::Array(conf) => { - doc = doc.append(Pretty::pretty(conf.as_terminal_doc(arena), arena)) - } + ConfigKind::Bool | ConfigKind::Integer | ConfigKind::Float | ConfigKind::String => (), ConfigKind::Struct(stc) => { doc = doc .append(arena.hardline()) .append(Color::Blue.paint("[Members]").to_string()) .append(arena.hardline()) .append(arena.intersperse( - stc.iter().map(|(member_name, member_conf)| { - arena - .text(Color::Blue.bold().paint(member_name).to_string()) - .append(": ") - .append( - Pretty::pretty(member_conf.as_terminal_doc(arena), arena) - .nest(4), - ) + stc.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::Blue.bold().paint(*member_name).to_string()), + ) + .append(": ") + .append( + Pretty::pretty(member_conf.as_terminal_doc(arena), arena).nest(4), + ) }), Doc::hardline(), )) } - ConfigKind::HashMap(conf) => { - doc = doc.append(Pretty::pretty(conf.as_terminal_doc(arena), arena)) + ConfigKind::Enum(enum_kind, variants) => { + doc = doc + .append(arena.hardline()) + .append(Color::Green.paint("One of:").to_string()) + .append(arena.space()) + .append(match enum_kind { + 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::White.dimmed().paint("[Untagged]").to_string()) + } + }) + .append(arena.hardline()) + .append( + arena.intersperse( + variants + .iter() + .map(|(member_name, member_doc, member_conf)| { + 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) => { + 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)); } - _ => (), }; doc.into_doc() diff --git a/crates/core/tedge_api/src/lib.rs b/crates/core/tedge_api/src/lib.rs index 40943224..5a9f77bb 100644 --- a/crates/core/tedge_api/src/lib.rs +++ b/crates/core/tedge_api/src/lib.rs @@ -12,6 +12,7 @@ pub use plugin::{Message, Plugin, PluginBuilder, PluginConfiguration, PluginDire /// Generic representation of a configuration pub mod config; +pub use config::{AsConfig, ConfigDescription, ConfigKind}; /// Addresses allow plugins to exchange messages pub mod address; @@ -29,6 +30,9 @@ pub use message::CoreMessages; /// pub use tokio_util::sync::CancellationToken; +/// Derive macro for self-describing configurations +pub use tedge_config_derive::Config; + #[doc(hidden)] pub mod _internal { pub use futures::future::BoxFuture; diff --git a/crates/core/tedge_api/tedge_config_derive/Cargo.toml b/crates/core/tedge_api/tedge_config_derive/Cargo.toml new file mode 100644 index 00000000..975daf1b --- /dev/null +++ b/crates/core/tedge_api/tedge_config_derive/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "tedge_config_derive" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0.38" +quote = "1.0.18" +syn = { version = "1.0.93", features = ["extra-traits"] } +proc-macro-error = "1.0.4" diff --git a/crates/core/tedge_api/tedge_config_derive/src/lib.rs b/crates/core/tedge_api/tedge_config_derive/src/lib.rs new file mode 100644 index 00000000..5c34dbf2 --- /dev/null +++ b/crates/core/tedge_api/tedge_config_derive/src/lib.rs @@ -0,0 +1,382 @@ +use proc_macro::TokenStream as TS; +use proc_macro2::TokenStream; +use proc_macro_error::{abort, emit_error, proc_macro_error, OptionExt, ResultExt}; +use quote::{quote, ToTokens, TokenStreamExt}; +use syn::{ + parse_macro_input, Attribute, DeriveInput, Ident, Lit, LitStr, Meta, MetaNameValue, NestedMeta, + Type, +}; + +#[derive(Debug)] +struct ConfigField<'q> { + ident: &'q Ident, + ty: &'q Type, + docs: Option<Vec<LitStr>>, +} + +#[derive(Debug)] +enum ConfigVariantKind<'q> { + String(&'q Ident), + Wrapped(&'q Ident, ConfigField<'q>), + Struct(&'q Ident, Vec<ConfigField<'q>>), +} + +#[derive(Debug)] +struct ConfigVariant<'q> { + kind: ConfigVariantKind<'q>, + docs: Option<Vec<LitStr>>, +} + +#[derive(Debug)] +enum ConfigEnumKind { + Tagged(LitStr), + Untagged, +} |