use parsanol::portable::ast::AstNode;
use parsanol::portable::AstArena;
#[derive(Debug, Clone)]
pub enum TNode {
Str(String),
Array(Vec<TNode>),
Hash(Vec<(String, TNode)>),
Nil,
}
pub fn pget<'a>(pairs: &'a [(String, TNode)], key: &str) -> Option<&'a TNode> {
pairs.iter().find(|(k, _)| k == key).map(|(_, v)| v)
}
impl TNode {
pub fn get(&self, key: &str) -> Option<&TNode> {
match self {
TNode::Hash(pairs) => pairs.iter().find(|(k, _)| k == key).map(|(_, v)| v),
_ => None,
}
}
pub fn ruby_array_hash(&self) -> Option<&Vec<(String, TNode)>> {
match self {
TNode::Hash(pairs) => Some(pairs),
_ => None,
}
}
pub fn has_key(&self, key: &str) -> bool {
matches!(self, TNode::Hash(pairs) if pairs.iter().any(|(k, _)| k == key))
}
pub fn single_key(&self) -> Option<&str> {
match self {
TNode::Hash(pairs) if pairs.len() == 1 => Some(pairs[0].0.as_str()),
_ => None,
}
}
pub fn as_str(&self) -> Option<&str> {
match self {
TNode::Str(s) => Some(s),
_ => None,
}
}
pub fn as_array(&self) -> Option<&[TNode]> {
match self {
TNode::Array(items) => Some(items),
_ => None,
}
}
pub fn ruby_array(&self) -> &[TNode] {
match self {
TNode::Array(items) => items,
TNode::Nil => &[],
other => std::slice::from_ref(other),
}
}
}
pub fn resolve(node: &AstNode, arena: &AstArena, input: &str) -> TNode {
match node {
AstNode::Nil => TNode::Nil,
AstNode::InputRef { offset, length } => {
let start = *offset as usize;
let end = start + *length as usize;
TNode::Str(input.get(start..end).unwrap_or_default().to_string())
}
AstNode::StringRef { pool_index } => {
TNode::Str(arena.get_string(*pool_index as usize).to_string())
}
AstNode::Array { pool_index, length } => TNode::Array(
arena
.get_array(*pool_index as usize, *length as usize)
.iter()
.map(|child| resolve(child, arena, input))
.collect(),
),
AstNode::Hash { pool_index, length } => {
let pairs = arena.get_hash_items(*pool_index as usize, *length as usize);
TNode::Hash(
pairs
.into_iter()
.map(|(k, v)| (k, resolve(&v, arena, input)))
.collect(),
)
}
other => TNode::Str(format!("{other:?}")),
}
}
pub fn leaf_text(node: &TNode) -> String {
match node {
TNode::Str(s) => s.clone(),
TNode::Array(items) => items.iter().map(leaf_text).collect(),
TNode::Hash(pairs) => pairs.iter().map(|(_, v)| leaf_text(v)).collect(),
TNode::Nil => String::new(),
}
}