Skip to main content

httpward_core/config/
strategy.rs

1use serde::{Deserialize, Serialize, Deserializer};
2use std::collections::HashMap;
3use std::sync::Arc;
4use anyhow::Result;
5use schemars::JsonSchema;
6use serde_yaml::Value;
7use serde_json;
8
9/// Universal wrapper type for working with YAML and JSON values
10#[derive(Debug, Clone)]
11pub enum UniversalValue {
12    Json(serde_json::Value),
13    Yaml(serde_yaml::Value),
14}
15
16impl UniversalValue {
17    /// Convert to JSON Value
18    pub fn as_json(&self) -> Result<serde_json::Value> {
19        match self {
20            UniversalValue::Json(v) => Ok(v.clone()),
21            UniversalValue::Yaml(v) => {
22                // Use direct conversion method via serde_json::to_value
23                serde_json::to_value(v).map_err(|e| {
24                    anyhow::anyhow!("Failed to convert YAML to JSON: {}", e)
25                })
26            }
27        }
28    }
29    
30    /// Convert to YAML Value
31    pub fn as_yaml(&self) -> Result<serde_yaml::Value> {
32        match self {
33            UniversalValue::Yaml(v) => Ok(v.clone()),
34            UniversalValue::Json(v) => {
35                // Use direct conversion method via serde_yaml::to_value
36                serde_yaml::to_value(v).map_err(|e| {
37                    anyhow::anyhow!("Failed to convert JSON to YAML: {}", e)
38                })
39            }
40        }
41    }
42    
43    /// Create from JSON Value
44    pub fn from_json(value: serde_json::Value) -> Self {
45        UniversalValue::Json(value)
46    }
47    
48    /// Create from YAML Value
49    pub fn from_yaml(value: serde_yaml::Value) -> Self {
50        UniversalValue::Yaml(value)
51    }
52    
53    /// Get as JSON string
54    pub fn to_json_string(&self) -> Result<String> {
55        let json_val = self.as_json()?;
56        serde_json::to_string_pretty(&json_val).map_err(Into::into)
57    }
58    
59    /// Get as YAML string
60    pub fn to_yaml_string(&self) -> Result<String> {
61        let yaml_val = self.as_yaml()?;
62        serde_yaml::to_string(&yaml_val).map_err(Into::into)
63    }
64}
65
66/// Deep merge YAML values - only add missing properties
67fn merge_yaml_missing_only(target: &mut Value, source: Value) {
68    let is_null = matches!(target, Value::Null);
69    
70    if let (Value::Mapping(target_map), Value::Mapping(source_map)) = (&mut *target, &source) {
71        for (k, v) in source_map {
72            if !target_map.contains_key(&k) {
73                // Key doesn't exist at all - add it
74                target_map.insert(k.clone(), v.clone());
75            } else {
76                // Key exists - check if we can recursively merge objects
77                match (target_map.get_mut(&k), &v) {
78                    (Some(Value::Mapping(_)), Value::Mapping(_)) => {
79                        // Both are mappings - recursively merge missing fields only
80                        let target_value = target_map.get_mut(&k).unwrap();
81                        merge_yaml_missing_only(target_value, v.clone());
82                    }
83                    _ => {
84                        // Target is not a mapping or source is not a mapping - don't overwrite
85                    }
86                }
87            }
88        }
89    } else if is_null {
90        // If target is null but source has value, replace target
91        *target = source;
92    }
93    // Don't modify existing non-null values
94}
95
96/// Deep merge JSON values - only add missing properties
97fn merge_json_missing_only(target: &mut serde_json::Value, source: serde_json::Value) {
98    let is_null = matches!(target, serde_json::Value::Null);
99    
100    if let (serde_json::Value::Object(target_map), serde_json::Value::Object(source_map)) = (&mut *target, &source) {
101        for (k, v) in source_map {
102            if !target_map.contains_key(k) {
103                // Key doesn't exist at all - add it
104                target_map.insert(k.clone(), v.clone());
105            } else {
106                // Key exists - check if we can recursively merge objects
107                let target_value = target_map.get_mut(k).unwrap();
108                match target_value {
109                    serde_json::Value::Object(_) => {
110                        // Both are objects - recursively merge missing fields only
111                        merge_json_missing_only(target_value, v.clone());
112                    }
113                    _ => {
114                        // Target is not an object - don't overwrite
115                    }
116                }
117            }
118        }
119    } else if is_null {
120        // If target is null but source has value, replace target
121        *target = source;
122    }
123    // Don't modify existing non-null values
124}
125
126/// Deep merge UniversalValue instances - only add missing properties
127fn merge_universal_missing_only(target: &mut UniversalValue, source: UniversalValue) -> Result<()> {
128    match (target, source) {
129        (UniversalValue::Yaml(t), UniversalValue::Yaml(s)) => {
130            merge_yaml_missing_only(t, s);
131        }
132        (UniversalValue::Json(t), UniversalValue::Json(s)) => {
133            merge_json_missing_only(t, s);
134        }
135        // Fallback if formats are different
136        (t, s) => {
137            let t_json = t.as_json()?;
138            let s_json = s.as_json()?;
139            let mut merged = t_json;
140            merge_json_missing_only(&mut merged, s_json);
141            *t = UniversalValue::from_json(merged);
142        }
143    }
144    Ok(())
145}
146
147/// Supplement middleware configurations - only merge existing middleware configs, don't add new ones
148/// Used for strategy inheritance where we only want to merge configs of existing middleware
149pub fn supplement_middleware_configs(
150    current: &mut Vec<MiddlewareConfig>,
151    incoming: &[MiddlewareConfig],
152) -> Result<()> {
153    // Build index: middleware name -> position
154    let mut index = HashMap::new();
155
156    for (i, m) in current.iter().enumerate() {
157        let name = m.name();
158        index.insert(name.to_string(), i);
159    }
160
161    for new_middleware in incoming {
162        let name = new_middleware.name();
163        
164        if let Some(&pos) = index.get(name) {
165            // Only merge existing middleware configs - don't add new ones
166            match (&mut current[pos], new_middleware) {
167                (MiddlewareConfig::Named { config: existing_config, .. }, MiddlewareConfig::Named { config, .. }) => {
168                    merge_universal_missing_only(existing_config, config.clone())?;
169                }
170                (current_on @ MiddlewareConfig::On { .. }, MiddlewareConfig::Named { name, config }) => {
171                    *current_on = MiddlewareConfig::Named {
172                        name: name.clone(),
173                        config: config.clone(),
174                    };
175                }
176                // Handle cases where one or both are Off - don't merge configs for Off middleware
177                _ => {
178                    // Do nothing - either current or incoming is Off, so no config merging needed
179                }
180            }
181        }
182        // If middleware doesn't exist in current, don't add it (this function only supplements existing)
183    }
184
185    Ok(())
186}
187
188/// Filter out disabled middleware, considering inheritance rules
189/// If middleware is disabled in current but enabled in parent, it stays disabled
190/// If middleware is disabled in parent but enabled in current, it becomes enabled
191pub fn filter_disabled_middleware(
192    current: &mut Vec<MiddlewareConfig>,
193    parent: &[MiddlewareConfig],
194) -> Result<()> {
195    // Build index of parent middleware: name -> (is_disabled, config)
196    let mut parent_index = HashMap::new();
197    for middleware in parent {
198        let name = middleware.name();
199        parent_index.insert(name.to_string(), middleware.is_off());
200    }
201
202    // Process current middleware
203    let mut result = Vec::new();
204    
205    for middleware in current.iter() {
206        let name = middleware.name();
207        
208        match middleware {
209            MiddlewareConfig::Off { .. } => {
210                // Current middleware is disabled - check parent
211                if let Some(parent_disabled) = parent_index.get(name) {
212                    if !parent_disabled {
213                        // Parent has it enabled, so keep it disabled (current takes precedence)
214                        result.push(middleware.clone());
215                    }
216                    // If parent also has it disabled, don't add anything (remove it)
217                } else {
218                    // Parent doesn't have this middleware, keep it disabled
219                    result.push(middleware.clone());
220                }
221            }
222            MiddlewareConfig::Named { .. } | MiddlewareConfig::On { .. } => {
223                // Current middleware is enabled - always keep it
224                result.push(middleware.clone());
225            }
226        }
227    }
228
229    // Add parent middleware that doesn't exist in current (if not disabled)
230    for middleware in parent {
231        let name = middleware.name();
232        
233        // Check if this middleware exists in current
234        let exists_in_current = current.iter().any(|m| m.name() == name);
235        
236        if !exists_in_current {
237            match middleware {
238                MiddlewareConfig::Named { .. } | MiddlewareConfig::On { .. } => {
239                    // Parent has it enabled and current doesn't have it - add it
240                    result.push(middleware.clone());
241                }
242                MiddlewareConfig::Off { .. } => {
243                    // Parent has it disabled and current doesn't have it - don't add
244                }
245            }
246        }
247    }
248
249    *current = result;
250    Ok(())
251}
252
253/// Supplement middleware configurations - add missing middleware and missing properties
254pub fn supplement_middleware(
255    current: &mut Vec<MiddlewareConfig>,
256    incoming: &[MiddlewareConfig],
257) -> Result<()> {
258    // Build index: middleware name -> position
259    let mut index = HashMap::new();
260
261    for (i, m) in current.iter().enumerate() {
262        let name = m.name();
263        index.insert(name.to_string(), i);
264    }
265
266    // Collect inherited middleware missing in `current` and prepend once at the end.
267    // This keeps inheritance order as parent -> child without O(n^2) front inserts.
268    let mut inherited_prefix: Vec<MiddlewareConfig> = Vec::new();
269
270    for new_middleware in incoming {
271        let name = new_middleware.name();
272        
273        if let Some(&pos) = index.get(name) {
274            // Middleware exists - check if we should merge or handle disabled state
275            match (&mut current[pos], new_middleware) {
276                (MiddlewareConfig::Named { config: existing_config, .. }, MiddlewareConfig::Named { config, .. }) => {
277                    // Both are enabled - merge configurations
278                    merge_universal_missing_only(existing_config, config.clone())?;
279                }
280                (current_on @ MiddlewareConfig::On { .. }, MiddlewareConfig::Named { name, config }) => {
281                    // Explicitly enabled without config - inherit missing config from parent.
282                    *current_on = MiddlewareConfig::Named {
283                        name: name.clone(),
284                        config: config.clone(),
285                    };
286                }
287                (MiddlewareConfig::Off { .. }, MiddlewareConfig::Named { .. } | MiddlewareConfig::On { .. }) => {
288                    // Current is disabled but incoming is enabled - DON'T enable it
289                    // Current (inline/off) takes precedence over incoming (site/global)
290                    // Do nothing - keep it disabled
291                }
292                (MiddlewareConfig::Named { .. } | MiddlewareConfig::On { .. }, MiddlewareConfig::Off { .. }) => {
293                    // Current is enabled but incoming is disabled - keep current enabled (takes precedence)
294                    // Do nothing
295                }
296                (MiddlewareConfig::Off { .. }, MiddlewareConfig::Off { .. }) => {
297                    // Both are disabled - keep disabled
298                    // Do nothing
299                }
300                (MiddlewareConfig::Named { .. }, MiddlewareConfig::On { .. }) => {
301                    // Current already has explicit config - keep it as-is.
302                }
303                (MiddlewareConfig::On { .. }, MiddlewareConfig::On { .. }) => {
304                    // Already explicitly enabled without config.
305                }
306            }
307        } else {
308            // Middleware doesn't exist in current - inherit if not disabled.
309            if !new_middleware.is_off() {
310                inherited_prefix.push(new_middleware.clone());
311            }
312        }
313    }
314
315    if !inherited_prefix.is_empty() {
316        let mut merged = Vec::with_capacity(inherited_prefix.len() + current.len());
317        merged.extend(inherited_prefix);
318        merged.append(current);
319        *current = merged;
320    }
321
322    Ok(())
323}
324
325// Serialize implementation for UniversalValue
326impl Serialize for UniversalValue {
327    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
328    where
329        S: serde::Serializer,
330    {
331        match self.as_json() {
332            Ok(json_val) => json_val.serialize(serializer),
333            Err(_) => serializer.serialize_none(),
334        }
335    }
336}
337
338// Deserialize implementation for UniversalValue
339impl<'de> Deserialize<'de> for UniversalValue {
340    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
341    where
342        D: Deserializer<'de>,
343    {
344        let yaml_value: serde_yaml::Value = Deserialize::deserialize(deserializer)?;
345        Ok(UniversalValue::Yaml(yaml_value))
346    }
347}
348
349// Legacy type for backward compatibility - use the optimized version in strategy_resolver
350pub type LegacyStrategyCollection = HashMap<String, Vec<MiddlewareConfig>>;
351
352#[derive(Debug, Clone, JsonSchema)]
353pub struct Strategy {
354    pub name: String,
355
356    #[serde(default)]
357    pub middleware: Arc<Vec<MiddlewareConfig>>,
358}
359
360impl<'de> serde::Deserialize<'de> for Strategy {
361    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
362    where
363        D: serde::Deserializer<'de>,
364    {
365        #[derive(serde::Deserialize)]
366        struct Helper {
367            name: String,
368            #[serde(default)]
369            middleware: Vec<MiddlewareConfig>,
370        }
371
372        let helper = Helper::deserialize(deserializer)?;
373        Ok(Strategy {
374            name: helper.name,
375            middleware: Arc::new(helper.middleware),
376        })
377    }
378}
379
380impl serde::Serialize for Strategy {
381    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
382    where
383        S: serde::Serializer,
384    {
385        use serde::ser::SerializeStruct;
386        let mut state = serializer.serialize_struct("Strategy", 2)?;
387        state.serialize_field("name", &self.name)?;
388        state.serialize_field("middleware", &*self.middleware)?;
389        state.end()
390    }
391}
392
393#[derive(Debug, Clone, Serialize, JsonSchema)]
394pub enum MiddlewareConfig {
395    Named {
396        name: String,
397        #[schemars(with = "serde_json::Value")]
398        config: UniversalValue,
399    },
400    On {
401        name: String,
402    },
403    Off {
404        name: String,
405    },
406}
407
408impl MiddlewareConfig {
409    /// Create new named middleware with JSON configuration
410    pub fn new_named_json(name: String, config: serde_json::Value) -> Self {
411        MiddlewareConfig::Named {
412            name,
413            config: UniversalValue::from_json(config),
414        }
415    }
416    
417    /// Create new named middleware with YAML configuration
418    pub fn new_named_yaml(name: String, config: serde_yaml::Value) -> Self {
419        MiddlewareConfig::Named {
420            name,
421            config: UniversalValue::from_yaml(config),
422        }
423    }
424
425    /// Create new enabled middleware without explicit configuration
426    pub fn new_on(name: String) -> Self {
427        MiddlewareConfig::On { name }
428    }
429
430    /// Create new disabled middleware
431    pub fn new_off(name: String) -> Self {
432        MiddlewareConfig::Off { name }
433    }
434    
435    /// Check if middleware is disabled
436    pub fn is_off(&self) -> bool {
437        matches!(self, MiddlewareConfig::Off { .. })
438    }
439    
440    /// Get middleware name
441    pub fn name(&self) -> &str {
442        match self {
443            MiddlewareConfig::Named { name, .. } => name,
444            MiddlewareConfig::On { name } => name,
445            MiddlewareConfig::Off { name } => name,
446        }
447    }
448    
449    /// Get configuration as JSON Value
450    pub fn config_as_json(&self) -> Result<serde_json::Value> {
451        match self {
452            MiddlewareConfig::Named { config, .. } => config.as_json(),
453            MiddlewareConfig::On { .. } => Ok(serde_json::Value::Object(serde_json::Map::new())),
454            MiddlewareConfig::Off { .. } => Err(anyhow::anyhow!("Cannot get config from disabled middleware")),
455        }
456    }
457    
458    /// Get configuration as YAML Value
459    pub fn config_as_yaml(&self) -> Result<serde_yaml::Value> {
460        match self {
461            MiddlewareConfig::Named { config, .. } => config.as_yaml(),
462            MiddlewareConfig::On { .. } => Ok(serde_yaml::Value::Mapping(serde_yaml::Mapping::new())),
463            MiddlewareConfig::Off { .. } => Err(anyhow::anyhow!("Cannot get config from disabled middleware")),
464        }
465    }
466    
467    /// Get configuration as JSON string
468    pub fn config_to_json_string(&self) -> Result<String> {
469        match self {
470            MiddlewareConfig::Named { config, .. } => config.to_json_string(),
471            MiddlewareConfig::On { .. } => serde_json::to_string_pretty(&serde_json::Value::Object(serde_json::Map::new())).map_err(Into::into),
472            MiddlewareConfig::Off { .. } => Err(anyhow::anyhow!("Cannot get config from disabled middleware")),
473        }
474    }
475    
476    /// Get configuration as YAML string
477    pub fn config_to_yaml_string(&self) -> Result<String> {
478        match self {
479            MiddlewareConfig::Named { config, .. } => config.to_yaml_string(),
480            MiddlewareConfig::On { .. } => serde_yaml::to_string(&serde_yaml::Value::Mapping(serde_yaml::Mapping::new())).map_err(Into::into),
481            MiddlewareConfig::Off { .. } => Err(anyhow::anyhow!("Cannot get config from disabled middleware")),
482        }
483    }
484    
485    /// Convert configuration to specific type using serde
486    pub fn config_into<T: for<'de> Deserialize<'de>>(&self) -> Result<T> {
487        let json_val = self.config_as_json()?;
488        serde_json::from_value(json_val).map_err(Into::into)
489    }
490
491    /// Create middleware config from any serializable struct (super convenient!)
492    /// 
493    /// Usage:
494    /// ```rust
495    /// # use httpward_core::config::MiddlewareConfig;
496    /// # use serde::Serialize;
497    /// # #[derive(Serialize)]
498    /// # struct MyConfig { level: String }
499    /// let config = MyConfig { level: "warn".to_string() };
500    /// let middleware = MiddlewareConfig::from_serializable("httpward_log_module", config).unwrap();
501    /// ```
502    pub fn from_serializable<T: Serialize>(name: impl Into<String>, config: T) -> Result<Self> {
503        let json_val = serde_json::to_value(config)
504            .map_err(|e| anyhow::anyhow!("Failed to serialize config: {}", e))?;
505        Ok(MiddlewareConfig::new_named_json(name.into(), json_val))
506    }
507
508    /// Create middleware config from YAML string (1-liner friendly)
509    /// 
510    /// Usage:
511    /// ```rust
512    /// # use httpward_core::config::MiddlewareConfig;
513    /// let middleware = MiddlewareConfig::from_yaml_str("httpward_log_module", "level: warn").unwrap();
514    /// ```
515    pub fn from_yaml_str(name: impl Into<String>, yaml_str: impl AsRef<str>) -> Result<Self> {
516        let yaml_val = serde_yaml::from_str(yaml_str.as_ref())
517            .map_err(|e| anyhow::anyhow!("Failed to parse YAML: {}", e))?;
518        Ok(MiddlewareConfig::new_named_yaml(name.into(), yaml_val))
519    }
520
521    /// Create middleware config from JSON string (1-liner friendly)
522    /// 
523    /// Usage:
524    /// ```rust
525    /// # use httpward_core::config::MiddlewareConfig;
526    /// let middleware = MiddlewareConfig::from_json_str("httpward_log_module", r#"{"level": "warn"}"#).unwrap();
527    /// ```
528    pub fn from_json_str(name: impl Into<String>, json_str: impl AsRef<str>) -> Result<Self> {
529        let json_val = serde_json::from_str(json_str.as_ref())
530            .map_err(|e| anyhow::anyhow!("Failed to parse JSON: {}", e))?;
531        Ok(MiddlewareConfig::new_named_json(name.into(), json_val))
532    }
533
534    /// Parse config into specific type with elegant error handling
535    /// 
536    /// Usage:
537    /// ```rust
538    /// # use httpward_core::config::MiddlewareConfig;
539    /// # use serde::Deserialize;
540    /// # #[derive(Deserialize)]
541    /// # struct MyConfig { level: String }
542    /// let middleware = MiddlewareConfig::from_json_str("httpward_log_module", r#"{"level": "warn"}"#).unwrap();
543    /// let config: MyConfig = middleware.parse_config().unwrap();
544    /// ```
545    pub fn parse_config<T: for<'de> Deserialize<'de>>(&self) -> Result<T> {
546        self.config_into()
547    }
548}
549
550impl<'de> Deserialize<'de> for MiddlewareConfig {
551    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
552    where
553        D: Deserializer<'de>,
554    {
555        let map: HashMap<String, serde_yaml::Value> =
556            HashMap::deserialize(deserializer)?;
557
558        if map.len() != 1 {
559            return Err(serde::de::Error::custom(
560                "middleware must contain exactly one key",
561            ));
562        }
563
564        let (name, yaml_value) = map.into_iter().next().unwrap();
565
566        // Check if the value is an explicit on/off toggle.
567        match &yaml_value {
568            serde_yaml::Value::String(s) if s.eq_ignore_ascii_case("off") => {
569                return Ok(MiddlewareConfig::Off { name });
570            }
571            serde_yaml::Value::Bool(b) if !b => {
572                return Ok(MiddlewareConfig::Off { name });
573            }
574            serde_yaml::Value::String(s) if s.eq_ignore_ascii_case("on") => {
575                return Ok(MiddlewareConfig::On { name });
576            }
577            serde_yaml::Value::Bool(b) if *b => {
578                return Ok(MiddlewareConfig::On { name });
579            }
580            _ => {
581                // Normal middleware configuration
582                Ok(MiddlewareConfig::Named { 
583                    name, 
584                    config: UniversalValue::from_yaml(yaml_value)
585                })
586            }
587        }
588    }
589}
590
591impl Strategy {
592    pub fn new(name: String) -> Self {
593        Self {
594            name,
595            middleware: Arc::new(Vec::new()),
596        }
597    }
598
599    /// Supplement middleware configurations - only add missing middleware and missing properties
600    pub fn supplement_with(&mut self, incoming: &[MiddlewareConfig]) -> Result<()> {
601        supplement_middleware(Arc::make_mut(&mut self.middleware), incoming)
602    }
603
604    pub fn merge_with(&self, other: &Strategy) -> Strategy {
605        let mut result = self.clone();
606
607        for middleware in other.middleware.iter() {
608            let merged = Arc::make_mut(&mut result.middleware);
609            if let Some(pos) = merged.iter().position(|existing| existing.name() == middleware.name()) {
610                merged[pos] = middleware.clone();
611            } else {
612                merged.push(middleware.clone());
613            }
614        }
615
616        result
617    }
618}
619
620#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
621#[serde(untagged)]
622pub enum StrategyRef {
623    Named(String),
624    InlineMiddleware(Vec<MiddlewareConfig>),
625}
626
627impl StrategyRef {
628    pub fn resolve(&self, strategies: &LegacyStrategyCollection) -> Option<Strategy> {
629        match self {
630            StrategyRef::Named(name) => strategies.get(name).map(|middleware| Strategy {
631                name: name.clone(),
632                middleware: Arc::new(middleware.clone()),
633            }),
634            StrategyRef::InlineMiddleware(middleware) => Some(Strategy {
635                name: "inline".to_string(),
636                middleware: Arc::new(middleware.clone()),
637            }),
638        }
639    }
640}
641
642#[cfg(test)]
643mod tests {
644    use super::*;
645    use serde_json::json;
646
647    #[test]
648    fn test_universal_value_json_conversion() {
649        let json_val = json!({
650            "key": "value",
651            "number": 42,
652            "nested": {
653                "array": [1, 2, 3]
654            }
655        });
656
657        let universal = UniversalValue::from_json(json_val.clone());
658        
659        // Convert back to JSON
660        let converted = universal.as_json().unwrap();
661        assert_eq!(json_val, converted);
662    }
663
664    #[test]
665    fn test_universal_value_yaml_conversion() {
666        let yaml_str = r#"key: value
667number: 42
668nested:
669  array: [1, 2, 3]"#;
670
671        let yaml_val: serde_yaml::Value = serde_yaml::from_str(yaml_str).unwrap();
672        let universal = UniversalValue::from_yaml(yaml_val);
673        
674        // Convert to JSON
675        let json_val = universal.as_json().unwrap();
676        assert_eq!(json_val["key"], "value");
677        assert_eq!(json_val["number"], 42);
678        assert_eq!(json_val["nested"]["array"][0], 1);
679    }
680
681    #[test]
682    fn test_universal_value_string_conversion() {
683        let json_val = json!({
684            "message": "Hello, World!",
685            "count": 100
686        });
687
688        let universal = UniversalValue::from_json(json_val);
689        
690        // JSON string
691        let json_str = universal.to_json_string().unwrap();
692        assert!(json_str.contains("Hello, World!"));
693        assert!(json_str.contains("100"));
694        
695        // YAML string
696        let yaml_str = universal.to_yaml_string().unwrap();
697        assert!(yaml_str.contains("message: Hello, World!"));
698        assert!(yaml_str.contains("count: 100"));
699    }
700
701    #[test]
702    fn test_middleware_config_with_universal_value() {
703        let json_config = json!({
704            "requests": 1000,
705            "window": "1m"
706        });
707
708        let middleware = MiddlewareConfig::new_named_json(
709            "rate_limit".to_string(),
710            json_config.clone()
711        );
712
713        assert_eq!(middleware.name(), "rate_limit");
714        
715        // Get as JSON
716        let retrieved_json = middleware.config_as_json().unwrap();
717        assert_eq!(json_config, retrieved_json);
718        
719        // Convert to specific type
720        #[derive(Deserialize)]
721        struct RateLimitConfig {
722            requests: u32,
723            window: String,
724        }
725        
726        let rate_limit: RateLimitConfig = middleware.config_into().unwrap();
727        assert_eq!(rate_limit.requests, 1000);
728        assert_eq!(rate_limit.window, "1m");
729    }
730
731    #[test]
732    fn test_middleware_config_yaml_deserialization() {
733        let yaml_str = r#"
734- rate_limit:
735    requests: 500
736    window: "30s"
737- logging:
738    level: info
739    format: json
740"#;
741
742        let middleware: Vec<MiddlewareConfig> = serde_yaml::from_str(yaml_str).unwrap();
743        assert_eq!(middleware.len(), 2);
744        
745        // Check first middleware
746        let rate_limit = &middleware[0];
747        assert_eq!(rate_limit.name(), "rate_limit");
748        
749        let config_json = rate_limit.config_as_json().unwrap();
750        assert_eq!(config_json["requests"], 500);
751        assert_eq!(config_json["window"], "30s");
752        
753        // Check second middleware
754        let logging = &middleware[1];
755        assert_eq!(logging.name(), "logging");
756        
757        let config_json = logging.config_as_json().unwrap();
758        assert_eq!(config_json["level"], "info");
759        assert_eq!(config_json["format"], "json");
760    }
761
762    #[test]
763    fn test_strategy_with_universal_values() {
764        let yaml_strategy = r#"
765name: "test_strategy"
766middleware:
767  - auth:
768      type: jwt
769      secret: "my-secret"
770  - cors:
771      origins: ["*"]
772      methods: ["GET", "POST"]
773"#;
774
775        let strategy: Strategy = serde_yaml::from_str(yaml_strategy).unwrap();
776        assert_eq!(strategy.name, "test_strategy");
777        assert_eq!(strategy.middleware.len(), 2);
778        
779        // Check auth middleware
780        let auth = &strategy.middleware[0];
781        assert_eq!(auth.name(), "auth");
782        
783        #[derive(Deserialize)]
784        struct AuthConfig {
785            r#type: String,
786            secret: String,
787        }
788        
789        let auth_config: AuthConfig = auth.config_into().unwrap();
790        assert_eq!(auth_config.r#type, "jwt");
791        assert_eq!(auth_config.secret, "my-secret");
792        
793        // Check CORS middleware
794        let cors = &strategy.middleware[1];
795        assert_eq!(cors.name(), "cors");
796        
797        let cors_json = cors.config_as_json().unwrap();
798        assert_eq!(cors_json["origins"][0], "*");
799        assert_eq!(cors_json["methods"][0], "GET");
800        assert_eq!(cors_json["methods"][1], "POST");
801    }
802
803    #[test]
804    fn test_strategy_ref_inline_middleware_collection() {
805        // Test InlineMiddleware(Vec<MiddlewareConfig>) functionality
806        let inline_yaml = r#"
807  - rate_limit:
808      requests: 100
809      window: "1m"
810  - logging:
811      level: debug
812      format: json
813"#;
814
815        let strategy_ref: StrategyRef = serde_yaml::from_str(inline_yaml).unwrap();
816        let resolved = strategy_ref.resolve(&LegacyStrategyCollection::new());
817        
818        assert!(resolved.is_some());
819        let strategy = resolved.unwrap();
820        assert_eq!(strategy.name, "inline");
821        assert_eq!(strategy.middleware.len(), 2);
822        
823        // Check rate_limit middleware
824        let rate_limit = &strategy.middleware[0];
825        assert_eq!(rate_limit.name(), "rate_limit");
826        
827        #[derive(Deserialize)]
828        struct RateLimitConfig {
829            requests: u32,
830            window: String,
831        }
832        
833        let rate_config: RateLimitConfig = rate_limit.config_into().unwrap();
834        assert_eq!(rate_config.requests, 100);
835        assert_eq!(rate_config.window, "1m");
836        
837        // Check logging middleware
838        let logging = &strategy.middleware[1];
839        assert_eq!(logging.name(), "logging");
840        
841        let logging_json = logging.config_as_json().unwrap();
842        assert_eq!(logging_json["level"], "debug");
843        assert_eq!(logging_json["format"], "json");
844    }
845
846    #[test]
847    fn test_strategy_ref_named_vs_inline() {
848        let mut strategies = LegacyStrategyCollection::new();
849        
850        // Add a named strategy
851        strategies.insert("test".to_string(), vec![
852            MiddlewareConfig::new_named_json(
853                "auth".to_string(),
854                json!({"type": "basic"})
855            )
856        ]);
857        
858        // Test Named strategy
859        let named_ref = StrategyRef::Named("test".to_string());
860        let named_resolved = named_ref.resolve(&strategies).unwrap();
861        assert_eq!(named_resolved.name, "test");
862        assert_eq!(named_resolved.middleware.len(), 1);
863        
864        // Test Inline middleware strategy
865        let inline_middleware = vec![
866            MiddlewareConfig::new_named_json(
867                "logging".to_string(),
868                json!({"level": "info"})
869            )
870        ];
871        let inline_ref = StrategyRef::InlineMiddleware(inline_middleware);
872        let inline_resolved = inline_ref.resolve(&strategies).unwrap();
873        assert_eq!(inline_resolved.name, "inline");
874        assert_eq!(inline_resolved.middleware.len(), 1);
875    }
876
877    #[test]
878    fn test_universal_value_roundtrip() {
879        let original = json!({
880            "string": "test",
881            "number": 42,
882            "boolean": true,
883            "null": null,
884            "array": [1, 2, 3],
885            "object": {
886                "nested": "value"
887            }
888        });
889
890        // JSON -> UniversalValue -> YAML -> UniversalValue -> JSON
891        let universal1 = UniversalValue::from_json(original.clone());
892        let yaml_val = universal1.as_yaml().unwrap();
893        let universal2 = UniversalValue::from_yaml(yaml_val);
894        let final_json = universal2.as_json().unwrap();
895        
896        // Compare via string since key order may differ
897        let original_str = serde_json::to_string(&original).unwrap();
898        let final_str = serde_json::to_string(&final_json).unwrap();
899        
900        // Parse both for value comparison
901        let parsed_original: serde_json::Value = serde_json::from_str(&original_str).unwrap();
902        let parsed_final: serde_json::Value = serde_json::from_str(&final_str).unwrap();
903        
904        assert_eq!(parsed_original, parsed_final);
905    }
906
907    #[test]
908    fn test_supplement_middleware_missing_only() {
909        let mut current = vec![
910            MiddlewareConfig::new_named_json(
911                "rate_limit".to_string(),
912                json!({
913                    "requests": 1000,
914                    "window": "1m",
915                    "burst": 100
916                })
917            ),
918            MiddlewareConfig::new_named_json(
919                "logging".to_string(),
920                json!({
921                    "level": "info",
922                    "format": "text"
923                })
924            )
925        ];
926
927        let incoming = vec![
928            MiddlewareConfig::new_named_json(
929                "rate_limit".to_string(),
930                json!({
931                    "requests": 2000,  // Should NOT be updated (exists)
932                    "timeout": "30s"   // Should be added (missing)
933                })
934            ),
935            MiddlewareConfig::new_named_json(
936                "cors".to_string(),
937                json!({
938                    "origins": ["*"],
939                    "methods": ["GET", "POST"]
940                })
941            )
942        ];
943
944        supplement_middleware(&mut current, &incoming).unwrap();
945
946        assert_eq!(current.len(), 3); // cors (inherited), rate_limit, logging
947
948        assert_eq!(current[0].name(), "cors");
949
950        // Check rate_limit was supplemented (not merged)
951        let rate_limit = current.iter().find(|m| m.name() == "rate_limit").unwrap();
952        assert_eq!(rate_limit.name(), "rate_limit");
953        let config = rate_limit.config_as_json().unwrap();
954        assert_eq!(config["requests"], 1000);  // NOT updated
955        assert_eq!(config["window"], "1m");    // Preserved
956        assert_eq!(config["burst"], 100);      // Preserved
957        assert_eq!(config["timeout"], "30s");  // Added (was missing)
958
959        // Check logging was preserved
960        let logging = current.iter().find(|m| m.name() == "logging").unwrap();
961        assert_eq!(logging.name(), "logging");
962        let config = logging.config_as_json().unwrap();
963        assert_eq!(config["level"], "info");
964        assert_eq!(config["format"], "text");
965
966        // Check cors was inherited and prepended
967        let cors = current.iter().find(|m| m.name() == "cors").unwrap();
968        assert_eq!(cors.name(), "cors");
969        let config = cors.config_as_json().unwrap();
970        assert_eq!(config["origins"][0], "*");
971        assert_eq!(config["methods"][0], "GET");
972    }
973
974    #[test]
975    fn test_strategy_supplement_with_method() {
976        let mut strategy = Strategy::new("test".to_string());
977        Arc::make_mut(&mut strategy.middleware).push(
978            MiddlewareConfig::new_named_json(
979                "auth".to_string(),
980                json!({
981                    "type": "basic",
982                    "realm": "protected"
983                })
984            )
985        );
986
987        let incoming = vec![
988            MiddlewareConfig::new_named_json(
989                "auth".to_string(),
990                json!({
991                    "timeout": 300,      // Should be added (missing)
992                    "max_attempts": 5,    // Should be added (missing)
993                    "type": "jwt"         // Should NOT be updated (exists)
994                })
995            ),
996            MiddlewareConfig::new_named_json(
997                "rate_limit".to_string(),
998                json!({
999                    "requests": 100
1000                })
1001            )
1002        ];
1003
1004        strategy.supplement_with(&incoming).unwrap();
1005
1006        assert_eq!(strategy.middleware.len(), 2);
1007
1008        // Check auth was supplemented (not merged)
1009        let auth = strategy.middleware.iter().find(|m| m.name() == "auth").unwrap();
1010        assert_eq!(auth.name(), "auth");
1011        let config = auth.config_as_json().unwrap();
1012        assert_eq!(config["type"], "basic");      // NOT updated
1013        assert_eq!(config["realm"], "protected"); // Preserved
1014        assert_eq!(config["timeout"], 300);       // Added (was missing)
1015        assert_eq!(config["max_attempts"], 5);     // Added (was missing)
1016
1017        // Check rate_limit was inherited and prepended
1018        assert_eq!(strategy.middleware[0].name(), "rate_limit");
1019        let rate_limit = &strategy.middleware[0];
1020        assert_eq!(rate_limit.name(), "rate_limit");
1021        let config = rate_limit.config_as_json().unwrap();
1022        assert_eq!(config["requests"], 100);
1023    }
1024
1025    #[test]
1026    fn test_supplement_middleware_yaml_formats() {
1027        let mut current = vec![
1028            MiddlewareConfig::new_named_yaml(
1029                "cache".to_string(),
1030                serde_yaml::from_str(r#"
1031ttl: 300
1032max_size: 1000
1033"#).unwrap()
1034            )
1035        ];
1036
1037        let incoming = vec![
1038            MiddlewareConfig::new_named_json(
1039                "cache".to_string(),
1040                json!({
1041                    "ttl": 600,        // Should NOT be updated (exists)
1042                    "strategy": "lru"  // Should be added (missing)
1043                })
1044            )
1045        ];
1046
1047        supplement_middleware(&mut current, &incoming).unwrap();
1048
1049        let cache = &current[0];
1050        assert_eq!(cache.name(), "cache");
1051        let config = cache.config_as_json().unwrap();
1052        assert_eq!(config["ttl"], 300);        // NOT updated
1053        assert_eq!(config["max_size"], 1000);  // Preserved
1054        assert_eq!(config["strategy"], "lru"); // Added (was missing)
1055    }
1056
1057    #[test]
1058    fn test_supplement_middleware_no_overwrite() {
1059        let mut current = vec![
1060            MiddlewareConfig::new_named_json(
1061                "test".to_string(),
1062                json!({
1063                    "existing_string": "old_value",
1064                    "existing_number": 42,
1065                    "existing_bool": true,
1066                    "existing_null": null,
1067                    "existing_array": [1, 2, 3],
1068                    "existing_object": {"key": "value"}
1069                })
1070            )
1071        ];
1072
1073        let incoming = vec![
1074            MiddlewareConfig::new_named_json(
1075                "test".to_string(),
1076                json!({
1077                    "existing_string": "new_value",    // Should NOT be updated
1078                    "existing_number": 100,            // Should NOT be updated
1079                    "existing_bool": false,             // Should NOT be updated
1080                    "existing_null": "not_null",       // Should NOT be updated (null exists)
1081                    "existing_array": [4, 5, 6],        // Should NOT be updated
1082                    "existing_object": {"new_key": "new_value"}, // Should NOT be updated
1083                    "new_string": "added",             // Should be added
1084                    "new_number": 999,                 // Should be added
1085                    "new_bool": false,                 // Should be added
1086                    "new_array": [7, 8, 9],            // Should be added
1087                    "new_object": {"added": "yes"}     // Should be added
1088                })
1089            )
1090        ];
1091
1092        supplement_middleware(&mut current, &incoming).unwrap();
1093
1094        let test = &current[0];
1095        let config = test.config_as_json().unwrap();
1096
1097        // Check existing values are NOT updated
1098        assert_eq!(config["existing_string"], "old_value");
1099        assert_eq!(config["existing_number"], 42);
1100        assert_eq!(config["existing_bool"], true);
1101        assert_eq!(config["existing_null"], serde_json::Value::Null);
1102        assert_eq!(config["existing_array"], json!([1, 2, 3]));
1103        assert_eq!(config["existing_object"], json!({"key": "value", "new_key": "new_value"}));
1104
1105        // Check new values are added
1106        assert_eq!(config["new_string"], "added");
1107        assert_eq!(config["new_number"], 999);
1108        assert_eq!(config["new_bool"], false);
1109        assert_eq!(config["new_array"], json!([7, 8, 9]));
1110        assert_eq!(config["new_object"], json!({"added": "yes"}));
1111    }
1112
1113    #[test]
1114    fn test_inline_middleware_strategy_ref() {
1115        let yaml_str = r#"
1116    - rate_limit:
1117        requests: 100
1118        window: "1m"
1119    - logging:
1120        level: debug
1121    "#;
1122        
1123        let strategy_ref: StrategyRef = serde_yaml::from_str(yaml_str).unwrap();
1124        
1125        match strategy_ref {
1126            StrategyRef::InlineMiddleware(middleware) => {
1127                assert_eq!(middleware.len(), 2);
1128                assert_eq!(middleware[0].name(), "rate_limit");
1129                assert_eq!(middleware[1].name(), "logging");
1130            }
1131            _ => panic!("Expected InlineMiddleware variant"),
1132        }
1133    }
1134
1135    #[test]
1136    fn test_inline_middleware_strategy_ref_resolution() {
1137        let yaml_str = r#"
1138    - rate_limit:
1139        requests: 50
1140        window: "30s"
1141    - logging:
1142        level: info
1143    "#;
1144        
1145        let strategy_ref: StrategyRef = serde_yaml::from_str(yaml_str).unwrap();
1146        let strategies = LegacyStrategyCollection::new();
1147        
1148        let resolved = strategy_ref.resolve(&strategies).unwrap();
1149        assert_eq!(resolved.name, "inline");
1150        assert_eq!(resolved.middleware.len(), 2);
1151        assert_eq!(resolved.middleware[0].name(), "rate_limit");
1152        assert_eq!(resolved.middleware[1].name(), "logging");
1153    }
1154
1155    #[test]
1156    fn test_from_yaml_str_convenience() {
1157        let middleware = MiddlewareConfig::from_yaml_str(
1158            "httpward_log_module", 
1159            "level: warn"
1160        ).unwrap();
1161
1162        assert_eq!(middleware.name(), "httpward_log_module");
1163        assert!(!middleware.is_off());
1164
1165        let config: serde_json::Value = middleware.config_as_json().unwrap();
1166        assert_eq!(config["level"], "warn");
1167    }
1168
1169    #[test]
1170    fn test_from_json_str_convenience() {
1171        let middleware = MiddlewareConfig::from_json_str(
1172            "httpward_log_module", 
1173            r#"{"level": "info", "tag": "api"}"#
1174        ).unwrap();
1175
1176        assert_eq!(middleware.name(), "httpward_log_module");
1177        assert!(!middleware.is_off());
1178
1179        let config: serde_json::Value = middleware.config_as_json().unwrap();
1180        assert_eq!(config["level"], "info");
1181        assert_eq!(config["tag"], "api");
1182    }
1183
1184    #[test]
1185    fn test_from_serializable_convenience() {
1186        #[derive(Serialize)]
1187        struct LogConfig {
1188            level: String,
1189            tag: Option<String>,
1190        }
1191
1192        let config = LogConfig {
1193            level: "debug".to_string(),
1194            tag: Some("test".to_string()),
1195        };
1196
1197        let middleware = MiddlewareConfig::from_serializable(
1198            "httpward_log_module", 
1199            config
1200        ).unwrap();
1201
1202        assert_eq!(middleware.name(), "httpward_log_module");
1203        assert!(!middleware.is_off());
1204
1205        let parsed_config: serde_json::Value = middleware.config_as_json().unwrap();
1206        assert_eq!(parsed_config["level"], "debug");
1207        assert_eq!(parsed_config["tag"], "test");
1208    }
1209
1210    #[test]
1211    fn test_parse_config_convenience() {
1212        #[derive(Deserialize, PartialEq, Debug)]
1213        struct LogConfig {
1214            level: String,
1215            tag: Option<String>,
1216        }
1217
1218        let middleware = MiddlewareConfig::from_yaml_str(
1219            "httpward_log_module", 
1220            "level: warn\ntag: test"
1221        ).unwrap();
1222
1223        let config: LogConfig = middleware.parse_config().unwrap();
1224        assert_eq!(config.level, "warn");
1225        assert_eq!(config.tag, Some("test".to_string()));
1226    }
1227
1228    #[test]
1229    fn test_yaml_round_trip_convenience() {
1230        let original = MiddlewareConfig::from_yaml_str(
1231            "test_middleware", 
1232            "level: warn\ntag: test"
1233        ).unwrap();
1234
1235        let yaml_string = original.config_to_yaml_string().unwrap();
1236        let restored = MiddlewareConfig::from_yaml_str("test_middleware", &yaml_string).unwrap();
1237
1238        let original_config: serde_json::Value = original.config_as_json().unwrap();
1239        let restored_config: serde_json::Value = restored.config_as_json().unwrap();
1240        
1241        assert_eq!(original_config, restored_config);
1242    }
1243
1244    #[test]
1245    fn test_json_round_trip_convenience() {
1246        let original = MiddlewareConfig::from_json_str(
1247            "test_middleware", 
1248            r#"{"level": "warn", "tag": "test"}"#
1249        ).unwrap();
1250
1251        let json_string = original.config_to_json_string().unwrap();
1252        let restored = MiddlewareConfig::from_json_str("test_middleware", &json_string).unwrap();
1253
1254        let original_config: serde_json::Value = original.config_as_json().unwrap();
1255        let restored_config: serde_json::Value = restored.config_as_json().unwrap();
1256        
1257        assert_eq!(original_config, restored_config);
1258    }
1259}
1260
1261// Include the off functionality tests
1262mod off_tests;