parse_net/sfv/write.rs
1//! RFC 9651 section 4.1: canonical serialization, as a streaming writer.
2
3use super::{BareItem, parse::INTEGER_MAX};
4use crate::{
5 class,
6 error::WriteError,
7 write::{CountSink, Sink, reserve, write_decimal_u64},
8};
9
10type WResult<T> = core::result::Result<T, WriteError>;
11
12/// What the writer produces.
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
14enum Kind {
15 Item,
16 List,
17 Dictionary,
18}
19
20/// Where the writer is.
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22enum State {
23 /// Nothing written yet.
24 Empty,
25 /// After a member (an item or a closed inner list): its parameters may
26 /// follow.
27 Member,
28 /// After a dictionary key with no value yet (Boolean true so far).
29 Key,
30 /// Inside an inner list, before its first item.
31 Inner,
32 /// Inside an inner list, after an item: its parameters may follow.
33 InnerItem,
34}
35
36/// A value to write after the separator.
37#[derive(Clone, Copy)]
38enum Val<'v> {
39 Bare(&'v BareItem<'v>),
40 Integer(i64),
41 Decimal(i64),
42 String(&'v str),
43 Token(&'v str),
44 Boolean(bool),
45 Date(i64),
46 Display(&'v str),
47}
48
49/// A canonical Structured Field writer (RFC 9651 section 4.1).
50///
51/// Calls must follow the structure: for an Item, one bare item and its
52/// parameters; for a List, members (bare items or inner lists), each with
53/// parameters; for a Dictionary, a key before each member. A dictionary
54/// member whose value is Boolean true is written as its key alone. Each call
55/// reserves its own room, so a call that fails writes nothing; the earlier
56/// calls' output stays in the sink, and a caller that gets an error discards
57/// the field.
58///
59/// # Examples
60///
61/// ```
62/// use parse_net::{sfv::{BareItem, Writer}, SliceSink};
63///
64/// let mut buf = [0u8; 64];
65/// let mut out = SliceSink::new(&mut buf);
66/// let mut w = Writer::dictionary(&mut out);
67/// w.key("u")?.integer(1)?;
68/// w.key("i")?.boolean(true)?;
69/// w.key("lang")?.open_inner_list()?.token("en")?.token("de")?.close_inner_list()?;
70/// w.key("q")?.decimal(500)?.param("x", &BareItem::Token("a"))?;
71/// w.finish()?;
72/// assert_eq!(out.written(), b"u=1, i, lang=(en de), q=0.5;x=a");
73/// # Ok::<(), parse_net::WriteError>(())
74/// ```
75#[derive(Debug)]
76pub struct Writer<'s, S: Sink> {
77 out: &'s mut S,
78 kind: Kind,
79 state: State,
80 /// A parameter key was written by `param_key`; the next value call is
81 /// its value.
82 pending: bool,
83}
84
85impl<'s, S: Sink> Writer<'s, S> {
86 /// A writer for an Item.
87 ///
88 /// # Examples
89 ///
90 /// ```
91 /// use parse_net::{sfv::Writer, SliceSink};
92 ///
93 /// let mut buf = [0u8; 32];
94 /// let mut out = SliceSink::new(&mut buf);
95 /// let mut w = Writer::item(&mut out);
96 /// w.integer(5)?.param_key("a")?.token("b")?;
97 /// w.finish()?;
98 /// assert_eq!(out.written(), b"5;a=b");
99 /// # Ok::<(), parse_net::WriteError>(())
100 /// ```
101 pub fn item(out: &'s mut S) -> Self {
102 Self {
103 out,
104 kind: Kind::Item,
105 state: State::Empty,
106 pending: false,
107 }
108 }
109
110 /// A writer for a List.
111 ///
112 /// # Examples
113 ///
114 /// ```
115 /// use parse_net::{sfv::Writer, SliceSink};
116 ///
117 /// let mut buf = [0u8; 32];
118 /// let mut out = SliceSink::new(&mut buf);
119 /// let mut w = Writer::list(&mut out);
120 /// w.token("a")?.token("b")?;
121 /// w.finish()?;
122 /// assert_eq!(out.written(), b"a, b");
123 /// # Ok::<(), parse_net::WriteError>(())
124 /// ```
125 pub fn list(out: &'s mut S) -> Self {
126 Self {
127 out,
128 kind: Kind::List,
129 state: State::Empty,
130 pending: false,
131 }
132 }
133
134 /// A writer for a Dictionary.
135 ///
136 /// # Examples
137 ///
138 /// ```
139 /// use parse_net::{sfv::Writer, SliceSink};
140 ///
141 /// let mut buf = [0u8; 32];
142 /// let mut out = SliceSink::new(&mut buf);
143 /// let mut w = Writer::dictionary(&mut out);
144 /// w.key("a")?.integer(1)?;
145 /// w.key("b")?;
146 /// w.finish()?;
147 /// assert_eq!(out.written(), b"a=1, b");
148 /// # Ok::<(), parse_net::WriteError>(())
149 /// ```
150 pub fn dictionary(out: &'s mut S) -> Self {
151 Self {
152 out,
153 kind: Kind::Dictionary,
154 state: State::Empty,
155 pending: false,
156 }
157 }
158
159 /// Writes `prefix`, then `key`, then `=` and `value` when both are given
160 /// (or `value` alone): sized first, so an error writes nothing.
161 fn emit(&mut self, prefix: &[u8], key: Option<&str>, value: Option<Val<'_>>) -> WResult<()> {
162 let mut count = CountSink::new();
163 put(prefix, key, value, &mut count)?;
164 reserve(&*self.out, count.count())?;
165 put(prefix, key, value, &mut *self.out)
166 }
167
168 /// The next member value (a bare item), according to the state.
169 fn value(&mut self, v: Val<'_>) -> WResult<&mut Self> {
170 if self.pending {
171 if !matches!(v, Val::Boolean(true) | Val::Bare(BareItem::Boolean(true))) {
172 self.emit(b"=", None, Some(v))?;
173 }
174 self.pending = false;
175 return Ok(self);
176 }
177 match (self.kind, self.state) {
178 (Kind::Item, State::Empty) => self.emit(b"", None, Some(v))?,
179 (Kind::List, State::Empty) => self.emit(b"", None, Some(v))?,
180 (Kind::List, State::Member) => self.emit(b", ", None, Some(v))?,
181 (Kind::Dictionary, State::Key) => {
182 // A Boolean true value is the key alone.
183 if !matches!(v, Val::Boolean(true) | Val::Bare(BareItem::Boolean(true))) {
184 self.emit(b"=", None, Some(v))?;
185 }
186 }
187 (_, State::Inner) => {
188 self.emit(b"", None, Some(v))?;
189 self.state = State::InnerItem;
190 return Ok(self);
191 }
192 (_, State::InnerItem) => {
193 self.emit(b" ", None, Some(v))?;
194 return Ok(self);
195 }
196 _ => return Err(WriteError::InvalidSequence),
197 }
198 self.state = State::Member;
199 Ok(self)
200 }
201
202 /// Writes a bare item.
203 ///
204 /// # Errors
205 ///
206 /// As the typed methods; [`WriteError::InvalidSequence`] out of order.
207 ///
208 /// # Examples
209 ///
210 /// ```
211 /// use parse_net::{sfv::{BareItem, Writer}, SliceSink};
212 ///
213 /// let mut buf = [0u8; 32];
214 /// let mut out = SliceSink::new(&mut buf);
215 /// let mut w = Writer::list(&mut out);
216 /// w.bare(&BareItem::Token("x"))?.bare(&BareItem::Integer(-2))?;
217 /// w.finish()?;
218 /// assert_eq!(out.written(), b"x, -2");
219 /// # Ok::<(), parse_net::WriteError>(())
220 /// ```
221 pub fn bare(&mut self, b: &BareItem<'_>) -> WResult<&mut Self> {
222 self.value(Val::Bare(b))
223 }
224
225 /// Writes an Integer.
226 ///
227 /// # Errors
228 ///
229 /// [`WriteError::OutOfRange`] beyond 15 digits; [`WriteError::Full`];
230 /// [`WriteError::InvalidSequence`].
231 ///
232 /// # Examples
233 ///
234 /// ```
235 /// use parse_net::{sfv::Writer, SliceSink};
236 ///
237 /// let mut buf = [0u8; 32];
238 /// let mut out = SliceSink::new(&mut buf);
239 /// let mut w = Writer::list(&mut out);
240 /// w.integer(-42)?;
241 /// assert!(w.integer(1_000_000_000_000_000).is_err());
242 /// w.finish()?;
243 /// assert_eq!(out.written(), b"-42");
244 /// # Ok::<(), parse_net::WriteError>(())
245 /// ```
246 pub fn integer(&mut self, v: i64) -> WResult<&mut Self> {
247 self.value(Val::Integer(v))
248 }
249
250 /// Writes a Decimal given in thousandths, with the shortest fractional
251 /// part (`1.5`, and `1.0` for a whole number).
252 ///
253 /// # Errors
254 ///
255 /// [`WriteError::OutOfRange`] beyond 12 integer digits;
256 /// [`WriteError::Full`]; [`WriteError::InvalidSequence`].
257 ///
258 /// # Examples
259 ///
260 /// ```
261 /// use parse_net::{sfv::Writer, SliceSink};
262 ///
263 /// let mut buf = [0u8; 32];
264 /// let mut out = SliceSink::new(&mut buf);
265 /// let mut w = Writer::list(&mut out);
266 /// w.decimal(1500)?.decimal(2000)?.decimal(-125)?;
267 /// w.finish()?;
268 /// assert_eq!(out.written(), b"1.5, 2.0, -0.125");
269 /// # Ok::<(), parse_net::WriteError>(())
270 /// ```
271 pub fn decimal(&mut self, thousandths: i64) -> WResult<&mut Self> {
272 self.value(Val::Decimal(thousandths))
273 }
274
275 /// Writes a String, escaping only `"` and `\`.
276 ///
277 /// # Errors
278 ///
279 /// [`WriteError::Forbidden`] for a byte outside `%x20-7E` (use
280 /// [`display_string`](Self::display_string) for other text);
281 /// [`WriteError::Full`]; [`WriteError::InvalidSequence`].
282 ///
283 /// # Examples
284 ///
285 /// ```
286 /// use parse_net::{sfv::Writer, SliceSink};
287 ///
288 /// let mut buf = [0u8; 32];
289 /// let mut out = SliceSink::new(&mut buf);
290 /// let mut w = Writer::item(&mut out);
291 /// w.string("say \"hi\"")?;
292 /// w.finish()?;
293 /// assert_eq!(out.written(), br#""say \"hi\"""#);
294 /// # Ok::<(), parse_net::WriteError>(())
295 /// ```
296 pub fn string(&mut self, s: &str) -> WResult<&mut Self> {
297 self.value(Val::String(s))
298 }
299
300 /// Writes a Token.
301 ///
302 /// # Errors
303 ///
304 /// [`WriteError::NotToken`]; [`WriteError::Full`];
305 /// [`WriteError::InvalidSequence`].
306 ///
307 /// # Examples
308 ///
309 /// ```
310 /// use parse_net::{sfv::Writer, SliceSink};
311 ///
312 /// let mut buf = [0u8; 32];
313 /// let mut out = SliceSink::new(&mut buf);
314 /// let mut w = Writer::list(&mut out);
315 /// w.token("gzip")?;
316 /// assert!(w.token("a b").is_err());
317 /// w.finish()?;
318 /// assert_eq!(out.written(), b"gzip");
319 /// # Ok::<(), parse_net::WriteError>(())
320 /// ```
321 pub fn token(&mut self, t: &str) -> WResult<&mut Self> {
322 self.value(Val::Token(t))
323 }
324
325 /// Writes a Boolean (`?1` or `?0`).
326 ///
327 /// # Errors
328 ///
329 /// [`WriteError::Full`]; [`WriteError::InvalidSequence`].
330 ///
331 /// # Examples
332 ///
333 /// ```
334 /// use parse_net::{sfv::Writer, SliceSink};
335 ///
336 /// let mut buf = [0u8; 32];
337 /// let mut out = SliceSink::new(&mut buf);
338 /// let mut w = Writer::list(&mut out);
339 /// w.boolean(true)?.boolean(false)?;
340 /// w.finish()?;
341 /// assert_eq!(out.written(), b"?1, ?0");
342 /// # Ok::<(), parse_net::WriteError>(())
343 /// ```
344 pub fn boolean(&mut self, b: bool) -> WResult<&mut Self> {
345 self.value(Val::Boolean(b))
346 }
347
348 /// Writes a Date: `@` and the seconds since the Unix epoch.
349 ///
350 /// # Errors
351 ///
352 /// [`WriteError::OutOfRange`] outside years 1 to 9999 (D-030);
353 /// [`WriteError::Full`]; [`WriteError::InvalidSequence`].
354 ///
355 /// # Examples
356 ///
357 /// ```
358 /// use parse_net::{sfv::Writer, SliceSink};
359 ///
360 /// let mut buf = [0u8; 32];
361 /// let mut out = SliceSink::new(&mut buf);
362 /// let mut w = Writer::item(&mut out);
363 /// w.date(1_659_578_233)?;
364 /// w.finish()?;
365 /// assert_eq!(out.written(), b"@1659578233");
366 /// # Ok::<(), parse_net::WriteError>(())
367 /// ```
368 pub fn date(&mut self, secs: i64) -> WResult<&mut Self> {
369 self.value(Val::Date(secs))
370 }
371
372 /// Writes a Display String: `%"`, the text with `%`, `"`, and every byte
373 /// outside `%x20-7E` as lowercase `%xx`, then `"`.
374 ///
375 /// # Errors
376 ///
377 /// [`WriteError::Full`]; [`WriteError::InvalidSequence`].
378 ///
379 /// # Examples
380 ///
381 /// ```
382 /// use parse_net::{sfv::Writer, SliceSink};
383 ///
384 /// let mut buf = [0u8; 32];
385 /// let mut out = SliceSink::new(&mut buf);
386 /// let mut w = Writer::item(&mut out);
387 /// w.display_string("caf\u{e9}")?;
388 /// w.finish()?;
389 /// assert_eq!(out.written(), b"%\"caf%c3%a9\"");
390 /// # Ok::<(), parse_net::WriteError>(())
391 /// ```
392 pub fn display_string(&mut self, s: &str) -> WResult<&mut Self> {
393 self.value(Val::Display(s))
394 }
395
396 /// Writes a parameter of the last item or inner list. A Boolean true
397 /// value is written as the key alone.
398 ///
399 /// # Errors
400 ///
401 /// [`WriteError::NotToken`] for a key that is not
402 /// `( lcalpha / "*" ) *( lcalpha / DIGIT / "_" / "-" / "." / "*" )`;
403 /// the value's errors; [`WriteError::InvalidSequence`] before any item;
404 /// [`WriteError::Full`].
405 ///
406 /// # Examples
407 ///
408 /// ```
409 /// use parse_net::{sfv::{BareItem, Writer}, SliceSink};
410 ///
411 /// let mut buf = [0u8; 32];
412 /// let mut out = SliceSink::new(&mut buf);
413 /// let mut w = Writer::item(&mut out);
414 /// w.token("a")?.param("q", &BareItem::Integer(1))?.param("x", &BareItem::Boolean(true))?;
415 /// w.finish()?;
416 /// assert_eq!(out.written(), b"a;q=1;x");
417 /// # Ok::<(), parse_net::WriteError>(())
418 /// ```
419 pub fn param(&mut self, key: &str, v: &BareItem<'_>) -> WResult<&mut Self> {
420 if self.pending || !matches!(self.state, State::Member | State::Key | State::InnerItem) {
421 return Err(WriteError::InvalidSequence);
422 }
423 let value = (!matches!(v, BareItem::Boolean(true))).then_some(Val::Bare(v));
424 self.emit(b";", Some(key), value)?;
425 if self.state == State::Key {
426 self.state = State::Member;
427 }
428 Ok(self)
429 }
430
431 /// Writes the key of a parameter of the last item or inner list; the next
432 /// value call ([`string`](Self::string), [`integer`](Self::integer), and
433 /// the others) writes its value, and a Boolean true writes nothing more.
434 /// For values that are not already a parsed [`BareItem`].
435 ///
436 /// # Errors
437 ///
438 /// As [`param`](Self::param).
439 ///
440 /// # Examples
441 ///
442 /// ```
443 /// use parse_net::{sfv::Writer, SliceSink};
444 ///
445 /// let mut buf = [0u8; 32];
446 /// let mut out = SliceSink::new(&mut buf);
447 /// let mut w = Writer::item(&mut out);
448 /// w.token("text")?.param_key("lang")?.string("en \"GB\"")?.param_key("x")?.boolean(true)?;
449 /// w.finish()?;
450 /// assert_eq!(out.written(), br#"text;lang="en \"GB\"";x"#);
451 /// # Ok::<(), parse_net::WriteError>(())
452 /// ```
453 pub fn param_key(&mut self, key: &str) -> WResult<&mut Self> {
454 if self.pending || !matches!(self.state, State::Member | State::Key | State::InnerItem) {
455 return Err(WriteError::InvalidSequence);
456 }
457 self.emit(b";", Some(key), None)?;
458 if self.state == State::Key {
459 self.state = State::Member;
460 }
461 self.pending = true;
462 Ok(self)
463 }
464
465 /// Writes a dictionary key; its value follows (none means Boolean true).
466 ///
467 /// # Errors
468 ///
469 /// [`WriteError::NotToken`] for an invalid key;
470 /// [`WriteError::InvalidSequence`] outside a dictionary or inside an
471 /// inner list; [`WriteError::Full`].
472 ///
473 /// # Examples
474 ///
475 /// ```
476 /// use parse_net::{sfv::Writer, SliceSink};
477 ///
478 /// let mut buf = [0u8; 32];
479 /// let mut out = SliceSink::new(&mut buf);
480 /// let mut w = Writer::dictionary(&mut out);
481 /// w.key("a")?;
482 /// w.key("b")?.integer(2)?;
483 /// w.finish()?;
484 /// assert_eq!(out.written(), b"a, b=2");
485 /// # Ok::<(), parse_net::WriteError>(())
486 /// ```
487 pub fn key(&mut self, key: &str) -> WResult<&mut Self> {
488 if self.pending {
489 return Err(WriteError::InvalidSequence);
490 }
491 let prefix: &[u8] = match (self.kind, self.state) {
492 (Kind::Dictionary, State::Empty) => b"",
493 (Kind::Dictionary, State::Member | State::Key) => b", ",
494 _ => return Err(WriteError::InvalidSequence),
495 };
496 self.emit(prefix, Some(key), None)?;
497 self.state = State::Key;
498 Ok(self)
499 }
500
501 /// Opens an inner list as the next list member or dictionary value.
502 ///
503 /// # Errors
504 ///
505 /// [`WriteError::InvalidSequence`] in an Item writer, inside an inner
506 /// list, or in a dictionary before a key; [`WriteError::Full`].
507 ///
508 /// # Examples
509 ///
510 /// ```
511 /// use parse_net::{sfv::Writer, SliceSink};
512 ///
513 /// let mut buf = [0u8; 32];
514 /// let mut out = SliceSink::new(&mut buf);
515 /// let mut w = Writer::list(&mut out);
516 /// w.open_inner_list()?.token("a")?.token("b")?.close_inner_list()?;
517 /// w.token("c")?;
518 /// w.finish()?;
519 /// assert_eq!(out.written(), b"(a b), c");
520 /// # Ok::<(), parse_net::WriteError>(())
521 /// ```
522 pub fn open_inner_list(&mut self) -> WResult<&mut Self> {
523 if self.pending {
524 return Err(WriteError::InvalidSequence);
525 }
526 let prefix: &[u8] = match (self.kind, self.state) {
527 (Kind::List, State::Empty) => b"(",
528 (Kind::List, State::Member) => b", (",
529 (Kind::Dictionary, State::Key) => b"=(",
530 _ => return Err(WriteError::InvalidSequence),
531 };
532 self.emit(prefix, None, None)?;
533 self.state = State::Inner;
534 Ok(self)
535 }
536
537 /// Closes the open inner list; its parameters may follow.
538 ///
539 /// # Errors
540 ///
541 /// [`WriteError::InvalidSequence`] with no inner list open;
542 /// [`WriteError::Full`].
543 ///
544 /// # Examples
545 ///
546 /// ```
547 /// use parse_net::{sfv::{BareItem, Writer}, SliceSink};
548 ///
549 /// let mut buf = [0u8; 32];
550 /// let mut out = SliceSink::new(&mut buf);
551 /// let mut w = Writer::list(&mut out);
552 /// w.open_inner_list()?.integer(1)?.close_inner_list()?.param("q", &BareItem::Integer(9))?;
553 /// w.finish()?;
554 /// assert_eq!(out.written(), b"(1);q=9");
555 /// # Ok::<(), parse_net::WriteError>(())
556 /// ```
557 pub fn close_inner_list(&mut self) -> WResult<&mut Self> {
558 if self.pending || !matches!(self.state, State::Inner | State::InnerItem) {
559 return Err(WriteError::InvalidSequence);
560 }
561 self.emit(b")", None, None)?;
562 self.state = State::Member;
563 Ok(self)
564 }
565
566 /// Ends the field.
567 ///
568 /// # Errors
569 ///
570 /// [`WriteError::InvalidSequence`] with an inner list open, a
571 /// parameter key without its value, or with nothing written (an empty List or Dictionary is not serialized at
572 /// all: omit the field, RFC 9651 section 4.1).
573 ///
574 /// # Examples
575 ///
576 /// ```
577 /// use parse_net::{sfv::Writer, SliceSink};
578 ///
579 /// let mut buf = [0u8; 8];
580 /// let mut out = SliceSink::new(&mut buf);
581 /// assert!(Writer::list(&mut out).finish().is_err());
582 /// let mut w = Writer::list(&mut out);
583 /// w.token("a")?;
584 /// w.finish()?;
585 /// assert_eq!(out.written(), b"a");
586 /// # Ok::<(), parse_net::WriteError>(())
587 /// ```
588 pub fn finish(self) -> WResult<()> {
589 if self.pending {
590 return Err(WriteError::InvalidSequence);
591 }
592 match self.state {
593 State::Member | State::Key => Ok(()),
594 State::Empty | State::Inner | State::InnerItem => Err(WriteError::InvalidSequence),
595 }
596 }
597}
598
599/// One byte of a Display String in canonical form: `%`, `"`, and every byte
600/// outside `%x20-7E` as lowercase `%xx`.
601fn put_display_byte(b: u8, out: &mut impl Sink) -> WResult<()> {
602 if b == b'%' || b == b'"' || !(0x20..=0x7e).contains(&b) {
603 const HEX: &[u8; 16] = b"0123456789abcdef";
604 let hi = HEX.get(usize::from(b >> 4)).copied().unwrap_or(b'0');
605 let lo = HEX.get(usize::from(b & 0xf)).copied().unwrap_or(b'0');
606 out.put(&[b'%', hi, lo])?;
607 } else {
608 out.put(&[b])?;
609 }
610 Ok(())
611}
612
613/// The value of a base64 symbol (validated at parse).
614const fn b64_value(c: u8) -> u8 {
615 match c {
616 b'A'..=b'Z' => c - b'A',
617 b'a'..=b'z' => c - b'a' + 26,
618 b'0'..=b'9' => c - b'0' + 52,
619 b'+' => 62,
620 _ => 63,
621 }
622}
623
624/// The base64 symbol for a 6-bit value.
625const fn b64_symbol(v: u8) -> u8 {
626 match v {
627 0..=25 => b'A' + v,
628 26..=51 => b'a' + (v - 26),
629 52..=61 => b'0' + (v - 52),
630 62 => b'+',
631 _ => b'/',
632 }
633}
634
635fn is_key(k: &[u8]) -> bool {
636 matches!(k.first(), Some(&b) if class::is_sf_key_start(b))
637 && k.iter().skip(1).all(|&b| class::is_sf_key_char(b))
638}
639
640fn is_sf_token(t: &[u8]) -> bool {
641 matches!(t.first(), Some(&b) if b == b'*' || b.is_ascii_alphabetic())
642 && t.iter().skip(1).all(|&b| class::is_sf_token_char(b))
643}
644
645fn put(
646 prefix: &[u8],
647 key: Option<&str>,
648 value: Option<Val<'_>>,
649 out: &mut impl Sink,
650) -> WResult<()> {
651 out.put(prefix)?;
652 if let Some(k) = key {
653 if !is_key(k.as_bytes()) {
654 return Err(WriteError::NotToken);
655 }
656 out.put(k.as_bytes())?;
657 if value.is_some() {
658 out.put(b"=")?;
659 }
660 }
661 match value {
662 None => Ok(()),
663 Some(v) => put_value(v, out),
664 }
665}
666
667fn put_integer(v: i64, out: &mut impl Sink) -> WResult<()> {
668 if !(-INTEGER_MAX..=INTEGER_MAX).contains(&v) {
669 return Err(WriteError::OutOfRange);
670 }
671 if v < 0 {
672 out.put(b"-")?;
673 }
674 write_decimal_u64(v.unsigned_abs(), out)
675}
676
677fn put_value(v: Val<'_>, out: &mut impl Sink) -> WResult<()> {
678 match v {
679 Val::Integer(i) => put_integer(i, out),
680 Val::Decimal(t) => {
681 if !(-INTEGER_MAX..=INTEGER_MAX).contains(&t) {
682 return Err(WriteError::OutOfRange);
683 }
684 if t < 0 {
685 out.put(b"-")?;
686 }
687 let a = t.unsigned_abs();
688 write_decimal_u64(a / 1000, out)?;
689 out.put(b".")?;
690 let frac = a % 1000;
691 let digits = [
692 b'0' + (frac / 100) as u8,
693 b'0' + (frac / 10 % 10) as u8,
694 b'0' + (frac % 10) as u8,
695 ];
696 let keep = if frac % 100 == 0 {
697 1
698 } else if frac % 10 == 0 {
699 2
700 } else {
701 3
702 };
703 out.put(digits.get(..keep).unwrap_or(&[]))?;
704 Ok(())
705 }
706 Val::String(s) => {
707 if let Some(at) = s.bytes().position(|b| !(0x20..=0x7e).contains(&b)) {
708 return Err(WriteError::forbidden(at));
709 }
710 out.put(b"\"")?;
711 for chunk in s.as_bytes().split_inclusive(|&b| b == b'"' || b == b'\\') {
712 match chunk.split_last() {
713 Some((&last, head)) if last == b'"' || last == b'\\' => {
714 out.put(head)?;
715 out.put(b"\\")?;
716 out.put(&[last])?;
717 }
718 _ => out.put(chunk)?,
719 }
720 }
721 out.put(b"\"")?;
722 Ok(())
723 }
724 Val::Token(t) => {
725 if !is_sf_token(t.as_bytes()) {
726 return Err(WriteError::NotToken);
727 }
728 out.put(t.as_bytes())?;
729 Ok(())
730 }
731 Val::Boolean(b) => {
732 out.put(if b { b"?1" } else { b"?0" })?;
733 Ok(())
734 }
735 Val::Date(d) => {
736 if d < crate::HttpDate::MIN.unix() || d > crate::HttpDate::MAX.unix() {
737 return Err(WriteError::OutOfRange);
738 }
739 out.put(b"@")?;
740 put_integer(d, out)
741 }
742 Val::Display(s) => {
743 out.put(b"%\"")?;
744 for &b in s.as_bytes() {
745 put_display_byte(b, out)?;
746 }
747 out.put(b"\"")?;
748 Ok(())
749 }
750 Val::Bare(b) => match *b {
751 BareItem::Integer(i) => put_integer(i, out),
752 BareItem::Decimal(d) => put_value(Val::Decimal(d.thousandths()), out),
753 BareItem::String(s) => {
754 // Validated at parse: only `\"` and `\\` escapes.
755 out.put(b"\"")?;
756 out.put(s.raw().as_bytes())?;
757 out.put(b"\"")?;
758 Ok(())
759 }
760 BareItem::Token(t) => put_value(Val::Token(t), out),
761 BareItem::ByteSeq(s) => {
762 let body: &[u8] = s.raw().split(|&b| b == b'=').next().unwrap_or(&[]);
763 out.put(b":")?;
764 // Canonical base64 has zero pad bits: the last symbol of a
765 // partial quantum keeps only the bits that carry data.
766 let (pad, mask): (&[u8], u8) = match body.len() % 4 {
767 2 => (b"==", 0x30),
768 3 => (b"=", 0x3c),
769 _ => (b"", 0x3f),
770 };
771 if let Some((&last, head)) = body.split_last() {
772 out.put(head)?;
773 out.put(&[b64_symbol(b64_value(last) & mask)])?;
774 }
775 out.put(pad)?;
776 out.put(b":")?;
777 Ok(())
778 }
779 BareItem::Boolean(v) => put_value(Val::Boolean(v), out),
780 BareItem::Date(d) => put_value(Val::Date(d), out),
781 BareItem::DisplayString(s) => {
782 // Validated at parse: lowercase `%xx` and printable ASCII.
783 // Each byte is decoded and written in canonical form, so an
784 // over-encoded `%61` comes out as `a`.
785 out.put(b"%\"")?;
786 let raw = s.raw();
787 let mut i = 0usize;
788 while let Some(&b) = raw.get(i) {
789 let byte = if b == b'%' {
790 let hi = raw
791 .get(i + 1)
792 .copied()
793 .and_then(class::hex_value)
794 .unwrap_or(0);
795 let lo = raw
796 .get(i + 2)
797 .copied()
798 .and_then(class::hex_value)
799 .unwrap_or(0);
800 i += 3;
801 (hi << 4) | lo
802 } else {
803 i += 1;
804 b
805 };
806 put_display_byte(byte, out)?;
807 }
808 out.put(b"\"")?;
809 Ok(())
810 }
811 },
812 }
813}
814
815#[cfg(test)]
816// The builder methods return `&mut Self` for chaining; a test that does
817// not chain drops it.
818#[allow(unused_results)]
819mod tests {
820 use super::*;
821 use crate::{SliceSink, sfv};
822
823 fn item(f: impl FnOnce(&mut Writer<'_, SliceSink<'_>>) -> WResult<()>) -> std::string::String {
824 let mut buf = [0u8; 256];
825 let mut out = SliceSink::new(&mut buf);
826 let mut w = Writer::item(&mut out);
827 f(&mut w).unwrap();
828 w.finish().unwrap();
829 std::string::String::from_utf8(out.written().to_vec()).unwrap()
830 }
831
832 #[test]
833 fn test_writer_bare_items_canonical() {
834 assert_eq!(
835 item(|w| w.integer(-999_999_999_999_999).map(|_| ())),
836 "-999999999999999"
837 );
838 assert_eq!(item(|w| w.decimal(1500).map(|_| ())), "1.5");
839 assert_eq!(item(|w| w.decimal(-1000).map(|_| ())), "-1.0");
840 assert_eq!(item(|w| w.decimal(-5).map(|_| ())), "-0.005");
841 assert_eq!(item(|w| w.decimal(1230).map(|_| ())), "1.23");
842 assert_eq!(item(|w| w.string("a\"b\\c").map(|_| ())), "\"a\\\"b\\\\c\"");
843 assert_eq!(item(|w| w.token("*foo/bar:1").map(|_| ())), "*foo/bar:1");
844 assert_eq!(item(|w| w.boolean(false).map(|_| ())), "?0");
845 assert_eq!(item(|w| w.date(0).map(|_| ())), "@0");
846 assert_eq!(
847 item(|w| w.display_string("f\u{fc}%\"").map(|_| ())),
848 "%\"f%c3%bc%25%22\""
849 );
850 let parsed = sfv::parse_item(b":YQ:").unwrap();
851 assert_eq!(item(|w| w.bare(&parsed.bare).map(|_| ())), ":YQ==:");
852 // Non-zero pad bits are accepted on parse and cleared on write.
853 let parsed = sfv::parse_item(b":iZ==:").unwrap();
854 assert_eq!(item(|w| w.bare(&parsed.bare).map(|_| ())), ":iQ==:");
855 let parsed = sfv::parse_item(b":YWJ=:").unwrap();
856 assert_eq!(item(|w| w.bare(&parsed.bare).map(|_| ())), ":YWI=:");
857 let parsed = sfv::parse_item(b"::").unwrap();
858 assert_eq!(item(|w| w.bare(&parsed.bare).map(|_| ())), "::");
859 }
860
861 #[test]
862 fn test_writer_refusals_write_nothing() {
863 let mut buf = [0u8; 64];
864 let mut out = SliceSink::new(&mut buf);
865 let mut w = Writer::item(&mut out);
866 assert_eq!(
867 w.integer(1_000_000_000_000_000).err(),
868 Some(WriteError::OutOfRange)
869 );
870 assert_eq!(
871 w.decimal(1_000_000_000_000_000).err(),
872 Some(WriteError::OutOfRange)
873 );
874 assert_eq!(w.date(253_402_300_800).err(), Some(WriteError::OutOfRange));
875 assert_eq!(
876 w.string("caf\u{e9}").err(),
877 Some(WriteError::Forbidden { at: 3 })
878 );
879 assert_eq!(
880 w.string("a\nb").err(),
881 Some(WriteError::Forbidden { at: 1 })
882 );
883 assert_eq!(w.token("1a").err(), Some(WriteError::NotToken));
884 assert_eq!(w.token("").err(), Some(WriteError::NotToken));
885 assert_eq!(
886 w.param("a", &BareItem::Integer(1)).err(),
887 Some(WriteError::InvalidSequence)
888 );
889 assert_eq!(w.key("a").err(), Some(WriteError::InvalidSequence));
890 assert_eq!(w.open_inner_list().err(), Some(WriteError::InvalidSequence));
891 w.integer(1).unwrap();
892 assert_eq!(w.integer(2).err(), Some(WriteError::InvalidSequence));
893 assert_eq!(
894 w.param("A", &BareItem::Integer(1)).err(),
895 Some(WriteError::NotToken)
896 );
897 assert_eq!(
898 w.param("1a", &BareItem::Integer(1)).err(),
899 Some(WriteError::NotToken)
900 );
901 w.finish().unwrap();
902 assert_eq!(out.written(), b"1");
903 }
904
905 #[test]
906 fn test_writer_list_and_inner_lists() {
907 let mut buf = [0u8; 128];
908 let mut out = SliceSink::new(&mut buf);
909 let mut w = Writer::list(&mut out);
910 w.token("a")
911 .unwrap()
912 .param("x", &BareItem::Boolean(true))
913 .unwrap();
914 w.open_inner_list().unwrap();
915 assert_eq!(
916 w.param("p", &BareItem::Integer(1)).err(),
917 Some(WriteError::InvalidSequence)
918 );
919 w.integer(1)
920 .unwrap()
921 .param("q", &BareItem::Boolean(false))
922 .unwrap()
923 .integer(2)
924 .unwrap();
925 w.close_inner_list()
926 .unwrap()
927 .param("lvl", &BareItem::Integer(5))
928 .unwrap();
929 w.open_inner_list().unwrap().close_inner_list().unwrap();
930 w.finish().unwrap();
931 assert_eq!(out.written(), b"a;x, (1;q=?0 2);lvl=5, ()");
932 assert!(sfv::parse_list(out.written()).is_ok());
933 }
934
935 #[test]
936 fn test_writer_param_key_then_value() {
937 let mut buf = [0u8; 64];
938 let mut out = SliceSink::new(&mut buf);
939 let mut w = Writer::list(&mut out);
940 w.token("a")
941 .unwrap()
942 .param_key("s")
943 .unwrap()
944 .string("x y")
945 .unwrap();
946 assert_eq!(
947 w.param_key("t").unwrap().key("k").err(),
948 Some(WriteError::InvalidSequence)
949 );
950 assert_eq!(
951 w.param("u", &BareItem::Integer(1)).err(),
952 Some(WriteError::InvalidSequence)
953 );
954 w.decimal(250).unwrap();
955 w.open_inner_list()
956 .unwrap()
957 .integer(1)
958 .unwrap()
959 .param_key("d")
960 .unwrap()
961 .display_string("\u{e9}")
962 .unwrap();
963 w.close_inner_list()
964 .unwrap()
965 .param_key("b")
966 .unwrap()
967 .boolean(true)
968 .unwrap();
969 w.finish().unwrap();
970 assert_eq!(out.written(), br#"a;s="x y";t=0.25, (1;d=%"%c3%a9");b"#);
971 let mut buf = [0u8; 16];
972 let mut out = SliceSink::new(&mut buf);
973 let mut w = Writer::item(&mut out);
974 w.integer(1).unwrap().param_key("k").unwrap();
975 assert_eq!(w.finish(), Err(WriteError::InvalidSequence));
976 }
977
978 #[test]
979 fn test_writer_finish_refusals() {
980 let mut buf = [0u8; 16];
981 let mut out = SliceSink::new(&mut buf);
982 assert_eq!(
983 Writer::list(&mut out).finish(),
984 Err(WriteError::InvalidSequence)
985 );
986 assert_eq!(
987 Writer::dictionary(&mut out).finish(),
988 Err(WriteError::InvalidSequence)
989 );
990 assert_eq!(
991 Writer::item(&mut out).finish(),
992 Err(WriteError::InvalidSequence)
993 );
994 let mut w = Writer::list(&mut out);
995 w.open_inner_list().unwrap();
996 assert_eq!(w.finish(), Err(WriteError::InvalidSequence));
997 let mut w = Writer::dictionary(&mut out);
998 assert_eq!(w.integer(1).err(), Some(WriteError::InvalidSequence));
999 assert_eq!(w.open_inner_list().err(), Some(WriteError::InvalidSequence));
1000 }
1001
1002 #[test]
1003 fn test_writer_dictionary_true_is_key_alone() {
1004 let mut buf = [0u8; 128];
1005 let mut out = SliceSink::new(&mut buf);
1006 let mut w = Writer::dictionary(&mut out);
1007 w.key("a").unwrap().boolean(true).unwrap();
1008 w.key("b").unwrap();
1009 w.key("c")
1010 .unwrap()
1011 .param("p", &BareItem::Integer(1))
1012 .unwrap();
1013 w.key("d").unwrap().boolean(false).unwrap();
1014 w.finish().unwrap();
1015 assert_eq!(out.written(), b"a, b, c;p=1, d=?0");
1016 assert!(sfv::parse_dictionary(out.written()).is_ok());
1017 }
1018
1019 #[test]
1020 fn test_writer_full_writes_nothing_for_the_call() {
1021 let mut buf = [0u8; 4];
1022 let mut out = SliceSink::new(&mut buf);
1023 let mut w = Writer::item(&mut out);
1024 assert_eq!(w.string("abcd").err(), Some(WriteError::Full));
1025 w.token("ab").unwrap();
1026 assert_eq!(
1027 w.param("cd", &BareItem::Integer(1)).err(),
1028 Some(WriteError::Full)
1029 );
1030 w.finish().unwrap();
1031 assert_eq!(out.written(), b"ab");
1032 }
1033}