use crate::v2_model::{V2Expr, V2Pipe, V2Start, V2Step};
use crate::v2_validator::is_valid_op;
use serde_json::Value as JsonValue;
use super::{
V2ParseError, extract_literal, is_literal_escape, is_pipe_value, is_v2_ref, parse_v2_ref,
parse_v2_step,
};
pub fn parse_v2_start(value: &JsonValue) -> Result<V2Start, V2ParseError> {
match value {
JsonValue::String(s) => {
if is_pipe_value(s) {
return Ok(V2Start::PipeValue);
}
if let Some(lit) = extract_literal(s) {
return Ok(V2Start::Literal(JsonValue::String(lit.to_string())));
}
if let Some(v2_ref) = parse_v2_ref(s) {
return Ok(V2Start::Ref(v2_ref));
}
if is_v2_ref(s) {
return Err(V2ParseError::InvalidStart(format!(
"invalid v2 reference: {}",
s
)));
}
Ok(V2Start::Literal(value.clone()))
}
JsonValue::Null | JsonValue::Bool(_) | JsonValue::Number(_) => {
Ok(V2Start::Literal(value.clone()))
}
JsonValue::Array(_) | JsonValue::Object(_) => {
Ok(V2Start::Literal(value.clone()))
}
}
}
pub(super) fn parse_v2_expr_args(value: &JsonValue) -> Result<Vec<V2Expr>, V2ParseError> {
match value {
JsonValue::Array(arr) => arr.iter().map(parse_v2_expr).collect(),
_ => Err(V2ParseError::InvalidArgs(
"args must be an array".to_string(),
)),
}
}
pub fn parse_v2_pipe_from_value(value: &JsonValue) -> Result<V2Pipe, V2ParseError> {
match value {
JsonValue::Array(arr) => parse_v2_pipe(arr),
JsonValue::String(_) => {
let start = parse_v2_start(value)?;
Ok(V2Pipe {
start,
steps: vec![],
})
}
_ => {
let start = parse_v2_start(value)?;
Ok(V2Pipe {
start,
steps: vec![],
})
}
}
}
pub fn parse_v2_pipe(arr: &[JsonValue]) -> Result<V2Pipe, V2ParseError> {
if arr.is_empty() {
return Err(V2ParseError::EmptyPipe);
}
if arr.len() == 1 && looks_like_step(&arr[0]) {
let steps: Result<Vec<V2Step>, _> = arr.iter().map(parse_v2_step).collect();
return Ok(V2Pipe {
start: V2Start::PipeValue,
steps: steps?,
});
}
let start = parse_v2_start(&arr[0])?;
let steps: Result<Vec<V2Step>, _> = arr[1..].iter().map(parse_v2_step).collect();
Ok(V2Pipe {
start,
steps: steps?,
})
}
fn looks_like_step(value: &JsonValue) -> bool {
match value {
JsonValue::Object(obj) => {
if obj.contains_key("op")
|| obj.contains_key("let")
|| obj.contains_key("if")
|| obj.contains_key("map")
{
return true;
}
if obj.len() == 1 {
let key = obj.keys().next().unwrap();
if !["op", "let", "if", "map", "then", "else", "cond", "ref"]
.contains(&key.as_str())
{
return is_valid_op(key);
}
}
false
}
JsonValue::String(_) => {
false
}
_ => false,
}
}
pub fn parse_v2_expr(value: &JsonValue) -> Result<V2Expr, V2ParseError> {
match value {
JsonValue::Array(arr) => {
let pipe = parse_v2_pipe(arr)?;
Ok(V2Expr::Pipe(pipe))
}
JsonValue::String(s) => {
if is_pipe_value(s) {
Ok(V2Expr::Pipe(V2Pipe {
start: V2Start::PipeValue,
steps: vec![],
}))
} else if let Some(lit) = extract_literal(s) {
Ok(V2Expr::Pipe(V2Pipe {
start: V2Start::Literal(JsonValue::String(lit.to_string())),
steps: vec![],
}))
} else if let Some(v2_ref) = parse_v2_ref(s) {
Ok(V2Expr::Pipe(V2Pipe {
start: V2Start::Ref(v2_ref),
steps: vec![],
}))
} else if is_v2_ref(s) {
Err(V2ParseError::InvalidStart(format!(
"invalid v2 reference: {}",
s
)))
} else {
Ok(V2Expr::Pipe(V2Pipe {
start: V2Start::Literal(value.clone()),
steps: vec![],
}))
}
}
_ => {
Ok(V2Expr::Pipe(V2Pipe {
start: V2Start::Literal(value.clone()),
steps: vec![],
}))
}
}
}
pub fn is_v2_expr(value: &JsonValue) -> bool {
match value {
JsonValue::Array(_) => {
true
}
JsonValue::String(s) => {
is_v2_ref(s) || is_pipe_value(s) || is_literal_escape(s)
}
JsonValue::Object(obj) => {
!(obj.contains_key("ref") || (obj.contains_key("op") && !obj.contains_key("if")))
}
_ => false,
}
}
#[cfg(test)]
mod tests;