diff --git a/src/definitions/config.rs b/src/definitions/config.rs index 0545196..c0f7276 100644 --- a/src/definitions/config.rs +++ b/src/definitions/config.rs @@ -7,6 +7,7 @@ use super::object; use super::service; use super::system; use super::vpn; +use crate::macro_db; #[derive(Serialize, Deserialize, Clone, Validate, Default, Debug)] pub struct Config { @@ -18,3 +19,14 @@ pub struct Config { pub vpn: vpn::VPN, pub firewall: firewall::Firewall, } + +macro_db!( + { + [ S: interface, network::StaticRoute, network.interfaces; network.static_routes ()], + [ S: interface, service::DHCPServer, network.interfaces; service.dhcp_servers ()], + [ S: interface, service::DNSServer, network.interfaces; service.dns_servers ()], + [ S: interface, service::NTPServer, network.interfaces; service.ntp_servers ()], + -> + network::NetworkInterface + }, +); diff --git a/src/definitions/macro_db.rs b/src/definitions/macro_db.rs new file mode 100644 index 0000000..68d15e2 --- /dev/null +++ b/src/definitions/macro_db.rs @@ -0,0 +1,194 @@ +/* +# TODO +thing_referencing is a vec without uniqe names. Should this be a thing? Firwall rules via index? -> Return Both index and name, backreferences are only shown to help users (update ref name might be a backend use but that can just always us the index) +validation function (ref exists, no duplicate names, custom functions), register globally/ on config? +add function to change name of referenced and update all references + +# Missing link types +link_opt -> link from option +link_single -> link source parent is not a vec + +link_enum_opt -> link from option in an enum +link_enum_multi -> link where source is in an enum and is multiple +*/ + +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct ReferencedBy { + pub name: String, + pub path: String, +} + +// Main Macro +#[macro_export] +macro_rules! macro_db { + ( + // all links + $( + { + // link sources + $( + [ $link_type:tt : + $field_name:ident, + $thing_referencing:ty, + $( $path_referenced:ident ).+; + $( $path_referencing:ident ).+ + ( $($arg:tt)* ) + ], + )+ -> + // link destination + $thing_referenced:ty + }, + )+ + ) => { + use crate::definitions::macro_db::ReferencedBy; + use crate::macro_db_link; + // Loop here to impl Relation + $( + $( + macro_db_link!($link_type, $field_name, $thing_referencing, $thing_referenced, $($path_referenced).+ ( $($arg)* )); + )+ + )+ + + // Validation here + impl Config { + #[allow(dead_code)] + // TODO make proper error + fn validate_relations() -> Result<(), String> { + // Loop for each + + Ok(()) + } + } + + // Loop here for back reference, grouped by destination + $( + impl $thing_referenced { + #[allow(dead_code)] + fn referenced_by(&self, config: Config) -> Vec { + let mut by = Vec::::new(); + $( + macro_rules! macro_db_back_link { + (S ()) => { + config.$($path_referencing).+.iter().filter(|e| *e.$field_name == self.name).for_each(|e| by.push(ReferencedBy{ + name: e.name.clone(), + path: stringify!(config.$($path_referencing).+).to_string(), + })); + }; + (M ()) => { + config.$($path_referencing).+.iter().filter(|e| e.$field_name.contains(&self.name)).for_each(|e| by.push(ReferencedBy{ + name: e.name.clone(), + path: stringify!(config.$($path_referencing).+).to_string(), + })); + }; + (E + ($enum_name:ident, + $enum_type:ident, + $enum_variant:ident, + $fn_name:ident ) + ) => { + for e in config.$($path_referencing).+.clone() { + if let $enum_type::$enum_variant { $field_name, .. } = e.$enum_name { + if &self.name == &$field_name { + by.push(ReferencedBy{ + name: e.name.clone(), + path: stringify!(config.$($path_referencing).+).to_string(), + }); + } + } + } + }; + } + + macro_db_back_link!($link_type ($($arg)*)); + )+ + return by + } + } + )+ + + }; +} + +#[macro_export] +macro_rules! macro_db_link { + ( S, + $field_name:ident, + $thing_referencing:ty, + $thing_referenced:ty, + $( $path_referenced:ident ).+ + () + ) => { + impl $thing_referencing { + fn $field_name(&self, config: Config) -> $thing_referenced { + + let index = config.$($path_referenced).+.iter().position(|e| *e.name == self.$field_name); + + match index { + Some(i) => config.$($path_referenced).+[i].clone(), + // This is fine since the config always has to validated before commiting + None => panic!("Referenced Thing: '{:?}' does not exist ", self.$field_name), + } + + } + } + }; + ( M, + $field_name:ident, + $thing_referencing:ty, + $thing_referenced:ty, + $( $path_referenced:ident ).+ + () + ) => { + impl $thing_referencing { + #[allow(dead_code)] + fn $field_name(&self, config: Config) -> Vec<$thing_referenced> { + let mut res = Vec::<$thing_referenced>::new(); + + for r in self.$field_name.clone() { + let index = config.$($path_referenced).+.iter().position(|e| *e.name == r); + + match index { + Some(i) => res.push(config.$($path_referenced).+[i].clone()), + // This is fine since the config always has to validated before commiting + None => panic!("Referenced Thing: '{:?}' does not exist ", self.$field_name), + } + } + return res + } + } + }; + ( E, + $field_name:ident, + $thing_referencing:ty, + $thing_referenced:ty, + $( $path_referenced:ident ).+ + ($enum_name:ident, + $enum_type:ident, + $enum_variant:ident, + $fn_name:ident ) + ) => { + // Unfortunetly Enum Variants are not Types, which is why we can't impl on the Variant and need seperate function names (since multiple variant could have the same field name) + impl $enum_type { + #[allow(dead_code)] + fn $fn_name(&self, config: Config) -> $thing_referenced { + let index = config.$($path_referenced).+.iter().position( + |e| { + if let $enum_type::$enum_variant { $field_name, .. } = self.clone() { + return *e.name == $field_name; + } + return false; + } + ); + + match index { + Some(i) => config.$($path_referenced).+[i].clone(), + // This is fine since the config always has to validated before commiting + None => panic!("Referenced Thing: does not exist (from Enum)"), + } + + } + } + }; +}