summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcel Müller <m.mueller@ifm.com>2022-05-12 09:14:42 +0200
committerMarcel Müller <m.mueller@ifm.com>2022-05-12 09:14:42 +0200
commit85abb91f4fa91c0f3b0991f5d59d2c5ecc68b8d8 (patch)
tree388986dbd7e4c8ef186646a17d23d6aa3212be17
parent473801d5dd64ea9c2e669afe650ee210cd9edfa2 (diff)
parent3773563e6ca33c8340d7d4e9e384809e3d182fe9 (diff)
Merge branch 'feature/add_config_exploration' into feature/add_tedge_api_only
-rw-r--r--Cargo.lock148
-rw-r--r--Cargo.toml1
-rw-r--r--crates/core/tedge_api/Cargo.toml2
-rw-r--r--crates/core/tedge_api/examples/print_config.rs85
-rw-r--r--crates/core/tedge_api/src/config.rs229
-rw-r--r--crates/core/tedge_api/src/lib.rs4
-rw-r--r--crates/core/tedge_api/tedge_config_derive/Cargo.toml15
-rw-r--r--crates/core/tedge_api/tedge_config_derive/src/lib.rs382
-rw-r--r--crates/core/tedge_api/tests/derive_config.rs76
9 files changed, 835 insertions, 107 deletions
diff --git a/Cargo.lock b/Cargo.lock
index b0cec5fc..6a8277f5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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",
]
diff --git a/Cargo.toml b/Cargo.toml
index 0bd9467e..bf58964b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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,
+}