use std::collections::HashSet;
use crate::{Cv, Gv, Perl, SvKind};
pub struct SubEntry {
pub package: String,
pub name: String,
pub cv: Cv,
pub gv: Option<Gv>,
}
pub struct StashWalker<'p> {
perl: &'p Perl,
seen: HashSet<String>,
}
impl<'p> StashWalker<'p> {
pub fn new(perl: &'p Perl) -> Self {
StashWalker {
perl,
seen: HashSet::new(),
}
}
pub fn walk(&mut self, pack: &str, emit: &mut dyn FnMut(&SubEntry)) {
if !self.seen.insert(pack.to_string()) {
return;
}
let Some(stash) = self.perl.gv_stashpv(pack, 0) else {
return;
};
for (key, val) in stash.iter(self.perl) {
let name = String::from_utf8_lossy(key).into_owned();
match val.kind() {
SvKind::Ref(target) => {
if let SvKind::Code(cv) = target.kind() {
emit(&SubEntry {
package: pack.to_string(),
name,
cv,
gv: None,
});
}
}
SvKind::Glob(gv) => {
if let Some(cv) = gv.cv() {
emit(&SubEntry {
package: pack.to_string(),
name: name.clone(),
cv,
gv: Some(gv),
});
}
if let Some(base) = name.strip_suffix("::") {
if !base.is_empty() {
let child = if pack == "main" {
base.to_string()
} else {
format!("{pack}::{base}")
};
self.walk(&child, emit);
}
}
}
_ => {}
}
}
}
}