someip_sd_wire/
config.rs

1use crate::error::ConfigError;
2
3/// A single configuration entry reference (zero-copy, no_std compatible).
4///
5/// Configuration entries follow DNS-SD TXT record format:
6/// - Key-only (boolean flag): `"enabled"`
7/// - Key with empty value: `"name="`
8/// - Key with value: `"version=1.0"`
9///
10/// Keys must be printable US-ASCII (0x20-0x7E) excluding '='.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct ConfigEntry<'a> {
13    key: &'a str,
14    /// None = boolean flag (key present without value)
15    /// Some("") = key with empty value (ends with '=')
16    /// Some("value") = key with value
17    value: Option<&'a str>,
18}
19
20impl<'a> ConfigEntry<'a> {
21    /// Create a new entry with a key and optional value.
22    ///
23    /// # Parameters
24    /// * `key` - The entry key (printable ASCII, no '=')
25    /// * `value` - None for boolean flag, Some(str) for key=value
26    ///
27    /// # Returns
28    /// * `Ok(ConfigEntry)` if key is valid
29    /// * `Err(ConfigError::InvalidKey)` if key is malformed
30    pub fn new(key: &'a str, value: Option<&'a str>) -> Result<Self, ConfigError> {
31        Self::validate_key(key)?;
32        Ok(ConfigEntry { key, value })
33    }
34
35    /// Create a boolean flag entry (key only, no value).
36    ///
37    /// # Parameters
38    /// * `key` - The flag name
39    ///
40    /// # Returns
41    /// * `Ok(ConfigEntry)` if key is valid
42    /// * `Err(ConfigError::InvalidKey)` if key is malformed
43    pub fn flag(key: &'a str) -> Result<Self, ConfigError> {
44        Self::new(key, None)
45    }
46
47    /// Create an entry with a value.
48    ///
49    /// # Parameters
50    /// * `key` - The entry key
51    /// * `value` - The entry value
52    ///
53    /// # Returns
54    /// * `Ok(ConfigEntry)` if key is valid
55    /// * `Err(ConfigError::InvalidKey)` if key is malformed
56    pub fn with_value(key: &'a str, value: &'a str) -> Result<Self, ConfigError> {
57        Self::new(key, Some(value))
58    }
59
60    /// Get the entry key.
61    ///
62    /// # Returns
63    /// The key string slice
64    pub fn key(&self) -> &'a str {
65        self.key
66    }
67
68    /// Get the entry value if present.
69    ///
70    /// # Returns
71    /// * `None` if this is a boolean flag
72    /// * `Some("")` if key ends with '='
73    /// * `Some(value)` if key=value
74    pub fn value(&self) -> Option<&'a str> {
75        self.value
76    }
77
78    /// Check if this is a boolean flag (no value).
79    ///
80    /// # Returns
81    /// True if entry is key-only, false if key=value
82    pub fn is_flag(&self) -> bool {
83        self.value.is_none()
84    }
85
86    /// Parse a configuration entry from a string (without length byte).
87    ///
88    /// # Parameters
89    /// * `s` - The string to parse (e.g., "key=value" or "flag")
90    ///
91    /// # Returns
92    /// * `Ok(ConfigEntry)` if parse succeeds
93    /// * `Err(ConfigError)` if format is invalid
94    pub fn from_str(s: &'a str) -> Result<Self, ConfigError> {
95        if s.is_empty() {
96            return Err(ConfigError::InvalidKey);
97        }
98
99        if s.as_bytes()[0] == b'=' {
100            return Err(ConfigError::KeyStartsWithEquals);
101        }
102
103        // Find the '=' separator
104        if let Some(eq_pos) = s.bytes().position(|b| b == b'=') {
105            let key = &s[..eq_pos];
106            let value = &s[eq_pos + 1..];
107            Self::validate_key(key)?;
108            Ok(ConfigEntry {
109                key,
110                value: Some(value),
111            })
112        } else {
113            Self::validate_key(s)?;
114            Ok(ConfigEntry { key: s, value: None })
115        }
116    }
117
118    /// Validate key according to DNS-SD TXT record spec.
119    ///
120    /// # Parameters
121    /// * `key` - The key string to validate
122    ///
123    /// # Returns
124    /// * `Ok(())` if key is valid
125    /// * `Err(ConfigError::InvalidKey)` if key is malformed
126    fn validate_key(key: &str) -> Result<(), ConfigError> {
127        if key.is_empty() {
128            return Err(ConfigError::InvalidKey);
129        }
130
131        let mut has_non_whitespace = false;
132
133        // Key must be printable US-ASCII (0x20-0x7E), excluding '='
134        for &byte in key.as_bytes() {
135            if byte < 0x20 || byte > 0x7E || byte == b'=' {
136                return Err(ConfigError::InvalidKey);
137            }
138            if byte != b' ' && byte != b'\t' {
139                has_non_whitespace = true;
140            }
141        }
142
143        if !has_non_whitespace {
144            return Err(ConfigError::InvalidKey);
145        }
146
147        Ok(())
148    }
149
150    /// Write entry to buffer (without length prefix).
151    ///
152    /// # Parameters
153    /// * `buf` - The buffer to write into
154    ///
155    /// # Returns
156    /// * `Ok(usize)` - Number of bytes written
157    /// * `Err(ConfigError::BufferTooSmall)` if buffer is insufficient
158    pub fn write_to(&self, buf: &mut [u8]) -> Result<usize, ConfigError> {
159        let key_bytes = self.key.as_bytes();
160        let needed = match self.value {
161            None => key_bytes.len(),
162            Some(v) => key_bytes.len() + 1 + v.len(), // +1 for '='
163        };
164
165        if buf.len() < needed {
166            return Err(ConfigError::BufferTooSmall);
167        }
168
169        let mut pos = 0;
170        buf[pos..pos + key_bytes.len()].copy_from_slice(key_bytes);
171        pos += key_bytes.len();
172
173        if let Some(val) = self.value {
174            buf[pos] = b'=';
175            pos += 1;
176            let val_bytes = val.as_bytes();
177            buf[pos..pos + val_bytes.len()].copy_from_slice(val_bytes);
178            pos += val_bytes.len();
179        }
180
181        Ok(pos)
182    }
183
184    /// Calculate wire format size (without length prefix).
185    ///
186    /// # Returns
187    /// Number of bytes needed for the entry data (excluding length byte)
188    pub fn wire_size(&self) -> usize {
189        match self.value {
190            None => self.key.len(),
191            Some(v) => self.key.len() + 1 + v.len(),
192        }
193    }
194}
195
196/// Iterator over configuration entries in wire format.
197///
198/// Parses entries from the DNS-SD TXT record format:
199/// `[len][string][len][string]...[0x00]`
200///
201/// Each entry is length-prefixed with a u8 length byte.
202/// The sequence ends with a zero-length terminator (0x00).
203pub struct ConfigEntryIter<'a> {
204    data: &'a [u8],
205    pos: usize,
206}
207
208impl<'a> ConfigEntryIter<'a> {
209    /// Create a new iterator over wire format configuration data.
210    ///
211    /// # Parameters
212    /// * `data` - The buffer containing length-prefixed configuration strings
213    ///
214    /// # Returns
215    /// An iterator that yields Result<ConfigEntry, ConfigError>
216    pub fn new(data: &'a [u8]) -> Self {
217        ConfigEntryIter { data, pos: 0 }
218    }
219}
220
221impl<'a> Iterator for ConfigEntryIter<'a> {
222    type Item = Result<ConfigEntry<'a>, ConfigError>;
223
224    fn next(&mut self) -> Option<Self::Item> {
225        // Check if we have at least the length byte
226        if self.pos >= self.data.len() {
227            return Some(Err(ConfigError::UnexpectedEnd));
228        }
229
230        let length = self.data[self.pos] as usize;
231        self.pos += 1;
232
233        // Terminator found
234        if length == 0 {
235            return None;
236        }
237
238        // Check if we have enough data for the string
239        if self.pos + length > self.data.len() {
240            return Some(Err(ConfigError::LengthOverflow));
241        }
242
243        // Extract the string
244        let string_bytes = &self.data[self.pos..self.pos + length];
245        let string = match core::str::from_utf8(string_bytes) {
246            Ok(s) => s,
247            Err(_) => return Some(Err(ConfigError::InvalidUtf8)),
248        };
249
250        self.pos += length;
251        Some(ConfigEntry::from_str(string))
252    }
253}
254
255/// Configuration Option - DNS-SD style TXT record format (no_std compatible).
256///
257/// Provides zero-copy parsing and serialization of configuration options
258/// following the DNS-SD TXT record format used in SOME/IP-SD.
259///
260/// Wire format:
261/// ```text
262/// [len1][string1][len2][string2]...[0x00]
263/// ```
264///
265/// Each string can be:
266/// - Boolean flag: "enabled"
267/// - Key with empty value: "name="
268/// - Key with value: "version=1.0"
269pub struct ConfigurationOption;
270
271impl ConfigurationOption {
272    /// Parse configuration entries from wire format (zero-copy iterator).
273    ///
274    /// # Parameters
275    /// * `data` - Wire format buffer: `[len][string][len][string]...[0x00]`
276    ///
277    /// # Returns
278    /// An iterator over Result<ConfigEntry, ConfigError>
279    ///
280    /// # Example
281    /// ```
282    /// use someip_sd_wire::config::ConfigurationOption;
283    /// 
284    /// let data = b"\x07enabled\x0cversion=1.0a\x00";
285    /// let mut count = 0;
286    /// for result in ConfigurationOption::parse(data) {
287    ///     let entry = result.unwrap();
288    ///     count += 1;
289    ///     if count == 1 {
290    ///         assert_eq!(entry.key(), "enabled");
291    ///         assert!(entry.is_flag());
292    ///     }
293    /// }
294    /// assert_eq!(count, 2);
295    /// ```
296    pub fn parse<'a>(data: &'a [u8]) -> ConfigEntryIter<'a> {
297        ConfigEntryIter::new(data)
298    }
299
300    /// Serialize configuration entries to wire format.
301    ///
302    /// # Parameters
303    /// * `entries` - Iterator over ConfigEntry items to serialize
304    /// * `buf` - Output buffer for wire format data
305    ///
306    /// # Returns
307    /// * `Ok(usize)` - Number of bytes written (including terminator)
308    /// * `Err(ConfigError)` - If buffer is too small or entry exceeds 255 bytes
309    ///
310    /// # Example
311    /// ```
312    /// use someip_sd_wire::config::{ConfigEntry, ConfigurationOption};
313    /// 
314    /// let mut buf = [0u8; 64];
315    /// let entries = [
316    ///     ConfigEntry::flag("enabled").unwrap(),
317    ///     ConfigEntry::with_value("version", "1.0").unwrap(),
318    /// ];
319    /// let size = ConfigurationOption::serialize(entries, &mut buf).unwrap();
320    /// assert!(size > 0);
321    /// assert_eq!(buf[size - 1], 0); // Ends with null terminator
322    /// ```
323    pub fn serialize<'a, I>(entries: I, buf: &mut [u8]) -> Result<usize, ConfigError>
324    where
325        I: IntoIterator<Item = ConfigEntry<'a>>,
326    {
327        let mut pos = 0;
328
329        for entry in entries {
330            let entry_size = entry.wire_size();
331            
332            // Check length fits in u8
333            if entry_size > 255 {
334                return Err(ConfigError::BufferTooSmall);
335            }
336
337            // Check buffer space for length + data
338            if pos + 1 + entry_size > buf.len() {
339                return Err(ConfigError::BufferTooSmall);
340            }
341
342            // Write length
343            buf[pos] = entry_size as u8;
344            pos += 1;
345
346            // Write entry
347            let written = entry.write_to(&mut buf[pos..])?;
348            pos += written;
349        }
350
351        // Write terminator
352        if pos >= buf.len() {
353            return Err(ConfigError::BufferTooSmall);
354        }
355        buf[pos] = 0x00;
356        pos += 1;
357
358        Ok(pos)
359    }
360
361    /// Calculate total wire format size for entries
362    pub fn wire_size<'a, I>(entries: I) -> usize
363    where
364        I: IntoIterator<Item = ConfigEntry<'a>>,
365    {
366        let mut size = 1; // Terminator
367        for entry in entries {
368            size += 1; // Length byte
369            size += entry.wire_size();
370        }
371        size
372    }
373}
374
375#[cfg(test)]
376mod tests {
377    use super::*;
378
379    #[test]
380    fn test_config_entry_flag() {
381        let entry = ConfigEntry::flag("debug").unwrap();
382        assert_eq!(entry.key(), "debug");
383        assert_eq!(entry.value(), None);
384        assert!(entry.is_flag());
385        assert_eq!(entry.wire_size(), 5);
386    }
387
388    #[test]
389    fn test_config_entry_with_value() {
390        let entry = ConfigEntry::with_value("key", "value").unwrap();
391        assert_eq!(entry.key(), "key");
392        assert_eq!(entry.value(), Some("value"));
393        assert!(!entry.is_flag());
394        assert_eq!(entry.wire_size(), 9); // "key=value"
395    }
396
397    #[test]
398    fn test_config_entry_empty_value() {
399        let entry = ConfigEntry::with_value("timeout", "").unwrap();
400        assert_eq!(entry.key(), "timeout");
401        assert_eq!(entry.value(), Some(""));
402        assert!(!entry.is_flag());
403    }
404
405    #[test]
406    fn test_config_entry_from_str() {
407        let entry = ConfigEntry::from_str("multicast=true").unwrap();
408        assert_eq!(entry.key(), "multicast");
409        assert_eq!(entry.value(), Some("true"));
410
411        let entry = ConfigEntry::from_str("priority").unwrap();
412        assert_eq!(entry.key(), "priority");
413        assert_eq!(entry.value(), None);
414
415        let entry = ConfigEntry::from_str("timeout=").unwrap();
416        assert_eq!(entry.key(), "timeout");
417        assert_eq!(entry.value(), Some(""));
418    }
419
420    #[test]
421    fn test_config_entry_validation() {
422        // Empty key
423        assert_eq!(ConfigEntry::flag(""), Err(ConfigError::InvalidKey));
424
425        // Key with only whitespace
426        assert_eq!(ConfigEntry::flag("   "), Err(ConfigError::InvalidKey));
427
428        // Key starts with '='
429        assert_eq!(
430            ConfigEntry::from_str("=invalid"),
431            Err(ConfigError::KeyStartsWithEquals)
432        );
433
434        // Key contains '='
435        assert_eq!(ConfigEntry::flag("key="), Err(ConfigError::InvalidKey));
436
437        // Non-printable ASCII
438        assert_eq!(ConfigEntry::flag("key\x01"), Err(ConfigError::InvalidKey));
439        assert_eq!(ConfigEntry::flag("key\x7F"), Err(ConfigError::InvalidKey));
440
441        // Valid keys
442        assert!(ConfigEntry::flag("valid").is_ok());
443        assert!(ConfigEntry::flag("valid-key_123").is_ok());
444        assert!(ConfigEntry::flag("a b c").is_ok());
445    }
446
447    #[test]
448    fn test_config_serialize_deserialize() {
449        // Create some entries
450        let entries = [
451            ConfigEntry::with_value("multicast", "true").unwrap(),
452            ConfigEntry::flag("priority").unwrap(),
453            ConfigEntry::with_value("timeout", "").unwrap(),
454            ConfigEntry::flag("debug").unwrap(),
455        ];
456
457        // Serialize
458        let mut buf = [0u8; 256];
459        let size = ConfigurationOption::serialize(entries.iter().copied(), &mut buf).unwrap();
460
461        // Expected wire format
462        let expected = [
463            0x0E, b'm', b'u', b'l', b't', b'i', b'c', b'a', b's', b't', b'=', b't', b'r', b'u', b'e',
464            0x08, b'p', b'r', b'i', b'o', b'r', b'i', b't', b'y',
465            0x08, b't', b'i', b'm', b'e', b'o', b'u', b't', b'=',
466            0x05, b'd', b'e', b'b', b'u', b'g',
467            0x00,
468        ];
469
470        assert_eq!(&buf[..size], &expected);
471
472        // Parse back
473        let parsed: Vec<_> = ConfigurationOption::parse(&buf[..size])
474            .collect::<Result<Vec<_>, _>>()
475            .unwrap();
476
477        assert_eq!(parsed.len(), 4);
478        assert_eq!(parsed[0].key(), "multicast");
479        assert_eq!(parsed[0].value(), Some("true"));
480        assert_eq!(parsed[1].key(), "priority");
481        assert_eq!(parsed[1].value(), None);
482        assert_eq!(parsed[2].key(), "timeout");
483        assert_eq!(parsed[2].value(), Some(""));
484        assert_eq!(parsed[3].key(), "debug");
485        assert_eq!(parsed[3].value(), None);
486    }
487
488    #[test]
489    fn test_config_wire_size() {
490        let entries = [
491            ConfigEntry::with_value("a", "b").unwrap(),
492            ConfigEntry::flag("c").unwrap(),
493        ];
494
495        let size = ConfigurationOption::wire_size(entries.iter().copied());
496        // 1(len) + 3("a=b") + 1(len) + 1("c") + 1(term) = 7
497        assert_eq!(size, 7);
498
499        let mut buf = [0u8; 256];
500        let written = ConfigurationOption::serialize(entries.iter().copied(), &mut buf).unwrap();
501        assert_eq!(written, size);
502    }
503
504    #[test]
505    fn test_config_parse_errors() {
506        // Unexpected end (no terminator)
507        let data = [0x03, b'k', b'e', b'y'];
508        let mut iter = ConfigurationOption::parse(&data);
509        assert_eq!(iter.next(), Some(Ok(ConfigEntry::flag("key").unwrap())));
510        assert_eq!(iter.next(), Some(Err(ConfigError::UnexpectedEnd)));
511
512        // Length overflow
513        let data = [0x0A, b'k', b'e', b'y'];
514        let mut iter = ConfigurationOption::parse(&data);
515        assert_eq!(iter.next(), Some(Err(ConfigError::LengthOverflow)));
516
517        // Invalid UTF-8
518        let data = [0x03, 0xFF, 0xFE, 0xFD, 0x00];
519        let mut iter = ConfigurationOption::parse(&data);
520        assert_eq!(iter.next(), Some(Err(ConfigError::InvalidUtf8)));
521    }
522
523    #[test]
524    fn test_config_buffer_too_small() {
525        let entries = [ConfigEntry::with_value("key", "value").unwrap()];
526        let mut buf = [0u8; 5]; // Too small
527        assert_eq!(
528            ConfigurationOption::serialize(entries.iter().copied(), &mut buf),
529            Err(ConfigError::BufferTooSmall)
530        );
531    }
532
533    #[test]
534    fn test_config_empty() {
535        let entries: [ConfigEntry; 0] = [];
536        let mut buf = [0u8; 256];
537        let size = ConfigurationOption::serialize(entries.iter().copied(), &mut buf).unwrap();
538        assert_eq!(size, 1); // Just terminator
539        assert_eq!(buf[0], 0x00);
540
541        let parsed: Vec<_> = ConfigurationOption::parse(&buf[..size])
542            .collect::<Result<Vec<_>, _>>()
543            .unwrap();
544        assert_eq!(parsed.len(), 0);
545    }
546
547    #[test]
548    fn test_config_duplicate_keys() {
549        let entries = [
550            ConfigEntry::with_value("key", "value1").unwrap(),
551            ConfigEntry::with_value("key", "value2").unwrap(),
552            ConfigEntry::flag("key").unwrap(),
553        ];
554
555        let mut buf = [0u8; 256];
556        let size = ConfigurationOption::serialize(entries.iter().copied(), &mut buf).unwrap();
557
558        let parsed: Vec<_> = ConfigurationOption::parse(&buf[..size])
559            .collect::<Result<Vec<_>, _>>()
560            .unwrap();
561
562        assert_eq!(parsed.len(), 3);
563        assert_eq!(parsed[0].key(), "key");
564        assert_eq!(parsed[0].value(), Some("value1"));
565        assert_eq!(parsed[1].key(), "key");
566        assert_eq!(parsed[1].value(), Some("value2"));
567        assert_eq!(parsed[2].key(), "key");
568        assert_eq!(parsed[2].value(), None);
569    }
570}