1use crate::ordered_map::OrderedMap;
9use crate::re::{Captures, Regex, RegexError};
10
11use crate::consts::*;
12use crate::value::{is_integer_f64, js_string, js_to_number, num_to_string, Value};
13
14const MIN_SAFE_INTEGER: i64 = -9007199254740991;
15const MAX_SAFE_INTEGER: i64 = 9007199254740991;
16
17pub fn type_name(t: i64) -> String {
21 let idx = (t as u32).leading_zeros() as usize;
22 TYPENAME
23 .get(idx)
24 .map(|s| s.to_string())
25 .unwrap_or_else(|| TYPENAME[0].to_string())
26}
27
28pub fn typify(value: &Value) -> i64 {
30 match value {
31 Value::Noval => T_NOVAL as i64,
32 Value::Null => (T_SCALAR | T_NULL) as i64,
33 Value::Num(n) => {
34 if is_integer_f64(*n) {
35 (T_SCALAR | T_NUMBER | T_INTEGER) as i64
36 } else if n.is_nan() {
37 T_NOVAL as i64
38 } else {
39 (T_SCALAR | T_NUMBER | T_DECIMAL) as i64
40 }
41 }
42 Value::Str(_) => (T_SCALAR | T_STRING) as i64,
43 Value::Bool(_) => (T_SCALAR | T_BOOLEAN) as i64,
44 Value::Func(_) => (T_SCALAR | T_FUNCTION) as i64,
45 Value::List(_) => (T_NODE | T_LIST) as i64,
46 Value::Map(_) | Value::Sentinel(_) => (T_NODE | T_MAP) as i64,
48 }
49}
50
51pub fn get_def(val: Value, alt: Value) -> Value {
54 if val.is_noval() {
55 alt
56 } else {
57 val
58 }
59}
60
61pub fn is_node(val: &Value) -> bool {
62 matches!(val, Value::List(_) | Value::Map(_))
63}
64
65pub fn is_map(val: &Value) -> bool {
66 matches!(val, Value::Map(_))
67}
68
69pub fn is_list(val: &Value) -> bool {
70 matches!(val, Value::List(_))
71}
72
73pub fn is_key(key: &Value) -> bool {
74 match key {
75 Value::Str(s) => !s.is_empty(),
76 Value::Num(_) => true,
77 _ => false,
78 }
79}
80
81pub fn is_empty(val: &Value) -> bool {
82 match val {
83 Value::Noval | Value::Null => true,
84 Value::Str(s) => s.is_empty(),
85 Value::List(l) => l.borrow().is_empty(),
86 Value::Map(m) => m.borrow().is_empty(),
87 _ => false,
88 }
89}
90
91pub fn is_func(val: &Value) -> bool {
92 matches!(val, Value::Func(_))
93}
94
95pub fn size(val: &Value) -> i64 {
98 match val {
99 Value::List(l) => l.borrow().len() as i64,
100 Value::Map(m) => m.borrow().len() as i64,
101 Value::Str(s) => s.encode_utf16().count() as i64,
102 Value::Num(n) if n.is_finite() => n.floor() as i64,
103 Value::Bool(b) => i64::from(*b),
104 _ => 0,
105 }
106}
107
108pub fn slice(val: Value, start: Option<i64>, end: Option<i64>, mutate: bool) -> Value {
114 if let Value::Num(n) = val {
115 let s = start.unwrap_or(MIN_SAFE_INTEGER);
116 let e = end.unwrap_or(MAX_SAFE_INTEGER) - 1;
117 let lo = n.max(s as f64);
118 let r = lo.min(e as f64);
119 return Value::Num(r);
120 }
121
122 let vlen = size(&val);
123
124 let mut start = start;
125 if end.is_some() && start.is_none() {
126 start = Some(0);
127 }
128
129 if let Some(mut s) = start {
130 let mut e: Option<i64>;
131 if s < 0 {
132 let mut ee = vlen + s;
133 if ee < 0 {
134 ee = 0;
135 }
136 e = Some(ee);
137 s = 0;
138 } else if let Some(mut ee) = end {
139 if ee < 0 {
140 ee += vlen;
141 if ee < 0 {
142 ee = 0;
143 }
144 } else if vlen < ee {
145 ee = vlen;
146 }
147 e = Some(ee);
148 } else {
149 e = Some(vlen);
150 }
151
152 if vlen < s {
153 s = vlen;
154 }
155
156 let e = e.take().unwrap_or(vlen);
157
158 if -1 < s && s <= e && e <= vlen {
159 match &val {
160 Value::List(l) => {
161 if mutate {
162 let mut lb = l.borrow_mut();
163 let sub: Vec<Value> = lb[s as usize..e as usize].to_vec();
164 *lb = sub;
165 drop(lb);
166 return val;
167 } else {
168 let lb = l.borrow();
169 return Value::list(lb[s as usize..e as usize].to_vec());
170 }
171 }
172 Value::Str(st) => {
173 let units: Vec<u16> = st.encode_utf16().collect();
175 let sub: Vec<u16> = units[s as usize..e as usize].to_vec();
176 return Value::Str(String::from_utf16_lossy(&sub));
177 }
178 _ => {}
179 }
180 } else {
181 match &val {
182 Value::List(_) => return Value::empty_list(),
183 Value::Str(_) => return Value::Str(String::new()),
184 _ => {}
185 }
186 }
187 }
188
189 val
190}
191
192pub fn pad(s: Value, padding: Option<i64>, padchar: Option<String>) -> String {
195 let mut s = match s {
196 Value::Str(s) => s,
197 other => stringify(&other, None, false),
198 };
199 let padding = padding.unwrap_or(44);
200 let padchar = {
201 let mut pc = padchar.unwrap_or_default();
202 pc.push(' ');
203 pc.chars().next().unwrap()
204 };
205 let cur = s.encode_utf16().count() as i64;
206 if padding > -1 {
207 if cur < padding {
208 for _ in 0..(padding - cur) {
209 s.push(padchar);
210 }
211 }
212 s
213 } else {
214 let target = -padding;
215 if cur < target {
216 let mut out = String::new();
217 for _ in 0..(target - cur) {
218 out.push(padchar);
219 }
220 out.push_str(&s);
221 out
222 } else {
223 s
224 }
225 }
226}
227
228pub fn get_elem(val: &Value, key: &Value, alt: Value) -> Value {
236 let out = get_elem_or_else(val, key, || Value::Noval);
237 if !out.is_nullish() {
238 return out;
239 }
240 match &alt {
241 Value::Func(f) => {
242 let inj = crate::major::Injection::from_def(None);
243 f(&inj, &Value::Noval, "", &Value::Noval)
244 }
245 _ => alt,
246 }
247}
248
249pub fn get_elem_or_else<F: FnOnce() -> Value>(val: &Value, key: &Value, alt: F) -> Value {
250 if val.is_noval() || key.is_noval() {
251 return alt();
252 }
253 let out = if let Value::List(l) = val {
254 let keystr = js_string(key);
255 if R_INTEGER_KEY.is_match(&keystr) {
256 match keystr.parse::<i64>() {
257 Ok(mut n) => {
258 let lb = l.borrow();
259 if n < 0 {
260 n += lb.len() as i64;
261 }
262 if n >= 0 && (n as usize) < lb.len() {
263 lb[n as usize].clone()
264 } else {
265 Value::Noval
266 }
267 }
268 Err(_) => Value::Noval,
269 }
270 } else {
271 Value::Noval
272 }
273 } else {
274 Value::Noval
275 };
276 if out.is_nullish() {
279 alt()
280 } else {
281 out
282 }
283}
284
285pub fn get_prop(val: &Value, key: &Value, alt: Value) -> Value {
287 if val.is_noval() || key.is_noval() {
288 return alt;
289 }
290 let out = match val {
291 Value::List(l) => {
292 let lb = l.borrow();
293 let ks = js_string(key);
295 ks.parse::<usize>()
296 .ok()
297 .and_then(|i| lb.get(i).cloned())
298 .unwrap_or(Value::Noval)
299 }
300 Value::Map(m) => m
301 .borrow()
302 .get(&js_string(key))
303 .cloned()
304 .unwrap_or(Value::Noval),
305 Value::Sentinel(s) => {
306 if js_string(key) == s.tag {
307 Value::Bool(true)
308 } else {
309 Value::Noval
310 }
311 }
312 _ => Value::Noval,
313 };
314 if out.is_nullish() {
317 alt
318 } else {
319 out
320 }
321}
322
323pub fn lookup(val: &Value, key: &Value) -> Value {
330 if val.is_noval() || key.is_noval() {
331 return Value::Noval;
332 }
333 match val {
334 Value::List(l) => {
335 let lb = l.borrow();
336 let keystr = js_string(key);
337 if R_INTEGER_KEY.is_match(&keystr) {
338 match keystr.parse::<i64>() {
339 Ok(mut n) => {
340 if n < 0 {
341 n += lb.len() as i64;
342 }
343 if n >= 0 && (n as usize) < lb.len() {
344 lb[n as usize].clone()
345 } else {
346 Value::Noval
347 }
348 }
349 Err(_) => Value::Noval,
350 }
351 } else {
352 Value::Noval
353 }
354 }
355 Value::Map(m) => m
356 .borrow()
357 .get(&js_string(key))
358 .cloned()
359 .unwrap_or(Value::Noval),
360 Value::Sentinel(s) => {
361 if js_string(key) == s.tag {
362 Value::Bool(true)
363 } else {
364 Value::Noval
365 }
366 }
367 _ => Value::Noval,
368 }
369}
370
371pub fn str_key(key: Value) -> String {
373 let t = typify(&key);
374 if t & (T_STRING as i64) != 0 {
375 return key.as_str().unwrap_or("").to_string();
376 }
377 if t & (T_BOOLEAN as i64) != 0 {
378 return String::new();
379 }
380 if t & (T_NUMBER as i64) != 0 {
381 if let Value::Num(n) = key {
382 return if n.fract() == 0.0 {
383 num_to_string(n)
384 } else {
385 num_to_string(n.floor())
386 };
387 }
388 }
389 String::new()
390}
391
392pub fn keysof_vec(val: &Value) -> Vec<String> {
394 match val {
395 Value::Map(m) => {
396 let mut ks: Vec<String> = m.borrow().keys().cloned().collect();
397 ks.sort();
398 ks
399 }
400 Value::List(l) => (0..l.borrow().len()).map(|i| i.to_string()).collect(),
401 _ => Vec::new(),
402 }
403}
404
405pub fn keys_of(val: &Value) -> Value {
406 Value::list(keysof_vec(val).into_iter().map(Value::Str).collect())
407}
408
409pub fn has_key(val: &Value, key: &Value) -> bool {
410 !get_prop(val, key, Value::Noval).is_noval()
411}
412
413pub fn items_vec(val: &Value) -> Vec<(String, Value)> {
417 keysof_vec(val)
418 .into_iter()
419 .map(|k| {
420 let v = lookup(val, &Value::str(k.clone()));
421 (k, v)
422 })
423 .collect()
424}
425
426pub fn items(val: &Value) -> Value {
427 Value::list(
428 items_vec(val)
429 .into_iter()
430 .map(|(k, v)| Value::list(vec![Value::Str(k), v]))
431 .collect(),
432 )
433}
434
435pub fn flatten(list: &Value, depth: Option<i64>) -> Value {
438 match list {
439 Value::List(l) => {
440 let d = depth.unwrap_or(1);
441 Value::list(flat_slice(&l.borrow(), d))
442 }
443 other => other.clone(),
444 }
445}
446
447fn flat_slice(v: &[Value], depth: i64) -> Vec<Value> {
448 let mut out = Vec::new();
449 for item in v {
450 match item {
451 Value::List(inner) if depth > 0 => out.extend(flat_slice(&inner.borrow(), depth - 1)),
452 other => out.push(other.clone()),
453 }
454 }
455 out
456}
457
458#[allow(dead_code)]
461pub fn flatten_vals(v: Vec<Value>, depth: i64) -> Vec<Value> {
462 flat_slice(&v, depth)
463}
464
465pub fn filter_vals<F: Fn(&(String, Value)) -> bool>(val: &Value, check: F) -> Vec<Value> {
466 let all = items_vec(val);
467 let mut out = Vec::new();
468 for item in &all {
469 if check(item) {
470 out.push(item.1.clone());
471 }
472 }
473 out
474}
475
476pub fn filter<F: Fn(&(String, Value)) -> bool>(val: &Value, check: F) -> Value {
477 Value::list(filter_vals(val, check))
478}
479
480pub fn esc_re(s: &Value) -> String {
484 let rs = coerce_for_replace(s);
485 R_ESCAPE_REGEXP
486 .replace_all(&rs, |caps: &Captures<'_>| format!("\\{}", &caps[0]))
487 .into_owned()
488}
489
490pub fn re_compile(pattern: &str) -> Result<Regex, RegexError> {
497 Regex::new(pattern)
498}
499
500pub fn re_find(pattern: &str, input: &str) -> Option<Vec<String>> {
502 let re = Regex::new(pattern).ok()?;
503 let m = re.captures(input)?;
504 Some(
505 m.iter()
506 .map(|c| c.map(|x| x.as_str().to_string()).unwrap_or_default())
507 .collect(),
508 )
509}
510
511pub fn re_find_all(pattern: &str, input: &str) -> Vec<Vec<String>> {
513 let re = match Regex::new(pattern) {
514 Ok(r) => r,
515 Err(_) => return Vec::new(),
516 };
517 re.captures_iter(input)
518 .map(|caps| {
519 caps.iter()
520 .map(|c| c.map(|x| x.as_str().to_string()).unwrap_or_default())
521 .collect()
522 })
523 .collect()
524}
525
526pub fn re_replace(pattern: &str, input: &str, replacement: &str) -> String {
528 let re = match Regex::new(pattern) {
529 Ok(r) => r,
530 Err(_) => return input.to_string(),
531 };
532 re.replace_all(input, replacement).into_owned()
533}
534
535pub fn re_test(pattern: &str, input: &str) -> bool {
537 Regex::new(pattern)
538 .map(|re| re.is_match(input))
539 .unwrap_or(false)
540}
541
542pub fn re_escape(s: &str) -> String {
544 esc_re(&Value::from(s))
545}
546
547pub fn esc_url(s: &Value) -> String {
549 let s = match s {
550 Value::Str(s) => s.clone(),
551 Value::Noval | Value::Null => String::new(),
552 other => js_string(other),
553 };
554 let mut out = String::new();
555 for b in s.bytes() {
556 let c = b as char;
557 if c.is_ascii_alphanumeric() || "-_.!~*'()".contains(c) {
558 out.push(c);
559 } else {
560 out.push_str(&format!("%{:02X}", b));
561 }
562 }
563 out
564}
565
566fn coerce_for_replace(s: &Value) -> String {
569 match s {
570 Value::Str(s) => s.clone(),
571 Value::Noval => String::new(),
572 other => stringify(other, None, false),
573 }
574}
575
576#[allow(dead_code)]
580pub fn replace_str(s: &Value, from: &Regex, to: &str) -> String {
581 let rs = coerce_for_replace(s);
582 from.replace_all(&rs, to).to_string()
583}
584
585pub fn join(arr: &Value, sep: Option<&str>, url: bool) -> String {
588 let sepdef = sep.unwrap_or(S_CM).to_string();
589 let sarr = size(arr);
590 let sepre: Option<String> = if sepdef.encode_utf16().count() == 1 {
591 Some(esc_re(&Value::str(sepdef.clone())))
592 } else {
593 None
594 };
595
596 let str_entries: Vec<Value> =
598 filter_vals(arr, |n| matches!(&n.1, Value::Str(s) if !s.is_empty()));
599
600 let processed: Vec<String> = items_vec(&Value::list(str_entries))
601 .into_iter()
602 .map(|(idx_str, v)| {
603 let i = idx_str.parse::<i64>().unwrap_or(0);
604 let mut s = v.as_str().unwrap_or("").to_string();
605 if let Some(sre) = &sepre {
606 if !sre.is_empty() {
607 if url && i == 0 {
608 s = re_replace_first(&format!("{sre}+$"), &s, "");
609 return s;
610 }
611 if i > 0 {
612 s = re_replace_first(&format!("^{sre}+"), &s, "");
613 }
614 if i < sarr - 1 || !url {
615 s = re_replace_first(&format!("{sre}+$"), &s, "");
616 }
617 let pat = format!("([^{sre}]){sre}+([^{sre}])");
618 let repl = format!("${{1}}{sepdef}${{2}}");
619 s = re_replace_first(&pat, &s, &repl);
620 }
621 }
622 s
623 })
624 .collect();
625
626 processed
627 .into_iter()
628 .filter(|s| !s.is_empty())
629 .collect::<Vec<_>>()
630 .join(&sepdef)
631}
632
633pub fn join_vals(arr: &[Value], sep: Option<&str>, url: bool) -> String {
635 join(&Value::list(arr.to_vec()), sep, url)
636}
637
638fn re_replace_first(pat: &str, s: &str, repl: &str) -> String {
639 match Regex::new(pat) {
640 Ok(re) => re.replace(s, repl).to_string(),
641 Err(_) => s.to_string(),
642 }
643}
644
645pub struct JsonFlags {
648 pub indent: usize,
649 pub offset: usize,
650}
651
652impl Default for JsonFlags {
653 fn default() -> Self {
654 JsonFlags {
655 indent: 2,
656 offset: 0,
657 }
658 }
659}
660
661pub fn jsonify(val: &Value, flags: Option<&JsonFlags>) -> String {
663 let def = JsonFlags::default();
664 let flags = flags.unwrap_or(&def);
665 if val.is_nullish() {
666 return S_null.to_string();
667 }
668 let mut s = match json_encode(val, flags.indent, 0) {
669 Some(s) => s,
670 None => return S_null.to_string(),
671 };
672 if flags.offset > 0 {
673 let lines: Vec<&str> = s.split('\n').collect();
675 let mut out = String::from("{\n");
676 let mut parts: Vec<String> = Vec::new();
677 for line in lines.iter().skip(1) {
678 parts.push(pad(
679 Value::str(*line),
680 Some(0 - flags.offset as i64 - line.encode_utf16().count() as i64),
681 None,
682 ));
683 }
684 out.push_str(&parts.join("\n"));
685 s = out;
686 }
687 s
688}
689
690fn json_encode(val: &Value, indent: usize, level: usize) -> Option<String> {
691 match val {
692 Value::Noval => None,
693 Value::Func(_) => None,
694 Value::Null => Some("null".to_string()),
695 Value::Bool(b) => Some(b.to_string()),
696 Value::Num(n) => {
697 if n.is_finite() {
698 Some(num_to_string(*n))
699 } else {
700 Some("null".to_string())
701 }
702 }
703 Value::Str(s) => Some(json_quote(s)),
704 Value::Sentinel(_) => {
705 let mut m = OrderedMap::new();
707 let tag = match val {
708 Value::Sentinel(s) => s.tag.to_string(),
709 _ => unreachable!(),
710 };
711 m.insert(tag, Value::Bool(true));
712 json_encode(&Value::map(m), indent, level)
713 }
714 Value::List(l) => {
715 let lb = l.borrow();
716 if lb.is_empty() {
717 return Some("[]".to_string());
718 }
719 let inner_pad = " ".repeat(indent * (level + 1));
720 let outer_pad = " ".repeat(indent * level);
721 let nl = if indent > 0 { "\n" } else { "" };
722 let sep = if indent > 0 { ",\n" } else { "," };
723 let items: Vec<String> = lb
724 .iter()
725 .map(|v| {
726 let enc =
727 json_encode(v, indent, level + 1).unwrap_or_else(|| "null".to_string());
728 if indent > 0 {
729 format!("{inner_pad}{enc}")
730 } else {
731 enc
732 }
733 })
734 .collect();
735 Some(format!("[{nl}{}{nl}{outer_pad}]", items.join(sep)))
736 }
737 Value::Map(m) => {
738 let mb = m.borrow();
739 let entries: Vec<(&String, &Value)> = mb
741 .iter()
742 .filter(|(_, v)| !matches!(v, Value::Noval | Value::Func(_)))
743 .collect();
744 if entries.is_empty() {
745 return Some("{}".to_string());
746 }
747 let inner_pad = " ".repeat(indent * (level + 1));
748 let outer_pad = " ".repeat(indent * level);
749 let nl = if indent > 0 { "\n" } else { "" };
750 let colon = if indent > 0 { ": " } else { ":" };
751 let sep = if indent > 0 { ",\n" } else { "," };
752 let items: Vec<String> = entries
753 .iter()
754 .map(|(k, v)| {
755 let enc =
756 json_encode(v, indent, level + 1).unwrap_or_else(|| "null".to_string());
757 if indent > 0 {
758 format!("{inner_pad}{}{colon}{enc}", json_quote(k))
759 } else {
760 format!("{}{colon}{enc}", json_quote(k))
761 }
762 })
763 .collect();
764 Some(format!("{{{nl}{}{nl}{outer_pad}}}", items.join(sep)))
765 }
766 }
767}
768
769fn json_quote(s: &str) -> String {
770 let mut out = String::from("\"");
771 for c in s.chars() {
772 match c {
773 '"' => out.push_str("\\\""),
774 '\\' => out.push_str("\\\\"),
775 '\n' => out.push_str("\\n"),
776 '\r' => out.push_str("\\r"),
777 '\t' => out.push_str("\\t"),
778 '\u{0008}' => out.push_str("\\b"),
779 '\u{000C}' => out.push_str("\\f"),
780 c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)),
781 c => out.push(c),
782 }
783 }
784 out.push('"');
785 out
786}
787
788pub fn stringify(val: &Value, maxlen: Option<i64>, pretty: bool) -> String {
790 let mut valstr;
791 if val.is_noval() {
792 return if pretty {
793 "<>".to_string()
794 } else {
795 String::new()
796 };
797 }
798 if let Value::Str(s) = val {
799 valstr = s.clone();
800 } else {
801 match human_json(val) {
802 Some(s) => {
803 valstr = s.replace('"', "");
805 }
806 None => return "__STRINGIFY_FAILED__".to_string(),
807 }
808 }
809
810 if let Some(m) = maxlen {
811 if m >= 0 {
812 let m = m as usize;
813 let chars: Vec<char> = valstr.chars().collect();
814 if chars.len() > m {
815 let keep = m.saturating_sub(3);
816 let head: String = chars.iter().take(keep).collect();
817 valstr = format!("{head}...");
818 }
819 }
820 }
821
822 if pretty {
823 return ansi_colour(&valstr);
824 }
825
826 valstr
827}
828
829fn human_json(val: &Value) -> Option<String> {
834 human_json_depth(val, 0)
835}
836
837fn human_json_depth(val: &Value, depth: usize) -> Option<String> {
838 if depth > 1_000 {
839 return None;
840 }
841 match val {
842 Value::Noval => None,
843 Value::Func(_) => None,
844 Value::Null => Some("null".to_string()),
845 Value::Bool(b) => Some(b.to_string()),
846 Value::Num(n) => Some(if n.is_finite() {
847 num_to_string(*n)
848 } else {
849 "null".to_string()
850 }),
851 Value::Str(s) => Some(json_quote(s)),
852 Value::Sentinel(s) => Some(format!("{{\"{}\":true}}", s.tag)),
853 Value::List(l) => {
854 let lb = l.borrow();
855 let parts: Vec<String> = lb
856 .iter()
857 .map(|v| human_json_depth(v, depth + 1).unwrap_or_else(|| "null".to_string()))
858 .collect();
859 Some(format!("[{}]", parts.join(",")))
860 }
861 Value::Map(m) => {
862 let mb = m.borrow();
863 let mut keys: Vec<&String> = mb
864 .iter()
865 .filter(|(_, v)| !matches!(v, Value::Noval | Value::Func(_)))
866 .map(|(k, _)| k)
867 .collect();
868 keys.sort();
869 let parts: Vec<String> = keys
870 .iter()
871 .map(|k| {
872 let v = mb.get(k).unwrap();
873 format!(
874 "{}:{}",
875 json_quote(k),
876 human_json_depth(v, depth + 1).unwrap_or_else(|| "null".to_string())
877 )
878 })
879 .collect();
880 Some(format!("{{{}}}", parts.join(",")))
881 }
882 }
883}
884
885fn ansi_colour(valstr: &str) -> String {
886 let colours = [
887 81, 118, 213, 39, 208, 201, 45, 190, 129, 51, 160, 121, 226, 33, 207, 69,
888 ];
889 let c: Vec<String> = colours.iter().map(|n| format!("\x1b[38;5;{n}m")).collect();
890 let r = "\x1b[0m";
891 let mut d: i64 = 0;
892 let mut o = c[0].clone();
893 let mut t = o.clone();
894 for ch in valstr.chars() {
895 if ch == '{' || ch == '[' {
896 d += 1;
897 o = c[(d as usize) % c.len()].clone();
898 t.push_str(&o);
899 t.push(ch);
900 } else if ch == '}' || ch == ']' {
901 t.push_str(&o);
902 t.push(ch);
903 d -= 1;
904 o = c[(d.rem_euclid(c.len() as i64)) as usize].clone();
905 } else {
906 t.push_str(&o);
907 t.push(ch);
908 }
909 }
910 t.push_str(r);
911 t
912}
913
914pub fn pathify(val: &Value, startin: Option<i64>, endin: Option<i64>) -> String {
917 let path: Option<Vec<Value>> = match val {
918 Value::List(l) => Some(l.borrow().clone()),
919 Value::Str(_) | Value::Num(_) => Some(vec![val.clone()]),
920 _ => None,
921 };
922 let start = match startin {
923 None => 0,
924 Some(s) if -1 < s => s,
925 _ => 0,
926 };
927 let end = match endin {
928 None => 0,
929 Some(e) if -1 < e => e,
930 _ => 0,
931 };
932
933 let mut pathstr: Option<String> = None;
934 if let Some(path) = &path {
935 if start >= 0 {
936 let plen = path.len() as i64;
937 let sliced = slice(
938 Value::list(path.clone()),
939 Some(start),
940 Some(plen - end),
941 false,
942 );
943 let sv: Vec<Value> = sliced
944 .as_list()
945 .map(|l| l.borrow().clone())
946 .unwrap_or_default();
947 if sv.is_empty() {
948 pathstr = Some("<root>".to_string());
949 } else {
950 let filtered = filter_vals(&Value::list(sv), |n| is_key(&n.1));
951 let mapped: Vec<Value> = filtered
952 .iter()
953 .map(|p| match p {
954 Value::Num(n) => Value::str(num_to_string(n.floor())),
955 _ => Value::str(p.as_str().unwrap_or("").replace('.', "")),
956 })
957 .collect();
958 pathstr = Some(join_vals(&mapped, Some("."), false));
959 }
960 }
961 }
962
963 pathstr.unwrap_or_else(|| {
964 let tail = if val.is_noval() {
965 String::new()
966 } else {
967 format!("{}{}", S_CN, stringify(val, Some(47), false))
968 };
969 format!("<unknown-path{tail}>")
970 })
971}
972
973pub fn clone(val: &Value) -> Value {
977 match val {
978 Value::Noval => Value::Noval,
979 Value::Null => Value::Null,
980 Value::Bool(b) => Value::Bool(*b),
981 Value::Num(n) => Value::Num(*n),
982 Value::Str(s) => Value::Str(s.clone()),
983 Value::List(l) => Value::list(l.borrow().iter().map(clone).collect()),
984 Value::Map(m) => {
985 let mut nm = OrderedMap::new();
986 for (k, v) in m.borrow().iter() {
987 nm.insert(k.clone(), clone(v));
988 }
989 Value::map(nm)
990 }
991 Value::Func(f) => Value::Func(f.clone()),
992 Value::Sentinel(s) => Value::Sentinel(s),
993 }
994}
995
996pub fn del_prop(parent: Value, key: &Value) -> Value {
999 if !is_key(key) {
1000 return parent;
1001 }
1002 match &parent {
1003 Value::Map(m) => {
1004 let k = str_key(key.clone());
1005 m.borrow_mut().shift_remove(&k);
1006 }
1007 Value::List(l) => {
1008 let ki = js_to_number(key);
1009 if ki.is_nan() {
1010 return parent;
1011 }
1012 let ki = ki.floor() as i64;
1013 let mut lb = l.borrow_mut();
1014 if ki >= 0 && (ki as usize) < lb.len() {
1015 lb.remove(ki as usize);
1016 }
1017 }
1018 _ => {}
1019 }
1020 parent
1021}
1022
1023pub fn set_prop(parent: Value, key: &Value, val: Value) -> Value {
1024 if !is_key(key) {
1025 return parent;
1026 }
1027 match &parent {
1028 Value::Map(m) => {
1029 let k = js_string(key);
1030 m.borrow_mut().insert(k, val);
1031 }
1032 Value::List(l) => {
1033 let ki = js_to_number(key);
1034 if ki.is_nan() {
1035 return parent;
1036 }
1037 let ki = ki.floor() as i64;
1038 let mut lb = l.borrow_mut();
1039 if ki >= 0 {
1040 let len = lb.len() as i64;
1041 let idx = ki.min(len).max(0) as usize;
1042 if idx < lb.len() {
1043 lb[idx] = val;
1044 } else {
1045 lb.push(val);
1046 }
1047 } else {
1048 lb.insert(0, val);
1049 }
1050 }
1051 _ => {}
1052 }
1053 parent
1054}
1055
1056pub fn jm(kv: &[Value]) -> Value {
1059 let mut o = OrderedMap::new();
1060 let n = kv.len();
1061 let mut i = 0;
1062 while i < n {
1063 let k = match kv.get(i) {
1064 Some(Value::Str(s)) => s.clone(),
1065 Some(other) => stringify(other, None, false),
1066 None => format!("$KEY{i}"),
1067 };
1068 let v = kv.get(i + 1).cloned().unwrap_or(Value::Null);
1069 o.insert(k, v);
1070 i += 2;
1071 }
1072 Value::map(o)
1073}
1074
1075pub fn jt(v: &[Value]) -> Value {
1076 Value::list(
1077 v.iter()
1078 .map(|x| if x.is_noval() { Value::Null } else { x.clone() })
1079 .collect(),
1080 )
1081}