use std::str::FromStr;
use roxmltree::Node;
use crate::{
uslm::{
AmendingAction, BillAmendment, ElementType, UscReference,
parser::{ParseError, extract_number},
},
utils::load_xml_file,
};
pub struct AmendmentData {
pub bill_id: String,
pub amendments: Vec<BillAmendment>,
}
pub type Result<T> = std::result::Result<T, ParseError>;
pub fn parse_bill_amendments(path: &str) -> Result<AmendmentData> {
let xml_str = load_xml_file(path)?;
let doc = roxmltree::Document::parse(&xml_str)?;
let plaw_node = doc.descendants().find(|n| n.has_tag_name("pLaw"));
match plaw_node {
None => Err(ParseError::UnableToParseElement(
"Could not find public law document".to_string(),
)),
Some(node) => {
let element_type = ElementType::from_str(node.tag_name().name())?;
let number = extract_number(element_type, &node)?;
let amendments = get_amendments(&node);
Ok(AmendmentData {
bill_id: number.value,
amendments,
})
}
}
}
pub fn get_amendments(node: &Node) -> Vec<BillAmendment> {
let nodes = node
.descendants()
.filter(|p| p.attribute("role").unwrap_or_default() == "instruction");
nodes
.map(|n| {
let source_path = n.attribute("identifier").unwrap();
get_amendment_data(&n, source_path)
})
.collect()
}
fn get_amendment_data(node: &Node, source_path: &str) -> BillAmendment {
let mut target_paths: Vec<UscReference> = Vec::new();
let mut action_types: Vec<AmendingAction> = Vec::new();
for descendant in node.descendants() {
match descendant.tag_name().name().to_lowercase().as_str() {
"ref" => {
if let Some(href) = descendant.attribute("href") {
if href.starts_with("/us/usc/") {
let display_text = descendant
.text()
.map(|s| s.to_string())
.unwrap_or_else(|| href.to_string());
target_paths.push(UscReference {
path: href.to_string(),
display_text,
});
}
}
}
"amendingaction" => {
let action_text = descendant
.attribute("type")
.expect("I expect that Amending Action tags are never empty, so I'll be surprised if this ever fails");
if let Ok(action) = AmendingAction::from_str(action_text) {
action_types.push(action);
}
}
_ => {}
}
}
BillAmendment {
source_path: source_path.to_string(),
target_paths,
action_types,
}
}