use rpm_spec::ast::{BuildScriptKind, Scriptlet, Section, ShellBody, Span, SpecFile, SpecItem};
const MAX_DEPTH: u32 = 128;
pub fn for_each_shell_body<'ast, F>(spec: &'ast SpecFile<Span>, mut f: F)
where
F: FnMut(BodyLocation, &'ast ShellBody),
{
walk_items(&spec.items, &mut f, 0);
}
pub fn for_each_buildscript<'ast, F>(spec: &'ast SpecFile<Span>, mut f: F)
where
F: FnMut(BuildScriptKind, &'ast ShellBody, Span),
{
for_each_shell_body(spec, |loc, body| {
if let BodyLocation::BuildScript { kind, span } = loc {
f(kind, body, span);
}
});
}
pub fn for_each_scriptlet<'ast, F>(spec: &'ast SpecFile<Span>, mut f: F)
where
F: FnMut(&'ast Scriptlet<Span>),
{
walk_scriptlets(&spec.items, &mut f, 0);
}
#[derive(Debug, Clone, Copy)]
pub enum BodyLocation {
BuildScript { kind: BuildScriptKind, span: Span },
Scriptlet {
kind: rpm_spec::ast::ScriptletKind,
span: Span,
},
Trigger {
#[allow(dead_code)]
kind: rpm_spec::ast::TriggerKind,
span: Span,
},
FileTrigger {
#[allow(dead_code)]
kind: rpm_spec::ast::FileTriggerKind,
span: Span,
},
Verify { span: Span },
Sepolicy { span: Span },
}
fn walk_items<'ast, F>(items: &'ast [SpecItem<Span>], f: &mut F, depth: u32)
where
F: FnMut(BodyLocation, &'ast ShellBody),
{
if depth >= MAX_DEPTH {
return;
}
for item in items {
match item {
SpecItem::Section(boxed) => visit_section(boxed.as_ref(), f),
SpecItem::Conditional(c) => {
for branch in &c.branches {
walk_items(&branch.body, f, depth + 1);
}
if let Some(els) = &c.otherwise {
walk_items(els, f, depth + 1);
}
}
_ => {}
}
}
}
fn visit_section<'ast, F>(section: &'ast Section<Span>, f: &mut F)
where
F: FnMut(BodyLocation, &'ast ShellBody),
{
match section {
Section::BuildScript { kind, body, data } => {
f(
BodyLocation::BuildScript {
kind: *kind,
span: *data,
},
body,
);
}
Section::Scriptlet(s) => {
f(
BodyLocation::Scriptlet {
kind: s.kind,
span: s.data,
},
&s.body,
);
}
Section::Trigger(t) => {
f(
BodyLocation::Trigger {
kind: t.kind,
span: t.data,
},
&t.body,
);
}
Section::FileTrigger(t) => {
f(
BodyLocation::FileTrigger {
kind: t.kind,
span: t.data,
},
&t.body,
);
}
Section::Verify { body, data, .. } => {
f(BodyLocation::Verify { span: *data }, body);
}
Section::Sepolicy { body, data, .. } => {
f(BodyLocation::Sepolicy { span: *data }, body);
}
_ => {}
}
}
fn walk_scriptlets<'ast, F>(items: &'ast [SpecItem<Span>], f: &mut F, depth: u32)
where
F: FnMut(&'ast Scriptlet<Span>),
{
if depth >= MAX_DEPTH {
return;
}
for item in items {
match item {
SpecItem::Section(boxed) => {
if let Section::Scriptlet(s) = boxed.as_ref() {
f(s);
}
}
SpecItem::Conditional(c) => {
for branch in &c.branches {
walk_scriptlets(&branch.body, f, depth + 1);
}
if let Some(els) = &c.otherwise {
walk_scriptlets(els, f, depth + 1);
}
}
_ => {}
}
}
}