const MANIFEST_URL: &str =
"https://settings.svc.halowaypoint.com/settings/hipc/e2a0a7c6-6efe-42af-9283-c2ab73250c48";
fn child_text<'a, 'input>(node: roxmltree::Node<'a, 'input>, name: &str) -> &'a str {
node.children()
.find(|child| child.is_element() && child.tag_name().name() == name)
.and_then(|child| child.text())
.unwrap_or("")
}
fn descendant_text<'a, 'input>(node: roxmltree::Node<'a, 'input>, name: &str) -> &'a str {
node.descendants()
.find(|child| child.is_element() && child.tag_name().name() == name)
.and_then(|child| child.text())
.unwrap_or("")
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let xml = reqwest::get(MANIFEST_URL)
.await?
.error_for_status()?
.text()
.await?;
let document = roxmltree::Document::parse(&xml)?;
let endpoints = document
.descendants()
.find(|node| node.has_tag_name("Endpoints"))
.ok_or("manifest has no Endpoints section")?;
let authorities = document
.descendants()
.find(|node| node.has_tag_name("Authorities"))
.ok_or("manifest has no Authorities section")?;
let authority_hosts = authorities
.children()
.filter(|node| node.is_element())
.filter_map(|entry| {
let key = child_text(entry, "Key");
let value = entry
.children()
.find(|child| child.is_element() && child.tag_name().name() == "Value")?;
Some((key, descendant_text(value, "Hostname")))
})
.collect::<std::collections::HashMap<_, _>>();
println!("NAME\tAUTHORITY\tHOST\tCLEARANCE\tPATH\tQUERY");
for entry in endpoints.children().filter(|node| node.is_element()) {
let key = child_text(entry, "Key");
let value = entry
.children()
.find(|child| child.is_element() && child.tag_name().name() == "Value")
.ok_or("endpoint entry has no Value")?;
let authority = descendant_text(value, "AuthorityId");
println!(
"{}\t{}\t{}\t{}\t{}\t{}",
key,
authority,
authority_hosts.get(authority).copied().unwrap_or(""),
descendant_text(value, "ClearanceAware"),
descendant_text(value, "Path"),
descendant_text(value, "QueryString"),
);
}
Ok(())
}