use crate::value::Value;
use crate::value::error::Error;
use crate::value::keys::Keys;
use crate::value::list::List;
use crate::value::map::Map;
use crate::value::span::Span;
use crate::value::tracer::Segment;
use crate::value::tracer::Tracer;
use saphyr::AnnotatedMapping;
use saphyr::AnnotatedSequence;
use saphyr::LoadableYamlNode;
use saphyr::MarkedYaml;
use saphyr::Scalar;
use saphyr::YamlData;
pub(crate) fn usize(value: &[u8]) -> Result<usize, Error> {
str(value)?
.parse::<usize>()
.map_err(|e| Error::Parse(e.to_string()))
}
pub(crate) fn str(value: &[u8]) -> Result<&str, Error> {
core::str::from_utf8(value)
.map_err(|_| Error::Parse(String::from_utf8_lossy(value).into_owned()))
}
pub fn json_map(data: &[u8]) -> Result<Map, Error> {
let data = parse_bytes(data)?;
let Ok(json) = serde_json::from_str(data) else {
return Err(Error::Parse(display_bytes(data)));
};
match json {
serde_json::Value::Object(v) => Ok(json_object(v)),
_ => Err(Error::Parse(display_bytes(data))),
}
}
fn json_value(json: serde_json::Value) -> Value {
match json {
serde_json::Value::Null => Value::default(),
serde_json::Value::Bool(v) => v.into(),
serde_json::Value::Number(v) => v.to_string().into(),
serde_json::Value::String(v) => v.into(),
serde_json::Value::Array(v) => json_array(v).into(),
serde_json::Value::Object(v) => json_object(v).into(),
}
}
fn json_object(json: serde_json::Map<String, serde_json::Value>) -> Map {
json.into_iter().map(|(k, v)| (k, json_value(v))).collect()
}
fn json_array(json: Vec<serde_json::Value>) -> List {
json.into_iter().map(json_value).collect()
}
pub fn yaml_map(source: String, span: Span, segment: Segment, data: &[u8]) -> Result<Map, Error> {
let data = parse_bytes(data)?;
let keys = Keys::default();
if data.trim().is_empty() {
return Ok(Map::default());
}
let yaml = MarkedYaml::load_from_str(data)?;
if yaml.len() != 1 {
return Err(Error::Parse(display_bytes(data)));
}
let Some(yaml) = yaml.into_iter().next() else {
return Err(Error::Parse(display_bytes(data)));
};
let Ok(Value::Map(mut map)) = yaml_value(&source, span, segment, data, &keys, yaml) else {
return Err(Error::Parse(display_bytes(data)));
};
map.tracer = Tracer::default()
.with_source(source)
.with_span(span)
.with_segment(segment);
Ok(map)
}
fn yaml_value(
source: &str,
span: Span,
segment: Segment,
data: &str,
keys: &Keys,
yaml: MarkedYaml<'_>,
) -> Result<Value, ()> {
let i0 = yaml.span.start.index();
let i1 = yaml.span.end.index();
let yaml_span = match &yaml.data {
YamlData::Sequence(_) if data.chars().nth(i0) == Some('[') => {
span.zoom(Span::new(i0.saturating_add(1), i1))
}
_ => span.zoom(Span::new(i0, i1)),
};
let value = match yaml.data {
YamlData::Sequence(d) => yaml_sequence(source, span, segment, data, keys, d)?,
YamlData::Mapping(d) => yaml_mapping(source, span, segment, data, keys, d)?,
YamlData::Value(Scalar::Null) => Value::default(),
YamlData::Value(Scalar::Boolean(d)) => d.into(),
YamlData::Value(Scalar::FloatingPoint(d)) => Value::from(d.to_string()),
YamlData::Value(Scalar::Integer(d)) => Value::from(d.to_string()),
YamlData::Value(Scalar::String(d)) => Value::from(d.into_owned()),
YamlData::Representation(..) => return Err(()),
YamlData::Tagged(..) => return Err(()),
YamlData::Alias(_) => return Err(()),
YamlData::BadValue => return Err(()),
};
let tracer = Tracer::default()
.with_source(source.to_string())
.with_span(yaml_span)
.with_segment(segment)
.with_keys(keys.clone());
Ok(value.with_tracer(tracer))
}
fn yaml_sequence(
source: &str,
span: Span,
segment: Segment,
data: &str,
keys: &Keys,
yaml: AnnotatedSequence<MarkedYaml<'_>>,
) -> Result<Value, ()> {
Ok(Value::List(
yaml.into_iter()
.enumerate()
.map(|(i, v)| yaml_value(source, span, segment, data, &keys.clone().fold(i), v))
.collect::<Result<List, _>>()?,
))
}
fn yaml_mapping(
source: &str,
span: Span,
segment: Segment,
data: &str,
keys: &Keys,
yaml: AnnotatedMapping<'_, MarkedYaml<'_>>,
) -> Result<Value, ()> {
Ok(Value::Map(
yaml.into_iter()
.map(|(k, v)| yaml_entry(source, span, segment, data, keys, k, v))
.collect::<Result<Map, _>>()?,
))
}
fn yaml_entry(
source: &str,
span: Span,
segment: Segment,
data: &str,
keys: &Keys,
k: MarkedYaml<'_>,
v: MarkedYaml<'_>,
) -> Result<(String, Value), ()> {
yaml_value(source, span, segment, data, keys, k)
.and_then(|k| String::from_utf8(k.join()).map_err(|_| ()))
.map(|k| (k.clone(), keys.clone().fold(k)))
.and_then(|(k, keys)| yaml_value(source, span, segment, data, &keys, v).map(|v| (k, v)))
}
#[cfg(test)]
mod test {
use crate::value::Value;
use crate::value::equivalent::Equivalent;
use crate::value::keys::Keys;
use crate::value::list::List;
use crate::value::map::Map;
use crate::value::parse::json_map;
use crate::value::parse::yaml_map;
use crate::value::span::Span;
use crate::value::tracer::Segment;
use crate::value::tracer::Tracer;
#[test]
fn test_json_map() {
let parsed = json_map(br#"{ "a": [1,2,"b"] }"#).unwrap();
let list = List::default().with("1").with("2").with("b");
let map = Map::default().with("a", list);
assert!(parsed.equivalent(&map), "{parsed}");
}
#[test]
fn test_yaml_map() {
let value = Value::from(String::from("value")).with_tracer(
Tracer::default()
.with_source(String::from("index.html"))
.with_span(Span::new(1016, 1021))
.with_keys(Keys::parse(b"outer.inner")),
);
let inner = Value::Map(Map::default().with("inner", value)).with_tracer(
Tracer::default()
.with_source(String::from("index.html"))
.with_span(Span::new(1000, 1023))
.with_keys(Keys::parse(b"outer")),
);
let outer = Value::Map(Map::default().with("outer", inner)).with_tracer(
Tracer::default()
.with_source("index.html".to_string())
.with_span(Span::new(1000, 1023)),
);
let parsed = yaml_map(
String::from("index.html"),
Span::new(1000, 1023),
Segment::Field,
b"outer: { inner: value }",
);
assert!(parsed.unwrap().equivalent(&outer.try_into().unwrap()));
}
}
fn parse_bytes(data: &[u8]) -> Result<&str, Error> {
str::from_utf8(data).map_err(|_| Error::Parse(display_bytes(data)))
}
fn display_bytes<T: AsRef<[u8]>>(t: T) -> String {
String::from_utf8_lossy(t.as_ref()).into_owned()
}