forked from gentoo-utils/gentoo-utils
impl atom parsing
This commit is contained in:
106
src/atom/mod.rs
Normal file
106
src/atom/mod.rs
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
use core::option::Option;
|
||||||
|
|
||||||
|
use crate::useflag::UseFlag;
|
||||||
|
use get::Get;
|
||||||
|
|
||||||
|
pub mod parsers;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Blocker {
|
||||||
|
Weak,
|
||||||
|
Strong,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum VersionOperator {
|
||||||
|
Lt,
|
||||||
|
Gt,
|
||||||
|
LtEq,
|
||||||
|
GtEq,
|
||||||
|
Roughly,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Get)]
|
||||||
|
pub struct Category(#[get(method = "get")] String);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Get)]
|
||||||
|
pub struct Name(#[get(method = "get")] String);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Get)]
|
||||||
|
pub struct VersionNumber(#[get(method = "get")] String);
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum VersionSuffixKind {
|
||||||
|
Alpha,
|
||||||
|
Beta,
|
||||||
|
Pre,
|
||||||
|
Rc,
|
||||||
|
P,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Get)]
|
||||||
|
pub struct VersionSuffix {
|
||||||
|
kind: VersionSuffixKind,
|
||||||
|
number: Option<VersionNumber>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Get)]
|
||||||
|
pub struct Version {
|
||||||
|
numbers: Vec<VersionNumber>,
|
||||||
|
letter: Option<char>,
|
||||||
|
suffixes: Vec<VersionSuffix>,
|
||||||
|
rev: Option<VersionNumber>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum SlotOperator {
|
||||||
|
Eq,
|
||||||
|
Star,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Get)]
|
||||||
|
pub struct SlotName(#[get(method = "name")] String);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Get)]
|
||||||
|
pub struct Slot {
|
||||||
|
slot: SlotName,
|
||||||
|
sub: Option<SlotName>,
|
||||||
|
operator: Option<SlotOperator>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum UseDepNegate {
|
||||||
|
Minus,
|
||||||
|
Exclamation,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum UseDepSign {
|
||||||
|
Enabled,
|
||||||
|
Disabled,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum UseDepCondition {
|
||||||
|
Eq,
|
||||||
|
Question,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Get)]
|
||||||
|
pub struct UseDep {
|
||||||
|
negate: Option<UseDepNegate>,
|
||||||
|
flag: UseFlag,
|
||||||
|
sign: Option<UseDepSign>,
|
||||||
|
condition: Option<UseDepCondition>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Get)]
|
||||||
|
pub struct Atom {
|
||||||
|
blocker: Option<Blocker>,
|
||||||
|
version_operator: Option<VersionOperator>,
|
||||||
|
category: Category,
|
||||||
|
name: Name,
|
||||||
|
version: Option<Version>,
|
||||||
|
slot: Option<Slot>,
|
||||||
|
usedeps: Vec<UseDep>,
|
||||||
|
}
|
||||||
227
src/atom/parsers.rs
Normal file
227
src/atom/parsers.rs
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
use mon::{Parser, r#if, not, numeric1, one_of, opt, tag, take_while};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
atom::{
|
||||||
|
Atom, Blocker, Category, Name, Slot, SlotName, SlotOperator, UseDep, UseDepCondition,
|
||||||
|
UseDepNegate, UseDepSign, Version, VersionNumber, VersionOperator, VersionSuffix,
|
||||||
|
VersionSuffixKind,
|
||||||
|
},
|
||||||
|
useflag::parsers::useflag,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn blocker<'a>() -> impl Parser<&'a str, Output = Blocker> {
|
||||||
|
tag("!!")
|
||||||
|
.map(|_| Blocker::Strong)
|
||||||
|
.or(tag("!").map(|_| Blocker::Weak))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn version_operator<'a>() -> impl Parser<&'a str, Output = VersionOperator> {
|
||||||
|
tag("<=")
|
||||||
|
.map(|_| VersionOperator::LtEq)
|
||||||
|
.or(tag(">=").map(|_| VersionOperator::GtEq))
|
||||||
|
.or(tag("<").map(|_| VersionOperator::Lt))
|
||||||
|
.or(tag(">").map(|_| VersionOperator::Gt))
|
||||||
|
.or(tag("~").map(|_| VersionOperator::Roughly))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn version_number<'a>() -> impl Parser<&'a str, Output = VersionNumber> {
|
||||||
|
numeric1().map(|output: &str| VersionNumber(output.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn version_suffix_kind<'a>() -> impl Parser<&'a str, Output = VersionSuffixKind> {
|
||||||
|
tag("alpha")
|
||||||
|
.map(|_| VersionSuffixKind::Alpha)
|
||||||
|
.or(tag("beta").map(|_| VersionSuffixKind::Beta))
|
||||||
|
.or(tag("pre").map(|_| VersionSuffixKind::Pre))
|
||||||
|
.or(tag("rc").map(|_| VersionSuffixKind::Rc))
|
||||||
|
.or(tag("p").map(|_| VersionSuffixKind::P))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn version_suffix<'a>() -> impl Parser<&'a str, Output = VersionSuffix> {
|
||||||
|
version_suffix_kind()
|
||||||
|
.and(opt(version_number()))
|
||||||
|
.map(|(kind, number)| VersionSuffix { kind, number })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn version<'a>() -> impl Parser<&'a str, Output = Version> {
|
||||||
|
let numbers = version_number().separated_list1(tag("."));
|
||||||
|
let suffixes = version_suffix().separated_list(tag("_"));
|
||||||
|
let rev = version_number().preceded_by(tag("-r"));
|
||||||
|
|
||||||
|
numbers
|
||||||
|
.and(opt(r#if(|c: &char| c.is_ascii_alphabetic())))
|
||||||
|
.and(opt(suffixes.preceded_by(tag("_"))))
|
||||||
|
.and(opt(rev))
|
||||||
|
.map(|(((numbers, letter), suffixes), rev)| Version {
|
||||||
|
numbers,
|
||||||
|
letter,
|
||||||
|
suffixes: suffixes.unwrap_or(Vec::new()),
|
||||||
|
rev,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn category<'a>() -> impl Parser<&'a str, Output = Category> {
|
||||||
|
let start = r#if(|c: &char| c.is_ascii_alphanumeric() || *c == '_');
|
||||||
|
let rest = take_while(r#if(|c: &char| {
|
||||||
|
c.is_ascii_alphanumeric() || "+_.-".contains(*c)
|
||||||
|
}));
|
||||||
|
|
||||||
|
start
|
||||||
|
.and(rest)
|
||||||
|
.recognize()
|
||||||
|
.map(|output: &str| Category(output.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name<'a>() -> impl Parser<&'a str, Output = Name> {
|
||||||
|
let start = r#if(|c: &char| c.is_ascii_alphanumeric() || *c == '_');
|
||||||
|
let rest = take_while(
|
||||||
|
r#if(|c: &char| c.is_ascii_alphanumeric() || "_+".contains(*c)).or(one_of("-".chars())
|
||||||
|
.and_not(
|
||||||
|
version()
|
||||||
|
.preceded_by(tag("-"))
|
||||||
|
.followed_by(not(r#if(|c: &char| {
|
||||||
|
c.is_ascii_alphanumeric() || "_+-".contains(*c)
|
||||||
|
}))),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
|
||||||
|
start
|
||||||
|
.and(rest)
|
||||||
|
.recognize()
|
||||||
|
.map(|output: &str| Name(output.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn slot_operator<'a>() -> impl Parser<&'a str, Output = SlotOperator> {
|
||||||
|
tag("=")
|
||||||
|
.map(|_| SlotOperator::Eq)
|
||||||
|
.or(tag("*").map(|_| SlotOperator::Star))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn slotname<'a>() -> impl Parser<&'a str, Output = SlotName> {
|
||||||
|
let start = r#if(|c: &char| c.is_ascii_alphanumeric() || *c == '_');
|
||||||
|
let rest = take_while(r#if(|c: &char| {
|
||||||
|
c.is_ascii_alphanumeric() || "+_.-".contains(*c)
|
||||||
|
}));
|
||||||
|
|
||||||
|
start
|
||||||
|
.and(rest)
|
||||||
|
.recognize()
|
||||||
|
.map(|output: &str| SlotName(output.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn slot<'a>() -> impl Parser<&'a str, Output = Slot> {
|
||||||
|
slotname()
|
||||||
|
.and(opt(slotname().preceded_by(tag("/"))))
|
||||||
|
.and(opt(slot_operator()))
|
||||||
|
.map(|((slot, sub), operator)| Slot {
|
||||||
|
slot,
|
||||||
|
sub,
|
||||||
|
operator,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn usedep_negate<'a>() -> impl Parser<&'a str, Output = UseDepNegate> {
|
||||||
|
tag("-")
|
||||||
|
.map(|_| UseDepNegate::Minus)
|
||||||
|
.or(tag("!").map(|_| UseDepNegate::Exclamation))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn usedep_sign<'a>() -> impl Parser<&'a str, Output = UseDepSign> {
|
||||||
|
tag("(-)")
|
||||||
|
.map(|_| UseDepSign::Disabled)
|
||||||
|
.or(tag("(+)").map(|_| UseDepSign::Enabled))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn usedep_condition<'a>() -> impl Parser<&'a str, Output = UseDepCondition> {
|
||||||
|
tag("=")
|
||||||
|
.map(|_| UseDepCondition::Eq)
|
||||||
|
.or(tag("?").map(|_| UseDepCondition::Question))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn usedep<'a>() -> impl Parser<&'a str, Output = UseDep> {
|
||||||
|
opt(usedep_negate())
|
||||||
|
.and(useflag())
|
||||||
|
.and(opt(usedep_sign()))
|
||||||
|
.and(opt(usedep_condition()))
|
||||||
|
.map(|(((negate, flag), sign), condition)| UseDep {
|
||||||
|
negate,
|
||||||
|
flag,
|
||||||
|
sign,
|
||||||
|
condition,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn atom<'a>() -> impl Parser<&'a str, Output = Atom> {
|
||||||
|
opt(blocker())
|
||||||
|
.and(opt(version_operator()))
|
||||||
|
.and(category())
|
||||||
|
.and(name().preceded_by(tag("/")))
|
||||||
|
.and(opt(version().preceded_by(tag("-"))))
|
||||||
|
.and(opt(slot().preceded_by(tag(":"))))
|
||||||
|
.and(opt(usedep()
|
||||||
|
.separated_list(tag(","))
|
||||||
|
.delimited_by(tag("["), tag("]"))))
|
||||||
|
.map(
|
||||||
|
|((((((blocker, version_operator), category), name), version), slot), usedeps)| Atom {
|
||||||
|
blocker,
|
||||||
|
version_operator,
|
||||||
|
category,
|
||||||
|
name,
|
||||||
|
version,
|
||||||
|
slot,
|
||||||
|
usedeps: usedeps.unwrap_or(Vec::new()),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
|
||||||
|
use mon::input::InputIter;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_version() {
|
||||||
|
let it = InputIter::new("1.0.0v_alpha1_beta1-r1");
|
||||||
|
|
||||||
|
version().check_finished(it).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_name() {
|
||||||
|
let it = InputIter::new("foo-1-bar-1.0.0");
|
||||||
|
|
||||||
|
match name().parse(it) {
|
||||||
|
Ok((_, output)) => {
|
||||||
|
assert_eq!(output.0.as_str(), "foo-1-bar");
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_atom() {
|
||||||
|
let it = InputIter::new(
|
||||||
|
"!!>=cat/pkg-1-foo-1.0.0v_alpha1_p20250326-r1:primary/sub=[!a(+),-b(-)=,c?]",
|
||||||
|
);
|
||||||
|
|
||||||
|
atom().check_finished(it).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cursed_atom() {
|
||||||
|
let it = InputIter::new(
|
||||||
|
"!!>=_.+-0-/_-test-T-123_beta1_-4a-6+-_p--1.00.02b_alpha3_pre_p4-r5:slot/_-+6-9=[test(+),test(-)]",
|
||||||
|
);
|
||||||
|
|
||||||
|
atom().check_finished(it).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_atom_with_star_in_non_empty_slot() {
|
||||||
|
let it = InputIter::new("foo/bar-1.0.0:*/subslot");
|
||||||
|
|
||||||
|
assert!(atom().check_finished(it).is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/lib.rs
Normal file
5
src/lib.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#![deny(clippy::pedantic)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
pub mod atom;
|
||||||
|
pub mod useflag;
|
||||||
10
src/useflag/mod.rs
Normal file
10
src/useflag/mod.rs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
pub mod parsers;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct UseFlag(String);
|
||||||
|
|
||||||
|
impl UseFlag {
|
||||||
|
pub fn get(&self) -> &str {
|
||||||
|
self.0.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/useflag/parsers.rs
Normal file
10
src/useflag/parsers.rs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
use mon::{Parser, alpha1, alphanumeric, one_of, take_while};
|
||||||
|
|
||||||
|
use crate::useflag::UseFlag;
|
||||||
|
|
||||||
|
pub fn useflag<'a>() -> impl Parser<&'a str, Output = UseFlag> {
|
||||||
|
alpha1()
|
||||||
|
.and(alphanumeric().or(take_while(one_of("+_@-".chars()))))
|
||||||
|
.recognize()
|
||||||
|
.map(|output: &str| UseFlag(output.to_string()))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user