use crate::legislation::{Chamber, CommitteeDocumentType, Congress, MeasureType};
use crate::Citation;
use winnow::ascii::{alphanumeric1, digit1};
use winnow::combinator::{alt, opt, peek};
use winnow::Parser;
use winnow::Result;
fn clean(input: &str) -> String {
input.to_lowercase().replace(['.', ' ', ':'], "")
}
pub fn parse(input: &str) -> Result<Citation> {
let cleaned = clean(input);
let mut input = cleaned.as_str();
alt((
parse_measure,
parse_amendment,
parse_committee_document,
parse_statute,
parse_law,
))
.parse_next(&mut input)
}
fn parse_congress(input: &mut &str) -> Result<Option<Congress>> {
Ok(parse_prefix.parse_next(input).ok().map(Congress))
}
fn parse_long_law(input: &mut &str) -> Result<Citation> {
let (_, congress, _, number) =
("publiclawno", parse_congress, "-", parse_number).parse_next(input)?;
Ok(Citation::Law { congress, number })
}
fn parse_short_law(input: &mut &str) -> Result<Citation> {
let (congress, _, number) =
(parse_congress, alt(("pl", "publ")), parse_number).parse_next(input)?;
Ok(Citation::Law { congress, number })
}
fn parse_law(input: &mut &str) -> Result<Citation> {
alt((parse_long_law, parse_short_law)).parse_next(input)
}
fn parse_amendment(input: &mut &str) -> Result<Citation> {
let (congress, chamber, _, number) = (
parse_congress,
parse_chamber,
alt(("amdt", "a")),
parse_number,
)
.parse_next(input)?;
Ok(Citation::Amendment {
congress,
chamber,
number,
})
}
fn parse_statute(input: &mut &str) -> Result<Citation> {
let (volume, _, page) = (parse_prefix, "stat", parse_number).parse_next(input)?;
Ok(Citation::Statute { volume, page })
}
fn parse_measure(input: &mut &str) -> Result<Citation> {
let (congress, chamber, measure_type, number, suffix) = (
parse_congress,
parse_chamber,
parse_measure_type,
parse_number,
parse_suffix,
)
.parse_next(input)?;
Ok(Citation::Measure {
congress,
chamber,
measure_type,
number,
version: suffix.map(String::from),
})
}
fn parse_committee_document(input: &mut &str) -> Result<Citation> {
let (congress, chamber, document_type, number) = (
parse_congress,
parse_chamber,
parse_committee_document_type,
parse_number,
)
.parse_next(input)?;
Ok(Citation::CommitteeDocument {
congress,
chamber,
document_type,
number,
})
}
fn parse_committee_document_type(input: &mut &str) -> Result<CommitteeDocumentType> {
alt((
alt(("rpt", "rept")).map(|_| CommitteeDocumentType::Report),
"prt".map(|_| CommitteeDocumentType::Print),
))
.parse_next(input)
}
fn parse_house(input: &mut &str) -> Result<Chamber> {
alt((
("hr", peek(digit1)).map(|_| Chamber::House),
"h".map(|_| Chamber::House),
))
.parse_next(input)
}
fn parse_chamber(input: &mut &str) -> Result<Chamber> {
alt((
parse_house,
'j'.map(|_| Chamber::Joint),
's'.map(|_| Chamber::Senate),
))
.parse_next(input)
}
fn parse_measure_type(input: &mut &str) -> Result<MeasureType> {
alt((
"cres".map(|_| MeasureType::ConcurrentResolution),
"jres".map(|_| MeasureType::JointResolution),
"res".map(|_| MeasureType::Resolution),
"".map(|_| MeasureType::Bill),
))
.parse_next(input)
}
fn parse_prefix(input: &mut &str) -> Result<usize> {
digit1.parse_to().parse_next(input)
}
fn parse_number(input: &mut &str) -> Result<usize> {
digit1.parse_to().parse_next(input)
}
fn parse_suffix<'s>(input: &mut &'s str) -> Result<Option<&'s str>> {
opt(alphanumeric1).parse_next(input)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_parse_house_bill() {
let mut input = "118hr8070";
let expected = Citation::Measure {
congress: Some(Congress(118)),
chamber: Chamber::House,
measure_type: MeasureType::Bill,
number: 8070,
version: None,
};
let result = parse_measure(&mut input);
assert_eq!(expected, result.unwrap());
}
#[test]
fn test_parse_senate_bill() {
let mut input = "118s8070";
let expected = Citation::Measure {
congress: Some(Congress(118)),
chamber: Chamber::Senate,
measure_type: MeasureType::Bill,
number: 8070,
version: None,
};
let result = parse_measure(&mut input);
assert_eq!(expected, result.unwrap());
}
#[test]
fn test_parse_sr_fails() {
let mut input = "118sr8070";
let result = parse_measure(&mut input);
assert!(result.is_err());
}
#[test]
fn test_parse_house_resolution() {
let mut input = "118hres111";
let expected = Citation::Measure {
congress: Some(Congress(118)),
chamber: Chamber::House,
measure_type: MeasureType::Resolution,
number: 111,
version: None,
};
let result = parse_measure(&mut input);
assert_eq!(expected, result.unwrap());
}
#[test]
fn test_parse_house_concurrent_resolution() {
let mut input = "118hcres111";
let expected = Citation::Measure {
congress: Some(Congress(118)),
chamber: Chamber::House,
measure_type: MeasureType::ConcurrentResolution,
number: 111,
version: None,
};
let result = parse_measure(&mut input);
assert_eq!(expected, result.unwrap());
}
#[test]
fn test_parse_house_joint_resolution() {
let mut input = "118hjres111";
let expected = Citation::Measure {
congress: Some(Congress(118)),
chamber: Chamber::House,
measure_type: MeasureType::JointResolution,
number: 111,
version: None,
};
let result = parse_measure(&mut input);
assert_eq!(expected, result.unwrap());
}
#[test]
fn test_parse_senate_resolution() {
let mut input = "118sres111";
let expected = Citation::Measure {
congress: Some(Congress(118)),
chamber: Chamber::Senate,
measure_type: MeasureType::Resolution,
number: 111,
version: None,
};
let result = parse_measure(&mut input);
assert_eq!(expected, result.unwrap());
}
#[test]
fn test_parse_senate_concurrent_resolution() {
let mut input = "118scres111";
let expected = Citation::Measure {
congress: Some(Congress(118)),
chamber: Chamber::Senate,
measure_type: MeasureType::ConcurrentResolution,
number: 111,
version: None,
};
let result = parse_measure(&mut input);
assert_eq!(expected, result.unwrap());
}
#[test]
fn test_parse_senate_joint_resolution() {
let mut input = "118sjres111";
let expected = Citation::Measure {
congress: Some(Congress(118)),
chamber: Chamber::Senate,
measure_type: MeasureType::JointResolution,
number: 111,
version: None,
};
let result = parse_measure(&mut input);
assert_eq!(expected, result.unwrap());
}
#[test]
fn test_parse_house_bill_with_version() {
let mut input = "118hr8070ih";
let expected = Citation::Measure {
congress: Some(Congress(118)),
chamber: Chamber::House,
measure_type: MeasureType::Bill,
number: 8070,
version: Some("ih".to_string()),
};
let result = parse_measure(&mut input);
assert_eq!(expected, result.unwrap());
}
#[test]
fn test_parse_house_report() {
let mut input = "118hrpt529";
let expected = Citation::CommitteeDocument {
congress: Some(Congress(118)),
chamber: Chamber::House,
document_type: CommitteeDocumentType::Report,
number: 529,
};
let result = parse_committee_document(&mut input);
assert_eq!(expected, result.unwrap());
}
#[test]
fn test_new_parse_measure() {
let mut input = "118hr8070ih";
let expected = Citation::Measure {
congress: Some(Congress(118)),
chamber: Chamber::House,
measure_type: MeasureType::Bill,
number: 8070,
version: Some("ih".to_string()),
};
let result = parse(&mut input);
assert_eq!(expected, result.unwrap());
}
#[test]
fn test_new_parse_committee_document() {
let mut input = "118hrpt529";
let expected = Citation::CommitteeDocument {
congress: Some(Congress(118)),
chamber: Chamber::House,
document_type: CommitteeDocumentType::Report,
number: 529,
};
let result = parse(&mut input);
assert_eq!(expected, result.unwrap());
}
#[test]
fn test_new_parse_amendtment() {
let mut input = "118samdt529";
let expected = Citation::Amendment {
congress: Some(Congress(118)),
chamber: Chamber::Senate,
number: 529,
};
let result = parse(&mut input);
assert_eq!(expected, result.unwrap());
}
#[test]
fn test_parse_statute() {
let mut input = "86Stat1326";
let expected = Citation::Statute {
volume: 86,
page: 1326,
};
let result = parse(&mut input);
assert_eq!(expected, result.unwrap());
}
#[test]
fn test_parse_long_law() {
let mut input = "Public Law No: 119-68";
let expected = Citation::Law {
congress: Some(Congress(119)),
number: 68,
};
let result = parse(&mut input);
assert_eq!(expected, result.unwrap())
}
#[test]
fn test_parse_short_law() {
let mut input = "119pl68";
let expected = Citation::Law {
congress: Some(Congress(119)),
number: 68,
};
let result = parse(&mut input);
assert_eq!(expected, result.unwrap())
}
#[test]
fn test_parse_longer_short_law() {
let mut input = "119publ68";
let expected = Citation::Law {
congress: Some(Congress(119)),
number: 68,
};
let result = parse(&mut input);
assert_eq!(expected, result.unwrap())
}
}