use std::fmt;
use crate::error::{Error, Result};
use crate::number::Number;
use crate::options::RenderOptions;
use crate::parse::{MultilineLocalEol, ParseOptions};
use crate::render::Renderer;
use crate::util::{
is_allowed_bare_string, is_forbidden_literal_tjson_char, is_pipe_like,
is_reserved_word, parse_bare_key_prefix,
};
#[derive(Clone, Copy)]
pub(crate) struct StrMeta<'a> {
pub(crate) s: &'a str,
pub(crate) has_eol: bool,
pub(crate) eol_type: Option<MultilineLocalEol>,
pub(crate) has_forbidden_literal: bool,
pub(crate) is_bare_eligible: bool,
pub(crate) is_reserved_word: bool,
pub(crate) has_pipe_like: bool,
}
impl<'a> StrMeta<'a> {
pub(crate) fn new(s: &'a str) -> Self {
let has_eol = s.as_bytes().contains(&b'\n');
let eol_type = if has_eol { detect_multiline_local_eol(s) } else { Some(MultilineLocalEol::default()) };
let has_forbidden_literal = s.chars().any(is_forbidden_literal_tjson_char);
let is_bare_eligible = is_allowed_bare_string(s);
let is_reserved_word = is_reserved_word(s);
let has_pipe_like = s.chars().any(is_pipe_like);
StrMeta { s, has_eol, eol_type, has_forbidden_literal, is_bare_eligible, is_reserved_word, has_pipe_like }
}
}
#[allow(dead_code)]
pub(crate) struct BareString<'a>(StrMeta<'a>);
#[allow(dead_code)]
impl<'a> BareString<'a> {
pub(crate) fn new(s: &'a str) -> Option<Self> {
let meta = StrMeta::new(s);
if meta.is_bare_eligible { Some(BareString(meta)) } else { None }
}
pub(crate) fn meta(&self) -> &StrMeta<'a> { &self.0 }
}
impl<'a> std::ops::Deref for BareString<'a> {
type Target = str;
fn deref(&self) -> &str { self.0.s }
}
#[allow(dead_code)]
pub(crate) struct TableBareString<'a>(BareString<'a>);
#[allow(dead_code)]
impl<'a> TableBareString<'a> {
pub(crate) fn new(s: &'a str) -> Option<Self> {
let meta = StrMeta::new(s);
if meta.is_bare_eligible && !meta.is_reserved_word && !meta.has_pipe_like {
Some(TableBareString(BareString(meta)))
} else {
None
}
}
}
impl<'a> std::ops::Deref for TableBareString<'a> {
type Target = BareString<'a>;
fn deref(&self) -> &BareString<'a> { &self.0 }
}
#[allow(dead_code)]
pub(crate) struct MultilineString<'a>(StrMeta<'a>);
#[allow(dead_code)]
impl<'a> MultilineString<'a> {
pub(crate) fn new(s: &'a str) -> Option<Self> {
let meta = StrMeta::new(s);
if meta.has_eol && meta.eol_type.is_some() && !meta.has_forbidden_literal {
Some(MultilineString(meta))
} else {
None
}
}
pub(crate) fn eol(&self) -> MultilineLocalEol { self.0.eol_type.unwrap() }
}
impl<'a> std::ops::Deref for MultilineString<'a> {
type Target = str;
fn deref(&self) -> &str { self.0.s }
}
#[allow(dead_code)]
pub(crate) struct BareKey<'a>(&'a str);
#[allow(dead_code)]
impl<'a> BareKey<'a> {
pub(crate) fn new(s: &'a str) -> Option<Self> {
if parse_bare_key_prefix(s).is_some_and(|end| end == s.len()) {
Some(BareKey(s))
} else {
None
}
}
}
impl<'a> std::ops::Deref for BareKey<'a> {
type Target = str;
fn deref(&self) -> &str { self.0 }
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Entry {
pub key: String,
pub value: Value,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Value {
Null,
Bool(bool),
Number(Number),
String(String),
Array(Vec<Value>),
Object(Vec<Entry>),
}
#[cfg(feature = "serde_json")]
impl From<serde_json::Value> for Value {
fn from(value: serde_json::Value) -> Self {
Self::from_serde_json(value)
}
}
#[cfg(feature = "serde_json")]
impl From<Value> for serde_json::Value {
fn from(value: Value) -> Self {
value.to_serde_json()
}
}
impl Value {
pub(crate) fn from_serde_json(value: serde_json::Value) -> Self {
match value {
serde_json::Value::Null => Self::Null,
serde_json::Value::Bool(v) => Self::Bool(v),
serde_json::Value::Number(n) => Self::Number(Number(n.to_string())),
serde_json::Value::String(s) => Self::String(s),
serde_json::Value::Array(values) => {
Self::Array(values.into_iter().map(Self::from_serde_json).collect())
}
serde_json::Value::Object(map) => Self::Object(
map.into_iter()
.map(|(key, value)| Entry { key, value: Self::from_serde_json(value) })
.collect(),
),
}
}
pub(crate) fn to_serde_json(&self) -> serde_json::Value {
match self {
Self::Null => serde_json::Value::Null,
Self::Bool(v) => serde_json::Value::Bool(*v),
Self::Number(n) => serde_json::Value::Number(n.to_serde_json_number()),
Self::String(s) => serde_json::Value::String(s.clone()),
Self::Array(values) => {
serde_json::Value::Array(values.iter().map(Value::to_serde_json).collect())
}
Self::Object(entries) => {
let mut map = serde_json::Map::new();
for Entry { key, value } in entries {
map.insert(key.clone(), value.to_serde_json());
}
serde_json::Value::Object(map)
}
}
}
pub(crate) fn parse_with(input: &str, options: ParseOptions) -> Result<Self> {
crate::parse::Parser::parse_document(input, options.start_indent).map_err(Error::Parse)
}
pub fn to_tjson_with(&self, options: RenderOptions) -> String {
Renderer::render(self, &options)
}
pub fn to_json(&self) -> String {
let mut out = String::new();
write_json(self, &mut out);
out
}
}
fn write_json(value: &Value, out: &mut String) {
match value {
Value::Null => out.push_str("null"),
Value::Bool(b) => out.push_str(if *b { "true" } else { "false" }),
Value::Number(n) => out.push_str(&n.to_string()),
Value::String(s) => {
out.push_str(&serde_json::to_string(s).expect("string serialization is infallible"))
}
Value::Array(values) => {
out.push('[');
for (i, v) in values.iter().enumerate() {
if i > 0 { out.push(','); }
write_json(v, out);
}
out.push(']');
}
Value::Object(entries) => {
out.push('{');
for (i, Entry { key, value }) in entries.iter().enumerate() {
if i > 0 { out.push(','); }
out.push_str(&serde_json::to_string(key).expect("string serialization is infallible"));
out.push(':');
write_json(value, out);
}
out.push('}');
}
}
}
impl serde::Serialize for Value {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
use serde::ser::{SerializeMap, SerializeSeq};
match self {
Self::Null => serializer.serialize_unit(),
Self::Bool(b) => serializer.serialize_bool(*b),
Self::Number(n) => serde::Serialize::serialize(n, serializer),
Self::String(s) => serializer.serialize_str(s),
Self::Array(values) => {
let mut seq = serializer.serialize_seq(Some(values.len()))?;
for v in values {
seq.serialize_element(v)?;
}
seq.end()
}
Self::Object(entries) => {
let mut map = serializer.serialize_map(Some(entries.len()))?;
for Entry { key, value } in entries {
map.serialize_entry(key, value)?;
}
map.end()
}
}
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.to_tjson_with(RenderOptions::default()))
}
}
impl std::str::FromStr for Value {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Self::parse_with(s, ParseOptions::default())
}
}
pub(crate) fn detect_multiline_local_eol(value: &str) -> Option<MultilineLocalEol> {
let bytes = value.as_bytes();
let mut index = 0usize;
let mut saw_lf = false;
let mut saw_crlf = false;
while index < bytes.len() {
match bytes[index] {
b'\r' => {
if bytes.get(index + 1) == Some(&b'\n') {
saw_crlf = true;
index += 2;
} else {
return None;
}
}
b'\n' => {
saw_lf = true;
index += 1;
}
_ => index += 1,
}
}
match (saw_lf, saw_crlf) {
(false, false) => None,
(true, false) => Some(MultilineLocalEol::Lf),
(false, true) => Some(MultilineLocalEol::CrLf),
(true, true) => None,
}
}