Skip to main content

toon/encode/
primitives.rs

1use 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    // Estimate: average 10 chars per primitive + delimiter
41    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}