use crate::globals::{RJ_BUILTIN, RJ_CALLABLE};
use serde_json::{json, Map, Value};
use std::borrow::Cow;
pub const RJ_SAFE: &str = "__runjucks_safe";
pub const RJ_UNDEFINED: &str = "__runjucks_undefined";
pub const RJ_REGEXP: &str = "__runjucks_regexp";
pub fn is_marked_safe(v: &Value) -> bool {
matches!(
v,
Value::Object(o) if o.get(RJ_SAFE).and_then(|x| x.as_str()).is_some()
)
}
pub fn is_undefined_value(v: &Value) -> bool {
matches!(
v,
Value::Object(o) if o.get(RJ_UNDEFINED) == Some(&Value::Bool(true))
)
}
pub fn is_regexp_value(v: &Value) -> bool {
matches!(
v,
Value::Object(o) if o.get(RJ_REGEXP).and_then(|x| x.as_bool()) == Some(true)
)
}
pub fn regexp_pattern_flags(v: &Value) -> Option<(String, String)> {
let Value::Object(o) = v else {
return None;
};
if o.get(RJ_REGEXP).and_then(|x| x.as_bool()) != Some(true) {
return None;
}
let p = o.get("pattern").and_then(|x| x.as_str())?;
let f = o.get("flags").and_then(|x| x.as_str()).unwrap_or("");
Some((p.to_string(), f.to_string()))
}
pub fn undefined_value() -> Value {
json!({ RJ_UNDEFINED: true })
}
fn safe_payload(v: &Value) -> Option<&str> {
match v {
Value::Object(o) => o.get(RJ_SAFE).and_then(|x| x.as_str()),
_ => None,
}
}
pub fn mark_safe(s: String) -> Value {
let mut m = Map::new();
m.insert(RJ_SAFE.to_string(), Value::String(s));
Value::Object(m)
}
fn is_empty_callable_marker_object(v: &Value) -> bool {
match v {
Value::Object(o) => {
if o.get(RJ_BUILTIN).is_some() {
return false;
}
o.len() == 1 && o.get(RJ_CALLABLE) == Some(&Value::Bool(true))
}
_ => false,
}
}
pub fn value_to_string(v: &Value) -> String {
if is_undefined_value(v) {
return String::new();
}
if is_empty_callable_marker_object(v) {
return String::new();
}
if let Some(s) = safe_payload(v) {
return s.to_string();
}
match v {
Value::Null => String::new(),
Value::Bool(b) => b.to_string(),
Value::Number(n) => n.to_string(),
Value::String(s) => s.clone(),
Value::Array(_) | Value::Object(_) => v.to_string(),
}
}
pub fn value_to_string_raw(v: &Value) -> Cow<'_, str> {
if is_undefined_value(v) {
return Cow::Borrowed("");
}
if let Some(s) = safe_payload(v) {
return Cow::Borrowed(s);
}
match v {
Value::Null => Cow::Borrowed(""),
Value::Bool(b) => Cow::Owned(b.to_string()),
Value::Number(n) => Cow::Owned(n.to_string()),
Value::String(s) => Cow::Borrowed(s.as_str()),
Value::Array(_) | Value::Object(_) => Cow::Owned(v.to_string()),
}
}