implement macro_db

This commit is contained in:
Samuel Lorch 2023-12-09 22:19:36 +01:00
parent ce5f0b4931
commit a0c04e3614
2 changed files with 206 additions and 0 deletions

View file

@ -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
},
);

194
src/definitions/macro_db.rs Normal file
View file

@ -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<ReferencedBy> {
let mut by = Vec::<ReferencedBy>::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)"),
}
}
}
};
}