use crate::ast::*;
pub fn run(p: &mut Program) {
let mut lifted_probes = Vec::new();
let mut probe_counter = 1;
for decl in &mut p.decls {
if let Decl::Backend {
body: Some(fields), ..
} = decl
{
for field in fields {
if let FieldValue::Probe(_) = field.value {
let probe_name = format!("$anon_probe_{}", probe_counter);
let span = field.span;
let probe_fields = if let FieldValue::Probe(pf) = std::mem::replace(
&mut field.value,
FieldValue::ProbeRef(probe_name.clone()),
) {
pf
} else {
unreachable!()
};
let new_probe = Decl::Probe {
name: probe_name.clone(),
body: probe_fields,
span,
};
lifted_probes.push(new_probe);
probe_counter += 1;
}
}
}
}
p.decls.extend(lifted_probes);
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn test_single_inline_probe_lifted() {
let inline_probe_fields = vec![
builder::fexpr("url", builder::str_("/")),
builder::fexpr("timeout", builder::dur(5.0)),
];
let mut prog = builder::program(vec![builder::backend(
"web",
vec![
builder::fexpr("host", builder::str_("example.com")),
builder::field("probe", FieldValue::Probe(inline_probe_fields.clone())),
],
)]);
run(&mut prog);
assert_eq!(prog.decls.len(), 2);
if let Decl::Backend {
body: Some(fields), ..
} = &prog.decls[0]
{
assert_eq!(fields.len(), 2);
assert!(
matches!(&fields[1].value, FieldValue::ProbeRef(name) if name == "$anon_probe_1")
);
} else {
panic!("Expected Backend declaration");
}
if let Decl::Probe { name, body, .. } = &prog.decls[1] {
assert_eq!(name, "$anon_probe_1");
assert_eq!(body.len(), 2);
assert_eq!(body[0].name, "url");
assert_eq!(body[1].name, "timeout");
} else {
panic!("Expected Probe declaration");
}
}
#[test]
fn test_two_backends_with_inline_probes() {
let probe1_fields = vec![builder::fexpr("url", builder::str_("/health"))];
let probe2_fields = vec![
builder::fexpr("url", builder::str_("/status")),
builder::fexpr("expected_response", builder::num("200")),
];
let mut prog = builder::program(vec![
builder::backend(
"web1",
vec![
builder::fexpr("host", builder::str_("web1.example.com")),
builder::field("probe", FieldValue::Probe(probe1_fields)),
],
),
builder::backend(
"web2",
vec![
builder::fexpr("host", builder::str_("web2.example.com")),
builder::field("probe", FieldValue::Probe(probe2_fields)),
],
),
]);
run(&mut prog);
assert_eq!(prog.decls.len(), 4);
if let Decl::Backend {
body: Some(fields), ..
} = &prog.decls[0]
{
if let FieldValue::ProbeRef(name) = &fields[1].value {
assert_eq!(name, "$anon_probe_1");
} else {
panic!("Expected ProbeRef in first backend");
}
}
if let Decl::Backend {
body: Some(fields), ..
} = &prog.decls[1]
{
if let FieldValue::ProbeRef(name) = &fields[1].value {
assert_eq!(name, "$anon_probe_2");
} else {
panic!("Expected ProbeRef in second backend");
}
}
if let Decl::Probe { name, body, .. } = &prog.decls[2] {
assert_eq!(name, "$anon_probe_1");
assert_eq!(body.len(), 1);
}
if let Decl::Probe { name, body, .. } = &prog.decls[3] {
assert_eq!(name, "$anon_probe_2");
assert_eq!(body.len(), 2);
}
}
#[test]
fn test_backend_without_inline_probe_unchanged() {
let mut prog = builder::program(vec![builder::backend(
"web",
vec![
builder::fexpr("host", builder::str_("example.com")),
builder::field("probe", FieldValue::ProbeRef("my_probe".to_string())),
],
)]);
let original_len = prog.decls.len();
run(&mut prog);
assert_eq!(prog.decls.len(), original_len);
if let Decl::Backend {
body: Some(fields), ..
} = &prog.decls[0]
{
assert!(matches!(&fields[1].value, FieldValue::ProbeRef(name) if name == "my_probe"));
}
}
#[test]
fn test_probe_span_reused() {
let inline_probe_fields = vec![builder::fexpr("url", builder::str_("/"))];
let backend = builder::backend(
"web",
vec![builder::field(
"probe",
FieldValue::Probe(inline_probe_fields),
)],
);
let field_span = if let Decl::Backend {
body: Some(ref fields),
..
} = backend
{
fields[0].span
} else {
panic!("Expected backend");
};
let mut prog = builder::program(vec![backend]);
run(&mut prog);
if let Decl::Probe { span, .. } = &prog.decls[1] {
assert_eq!(*span, field_span);
}
}
}