diff --git a/kparams-core/src/error.rs b/kparams-core/src/error.rs index f0e6fa2..fe3231a 100644 --- a/kparams-core/src/error.rs +++ b/kparams-core/src/error.rs @@ -6,6 +6,9 @@ pub enum Error { /// Error that might occur during I/O operations. #[error("IO error: `{0}`")] IoError(#[from] std::io::Error), + /// Error that might occur while parsing documents. + #[error("parse error: `{0}`")] + ParseError(String), } /// Type alias for the standard [`Result`] type. diff --git a/kparams-parser/src/lib.rs b/kparams-parser/src/lib.rs index f3e8048..a30b959 100644 --- a/kparams-parser/src/lib.rs +++ b/kparams-parser/src/lib.rs @@ -5,5 +5,8 @@ /// RST parser. pub mod parser; +/// Parsed title. +pub mod title; + #[macro_use] extern crate pest_derive; diff --git a/kparams-parser/src/parser.rs b/kparams-parser/src/parser.rs index a9ed787..0dc3182 100644 --- a/kparams-parser/src/parser.rs +++ b/kparams-parser/src/parser.rs @@ -1,8 +1,10 @@ -#![allow(missing_docs)] // pest_derive does not have doc comments. +#![allow(missing_docs)] // pest_derive does not generate doc comments +use crate::title::Title; +use kparams_core::error::{Error, Result}; use kparams_core::kernel::{Documentation, Parameter}; use pest::Parser; -use pest::Token; +use std::convert::TryFrom; /// Parser for the reStructuredText format. #[derive(Parser)] @@ -13,37 +15,23 @@ impl RstParser { /// Parses the given reStructuredText input and returns the [`kernel documentation`]. /// /// [`kernel documentation`]: Documentation - pub fn parse_docs(input: &str) -> Documentation { - let rst_document = Self::parse(Rule::document, input).expect("failed to parse document"); + pub fn parse_docs(input: &str) -> Result { let mut kernel_parameters = Vec::new(); + let rst_document = + Self::parse(Rule::document, input).map_err(|e| Error::ParseError(e.to_string()))?; let titles = rst_document - .filter(|block| block.as_rule() == Rule::title) - .map(|title| { - ( - title.as_str().lines().next().unwrap(), - title - .tokens() - .filter_map(|token| match token { - Token::Start { rule, pos } | Token::End { rule, pos } => { - if rule == Rule::title { - Some(pos.pos()) - } else { - None - } - } - }) - .collect::>(), - ) - }) - .collect::)>>(); - for (i, (title, pos)) in titles.iter().enumerate() { - assert_eq!(2, pos.len()); - kernel_parameters.push(if let Some(next_title) = titles.get(i + 1) { - Parameter::new(*title, (input[pos[1]..next_title.1[0]]).as_ref()) - } else { - Parameter::new(*title, (input[pos[1]..]).as_ref()) - }); + .filter_map(|pair| Title::try_from(pair).ok()) + .collect::>>(); + for (i, title) in titles.iter().enumerate() { + kernel_parameters.push(Parameter::new( + title.value, + if let Some(next_title) = titles.get(i + 1) { + (input[title.end_pos..next_title.start_pos]).as_ref() + } else { + (input[title.end_pos..]).as_ref() + }, + )); } - Documentation::new(kernel_parameters) + Ok(Documentation::new(kernel_parameters)) } } diff --git a/kparams-parser/src/title.rs b/kparams-parser/src/title.rs new file mode 100644 index 0000000..0e9e849 --- /dev/null +++ b/kparams-parser/src/title.rs @@ -0,0 +1,54 @@ +use crate::parser::Rule; +use kparams_core::error::Error as ErrorImpl; +use pest::iterators::Pair; +use pest::Token; +use std::convert::TryFrom; + +/// Title from the parsed RST document. +#[derive(Debug, Default)] +pub struct Title<'a> { + /// Title value. + pub value: &'a str, + /// Start position of the title. + pub start_pos: usize, + /// End position of the title. + pub end_pos: usize, +} + +impl<'a> TryFrom> for Title<'a> { + type Error = ErrorImpl; + + fn try_from(pair: Pair<'a, Rule>) -> Result { + let mut title = Title::default(); + + // check if the rule matches + if pair.as_rule() == Rule::title { + return Err(ErrorImpl::ParseError(String::from( + "parsed section is not a title", + ))); + } + + // set the actual title + if let Some(value) = pair.as_str().lines().next() { + title.value = value; + } else { + return Err(ErrorImpl::ParseError(String::from("invalid title"))); + } + + // set token positions + pair.tokens().for_each(|token| match token { + Token::Start { rule, pos } => { + if rule == Rule::title { + title.start_pos = pos.pos(); + } + } + Token::End { rule, pos } => { + if rule == Rule::title { + title.end_pos = pos.pos(); + } + } + }); + + Ok(title) + } +} diff --git a/kparams/src/lib.rs b/kparams/src/lib.rs index 176e964..0cbde4b 100644 --- a/kparams/src/lib.rs +++ b/kparams/src/lib.rs @@ -13,7 +13,7 @@ pub fn run() -> Result<()> { let sysctl_docs = kernel_docs.join("admin-guide").join("sysctl"); let kernel_section = reader::read_to_string(&sysctl_docs.join("kernel.rst"))?; - let kernel_section_docs = RstParser::parse_docs(&kernel_section); + let kernel_section_docs = RstParser::parse_docs(&kernel_section)?; for kernel_parameter in kernel_section_docs.parameters { println!("## {}", kernel_parameter.name); println!("{}", kernel_parameter.description);