1use parsanol::portable::ast::AstNode;
8use parsanol::portable::AstArena;
9
10#[derive(Debug, Clone)]
11pub enum TNode {
12 Str(String),
13 Array(Vec<TNode>),
14 Hash(Vec<(String, TNode)>),
16 Nil,
17}
18
19pub fn pget<'a>(pairs: &'a [(String, TNode)], key: &str) -> Option<&'a TNode> {
21 pairs.iter().find(|(k, _)| k == key).map(|(_, v)| v)
22}
23
24impl TNode {
25 pub fn get(&self, key: &str) -> Option<&TNode> {
26 match self {
27 TNode::Hash(pairs) => pairs.iter().find(|(k, _)| k == key).map(|(_, v)| v),
28 _ => None,
29 }
30 }
31
32 pub fn ruby_array_hash(&self) -> Option<&Vec<(String, TNode)>> {
34 match self {
35 TNode::Hash(pairs) => Some(pairs),
36 _ => None,
37 }
38 }
39
40 pub fn has_key(&self, key: &str) -> bool {
41 matches!(self, TNode::Hash(pairs) if pairs.iter().any(|(k, _)| k == key))
42 }
43
44 pub fn single_key(&self) -> Option<&str> {
46 match self {
47 TNode::Hash(pairs) if pairs.len() == 1 => Some(pairs[0].0.as_str()),
48 _ => None,
49 }
50 }
51
52 pub fn as_str(&self) -> Option<&str> {
53 match self {
54 TNode::Str(s) => Some(s),
55 _ => None,
56 }
57 }
58
59 pub fn as_array(&self) -> Option<&[TNode]> {
60 match self {
61 TNode::Array(items) => Some(items),
62 _ => None,
63 }
64 }
65
66 pub fn ruby_array(&self) -> &[TNode] {
68 match self {
69 TNode::Array(items) => items,
70 TNode::Nil => &[],
71 other => std::slice::from_ref(other),
72 }
73 }
74}
75
76pub fn resolve(node: &AstNode, arena: &AstArena, input: &str) -> TNode {
78 match node {
79 AstNode::Nil => TNode::Nil,
80 AstNode::InputRef { offset, length } => {
81 let start = *offset as usize;
82 let end = start + *length as usize;
83 TNode::Str(input.get(start..end).unwrap_or_default().to_string())
84 }
85 AstNode::StringRef { pool_index } => {
86 TNode::Str(arena.get_string(*pool_index as usize).to_string())
87 }
88 AstNode::Array { pool_index, length } => TNode::Array(
89 arena
90 .get_array(*pool_index as usize, *length as usize)
91 .iter()
92 .map(|child| resolve(child, arena, input))
93 .collect(),
94 ),
95 AstNode::Hash { pool_index, length } => {
96 let pairs = arena.get_hash_items(*pool_index as usize, *length as usize);
97 TNode::Hash(
98 pairs
99 .into_iter()
100 .map(|(k, v)| (k, resolve(&v, arena, input)))
101 .collect(),
102 )
103 }
104 other => TNode::Str(format!("{other:?}")),
105 }
106}
107
108pub fn leaf_text(node: &TNode) -> String {
111 match node {
112 TNode::Str(s) => s.clone(),
113 TNode::Array(items) => items.iter().map(leaf_text).collect(),
114 TNode::Hash(pairs) => pairs.iter().map(|(_, v)| leaf_text(v)).collect(),
115 TNode::Nil => String::new(),
116 }
117}