1use crate::globals::{RJ_BUILTIN, RJ_CALLABLE};
7use serde_json::{json, Map, Value};
8use std::borrow::Cow;
9
10pub const RJ_SAFE: &str = "__runjucks_safe";
12
13pub const RJ_UNDEFINED: &str = "__runjucks_undefined";
16
17pub const RJ_REGEXP: &str = "__runjucks_regexp";
19
20pub fn is_marked_safe(v: &Value) -> bool {
22 matches!(
23 v,
24 Value::Object(o) if o.get(RJ_SAFE).and_then(|x| x.as_str()).is_some()
25 )
26}
27
28pub fn is_undefined_value(v: &Value) -> bool {
30 matches!(
31 v,
32 Value::Object(o) if o.get(RJ_UNDEFINED) == Some(&Value::Bool(true))
33 )
34}
35
36pub fn is_regexp_value(v: &Value) -> bool {
38 matches!(
39 v,
40 Value::Object(o) if o.get(RJ_REGEXP).and_then(|x| x.as_bool()) == Some(true)
41 )
42}
43
44pub fn regexp_pattern_flags(v: &Value) -> Option<(String, String)> {
46 let Value::Object(o) = v else {
47 return None;
48 };
49 if o.get(RJ_REGEXP).and_then(|x| x.as_bool()) != Some(true) {
50 return None;
51 }
52 let p = o.get("pattern").and_then(|x| x.as_str())?;
53 let f = o.get("flags").and_then(|x| x.as_str()).unwrap_or("");
54 Some((p.to_string(), f.to_string()))
55}
56
57pub fn undefined_value() -> Value {
59 json!({ RJ_UNDEFINED: true })
60}
61
62fn safe_payload(v: &Value) -> Option<&str> {
63 match v {
64 Value::Object(o) => o.get(RJ_SAFE).and_then(|x| x.as_str()),
65 _ => None,
66 }
67}
68
69pub fn mark_safe(s: String) -> Value {
71 let mut m = Map::new();
72 m.insert(RJ_SAFE.to_string(), Value::String(s));
73 Value::Object(m)
74}
75
76fn is_empty_callable_marker_object(v: &Value) -> bool {
89 match v {
90 Value::Object(o) => {
91 if o.get(RJ_BUILTIN).is_some() {
92 return false;
93 }
94 o.len() == 1 && o.get(RJ_CALLABLE) == Some(&Value::Bool(true))
95 }
96 _ => false,
97 }
98}
99
100pub fn value_to_string(v: &Value) -> String {
101 if is_undefined_value(v) {
102 return String::new();
103 }
104 if is_empty_callable_marker_object(v) {
105 return String::new();
106 }
107 if let Some(s) = safe_payload(v) {
108 return s.to_string();
109 }
110 match v {
111 Value::Null => String::new(),
112 Value::Bool(b) => b.to_string(),
113 Value::Number(n) => n.to_string(),
114 Value::String(s) => s.clone(),
115 Value::Array(_) | Value::Object(_) => v.to_string(),
116 }
117}
118
119pub fn value_to_string_raw(v: &Value) -> Cow<'_, str> {
121 if is_undefined_value(v) {
122 return Cow::Borrowed("");
123 }
124 if let Some(s) = safe_payload(v) {
125 return Cow::Borrowed(s);
126 }
127 match v {
128 Value::Null => Cow::Borrowed(""),
129 Value::Bool(b) => Cow::Owned(b.to_string()),
130 Value::Number(n) => Cow::Owned(n.to_string()),
131 Value::String(s) => Cow::Borrowed(s.as_str()),
132 Value::Array(_) | Value::Object(_) => Cow::Owned(v.to_string()),
133 }
134}