fuzzy_parser/schema.rs
1//! Schema definitions for fuzzy repair
2//!
3//! This module provides schema types that callers use to define
4//! valid field names and type discriminators for fuzzy matching.
5
6/// Schema for a tagged enum (discriminated union)
7///
8/// Used for types with a discriminator field (e.g., tag: "type", "kind")
9///
10/// # Example
11///
12/// ```
13/// use fuzzy_parser::TaggedEnumSchema;
14///
15/// let schema = TaggedEnumSchema::new(
16/// "type",
17/// &["AddDerive", "RemoveDerive"],
18/// |tag| match tag {
19/// "AddDerive" | "RemoveDerive" => Some(&["target", "derives"][..]),
20/// _ => None,
21/// },
22/// )
23/// .with_enum_array("derives", &["Debug", "Clone", "Serialize"])
24/// .with_nested_object("config", &["timeout", "retries"]);
25/// ```
26pub struct TaggedEnumSchema<F>
27where
28 F: Fn(&str) -> Option<&'static [&'static str]>,
29{
30 /// The discriminator field name (e.g., "type", "kind")
31 pub tag_field: &'static str,
32 /// Valid tag values (e.g., ["AddDerive", "RenameIdent", ...])
33 pub valid_tags: &'static [&'static str],
34 /// Function to get valid fields for a given tag value
35 pub fields_for_tag: F,
36 /// Fields that contain arrays of enum values: (field_name, valid_values)
37 pub enum_arrays: Vec<(&'static str, &'static [&'static str])>,
38 /// Fields that contain nested objects: (field_name, valid_fields)
39 pub nested_objects: Vec<(&'static str, &'static [&'static str])>,
40}
41
42impl<F> TaggedEnumSchema<F>
43where
44 F: Fn(&str) -> Option<&'static [&'static str]>,
45{
46 /// Create a new tagged enum schema
47 pub fn new(
48 tag_field: &'static str,
49 valid_tags: &'static [&'static str],
50 fields_for_tag: F,
51 ) -> Self {
52 Self {
53 tag_field,
54 valid_tags,
55 fields_for_tag,
56 enum_arrays: Vec::new(),
57 nested_objects: Vec::new(),
58 }
59 }
60
61 /// Add an enum array field for repair
62 ///
63 /// Values in this array field will be fuzzy-matched against `valid_values`.
64 ///
65 /// # Example
66 ///
67 /// ```
68 /// use fuzzy_parser::TaggedEnumSchema;
69 ///
70 /// let schema = TaggedEnumSchema::new("type", &["AddDerive"], |_| Some(&["derives"][..]))
71 /// .with_enum_array("derives", &["Debug", "Clone", "Serialize"]);
72 /// // Now "Debg" in derives array will be corrected to "Debug"
73 /// ```
74 pub fn with_enum_array(
75 mut self,
76 field: &'static str,
77 valid_values: &'static [&'static str],
78 ) -> Self {
79 self.enum_arrays.push((field, valid_values));
80 self
81 }
82
83 /// Add a nested object field for repair
84 ///
85 /// Field names in this nested object will be fuzzy-matched against `valid_fields`.
86 ///
87 /// # Example
88 ///
89 /// ```
90 /// use fuzzy_parser::TaggedEnumSchema;
91 ///
92 /// let schema = TaggedEnumSchema::new("type", &["Configure"], |_| Some(&["config"][..]))
93 /// .with_nested_object("config", &["timeout", "retries", "enabled"]);
94 /// // Now "timout" in config object will be corrected to "timeout"
95 /// ```
96 pub fn with_nested_object(
97 mut self,
98 field: &'static str,
99 valid_fields: &'static [&'static str],
100 ) -> Self {
101 self.nested_objects.push((field, valid_fields));
102 self
103 }
104
105 /// Check if a tag value is valid
106 pub fn is_valid_tag(&self, tag: &str) -> bool {
107 self.valid_tags.contains(&tag)
108 }
109
110 /// Get valid fields for a tag value
111 pub fn get_fields(&self, tag: &str) -> Option<&'static [&'static str]> {
112 (self.fields_for_tag)(tag)
113 }
114
115 /// Get valid enum values for an array field
116 pub fn get_enum_array_values(&self, field: &str) -> Option<&'static [&'static str]> {
117 self.enum_arrays
118 .iter()
119 .find(|(f, _)| *f == field)
120 .map(|(_, v)| *v)
121 }
122
123 /// Get valid fields for a nested object
124 pub fn get_nested_object_fields(&self, field: &str) -> Option<&'static [&'static str]> {
125 self.nested_objects
126 .iter()
127 .find(|(f, _)| *f == field)
128 .map(|(_, v)| *v)
129 }
130}
131
132/// Schema for a simple object with known fields
133pub struct ObjectSchema {
134 /// Valid field names
135 pub valid_fields: &'static [&'static str],
136}
137
138impl ObjectSchema {
139 /// Create a new object schema
140 pub const fn new(valid_fields: &'static [&'static str]) -> Self {
141 Self { valid_fields }
142 }
143
144 /// Check if a field name is valid
145 pub fn is_valid_field(&self, field: &str) -> bool {
146 self.valid_fields.contains(&field)
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 #[test]
155 fn test_tagged_enum_schema() {
156 let schema =
157 TaggedEnumSchema::new("type", &["AddDerive", "RemoveDerive"], |tag| match tag {
158 "AddDerive" => Some(&["target", "derives"][..]),
159 "RemoveDerive" => Some(&["target", "derives"][..]),
160 _ => None,
161 });
162
163 assert!(schema.is_valid_tag("AddDerive"));
164 assert!(!schema.is_valid_tag("InvalidType"));
165 assert_eq!(
166 schema.get_fields("AddDerive"),
167 Some(&["target", "derives"][..])
168 );
169 }
170
171 #[test]
172 fn test_object_schema() {
173 let schema = ObjectSchema::new(&["name", "value", "is_pub"]);
174
175 assert!(schema.is_valid_field("name"));
176 assert!(!schema.is_valid_field("invalid"));
177 }
178}