Skip to content

Commit

Permalink
feat: use winnow instead of nom
Browse files Browse the repository at this point in the history
Winnow provides a similar, and arguably cleaner, combinator-based
parsing capability as nom.

Using winnow reduces the number of dependencies by one. Nom was only
used by StGit, but winnow is also used by gix for its parsing needs, and
was thus already a transitive dependency.
  • Loading branch information
jpgrayson committed Aug 19, 2024
1 parent e666bf8 commit f826271
Show file tree
Hide file tree
Showing 19 changed files with 558 additions and 412 deletions.
18 changes: 1 addition & 17 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ flate2 = "1"
gix = { version = "0.64", default-features = false, features = ["command", "revision"] }
indexmap = "2.1"
is-terminal = "0.4"
nom = { version = "7", default-features = false, features = ["std"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
strsim = "0.11"
Expand All @@ -54,6 +53,7 @@ time = { version = "0.3.31", default-features = false, features = [
"macros",
"parsing",
] }
winnow = "0.6.18"

curl = { version = "0.4", optional = true }

Expand Down
6 changes: 3 additions & 3 deletions src/branchloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ impl FromStr for BranchLocator {
type Err = anyhow::Error;

fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
use nom::combinator::{all_consuming, complete};
complete(all_consuming(crate::patch::parse::branch_locator))(s)
.map(|(_, loc)| loc)
use winnow::Parser;
crate::patch::parse::branch_locator
.parse(s)
.map_err(|_| anyhow::anyhow!("invalid branch locator `{s}`"))
}
}
Expand Down
38 changes: 9 additions & 29 deletions src/patch/locator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ impl FromStr for PatchLocator {
type Err = Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
use nom::combinator::{all_consuming, complete};
complete(all_consuming(super::parse::patch_locator))(s)
.map(|(_, location)| location)
use winnow::Parser;
super::parse::patch_locator
.parse(s)
.map_err(|_| Error::InvalidPatchLocator(s.to_string()))
}
}
Expand Down Expand Up @@ -412,32 +412,12 @@ impl PatchLocator {
offsets: self.offsets.clone(),
},
PatchId::Name(patchname) => {
use nom::combinator::{all_consuming, complete};
let patch_offsets = |s| {
complete(all_consuming(super::parse::patch_offsets))(s)
.map(|(_, output)| output)
.ok()
};
let oid_prefix_offsets = |s| {
complete(all_consuming(super::parse::oid_prefix_offsets))(s)
.map(|(_, output)| output)
.ok()
};
let sign_number_offsets = |s| {
complete(all_consuming(super::parse::sign_number_offsets))(s)
.map(|(_, output)| output)
.ok()
};
let patch_locator_top = |s| {
complete(all_consuming(super::parse::patch_locator_top))(s)
.map(|(_, output)| output)
.ok()
};
let patch_locator_base = |s| {
complete(all_consuming(super::parse::patch_locator_base))(s)
.map(|(_, output)| output)
.ok()
};
use winnow::Parser;
let patch_offsets = |s| super::parse::patch_offsets.parse(s).ok();
let oid_prefix_offsets = |s| super::parse::oid_prefix_offsets.parse(s).ok();
let sign_number_offsets = |s| super::parse::sign_number_offsets.parse(s).ok();
let patch_locator_top = |s| super::parse::patch_locator_top.parse(s).ok();
let patch_locator_base = |s| super::parse::patch_locator_base.parse(s).ok();

let name: &str = patchname.as_ref();

Expand Down
11 changes: 7 additions & 4 deletions src/patch/offset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

use std::{fmt::Write, str::FromStr};

use winnow::Parser;

use super::{PatchOffsetAtom, PatchOffsets};

#[derive(thiserror::Error, Debug)]
Expand Down Expand Up @@ -36,9 +38,9 @@ impl FromStr for PatchOffsets {
type Err = Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
use nom::combinator::{all_consuming, complete};
complete(all_consuming(super::parse::patch_offsets))(s)
.map(|(_, offsets)| offsets)
use winnow::Parser;
super::parse::patch_offsets
.parse(s)
.map_err(|_| Error::InvalidPatchOffsets(s.to_string()))
}
}
Expand All @@ -62,7 +64,8 @@ impl PatchOffsets {

/// Construct constituent [`PatchOffsetAtom`] instances.
pub(super) fn atoms(&self) -> Vec<PatchOffsetAtom> {
super::parse::patch_offset_atoms(&self.0)
super::parse::patch_offset_atoms
.parse_peek(&self.0)
.expect("previously validated")
.1
}
Expand Down
144 changes: 66 additions & 78 deletions src/patch/parse/locator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@

//! Parsing support for [`PatchLocator`].

use nom::{
branch::alt,
bytes::complete::tag,
character::complete::{char as the_char, hex_digit1},
combinator::{map, map_res, opt, recognize},
multi::{many0, many0_count},
sequence::tuple,
use winnow::{
ascii::hex_digit1,
combinator::{alt, opt, repeat},
PResult, Parser,
};

use super::{
Expand All @@ -18,115 +15,106 @@ use super::{
};
use crate::patch::{PatchId, PatchLocator, PatchOffsetAtom, PatchOffsets};

pub(in super::super) fn patch_locator(input: &str) -> nom::IResult<&str, PatchLocator> {
pub(in super::super) fn patch_locator(input: &mut &str) -> PResult<PatchLocator> {
alt((
patch_locator_name,
patch_locator_from_last,
patch_locator_top,
patch_locator_base,
))(input)
))
.parse_next(input)
}

fn patch_locator_name(input: &str) -> nom::IResult<&str, PatchLocator> {
map(
tuple((patch_name, patch_offsets)),
|(patchname, offsets)| PatchLocator {
fn patch_locator_name(input: &mut &str) -> PResult<PatchLocator> {
(patch_name, patch_offsets)
.map(|(patchname, offsets)| PatchLocator {
id: PatchId::Name(patchname),
offsets,
},
)(input)
})
.parse_next(input)
}

fn patch_locator_from_last(input: &str) -> nom::IResult<&str, PatchLocator> {
map(
tuple((the_char('^'), opt(nonplussed_int), patch_offsets)),
|(_, below_last, offsets)| PatchLocator {
fn patch_locator_from_last(input: &mut &str) -> PResult<PatchLocator> {
('^', opt(nonplussed_int), patch_offsets)
.map(|(_, below_last, offsets)| PatchLocator {
id: PatchId::BelowLast(below_last),
offsets,
},
)(input)
})
.parse_next(input)
}

pub(in super::super) fn patch_locator_top(input: &str) -> nom::IResult<&str, PatchLocator> {
pub(in super::super) fn patch_locator_top(input: &mut &str) -> PResult<PatchLocator> {
alt((
map(tuple((the_char('@'), patch_offsets)), |(_, offsets)| {
PatchLocator {
id: PatchId::Top,
offsets,
}
('@', patch_offsets).map(|(_, offsets)| PatchLocator {
id: PatchId::Top,
offsets,
}),
('~', opt(unsigned_int), patch_offsets).map(|(_, n, offsets)| PatchLocator {
id: PatchId::BelowTop(n),
offsets,
}),
map(
tuple((the_char('~'), opt(unsigned_int), patch_offsets)),
|(_, n, offsets)| PatchLocator {
id: PatchId::BelowTop(n),
offsets,
},
),
))(input)
))
.parse_next(input)
}

pub(in super::super) fn patch_locator_base(input: &str) -> nom::IResult<&str, PatchLocator> {
map(tuple((tag("{base}"), patch_offsets)), |(_, offsets)| {
PatchLocator {
pub(in super::super) fn patch_locator_base(input: &mut &str) -> PResult<PatchLocator> {
("{base}", patch_offsets)
.map(|(_, offsets)| PatchLocator {
id: PatchId::Base,
offsets,
}
})(input)
})
.parse_next(input)
}

pub(in super::super) fn patch_offsets(input: &str) -> nom::IResult<&str, PatchOffsets> {
map(recognize(many0_count(patch_offset_atom)), |s| {
PatchOffsets(String::from(s))
})(input)
pub(in super::super) fn patch_offsets(input: &mut &str) -> PResult<PatchOffsets> {
repeat::<_, _, Vec<PatchOffsetAtom>, _, _>(0.., patch_offset_atom)
.take()
.map(|s: &str| PatchOffsets(s.to_string()))
.parse_next(input)
}

pub(in super::super) fn patch_offset_atoms(
input: &str,
) -> nom::IResult<&str, Vec<PatchOffsetAtom>> {
many0(patch_offset_atom)(input)
pub(in super::super) fn patch_offset_atoms(input: &mut &str) -> PResult<Vec<PatchOffsetAtom>> {
repeat(0.., patch_offset_atom).parse_next(input)
}

pub(in super::super) fn patch_offset_atom(input: &str) -> nom::IResult<&str, PatchOffsetAtom> {
alt((patch_offset_atom_plus, patch_offset_atom_tilde))(input)
pub(in super::super) fn patch_offset_atom(input: &mut &str) -> PResult<PatchOffsetAtom> {
alt((patch_offset_atom_plus, patch_offset_atom_tilde)).parse_next(input)
}

fn patch_offset_atom_plus(input: &str) -> nom::IResult<&str, PatchOffsetAtom> {
map(tuple((the_char('+'), opt(unsigned_int))), |(_, n)| {
PatchOffsetAtom::Plus(n)
})(input)
fn patch_offset_atom_plus(input: &mut &str) -> PResult<PatchOffsetAtom> {
('+', opt(unsigned_int))
.map(|(_, n)| PatchOffsetAtom::Plus(n))
.parse_next(input)
}

fn patch_offset_atom_tilde(input: &str) -> nom::IResult<&str, PatchOffsetAtom> {
map(tuple((the_char('~'), opt(unsigned_int))), |(_, n)| {
PatchOffsetAtom::Tilde(n)
})(input)
fn patch_offset_atom_tilde(input: &mut &str) -> PResult<PatchOffsetAtom> {
('~', opt(unsigned_int))
.map(|(_, n)| PatchOffsetAtom::Tilde(n))
.parse_next(input)
}

pub(in super::super) fn oid_prefix_offsets(
input: &str,
) -> nom::IResult<&str, (gix::hash::Prefix, PatchOffsets)> {
tuple((oid_prefix, patch_offsets))(input)
input: &mut &str,
) -> PResult<(gix::hash::Prefix, PatchOffsets)> {
(oid_prefix, patch_offsets).parse_next(input)
}

fn oid_prefix(input: &str) -> nom::IResult<&str, gix::hash::Prefix> {
map_res(hex_digit1, gix::hash::Prefix::from_hex)(input)
fn oid_prefix(input: &mut &str) -> PResult<gix::hash::Prefix> {
hex_digit1
.try_map(gix::hash::Prefix::from_hex)
.parse_next(input)
}

pub(in super::super) fn sign_number_offsets(
input: &str,
) -> nom::IResult<&str, (Option<Sign>, Option<usize>, PatchOffsets)> {
input: &mut &str,
) -> PResult<(Option<Sign>, Option<usize>, PatchOffsets)> {
alt((
map(tuple((negative_int, patch_offsets)), |(n, offsets)| {
(Some(Sign::Minus), Some(n.unsigned_abs()), offsets)
}),
map(tuple((plusative_int, patch_offsets)), |(n, offsets)| {
(Some(Sign::Plus), Some(n.unsigned_abs()), offsets)
}),
map(tuple((unsigned_int, patch_offsets)), |(n, offsets)| {
(None, Some(n), offsets)
}),
map(tuple((sign, patch_offsets)), |(sign, offsets)| {
(Some(sign), None, offsets)
}),
))(input)
(negative_int, patch_offsets)
.map(|(n, offsets)| (Some(Sign::Minus), Some(n.unsigned_abs()), offsets)),
(plusative_int, patch_offsets)
.map(|(n, offsets)| (Some(Sign::Plus), Some(n.unsigned_abs()), offsets)),
(unsigned_int, patch_offsets).map(|(n, offsets)| (None, Some(n), offsets)),
(sign, patch_offsets).map(|(sign, offsets)| (Some(sign), None, offsets)),
))
.parse_next(input)
}
2 changes: 1 addition & 1 deletion src/patch/parse/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only

//! Parsers for patch and revision specifications using [`nom`].
//! Parsers for patch and revision specifications using [`winnow`].

mod locator;
mod name;
Expand Down
Loading

0 comments on commit f826271

Please sign in to comment.