@@ -10,6 +10,8 @@ use std::{
1010use anyhow:: { Context , bail} ;
1111use clap:: Parser ;
1212use log:: info;
13+ use schemars:: JsonSchema ;
14+ use serde:: { Deserialize , Serialize } ;
1315use yaml_rust2:: { Yaml , YamlLoader , yaml} ;
1416
1517pub mod reloader;
@@ -23,11 +25,14 @@ const CONFIG_FILES: [&str; 4] = [
2325 "fact.yaml" ,
2426] ;
2527
26- #[ derive( Debug , Default , PartialEq , Eq , Clone ) ]
28+ #[ derive( Debug , Default , PartialEq , Eq , Clone , JsonSchema , Serialize , Deserialize ) ]
2729pub struct FactConfig {
2830 paths : Option < Vec < PathBuf > > ,
31+ #[ serde( default ) ]
2932 pub grpc : GrpcConfig ,
33+ #[ serde( default ) ]
3034 pub endpoint : EndpointConfig ,
35+ #[ serde( default ) ]
3136 pub bpf : BpfConfig ,
3237 skip_pre_flight : Option < bool > ,
3338 json : Option < bool > ,
@@ -125,10 +130,56 @@ impl FactConfig {
125130 self . rate_limit . unwrap_or ( 0 )
126131 }
127132
128- #[ cfg( test) ]
129133 pub fn set_paths ( & mut self , paths : Vec < PathBuf > ) {
130134 self . paths = Some ( paths) ;
131135 }
136+
137+ pub fn to_yaml ( & self ) -> Yaml {
138+ let mut config = yaml:: Hash :: new ( ) ;
139+
140+ if let Some ( paths) = & self . paths {
141+ let paths = paths
142+ . iter ( )
143+ . filter_map ( |p| p. to_str ( ) . map ( |p| Yaml :: String ( p. to_string ( ) ) ) )
144+ . collect :: < Vec < _ > > ( ) ;
145+ config. insert ( Yaml :: String ( "paths" . into ( ) ) , Yaml :: Array ( paths) ) ;
146+ }
147+
148+ config. insert ( Yaml :: String ( "grpc" . into ( ) ) , self . grpc . to_yaml ( ) ) ;
149+ config. insert ( Yaml :: String ( "endpoint" . into ( ) ) , self . endpoint . to_yaml ( ) ) ;
150+ config. insert ( Yaml :: String ( "bpf" . into ( ) ) , self . bpf . to_yaml ( ) ) ;
151+
152+ if let Some ( skip_pre_flight) = self . skip_pre_flight {
153+ config. insert (
154+ Yaml :: String ( "skip_pre_flight" . into ( ) ) ,
155+ Yaml :: Boolean ( skip_pre_flight) ,
156+ ) ;
157+ }
158+
159+ if let Some ( json) = self . skip_pre_flight {
160+ config. insert ( Yaml :: String ( "json" . into ( ) ) , Yaml :: Boolean ( json) ) ;
161+ }
162+
163+ if let Some ( hotreload) = self . hotreload {
164+ config. insert ( Yaml :: String ( "hotreload" . into ( ) ) , Yaml :: Boolean ( hotreload) ) ;
165+ }
166+
167+ if let Some ( scan_interval) = self . scan_interval {
168+ config. insert (
169+ Yaml :: String ( "scan_interval" . into ( ) ) ,
170+ Yaml :: Integer ( scan_interval. as_secs ( ) as i64 ) ,
171+ ) ;
172+ }
173+
174+ if let Some ( rate_limit) = self . rate_limit {
175+ config. insert (
176+ Yaml :: String ( "rate_limit" . into ( ) ) ,
177+ Yaml :: Integer ( rate_limit as i64 ) ,
178+ ) ;
179+ }
180+
181+ Yaml :: Hash ( config)
182+ }
132183}
133184
134185impl TryFrom < & str > for FactConfig {
@@ -189,10 +240,16 @@ impl TryFrom<Vec<Yaml>> for FactConfig {
189240 let grpc = v. as_hash ( ) . unwrap ( ) ;
190241 config. grpc = GrpcConfig :: try_from ( grpc) ?;
191242 }
243+ "grpc" if v. is_null ( ) => {
244+ // Nothing to do
245+ }
192246 "endpoint" if v. is_hash ( ) => {
193247 let endpoint = v. as_hash ( ) . unwrap ( ) ;
194248 config. endpoint = EndpointConfig :: try_from ( endpoint) ?;
195249 }
250+ "endpoint" if v. is_null ( ) => {
251+ // Nothing to do
252+ }
196253 "skip_pre_flight" => {
197254 let Some ( spf) = v. as_bool ( ) else {
198255 bail ! ( "skip_pre_flight field has incorrect type: {v:?}" ) ;
@@ -205,12 +262,13 @@ impl TryFrom<Vec<Yaml>> for FactConfig {
205262 } ;
206263 config. json = Some ( json) ;
207264 }
208- "bpf" => {
209- let Some ( bpf) = v. as_hash ( ) else {
210- bail ! ( "bpf section has incorrect type: {v:#?}" ) ;
211- } ;
265+ "bpf" if v. is_hash ( ) => {
266+ let bpf = v. as_hash ( ) . unwrap ( ) ;
212267 config. bpf = BpfConfig :: try_from ( bpf) ?;
213268 }
269+ "bpf" if v. is_null ( ) => {
270+ // Nothing to do
271+ }
214272 "hotreload" => {
215273 let Some ( hotreload) = v. as_bool ( ) else {
216274 bail ! ( "hotreload field has incorrect type: {v:?}" ) ;
@@ -251,7 +309,7 @@ impl TryFrom<Vec<Yaml>> for FactConfig {
251309 }
252310}
253311
254- #[ derive( Debug , Default , PartialEq , Eq , Clone ) ]
312+ #[ derive( Debug , Default , PartialEq , Eq , Clone , JsonSchema , Serialize , Deserialize ) ]
255313pub struct EndpointConfig {
256314 address : Option < SocketAddr > ,
257315 expose_metrics : Option < bool > ,
@@ -285,6 +343,37 @@ impl EndpointConfig {
285343 pub fn health_check ( & self ) -> bool {
286344 self . health_check . unwrap_or ( false )
287345 }
346+
347+ fn to_yaml ( & self ) -> Yaml {
348+ let mut endpoint = yaml:: Hash :: new ( ) ;
349+
350+ if let Some ( address) = self . address {
351+ endpoint. insert (
352+ Yaml :: String ( "address" . into ( ) ) ,
353+ Yaml :: String ( address. to_string ( ) ) ,
354+ ) ;
355+ }
356+
357+ if let Some ( expose_metrics) = self . expose_metrics {
358+ endpoint. insert (
359+ Yaml :: String ( "expose_metrics" . into ( ) ) ,
360+ Yaml :: Boolean ( expose_metrics) ,
361+ ) ;
362+ }
363+
364+ if let Some ( health_check) = self . health_check {
365+ endpoint. insert (
366+ Yaml :: String ( "health_check" . into ( ) ) ,
367+ Yaml :: Boolean ( health_check) ,
368+ ) ;
369+ }
370+
371+ if !endpoint. is_empty ( ) {
372+ Yaml :: Hash ( endpoint)
373+ } else {
374+ Yaml :: Null
375+ }
376+ }
288377}
289378
290379impl TryFrom < & yaml:: Hash > for EndpointConfig {
@@ -328,7 +417,7 @@ impl TryFrom<&yaml::Hash> for EndpointConfig {
328417 }
329418}
330419
331- #[ derive( Debug , Default , PartialEq , Eq , Clone ) ]
420+ #[ derive( Debug , Default , PartialEq , Eq , Clone , JsonSchema , Serialize , Deserialize ) ]
332421pub struct GrpcConfig {
333422 url : Option < String > ,
334423 certs : Option < PathBuf > ,
@@ -352,6 +441,26 @@ impl GrpcConfig {
352441 pub fn certs ( & self ) -> Option < & Path > {
353442 self . certs . as_deref ( )
354443 }
444+
445+ fn to_yaml ( & self ) -> Yaml {
446+ let mut grpc = yaml:: Hash :: new ( ) ;
447+
448+ if let Some ( url) = & self . url {
449+ grpc. insert ( Yaml :: String ( "url" . into ( ) ) , Yaml :: String ( url. clone ( ) ) ) ;
450+ }
451+
452+ if let Some ( certs) = & self . certs
453+ && let Some ( certs) = certs. to_str ( )
454+ {
455+ grpc. insert ( Yaml :: String ( "certs" . into ( ) ) , Yaml :: String ( certs. into ( ) ) ) ;
456+ }
457+
458+ if !grpc. is_empty ( ) {
459+ Yaml :: Hash ( grpc)
460+ } else {
461+ Yaml :: Null
462+ }
463+ }
355464}
356465
357466impl TryFrom < & yaml:: Hash > for GrpcConfig {
@@ -385,7 +494,7 @@ impl TryFrom<&yaml::Hash> for GrpcConfig {
385494 }
386495}
387496
388- #[ derive( Debug , Default , PartialEq , Eq , Clone ) ]
497+ #[ derive( Debug , Default , PartialEq , Eq , Clone , JsonSchema , Serialize , Deserialize ) ]
389498pub struct BpfConfig {
390499 ringbuf_size : Option < u32 > ,
391500 inodes_max : Option < u32 > ,
@@ -409,6 +518,30 @@ impl BpfConfig {
409518 pub fn inodes_max ( & self ) -> u32 {
410519 self . inodes_max . unwrap_or ( 65536 )
411520 }
521+
522+ fn to_yaml ( & self ) -> Yaml {
523+ let mut bpf = yaml:: Hash :: new ( ) ;
524+
525+ if let Some ( ringbuf_size) = self . ringbuf_size {
526+ bpf. insert (
527+ Yaml :: String ( "ringbuf_size" . into ( ) ) ,
528+ Yaml :: Integer ( ringbuf_size as i64 ) ,
529+ ) ;
530+ }
531+
532+ if let Some ( inodes_max) = self . ringbuf_size {
533+ bpf. insert (
534+ Yaml :: String ( "inodes_max" . into ( ) ) ,
535+ Yaml :: Integer ( inodes_max as i64 ) ,
536+ ) ;
537+ }
538+
539+ if !bpf. is_empty ( ) {
540+ Yaml :: Hash ( bpf)
541+ } else {
542+ Yaml :: Null
543+ }
544+ }
412545}
413546
414547impl TryFrom < & yaml:: Hash > for BpfConfig {
0 commit comments