deb822_fast/
convert.rs

1//! Conversion between Deb822-like paragraphs and Rust objects.
2
3/// Abstract trait for accessing and modifying key-value pairs in a paragraph.
4pub trait Deb822LikeParagraph: FromIterator<(String, String)> {
5    /// Get the value for the given key.
6    fn get(&self, key: &str) -> Option<String>;
7
8    /// Insert a key-value pair.
9    fn set(&mut self, key: &str, value: &str);
10
11    /// Remove a key-value pair.
12    fn remove(&mut self, key: &str);
13}
14
15impl Deb822LikeParagraph for crate::Paragraph {
16    fn get(&self, key: &str) -> Option<String> {
17        crate::Paragraph::get(self, key).map(|v| v.to_string())
18    }
19
20    fn set(&mut self, key: &str, value: &str) {
21        crate::Paragraph::set(self, key, value);
22    }
23
24    fn remove(&mut self, key: &str) {
25        crate::Paragraph::remove(self, key);
26    }
27}
28
29/// Convert a paragraph to this object.
30pub trait FromDeb822Paragraph<P: Deb822LikeParagraph> {
31    /// Convert a paragraph to this object.
32    fn from_paragraph(paragraph: &P) -> Result<Self, String>
33    where
34        Self: Sized;
35}
36
37/// Convert this object to a paragraph.
38pub trait ToDeb822Paragraph<P: Deb822LikeParagraph> {
39    /// Convert this object to a paragraph.
40    fn to_paragraph(&self) -> P;
41
42    /// Update the given paragraph with the values from this object.
43    fn update_paragraph(&self, paragraph: &mut P);
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49
50    #[test]
51    fn test_trait_impl_directly() {
52        // Test the trait methods directly to improve coverage
53        let mut para = crate::Paragraph {
54            fields: vec![crate::Field {
55                name: "Test".to_string(),
56                value: "Value".to_string(),
57            }],
58        };
59
60        // Test Deb822LikeParagraph::get
61        let result: Option<String> = Deb822LikeParagraph::get(&para, "Test");
62        assert_eq!(result, Some("Value".to_string()));
63
64        // Test Deb822LikeParagraph::set
65        Deb822LikeParagraph::set(&mut para, "Test", "NewValue");
66        assert_eq!(para.get("Test"), Some("NewValue"));
67
68        // Test Deb822LikeParagraph::remove
69        Deb822LikeParagraph::remove(&mut para, "Test");
70        assert_eq!(para.get("Test"), None);
71    }
72
73    #[test]
74    fn test_deb822like_paragraph_impl() {
75        // Create mock crate::Paragraph for tests
76        let mut para = crate::Paragraph {
77            fields: vec![crate::Field {
78                name: "Name".to_string(),
79                value: "Test".to_string(),
80            }],
81        };
82
83        // Test get() - this calls the implementation on line 16-17
84        assert_eq!(para.get("Name"), Some("Test"));
85        assert_eq!(para.get("NonExistent"), None);
86
87        // Test set() - this calls the implementation on line 20-21
88        para.set("Name", "NewValue");
89        assert_eq!(para.get("Name"), Some("NewValue"));
90
91        // Test set() with new key
92        para.set("NewKey", "Value");
93        assert_eq!(para.get("NewKey"), Some("Value"));
94
95        // Test remove() - this calls the implementation on line 24-25
96        para.remove("Name");
97        assert_eq!(para.get("Name"), None);
98        assert_eq!(para.get("NewKey"), Some("Value"));
99
100        // Create a new paragraph with multiple fields of the same name
101        let mut para = crate::Paragraph {
102            fields: vec![
103                crate::Field {
104                    name: "Duplicate".to_string(),
105                    value: "Value1".to_string(),
106                },
107                crate::Field {
108                    name: "Duplicate".to_string(),
109                    value: "Value2".to_string(),
110                },
111            ],
112        };
113
114        // Test remove() removes all matches
115        para.remove("Duplicate");
116        assert_eq!(para.get("Duplicate"), None);
117        assert_eq!(para.fields.len(), 0);
118    }
119
120    #[cfg(feature = "derive")]
121    mod derive {
122        use super::*;
123        use crate as deb822_fast;
124        use crate::{FromDeb822, ToDeb822};
125
126        #[test]
127        fn test_derive() {
128            #[derive(ToDeb822)]
129            struct Foo {
130                bar: String,
131                baz: i32,
132                blah: Option<String>,
133            }
134
135            let foo = Foo {
136                bar: "hello".to_string(),
137                baz: 42,
138                blah: None,
139            };
140
141            let paragraph: crate::Paragraph = foo.to_paragraph();
142            assert_eq!(paragraph.get("bar"), Some("hello"));
143            assert_eq!(paragraph.get("baz"), Some("42"));
144            assert_eq!(paragraph.get("blah"), None);
145        }
146
147        #[test]
148        fn test_optional_missing() {
149            #[derive(ToDeb822)]
150            struct Foo {
151                bar: String,
152                baz: Option<String>,
153            }
154
155            let foo = Foo {
156                bar: "hello".to_string(),
157                baz: None,
158            };
159
160            let paragraph: crate::Paragraph = foo.to_paragraph();
161            assert_eq!(paragraph.get("bar"), Some("hello"));
162            assert_eq!(paragraph.get("baz"), None);
163
164            assert_eq!("bar: hello\n", paragraph.to_string());
165        }
166
167        #[test]
168        fn test_deserialize_with() {
169            let mut para: crate::Paragraph = "bar: bar\n# comment\nbaz: blah\n".parse().unwrap();
170
171            fn to_bool(s: &str) -> Result<bool, String> {
172                Ok(s == "ja")
173            }
174
175            fn from_bool(s: &bool) -> String {
176                if *s {
177                    "ja".to_string()
178                } else {
179                    "nee".to_string()
180                }
181            }
182
183            #[derive(FromDeb822, ToDeb822)]
184            struct Foo {
185                bar: String,
186                #[deb822(deserialize_with = to_bool, serialize_with = from_bool)]
187                baz: bool,
188            }
189
190            let mut foo: Foo = Foo::from_paragraph(&para).unwrap();
191            assert_eq!(foo.bar, "bar");
192            assert!(!foo.baz);
193
194            foo.bar = "new".to_string();
195
196            foo.update_paragraph(&mut para);
197
198            assert_eq!(para.get("bar"), Some("new"));
199            assert_eq!(para.get("baz"), Some("nee"));
200            assert_eq!(para.to_string(), "bar: new\nbaz: nee\n");
201        }
202
203        #[test]
204        fn test_update_remove() {
205            let mut para: crate::Paragraph = "bar: bar\n# comment\nbaz: blah\n".parse().unwrap();
206
207            #[derive(FromDeb822, ToDeb822)]
208            struct Foo {
209                bar: Option<String>,
210                baz: String,
211            }
212
213            let mut foo: Foo = Foo::from_paragraph(&para).unwrap();
214            assert_eq!(foo.bar, Some("bar".to_string()));
215            assert_eq!(foo.baz, "blah");
216
217            foo.bar = None;
218
219            foo.update_paragraph(&mut para);
220
221            assert_eq!(para.get("bar"), None);
222            assert_eq!(para.get("baz"), Some("blah"));
223            assert_eq!(para.to_string(), "baz: blah\n");
224        }
225    }
226}