1use std::fmt;
2
3use crate::error::{Error, Result};
4use crate::number::Number;
5use crate::options::RenderOptions;
6use crate::parse::{MultilineLocalEol, ParseOptions};
7use crate::render::Renderer;
8use crate::util::{
9 is_allowed_bare_string, is_forbidden_literal_tjson_char, is_pipe_like,
10 is_reserved_word, parse_bare_key_prefix,
11};
12
13#[derive(Clone, Copy)]
16pub(crate) struct StrMeta<'a> {
17 pub(crate) s: &'a str,
18 pub(crate) has_eol: bool,
20 pub(crate) eol_type: Option<MultilineLocalEol>,
22 pub(crate) has_forbidden_literal: bool,
24 pub(crate) is_bare_eligible: bool,
26 pub(crate) is_reserved_word: bool,
28 pub(crate) has_pipe_like: bool,
30 }
33
34impl<'a> StrMeta<'a> {
35 pub(crate) fn new(s: &'a str) -> Self {
36 let has_eol = s.as_bytes().contains(&b'\n');
37 let eol_type = if has_eol { detect_multiline_local_eol(s) } else { Some(MultilineLocalEol::default()) };
38 let has_forbidden_literal = s.chars().any(is_forbidden_literal_tjson_char);
39 let is_bare_eligible = is_allowed_bare_string(s);
40 let is_reserved_word = is_reserved_word(s);
41 let has_pipe_like = s.chars().any(is_pipe_like);
42 StrMeta { s, has_eol, eol_type, has_forbidden_literal, is_bare_eligible, is_reserved_word, has_pipe_like}
44 }
45}
46
47#[allow(dead_code)]
49pub(crate) struct BareString<'a>(StrMeta<'a>);
50
51#[allow(dead_code)]
52impl<'a> BareString<'a> {
53 pub(crate) fn new(s: &'a str) -> Option<Self> {
54 let meta = StrMeta::new(s);
55 if meta.is_bare_eligible { Some(BareString(meta)) } else { None }
56 }
57
58 pub(crate) fn meta(&self) -> &StrMeta<'a> { &self.0 }
59}
60
61impl<'a> std::ops::Deref for BareString<'a> {
62 type Target = str;
63 fn deref(&self) -> &str { self.0.s }
64}
65
66#[allow(dead_code)]
71pub(crate) struct TableBareString<'a>(BareString<'a>);
72
73#[allow(dead_code)]
74impl<'a> TableBareString<'a> {
75 pub(crate) fn new(s: &'a str) -> Option<Self> {
76 let meta = StrMeta::new(s);
77 if meta.is_bare_eligible && !meta.is_reserved_word && !meta.has_pipe_like {
78 Some(TableBareString(BareString(meta)))
79 } else {
80 None
81 }
82 }
83}
84
85impl<'a> std::ops::Deref for TableBareString<'a> {
86 type Target = BareString<'a>;
87 fn deref(&self) -> &BareString<'a> { &self.0 }
88}
89
90#[allow(dead_code)]
93pub(crate) struct MultilineString<'a>(StrMeta<'a>);
94
95#[allow(dead_code)]
96impl<'a> MultilineString<'a> {
97 pub(crate) fn new(s: &'a str) -> Option<Self> {
98 let meta = StrMeta::new(s);
99 if meta.has_eol && meta.eol_type.is_some() && !meta.has_forbidden_literal {
100 Some(MultilineString(meta))
101 } else {
102 None
103 }
104 }
105
106 pub(crate) fn eol(&self) -> MultilineLocalEol { self.0.eol_type.unwrap() }
107}
108
109impl<'a> std::ops::Deref for MultilineString<'a> {
110 type Target = str;
111 fn deref(&self) -> &str { self.0.s }
112}
113
114#[allow(dead_code)]
116pub(crate) struct BareKey<'a>(&'a str);
117
118#[allow(dead_code)]
119impl<'a> BareKey<'a> {
120 pub(crate) fn new(s: &'a str) -> Option<Self> {
121 if parse_bare_key_prefix(s).is_some_and(|end| end == s.len()) {
122 Some(BareKey(s))
123 } else {
124 None
125 }
126 }
127}
128
129impl<'a> std::ops::Deref for BareKey<'a> {
130 type Target = str;
131 fn deref(&self) -> &str { self.0 }
132}
133
134#[derive(Clone, Debug, PartialEq, Eq)]
140pub struct Entry {
141 pub key: String,
142 pub value: Value,
143}
144
145#[derive(Clone, Debug, PartialEq, Eq)]
151pub enum Value {
152 Null,
154 Bool(bool),
156 Number(Number),
158 String(String),
160 Array(Vec<Value>),
162 Object(Vec<Entry>),
164}
165
166#[cfg(feature = "serde_json")]
167impl From<serde_json::Value> for Value {
168 fn from(value: serde_json::Value) -> Self {
169 Self::from_serde_json(value)
170 }
171}
172
173#[cfg(feature = "serde_json")]
174impl From<Value> for serde_json::Value {
175 fn from(value: Value) -> Self {
176 value.to_serde_json()
177 }
178}
179
180impl Value {
181 pub(crate) fn from_serde_json(value: serde_json::Value) -> Self {
184 match value {
185 serde_json::Value::Null => Self::Null,
186 serde_json::Value::Bool(v) => Self::Bool(v),
187 serde_json::Value::Number(n) => Self::Number(Number(n.to_string())),
188 serde_json::Value::String(s) => Self::String(s),
189 serde_json::Value::Array(values) => {
190 Self::Array(values.into_iter().map(Self::from_serde_json).collect())
191 }
192 serde_json::Value::Object(map) => Self::Object(
193 map.into_iter()
194 .map(|(key, value)| Entry { key, value: Self::from_serde_json(value) })
195 .collect(),
196 ),
197 }
198 }
199
200 pub(crate) fn to_serde_json(&self) -> serde_json::Value {
201 match self {
202 Self::Null => serde_json::Value::Null,
203 Self::Bool(v) => serde_json::Value::Bool(*v),
204 Self::Number(n) => serde_json::Value::Number(n.to_serde_json_number()),
205 Self::String(s) => serde_json::Value::String(s.clone()),
206 Self::Array(values) => {
207 serde_json::Value::Array(values.iter().map(Value::to_serde_json).collect())
208 }
209 Self::Object(entries) => {
210 let mut map = serde_json::Map::new();
211 for Entry { key, value } in entries {
212 map.insert(key.clone(), value.to_serde_json());
213 }
214 serde_json::Value::Object(map)
215 }
216 }
217 }
218
219 pub(crate) fn parse_with(input: &str, options: ParseOptions) -> Result<Self> {
220 crate::parse::Parser::parse_document(input, options.start_indent).map_err(Error::Parse)
221 }
222
223 pub fn to_tjson_with(&self, options: RenderOptions) -> String {
233 Renderer::render(self, &options)
234 }
235
236 pub fn to_json(&self) -> String {
245 let mut out = String::new();
246 write_json(self, &mut out);
247 out
248 }
249}
250
251fn write_json(value: &Value, out: &mut String) {
252 match value {
253 Value::Null => out.push_str("null"),
254 Value::Bool(b) => out.push_str(if *b { "true" } else { "false" }),
255 Value::Number(n) => out.push_str(&n.to_string()),
256 Value::String(s) => {
257 out.push_str(&serde_json::to_string(s).expect("string serialization is infallible"))
259 }
260 Value::Array(values) => {
261 out.push('[');
262 for (i, v) in values.iter().enumerate() {
263 if i > 0 { out.push(','); }
264 write_json(v, out);
265 }
266 out.push(']');
267 }
268 Value::Object(entries) => {
269 out.push('{');
270 for (i, Entry { key, value }) in entries.iter().enumerate() {
271 if i > 0 { out.push(','); }
272 out.push_str(&serde_json::to_string(key).expect("string serialization is infallible"));
273 out.push(':');
274 write_json(value, out);
275 }
276 out.push('}');
277 }
278 }
279}
280
281impl serde::Serialize for Value {
282 fn serialize<S: serde::Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
283 use serde::ser::{SerializeMap, SerializeSeq};
284 match self {
285 Self::Null => serializer.serialize_unit(),
286 Self::Bool(b) => serializer.serialize_bool(*b),
287 Self::Number(n) => serde::Serialize::serialize(n, serializer),
288 Self::String(s) => serializer.serialize_str(s),
289 Self::Array(values) => {
290 let mut seq = serializer.serialize_seq(Some(values.len()))?;
291 for v in values {
292 seq.serialize_element(v)?;
293 }
294 seq.end()
295 }
296 Self::Object(entries) => {
297 let mut map = serializer.serialize_map(Some(entries.len()))?;
298 for Entry { key, value } in entries {
299 map.serialize_entry(key, value)?;
300 }
301 map.end()
302 }
303 }
304 }
305}
306
307impl fmt::Display for Value {
308 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
309 f.write_str(&self.to_tjson_with(RenderOptions::default()))
310 }
311}
312
313impl std::str::FromStr for Value {
318 type Err = Error;
319
320 fn from_str(s: &str) -> Result<Self> {
321 Self::parse_with(s, ParseOptions::default())
322 }
323}
324
325pub(crate) fn detect_multiline_local_eol(value: &str) -> Option<MultilineLocalEol> {
326 let bytes = value.as_bytes();
327 let mut index = 0usize;
328 let mut saw_lf = false;
329 let mut saw_crlf = false;
330
331 while index < bytes.len() {
332 match bytes[index] {
333 b'\r' => {
334 if bytes.get(index + 1) == Some(&b'\n') {
335 saw_crlf = true;
336 index += 2;
337 } else {
338 return None;
339 }
340 }
341 b'\n' => {
342 saw_lf = true;
343 index += 1;
344 }
345 _ => index += 1,
346 }
347 }
348
349 match (saw_lf, saw_crlf) {
350 (false, false) => None,
351 (true, false) => Some(MultilineLocalEol::Lf),
352 (false, true) => Some(MultilineLocalEol::CrLf),
353 (true, true) => None,
354 }
355}