Skip to content

Commit

Permalink
feat: Add type4 parser (untested)
Browse files Browse the repository at this point in the history
  • Loading branch information
ysthakur committed Aug 18, 2023
1 parent 59ed1c8 commit 0745256
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 11 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ so to configure that, set the `RUST_LOG` environment variable (the link has inst

Things to do:

- Port type 3, type 4, darwin, scdoc, and degroff parsers
- Port darwin, scdoc, and degroff parsers
- Find samples of type 4 to test
- Ensure nested subcommands and multiple subcommands work
- Test .gz, test excluding/including commands and directories
- Add .gz files to the tests, test excluding/including commands and directories
- Figure out why fish only seems to use man1, man6, and man8
12 changes: 9 additions & 3 deletions src/parse/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod type1;
mod type2;
mod type3;
mod type4;
mod util;

use std::{
Expand Down Expand Up @@ -60,13 +61,18 @@ where
/// Parse flags from a man page, trying all of the different parsers and merging
/// their results if multiple parsers could parse the man page. Returns
/// None if none of them could parse the man page.
pub fn parse_manpage_text<S>(text: S) -> Option<Vec<Flag>>
pub fn parse_manpage_text<S>(cmd_name: &str, text: S) -> Option<Vec<Flag>>
where
S: AsRef<str>,
{
let text = text.as_ref();
let mut all_flags: Option<Vec<Flag>> = None;
for res in vec![type1::parse(text), type2::parse(text), type3::parse(text)] {
for res in vec![
type1::parse(cmd_name, text),
type2::parse(cmd_name, text),
type3::parse(cmd_name, text),
type4::parse(cmd_name, text),
] {
if let Some(mut flags) = res {
match &mut all_flags {
Some(prev_flags) => {
Expand Down Expand Up @@ -121,7 +127,7 @@ pub fn parse_from(
if let Some(path) = pre_info.path {
match read_manpage(&path) {
Ok(text) => {
if let Some(mut parsed) = parse_manpage_text(text) {
if let Some(mut parsed) = parse_manpage_text(cmd_name, text) {
flags.append(&mut parsed);
} else {
errors.push(anyhow!("Could not parse man page for '{}'", cmd_name));
Expand Down
4 changes: 2 additions & 2 deletions src/parse/type1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use super::{util, Flag};
/// Ported from Fish's `Type1ManParser`
///
/// todo implement fallback and fallback2 like the Fish script
pub fn parse(page_text: &str) -> Option<Vec<Flag>> {
pub fn parse(cmd_name: &str, page_text: &str) -> Option<Vec<Flag>> {
match util::get_section(r#""OPTIONS""#, page_text) {
Some(content) => {
let mut flags = Vec::new();
Expand All @@ -24,7 +24,7 @@ pub fn parse(page_text: &str) -> Option<Vec<Flag>> {
}
} else {
debug!(
"No .RE found to end description, para: {}",
"In command {cmd_name}, no .RE found to end description, para: {}",
util::truncate(para, 40)
);
}
Expand Down
7 changes: 5 additions & 2 deletions src/parse/type2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use regex::Regex;
use super::{util, Flag};

/// Ported from Fish's `Type2ManParser`
pub fn parse(page_text: &str) -> Option<Vec<Flag>> {
pub fn parse(cmd_name: &str, page_text: &str) -> Option<Vec<Flag>> {
match util::get_section("OPTIONS", page_text) {
Some(content) => {
let mut flags = Vec::new();
Expand All @@ -30,7 +30,10 @@ pub fn parse(page_text: &str) -> Option<Vec<Flag>> {
util::make_flag(options, Some(desc))
} else {
// todo should this be an error instead?
debug!("No description, data: {}", util::truncate(data, 40));
debug!(
"In command {cmd_name}, no description, data: {}",
util::truncate(data, 40)
);
util::make_flag(data, None)
};
if let Some(flag) = flag {
Expand Down
7 changes: 5 additions & 2 deletions src/parse/type3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use super::{util, Flag};
/// Fish's `Type3ManParser` doesn't handle HP...IP...HP, but the man page for
/// sed, at least, uses that, so this parser handles that too.
#[allow(clippy::case_sensitive_file_extension_comparisons)]
pub fn parse(page_text: &str) -> Option<Vec<Flag>> {
pub fn parse(cmd_name: &str, page_text: &str) -> Option<Vec<Flag>> {
match util::get_section("DESCRIPTION", page_text) {
Some(content) => {
let mut flags = Vec::new();
Expand Down Expand Up @@ -52,7 +52,10 @@ pub fn parse(page_text: &str) -> Option<Vec<Flag>> {
}
} else {
// todo should this be an error instead?
debug!("No description, data: {}", util::truncate(data, 40));
debug!(
"In command {cmd_name}, no description, data: {}",
util::truncate(data, 40)
);
}
}
}
Expand Down
39 changes: 39 additions & 0 deletions src/parse/type4.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use log::warn;

use super::{util, Flag};

/// Ported from Fish's `Type4ManParser`
///
/// TODO This is completely untested
#[allow(clippy::case_sensitive_file_extension_comparisons)]
pub fn parse(cmd_name: &str, page_text: &str) -> Option<Vec<Flag>> {
match util::get_section("FUNCTION LETTERS", page_text) {
Some(content) => {
let mut flags = Vec::new();

let mut paras = content.split(".TP");
paras.next(); // Discard the part before the first option
for para in paras {
let data = util::remove_groff_formatting(para);
let data = data.trim();
if let Some((options, desc)) = data.split_once("\n") {
if let Some(flag) = util::make_flag(options, Some(desc)) {
flags.push(flag);
}
} else {
warn!(
"In command {cmd_name}, no description, data: {}",
util::truncate(data, 40)
);
}
}

if flags.is_empty() {
None
} else {
Some(flags)
}
}
None => None,
}
}

0 comments on commit 0745256

Please sign in to comment.