toon/encode/
primitives.rs1use std::fmt::Write;
2
3use crate::JsonPrimitive;
4use crate::StringOrNumberOrBoolOrNull;
5use crate::shared::constants::{DEFAULT_DELIMITER, DOUBLE_QUOTE};
6use crate::shared::string_utils::escape_string;
7use crate::shared::validation::{is_safe_unquoted, is_valid_unquoted_key};
8
9#[must_use]
10pub fn encode_primitive(value: &JsonPrimitive, delimiter: char) -> String {
11 match value {
12 StringOrNumberOrBoolOrNull::Null => "null".to_string(),
13 StringOrNumberOrBoolOrNull::Bool(value) => value.to_string(),
14 StringOrNumberOrBoolOrNull::Number(value) => format_number(*value),
15 StringOrNumberOrBoolOrNull::String(value) => encode_string_literal(value, delimiter),
16 }
17}
18
19#[must_use]
20pub fn encode_string_literal(value: &str, delimiter: char) -> String {
21 if is_safe_unquoted(value, delimiter) {
22 return value.to_string();
23 }
24 format!("{DOUBLE_QUOTE}{}{DOUBLE_QUOTE}", escape_string(value))
25}
26
27#[must_use]
28pub fn encode_key(key: &str) -> String {
29 if is_valid_unquoted_key(key) {
30 return key.to_string();
31 }
32 format!("{DOUBLE_QUOTE}{}{DOUBLE_QUOTE}", escape_string(key))
33}
34
35#[must_use]
36pub fn encode_and_join_primitives(values: &[JsonPrimitive], delimiter: char) -> String {
37 if values.is_empty() {
38 return String::new();
39 }
40 let mut out = String::with_capacity(values.len() * 11);
42 for (idx, value) in values.iter().enumerate() {
43 if idx > 0 {
44 out.push(delimiter);
45 }
46 out.push_str(&encode_primitive(value, delimiter));
47 }
48 out
49}
50
51#[must_use]
52pub fn format_header(
53 length: usize,
54 key: Option<&str>,
55 fields: Option<&[String]>,
56 delimiter: char,
57) -> String {
58 let mut header = String::new();
59
60 if let Some(key) = key {
61 header.push_str(&encode_key(key));
62 }
63
64 if delimiter == DEFAULT_DELIMITER {
65 let _ = write!(header, "[{length}]");
66 } else {
67 let _ = write!(header, "[{length}{delimiter}]");
68 }
69
70 if let Some(fields) = fields {
71 header.push('{');
72 for (idx, field) in fields.iter().enumerate() {
73 if idx > 0 {
74 header.push(delimiter);
75 }
76 header.push_str(&encode_key(field));
77 }
78 header.push('}');
79 }
80
81 header.push(':');
82 header
83}
84
85fn format_number(value: f64) -> String {
86 if value == 0.0 {
87 return "0".to_string();
88 }
89 if value.is_nan() || !value.is_finite() {
90 return "null".to_string();
91 }
92 value.to_string()
93}