diff --git a/src/tools.rs b/src/tools.rs index 63222468..f6e80833 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -33,6 +33,7 @@ pub mod ticket; pub mod timer; pub mod statistics; pub mod systemd; +pub mod nom; mod wrapped_reader_stream; pub use wrapped_reader_stream::*; diff --git a/src/tools/disks/zpool_list.rs b/src/tools/disks/zpool_list.rs index 44ce5883..d7c7c1fc 100644 --- a/src/tools/disks/zpool_list.rs +++ b/src/tools/disks/zpool_list.rs @@ -1,8 +1,11 @@ use anyhow::{bail, Error}; +use crate::tools::nom::{ + multispace0, multispace1, notspace1, IResult, +}; + use nom::{ - error::VerboseError, - bytes::complete::{take_while, take_while1, take_till, take_till1}, + bytes::complete::{take_while1, take_till, take_till1}, combinator::{map_res, all_consuming, recognize, opt}, sequence::{preceded, tuple}, character::complete::{digit1, char, line_ending}, @@ -26,22 +29,6 @@ pub struct ZFSPoolInfo { pub devices: Vec, } -type IResult> = Result<(I, O), nom::Err>; - -/// Recognizes zero or more spaces and tabs (but not carage returns or line feeds) -fn multispace0(i: &str) -> IResult<&str, &str> { - take_while(|c| c == ' ' || c == '\t')(i) -} - -/// Recognizes one or more spaces and tabs (but not carage returns or line feeds) -fn multispace1(i: &str) -> IResult<&str, &str> { - take_while1(|c| c == ' ' || c == '\t')(i) -} - -/// Recognizes one or more non-whitespace-characters -fn notspace1(i: &str) -> IResult<&str, &str> { - take_while1(|c| !(c == ' ' || c == '\t' || c == '\n'))(i) -} fn parse_optional_u64(i: &str) -> IResult<&str, Option> { if i.starts_with('-') { diff --git a/src/tools/disks/zpool_status.rs b/src/tools/disks/zpool_status.rs index 72995228..44d04de0 100644 --- a/src/tools/disks/zpool_status.rs +++ b/src/tools/disks/zpool_status.rs @@ -1,36 +1,19 @@ -use anyhow::{bail, Error}; +use anyhow::{Error}; use serde_json::{json, Value}; use ::serde::{Deserialize, Serialize}; -use nom::{ - error::VerboseError, - bytes::complete::{tag, take_while, take_while1}, - combinator::{map_res, all_consuming, recognize, opt}, - sequence::{preceded}, - character::complete::{digit1, line_ending}, - multi::{many0}, +use crate::tools::nom::{ + parse_complete, parse_failure, multispace0, multispace1, notspace1, parse_u64, IResult, }; -type IResult> = Result<(I, O), nom::Err>; +use nom::{ + bytes::complete::{tag, take_while, take_while1}, + combinator::{opt}, + sequence::{preceded}, + character::complete::{line_ending}, + multi::{many0,many1}, +}; -/// Recognizes zero or more spaces and tabs (but not carage returns or line feeds) -fn multispace0(i: &str) -> IResult<&str, &str> { - take_while(|c| c == ' ' || c == '\t')(i) -} - -// Recognizes one or more spaces and tabs (but not carage returns or line feeds) -fn multispace1(i: &str) -> IResult<&str, &str> { - take_while1(|c| c == ' ' || c == '\t')(i) -} - -/// Recognizes one or more non-whitespace-characters -fn notspace1(i: &str) -> IResult<&str, &str> { - take_while1(|c| !(c == ' ' || c == '\t' || c == '\n'))(i) -} - -fn parse_u64(i: &str) -> IResult<&str, u64> { - map_res(recognize(digit1), str::parse)(i) -} #[derive(Debug, Serialize, Deserialize)] pub struct ZFSPoolVDevState { @@ -46,7 +29,12 @@ pub struct ZFSPoolVDevState { fn parse_zpool_status_vdev(i: &str) -> IResult<&str, ZFSPoolVDevState> { - let (i, indent) = multispace0(i)?; + let (n, indent) = multispace0(i)?; + if (indent.len() & 1) != 0 { + return Err(parse_failure(n, "wrong indent length")); + } + let i = n; + let (i, vdev_name) = notspace1(i)?; let (i, state) = preceded(multispace1, notspace1)(i)?; let (i, read) = preceded(multispace1, parse_u64)(i)?; @@ -81,7 +69,7 @@ fn parse_zpool_status_tree(i: &str) -> IResult<&str, Vec> { let (i, _) = line_ending(i)?; // parse vdev list - many0(parse_zpool_status_vdev)(i) + many1(parse_zpool_status_vdev)(i) } fn parse_zpool_status_field(i: &str) -> IResult<&str, (String, String)> { @@ -127,29 +115,11 @@ fn parse_zpool_status_field(i: &str) -> IResult<&str, (String, String)> { } pub fn parse_zpool_status_config_tree(i: &str) -> Result, Error> { - match all_consuming(parse_zpool_status_tree)(&i) { - Err(nom::Err::Error(err)) | - Err(nom::Err::Failure(err)) => { - bail!("unable to parse zfs status config tree - {}", nom::error::convert_error(&i, err)); - } - Err(err) => { - bail!("unable to parse zfs status config tree: {}", err); - } - Ok((_, data)) => Ok(data), - } + parse_complete("zfs status config tree", i, parse_zpool_status_tree) } fn parse_zpool_status(i: &str) -> Result, Error> { - match all_consuming(many0(parse_zpool_status_field))(i) { - Err(nom::Err::Error(err)) | - Err(nom::Err::Failure(err)) => { - bail!("unable to parse zfs status output - {}", nom::error::convert_error(i, err)); - } - Err(err) => { - bail!("unable to parse zfs status output - {}", err); - } - Ok((_, data)) => Ok(data), - } + parse_complete("zfs status output", i, many0(parse_zpool_status_field)) } pub fn vdev_list_to_tree(vdev_list: &[ZFSPoolVDevState]) -> Value { @@ -265,7 +235,7 @@ config: NAME STATE READ WRITE CKSUM tank DEGRADED 0 0 0 - mirror-0 DEGRADED 0 0 0 + mirror-0 DEGRADED 0 0 0 c1t0d0 ONLINE 0 0 0 c1t2d0 ONLINE 0 0 0 c1t1d0 UNAVAIL 0 0 0 cannot open diff --git a/src/tools/nom.rs b/src/tools/nom.rs new file mode 100644 index 00000000..0b0fe816 --- /dev/null +++ b/src/tools/nom.rs @@ -0,0 +1,58 @@ +use anyhow::{bail, Error}; + +use nom::{ + error::{ParseError, VerboseError}, + bytes::complete::{take_while, take_while1}, + combinator::{map_res, all_consuming, recognize}, + character::complete::{digit1}, +}; + +pub type IResult> = Result<(I, O), nom::Err>; + +pub fn parse_error<'a>(i: &'a str, context: &'static str) -> nom::Err> { + let err = VerboseError { errors: Vec::new() }; + let err = VerboseError::add_context(i, context, err); + nom::Err::Error(err) +} + +pub fn parse_failure<'a>(i: &'a str, context: &'static str) -> nom::Err> { + let err = VerboseError { errors: Vec::new() }; + let err = VerboseError::add_context(i, context, err); + nom::Err::Failure(err) +} + +/// Recognizes zero or more spaces and tabs (but not carage returns or line feeds) +pub fn multispace0(i: &str) -> IResult<&str, &str> { + take_while(|c| c == ' ' || c == '\t')(i) +} + +/// Recognizes one or more spaces and tabs (but not carage returns or line feeds) +pub fn multispace1(i: &str) -> IResult<&str, &str> { + take_while1(|c| c == ' ' || c == '\t')(i) +} + +/// Recognizes one or more non-whitespace-characters +pub fn notspace1(i: &str) -> IResult<&str, &str> { + take_while1(|c| !(c == ' ' || c == '\t' || c == '\n'))(i) +} + +/// Parse a 64 bit unsigned integer +pub fn parse_u64(i: &str) -> IResult<&str, u64> { + map_res(recognize(digit1), str::parse)(i) +} + +pub fn parse_complete<'a, F, O>(what: &str, i: &'a str, parser: F) -> Result + where F: Fn(&'a str) -> IResult<&'a str, O>, +{ + match all_consuming(parser)(i) { + Err(nom::Err::Error(err)) | + Err(nom::Err::Failure(err)) => { + bail!("unable to parse {} - {}", what, nom::error::convert_error(i, err)); + } + Err(err) => { + bail!("unable to parse {} - {}", what, err); + } + Ok((_, data)) => Ok(data), + } + +} diff --git a/src/tools/systemd/parse_time.rs b/src/tools/systemd/parse_time.rs index 2cd8d38c..00bcbfba 100644 --- a/src/tools/systemd/parse_time.rs +++ b/src/tools/systemd/parse_time.rs @@ -5,8 +5,12 @@ use lazy_static::lazy_static; use super::time::*; +use crate::tools::nom::{ + parse_complete, parse_u64, parse_error, IResult, +}; + use nom::{ - error::{context, ParseError, VerboseError}, + error::{context}, bytes::complete::{tag, take_while1}, combinator::{map_res, all_consuming, opt, recognize}, sequence::{pair, preceded, tuple}, @@ -14,14 +18,6 @@ use nom::{ multi::separated_nonempty_list, }; -type IResult> = Result<(I, O), nom::Err>; - -fn parse_error<'a>(i: &'a str, context: &'static str) -> nom::Err> { - let err = VerboseError { errors: Vec::new() }; - let err = VerboseError::add_context(i, context, err); - nom::Err::Error(err) -} - lazy_static! { pub static ref TIME_SPAN_UNITS: HashMap<&'static str, f64> = { let mut map = HashMap::new(); @@ -100,11 +96,7 @@ fn parse_time_comp(max: usize) -> impl Fn(&str) -> IResult<&str, u32> { } } -fn parse_u64(i: &str) -> IResult<&str, u64> { - map_res(recognize(digit1), str::parse)(i) -} - -fn parse_weekday(i: &str) -> IResult<&str, WeekDays, VerboseError<&str>> { +fn parse_weekday(i: &str) -> IResult<&str, WeekDays> { let (i, text) = alpha1(i)?; match text.to_ascii_lowercase().as_str() { @@ -192,20 +184,7 @@ fn parse_time_spec(i: &str) -> IResult<&str, (Vec, Vec Result { - match all_consuming(parse_calendar_event_incomplete)(i) { - Err(nom::Err::Error(VerboseError { errors })) | - Err(nom::Err::Failure(VerboseError { errors })) => { - if errors.is_empty() { - bail!("unable to parse calendar event"); - } else { - bail!("unable to parse calendar event at '{}': {:?}", errors[0].0, errors[0].1); - } - } - Err(err) => { - bail!("unable to parse calendar event: {}", err); - } - Ok((_, ce)) => Ok(ce), - } + parse_complete("calendar event", i, parse_calendar_event_incomplete) } fn parse_calendar_event_incomplete(mut i: &str) -> IResult<&str, CalendarEvent> {