summaryrefslogtreecommitdiff
path: root/src/atom
diff options
context:
space:
mode:
authorJohn Turner <jturner.usa@gmail.com>2025-12-17 06:45:27 +0000
committerJohn Turner <jturner.usa@gmail.com>2025-12-17 06:45:27 +0000
commitc63c3e8c8c73ed7c036df7511ca190cdb96d92e2 (patch)
tree3262ef318f03388d37ac28493e42c1638c227c27 /src/atom
parent0ec856797256b5d9807929e1b32c03756eb43124 (diff)
downloadgentoo-utils-c63c3e8c8c73ed7c036df7511ca190cdb96d92e2.tar.gz
Diffstat (limited to 'src/atom')
-rw-r--r--src/atom/meson.build1
-rw-r--r--src/atom/mod.rs772
-rw-r--r--src/atom/parsers.rs599
3 files changed, 0 insertions, 1372 deletions
diff --git a/src/atom/meson.build b/src/atom/meson.build
deleted file mode 100644
index a7331a8..0000000
--- a/src/atom/meson.build
+++ /dev/null
@@ -1 +0,0 @@
-sources += files('mod.rs', 'parsers.rs')
diff --git a/src/atom/mod.rs b/src/atom/mod.rs
deleted file mode 100644
index 24cb555..0000000
--- a/src/atom/mod.rs
+++ /dev/null
@@ -1,772 +0,0 @@
-use core::{
- fmt::{self},
- option::Option,
-};
-use std::cmp::Ordering;
-
-use crate::useflag::UseFlag;
-
-use get::Get;
-
-use itertools::Itertools;
-
-mod parsers;
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub enum Blocker {
- Weak,
- Strong,
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub enum VersionOperator {
- Lt,
- Gt,
- Eq,
- LtEq,
- GtEq,
- Roughly,
-}
-
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Get)]
-pub struct Category(#[get(method = "get", kind = "deref")] String);
-
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Get)]
-pub struct Name(#[get(method = "get", kind = "deref")] String);
-
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Get)]
-pub struct VersionNumber(#[get(method = "get", kind = "deref")] String);
-
-#[derive(Debug, Clone, PartialEq, Eq, Hash, Get)]
-struct VersionNumbers(#[get(method = "get", kind = "deref")] Vec<VersionNumber>);
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub enum VersionSuffixKind {
- Alpha,
- Beta,
- Pre,
- Rc,
- P,
-}
-
-#[derive(Clone, Debug, Hash, PartialEq, Eq, Get)]
-pub struct VersionSuffix {
- kind: VersionSuffixKind,
- number: Option<VersionNumber>,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, Hash, Get)]
-pub struct VersionSuffixes(#[get(method = "get", kind = "deref")] Vec<VersionSuffix>);
-
-#[derive(Debug, Clone, Get, PartialEq, Eq, Hash)]
-pub struct BuildId(#[get(method = "get", kind = "deref")] String);
-
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Get)]
-pub struct Version {
- numbers: VersionNumbers,
- letter: Option<char>,
- suffixes: VersionSuffixes,
- rev: Option<VersionNumber>,
- build_id: Option<BuildId>,
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub struct Wildcard;
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub enum SlotOperator {
- Eq,
- Star,
-}
-
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Get)]
-pub struct SlotName(#[get(method = "name", kind = "deref")] String);
-
-#[derive(Clone, Debug, PartialEq, Eq, Hash)]
-pub enum Slot {
- Wildcard,
- Equal,
- NameEqual {
- primary: SlotName,
- sub: Option<SlotName>,
- },
- Name {
- primary: SlotName,
- sub: Option<SlotName>,
- },
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub enum UseDepNegate {
- Minus,
- Exclamation,
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub enum UseDepSign {
- Enabled,
- Disabled,
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub enum UseDepCondition {
- Eq,
- Question,
-}
-
-#[derive(Clone, Debug, PartialEq, Eq, Hash)]
-pub struct Repo(String);
-
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Get)]
-pub struct UseDep {
- negate: Option<UseDepNegate>,
- flag: UseFlag,
- sign: Option<UseDepSign>,
- condition: Option<UseDepCondition>,
-}
-
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Get)]
-pub struct Cp {
- category: Category,
- name: Name,
-}
-
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Get)]
-pub struct Cpv {
- category: Category,
- name: Name,
- version: Version,
- slot: Option<Slot>,
-}
-
-#[derive(Clone, Debug, Get, PartialEq, Eq, Hash)]
-pub struct Atom {
- blocker: Option<Blocker>,
- category: Category,
- name: Name,
- version: Option<(VersionOperator, Version, Option<Wildcard>)>,
- slot: Option<Slot>,
- repo: Option<Repo>,
- #[get(kind = "deref")]
- usedeps: Vec<UseDep>,
-}
-
-impl Cpv {
- #[must_use]
- pub fn into_cp(self) -> Cp {
- Cp {
- name: self.name,
- category: self.category,
- }
- }
-}
-
-impl Atom {
- #[must_use]
- pub fn version_operator(&self) -> Option<VersionOperator> {
- self.version.clone().map(|(oper, _, _)| oper)
- }
-
- #[must_use]
- pub fn into_cp(self) -> Cp {
- Cp {
- category: self.category,
- name: self.name,
- }
- }
-
- #[must_use]
- pub fn into_cpv(self) -> Option<Cpv> {
- match self.version {
- Some((_, version, _)) => Some(Cpv {
- category: self.category,
- name: self.name,
- version,
- slot: self.slot,
- }),
- None => None,
- }
- }
-}
-
-impl VersionNumber {
- #[must_use]
- pub fn cmp_as_ints(&self, other: &Self) -> Ordering {
- let a = self.get().trim_start_matches('0');
- let b = other.get().trim_start_matches('0');
-
- a.len().cmp(&b.len()).then_with(|| a.cmp(b))
- }
-
- #[must_use]
- pub fn cmp_as_str(&self, other: &Self) -> Ordering {
- if self.get().starts_with('0') || other.get().starts_with('0') {
- let a = self.get().trim_end_matches('0');
- let b = other.get().trim_end_matches('0');
-
- a.cmp(b)
- } else {
- self.cmp_as_ints(other)
- }
- }
-}
-
-impl PartialOrd for BuildId {
- fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-impl Ord for BuildId {
- fn cmp(&self, other: &Self) -> Ordering {
- // build-id may not start with a zero so we dont need to strip them
- self.get()
- .len()
- .cmp(&other.get().len())
- .then_with(|| self.get().cmp(other.get()))
- }
-}
-
-impl PartialOrd for VersionSuffix {
- fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
- Some(self.cmp(other))
- }
-}
-
-impl Ord for VersionSuffix {
- fn cmp(&self, other: &Self) -> Ordering {
- match &self.kind.cmp(&other.kind) {
- Ordering::Less => Ordering::Less,
- Ordering::Greater => Ordering::Greater,
- Ordering::Equal => match (&self.number, &other.number) {
- (Some(a), Some(b)) => a.cmp_as_ints(b),
- (Some(a), None) if a.get().chars().all(|c| c == '0') => Ordering::Equal,
- (None, Some(b)) if b.get().chars().all(|c| c == '0') => Ordering::Equal,
- (Some(_), None) => Ordering::Greater,
- (None, Some(_)) => Ordering::Less,
- (None, None) => Ordering::Equal,
- },
- }
- }
-}
-
-impl PartialOrd for VersionSuffixes {
- fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-impl Ord for VersionSuffixes {
- fn cmp(&self, other: &Self) -> Ordering {
- let mut a = self.get().iter();
- let mut b = other.get().iter();
-
- loop {
- match (a.next(), b.next()) {
- (Some(a), Some(b)) => match a.cmp(b) {
- Ordering::Less => break Ordering::Less,
- Ordering::Greater => break Ordering::Greater,
- Ordering::Equal => (),
- },
- (Some(a), None) if matches!(a.kind, VersionSuffixKind::P) => {
- break Ordering::Greater;
- }
- (Some(_), None) => break Ordering::Less,
- (None, Some(b)) if matches!(b.kind, VersionSuffixKind::P) => break Ordering::Less,
- (None, Some(_)) => break Ordering::Greater,
- (None, None) => break Ordering::Equal,
- }
- }
- }
-}
-
-impl PartialOrd for VersionNumbers {
- fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-impl Ord for VersionNumbers {
- fn cmp(&self, other: &Self) -> Ordering {
- match self
- .get()
- .first()
- .unwrap()
- .cmp_as_ints(other.get().first().unwrap())
- {
- Ordering::Less => Ordering::Less,
- Ordering::Greater => Ordering::Greater,
- Ordering::Equal => {
- let mut a = self.get().iter().skip(1);
- let mut b = other.get().iter().skip(1);
-
- loop {
- match (a.next(), b.next()) {
- (Some(a), Some(b)) => match a.cmp_as_str(b) {
- Ordering::Less => break Ordering::Less,
- Ordering::Greater => break Ordering::Greater,
- Ordering::Equal => (),
- },
-
- (Some(_), None) => break Ordering::Greater,
- (None, Some(_)) => break Ordering::Less,
- (None, None) => break Ordering::Equal,
- }
- }
- }
- }
- }
-}
-
-impl PartialOrd for Version {
- fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-impl Ord for Version {
- fn cmp(&self, other: &Self) -> Ordering {
- match self.numbers.cmp(&other.numbers) {
- Ordering::Less => return Ordering::Less,
- Ordering::Greater => return Ordering::Greater,
- Ordering::Equal => (),
- }
-
- match (self.letter, other.letter) {
- (Some(a), Some(b)) if a < b => return Ordering::Less,
- (Some(a), Some(b)) if a > b => return Ordering::Greater,
- (Some(a), Some(b)) if a == b => (),
- (Some(_), None) => return Ordering::Greater,
- (None, Some(_)) => return Ordering::Less,
- (None, None) => (),
- _ => unreachable!(),
- }
-
- match self.suffixes.cmp(&other.suffixes) {
- Ordering::Less => return Ordering::Less,
- Ordering::Greater => return Ordering::Greater,
- Ordering::Equal => (),
- }
-
- match (&self.rev, &other.rev) {
- (Some(a), Some(b)) => match a.cmp_as_ints(b) {
- Ordering::Less => return Ordering::Less,
- Ordering::Greater => return Ordering::Greater,
- Ordering::Equal => (),
- },
- (Some(a), None) if a.get().chars().all(|c| c == '0') => (),
- (Some(_), None) => return Ordering::Greater,
- (None, Some(b)) if b.get().chars().all(|c| c == '0') => (),
- (None, Some(_)) => return Ordering::Less,
- (None, None) => (),
- }
-
- match (&self.build_id, &other.build_id) {
- (Some(a), Some(b)) => a.cmp(b),
- (Some(_), None) => Ordering::Greater,
- (None, Some(_)) => Ordering::Less,
- (None, None) => Ordering::Equal,
- }
- }
-}
-
-impl PartialOrd for Cpv {
- fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- if self.category == other.category && self.name == other.name {
- Some(self.version.cmp(&other.version))
- } else {
- None
- }
- }
-}
-
-impl fmt::Display for Blocker {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::Weak => write!(f, "!"),
- Self::Strong => write!(f, "!!"),
- }
- }
-}
-
-impl fmt::Display for VersionOperator {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::Lt => write!(f, "<"),
- Self::Gt => write!(f, ">"),
- Self::Eq => write!(f, "="),
- Self::LtEq => write!(f, "<="),
- Self::GtEq => write!(f, ">="),
- Self::Roughly => write!(f, "~"),
- }
- }
-}
-
-impl fmt::Display for Category {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{}", self.0)
- }
-}
-
-impl fmt::Display for Name {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{}", self.0)
- }
-}
-
-impl fmt::Display for VersionNumber {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{}", self.0)
- }
-}
-
-impl fmt::Display for BuildId {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{}", self.get())
- }
-}
-
-impl fmt::Display for VersionSuffixKind {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::Alpha => write!(f, "alpha"),
- Self::Beta => write!(f, "beta"),
- Self::Pre => write!(f, "pre"),
- Self::Rc => write!(f, "rc"),
- Self::P => write!(f, "p"),
- }
- }
-}
-
-impl fmt::Display for VersionSuffix {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{}", self.kind)?;
-
- if let Some(number) = self.number.as_ref() {
- write!(f, "{number}")?;
- }
-
- Ok(())
- }
-}
-
-impl fmt::Display for Version {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let numbers = self
- .numbers
- .get()
- .iter()
- .map(VersionNumber::get)
- .intersperse(".")
- .collect::<String>();
-
- let suffixes = self
- .suffixes
- .get()
- .iter()
- .map(VersionSuffix::to_string)
- .intersperse("_".to_string())
- .collect::<String>();
-
- write!(f, "{numbers}")?;
-
- if let Some(letter) = self.letter {
- write!(f, "{letter}")?;
- }
-
- if !suffixes.is_empty() {
- write!(f, "_{suffixes}")?;
- }
-
- if let Some(rev) = self.rev.as_ref() {
- write!(f, "-r{rev}")?;
- }
-
- if let Some(build_id) = self.build_id.as_ref() {
- write!(f, "-{build_id}")?;
- }
-
- Ok(())
- }
-}
-
-impl fmt::Display for SlotOperator {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::Eq => write!(f, "="),
- Self::Star => write!(f, "*"),
- }
- }
-}
-
-impl fmt::Display for SlotName {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{}", self.0)
- }
-}
-
-impl fmt::Display for Slot {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::Wildcard => write!(f, "*"),
- Self::Equal => {
- write!(f, "=")
- }
- Self::NameEqual { primary, sub } => {
- write!(f, "{primary}")?;
-
- if let Some(sub) = sub {
- write!(f, "/{sub}")?;
- }
-
- write!(f, "=")
- }
- Self::Name { primary, sub } => {
- write!(f, "{primary}")?;
-
- if let Some(sub) = sub {
- write!(f, "/{sub}")?;
- }
-
- Ok(())
- }
- }
- }
-}
-
-impl fmt::Display for UseDepNegate {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::Minus => write!(f, "-"),
- Self::Exclamation => write!(f, "!"),
- }
- }
-}
-
-impl fmt::Display for UseDepSign {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::Enabled => write!(f, "(+)"),
- Self::Disabled => write!(f, "(-)"),
- }
- }
-}
-
-impl fmt::Display for UseDepCondition {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::Eq => write!(f, "="),
- Self::Question => write!(f, "?"),
- }
- }
-}
-
-impl fmt::Display for UseDep {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- if let Some(negate) = self.negate.as_ref() {
- write!(f, "{negate}")?;
- }
-
- write!(f, "{}", self.flag)?;
-
- if let Some(sign) = self.sign.as_ref() {
- write!(f, "{sign}")?;
- }
-
- if let Some(condition) = self.condition.as_ref() {
- write!(f, "{condition}")?;
- }
-
- Ok(())
- }
-}
-
-impl fmt::Display for Cp {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{}/{}", &self.category, &self.name)
- }
-}
-
-impl fmt::Display for Cpv {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{}/{}-{}", &self.category, &self.name, &self.version)?;
-
- if let Some(slot) = self.slot.as_ref() {
- write!(f, ":{slot}")?;
- }
-
- Ok(())
- }
-}
-
-impl fmt::Display for Atom {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- if let Some(blocker) = self.blocker.as_ref() {
- write!(f, "{blocker}")?;
- }
-
- if let Some(version_operator) = self.version_operator().as_ref() {
- write!(f, "{version_operator}")?;
- }
-
- write!(f, "{}", self.category)?;
- write!(f, "/")?;
- write!(f, "{}", self.name)?;
-
- if let Some((_, version, None)) = self.version() {
- write!(f, "-{version}")?;
- } else if let Some((_, version, Some(_))) = self.version() {
- write!(f, "-{version}*")?;
- }
-
- if let Some(slot) = self.slot.as_ref() {
- write!(f, ":{slot}")?;
- }
-
- let usedeps = self
- .usedeps
- .iter()
- .map(UseDep::to_string)
- .intersperse(",".to_string())
- .collect::<String>();
-
- if !usedeps.is_empty() {
- write!(f, "[{usedeps}]")?;
- }
-
- Ok(())
- }
-}
-
-#[cfg(test)]
-mod test {
- use mon::{Parser, input::InputIter};
-
- use super::*;
-
- use crate::Parseable;
-
- macro_rules! assert_cmp_display {
- ($a:expr, $b:expr, $ordering:expr) => {
- if $a.cmp(&$b) != $ordering {
- panic!("{} ~ {} != {:?}", $a, $b, $ordering)
- }
- };
- }
-
- macro_rules! assert_partial_cmp_display {
- ($a:expr, $b:expr, $ordering:expr) => {
- if $a.partial_cmp(&$b) != $ordering {
- panic!("{} ~ {} != {:?}", $a, $b, $ordering)
- }
- };
- }
-
- #[test]
- fn test_version_display() {
- let s = "1.0.0_alpha1_beta1-r1";
- let version = Version::parser().parse_finished(InputIter::new(s)).unwrap();
-
- assert_eq!(version.to_string().as_str(), s);
- }
-
- #[test]
- fn test_display_atom() {
- let s = "!!>=foo/bar-1.0.0v_alpha1_beta1-r1:slot/sub=[a,b,c]";
- let atom = Atom::parser().parse_finished(InputIter::new(s)).unwrap();
-
- assert_eq!(atom.to_string().as_str(), s);
- }
-
- #[test]
- fn test_version_cmp() {
- let versions = [
- ("1.0.1", "1.0", Ordering::Greater),
- ("1.0.0", "1.0.0_alpha", Ordering::Greater),
- ("1.0.0_alpha", "1.0.0_alpha_p", Ordering::Less),
- ("1.0.0-r0", "1.0.0", Ordering::Equal),
- ("1.0.0-r0000", "1.0.0", Ordering::Equal),
- ("1.0.0-r1-1", "1.0.0-r1-2", Ordering::Less),
- ];
-
- for (a, b, ordering) in versions.iter().map(|(a, b, ordering)| {
- (
- Version::parser().parse_finished(InputIter::new(a)).unwrap(),
- Version::parser().parse_finished(InputIter::new(b)).unwrap(),
- ordering,
- )
- }) {
- assert_cmp_display!(a, b, *ordering);
- }
- }
-
- #[test]
- fn test_cpv_eq() {
- let cpvs = [
- ("foo/bar-1", "foo/bar-1", Some(Ordering::Equal)),
- ("foo/baz-1", "foo/bar-1", None),
- ];
-
- for (a, b, ordering) in cpvs.iter().copied().map(|(a, b, ordering)| {
- (
- Cpv::parser().parse_finished(InputIter::new(a)).unwrap(),
- Cpv::parser().parse_finished(InputIter::new(b)).unwrap(),
- ordering,
- )
- }) {
- assert_partial_cmp_display!(a, b, ordering);
- }
- }
-
- #[test]
- fn test_version_cmp_letter() {
- let a = Version::parser()
- .parse_finished(InputIter::new("1.0.0"))
- .unwrap();
- let b = Version::parser()
- .parse_finished(InputIter::new("1.0.0a"))
- .unwrap();
-
- assert_cmp_display!(a, b, Ordering::Less);
- }
-
- #[test]
- fn test_version_cmp_where_b_has_leading_zeros() {
- let a = Version::parser()
- .parse_finished(InputIter::new("1.2"))
- .unwrap();
- let b = Version::parser()
- .parse_finished(InputIter::new("1.054"))
- .unwrap();
-
- assert_cmp_display!(a, b, Ordering::Greater);
- }
-
- #[test]
- fn test_version_has_more_zeros() {
- let a = Version::parser()
- .parse_finished(InputIter::new("1.0.0"))
- .unwrap();
- let b = Version::parser()
- .parse_finished(InputIter::new("1.0"))
- .unwrap();
-
- assert_cmp_display!(a, b, Ordering::Greater);
- }
-
- #[test]
- fn test_fuzzer_cases() {
- let control = Version::parser()
- .parse_finished(InputIter::new("1.2.0a_alpha1_beta2-r1-8"))
- .unwrap();
-
- #[allow(clippy::single_element_loop)]
- for (version_str, expected) in [("1.2.0", Ordering::Greater)] {
- let version = Version::parser()
- .parse_finished(InputIter::new(version_str))
- .unwrap();
-
- assert_cmp_display!(control, version, expected);
- }
- }
-}
diff --git a/src/atom/parsers.rs b/src/atom/parsers.rs
deleted file mode 100644
index c7ff586..0000000
--- a/src/atom/parsers.rs
+++ /dev/null
@@ -1,599 +0,0 @@
-use core::option::Option::None;
-
-use mon::{
- Parser, ParserIter, ascii_alphanumeric, ascii_numeric, ascii_numeric1, eof, r#if,
- input::InputIter, one_of, tag,
-};
-
-use crate::{
- Parseable,
- atom::{
- Atom, Blocker, BuildId, Category, Cp, Cpv, Name, Repo, Slot, SlotName, SlotOperator,
- UseDep, UseDepCondition, UseDepNegate, UseDepSign, Version, VersionNumber, VersionNumbers,
- VersionOperator, VersionSuffix, VersionSuffixKind, VersionSuffixes, Wildcard,
- },
- useflag::UseFlag,
-};
-
-impl<'a> Parseable<'a, &'a str> for Blocker {
- type Parser = impl Parser<&'a str, Output = Self>;
-
- fn parser() -> Self::Parser {
- tag("!!")
- .map(|_| Blocker::Strong)
- .or(tag("!").map(|_| Blocker::Weak))
- }
-}
-
-impl<'a> Parseable<'a, &'a str> for VersionOperator {
- type Parser = impl Parser<&'a str, Output = Self>;
-
- fn parser() -> Self::Parser {
- tag("<=")
- .map(|_| VersionOperator::LtEq)
- .or(tag(">=").map(|_| VersionOperator::GtEq))
- .or(tag("<").map(|_| VersionOperator::Lt))
- .or(tag(">").map(|_| VersionOperator::Gt))
- .or(tag("=").map(|_| VersionOperator::Eq))
- .or(tag("~").map(|_| VersionOperator::Roughly))
- }
-}
-
-impl<'a> Parseable<'a, &'a str> for VersionNumber {
- type Parser = impl Parser<&'a str, Output = Self>;
-
- fn parser() -> Self::Parser {
- ascii_numeric1().map(|output: &str| VersionNumber(output.to_string()))
- }
-}
-
-impl<'a> Parseable<'a, &'a str> for BuildId {
- type Parser = impl Parser<&'a str, Output = Self>;
-
- fn parser() -> Self::Parser {
- let start = ascii_numeric().and_not(tag("0"));
- let rest = ascii_numeric().repeated().many();
-
- start
- .and(rest)
- .recognize()
- .or(tag("0"))
- .map(|output: &str| BuildId(output.to_string()))
- }
-}
-
-impl<'a> Parseable<'a, &'a str> for VersionSuffixKind {
- type Parser = impl Parser<&'a str, Output = Self>;
-
- fn parser() -> Self::Parser {
- 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))
- }
-}
-
-impl<'a> Parseable<'a, &'a str> for VersionSuffix {
- type Parser = impl Parser<&'a str, Output = Self>;
-
- fn parser() -> Self::Parser {
- VersionSuffixKind::parser()
- .and(VersionNumber::parser().opt())
- .map(|(kind, number)| VersionSuffix { kind, number })
- }
-}
-
-impl<'a> Parseable<'a, &'a str> for VersionNumbers {
- type Parser = impl Parser<&'a str, Output = Self>;
-
- fn parser() -> Self::Parser {
- VersionNumber::parser()
- .separated_by(tag("."))
- .at_least(1)
- .map(VersionNumbers)
- }
-}
-
-impl<'a> Parseable<'a, &'a str> for VersionSuffixes {
- type Parser = impl Parser<&'a str, Output = Self>;
-
- fn parser() -> Self::Parser {
- VersionSuffix::parser()
- .separated_by(tag("_"))
- .at_least(1)
- .map(VersionSuffixes)
- }
-}
-
-impl<'a> Parseable<'a, &'a str> for Version {
- type Parser = impl Parser<&'a str, Output = Self>;
-
- fn parser() -> Self::Parser {
- let rev = VersionNumber::parser().preceded_by(tag("-r"));
- let build_id = BuildId::parser().preceded_by(tag("-"));
-
- VersionNumbers::parser()
- .and(r#if(|c: &char| c.is_ascii_alphabetic() && c.is_ascii_lowercase()).opt())
- .and(VersionSuffixes::parser().preceded_by(tag("_")).opt())
- .and(rev.opt())
- .and(build_id.opt())
- .map(|((((numbers, letter), suffixes), rev), build_id)| Version {
- numbers,
- letter,
- suffixes: suffixes.unwrap_or(VersionSuffixes(Vec::new())),
- rev,
- build_id,
- })
- }
-}
-
-impl<'a> Parseable<'a, &'a str> for Category {
- type Parser = impl Parser<&'a str, Output = Self>;
-
- fn parser() -> Self::Parser {
- let start = ascii_alphanumeric().or(one_of("_".chars()));
- let rest = ascii_alphanumeric()
- .or(one_of("+_.-".chars()))
- .repeated()
- .many();
-
- start
- .and(rest)
- .recognize()
- .map(|output: &str| Category(output.to_string()))
- }
-}
-
-impl<'a> Parseable<'a, &'a str> for Name {
- type Parser = impl Parser<&'a str, Output = Self>;
-
- fn parser() -> Self::Parser {
- let start = || ascii_alphanumeric().or(one_of("_".chars()));
-
- let rest = ascii_alphanumeric()
- .or(one_of("_+".chars()))
- .or(one_of("-".chars()).and_not(
- Version::parser()
- .preceded_by(tag("-"))
- .followed_by(ascii_alphanumeric().or(one_of("_+-".chars())).not()),
- ))
- .repeated()
- .many();
-
- let verify = ascii_alphanumeric()
- .or(one_of("_+".chars()))
- .or(one_of("-".chars())
- .and_not(Version::parser().preceded_by(tag("-")).followed_by(eof())))
- .repeated()
- .many();
-
- start()
- .and(rest)
- .recognize()
- .verify_output(move |output: &&str| {
- verify.check_finished(InputIter::new(*output)).is_ok()
- })
- .map(|output: &str| Name(output.to_string()))
- }
-}
-
-impl<'a> Parseable<'a, &'a str> for SlotOperator {
- type Parser = impl Parser<&'a str, Output = Self>;
-
- fn parser() -> Self::Parser {
- tag("=")
- .map(|_| SlotOperator::Eq)
- .or(tag("*").map(|_| SlotOperator::Star))
- }
-}
-
-impl<'a> Parseable<'a, &'a str> for SlotName {
- type Parser = impl Parser<&'a str, Output = Self>;
-
- fn parser() -> Self::Parser {
- let start = ascii_alphanumeric().or(one_of("_".chars()));
- let rest = ascii_alphanumeric()
- .or(one_of("+_.-".chars()))
- .repeated()
- .many();
-
- start
- .and(rest)
- .recognize()
- .map(|output: &str| SlotName(output.to_string()))
- }
-}
-
-impl<'a> Parseable<'a, &'a str> for Slot {
- type Parser = impl Parser<&'a str, Output = Self>;
-
- fn parser() -> Self::Parser {
- let wildcard = tag("*").map(|_| Slot::Wildcard);
- let equal = tag("=").map(|_| Slot::Equal);
- let name_equal = SlotName::parser()
- .and(SlotName::parser().preceded_by(tag("/")).opt())
- .followed_by(tag("="))
- .map(|(primary, sub)| Slot::NameEqual { primary, sub });
- let name = SlotName::parser()
- .and(SlotName::parser().preceded_by(tag("/")).opt())
- .map(|(primary, sub)| Self::Name { primary, sub });
-
- wildcard.or(equal).or(name_equal).or(name)
- }
-}
-
-impl<'a> Parseable<'a, &'a str> for UseDepSign {
- type Parser = impl Parser<&'a str, Output = Self>;
-
- fn parser() -> Self::Parser {
- tag("(-)")
- .map(|_| UseDepSign::Disabled)
- .or(tag("(+)").map(|_| UseDepSign::Enabled))
- }
-}
-
-impl<'a> Parseable<'a, &'a str> for Repo {
- type Parser = impl Parser<&'a str, Output = Self>;
-
- fn parser() -> Self::Parser {
- let start = ascii_alphanumeric().or(one_of("_".chars()));
- let rest = ascii_alphanumeric()
- .or(one_of("_-".chars()))
- .repeated()
- .many();
-
- start
- .and(rest)
- .recognize()
- .verify_output(move |output: &&str| {
- Name::parser()
- .check_finished(InputIter::new(*output))
- .is_ok()
- })
- .map(|output: &str| Repo(output.to_string()))
- }
-}
-
-impl<'a> Parseable<'a, &'a str> for UseDep {
- type Parser = impl Parser<&'a str, Output = Self>;
-
- #[allow(clippy::many_single_char_names)]
- fn parser() -> Self::Parser {
- let a = UseFlag::parser()
- .and(UseDepSign::parser().opt())
- .preceded_by(tag("-"))
- .map(|(flag, sign)| UseDep {
- negate: Some(UseDepNegate::Minus),
- flag,
- sign,
- condition: None,
- });
-
- let b = UseFlag::parser()
- .and(UseDepSign::parser().opt())
- .preceded_by(tag("!"))
- .followed_by(tag("?"))
- .map(|(flag, sign)| UseDep {
- negate: Some(UseDepNegate::Exclamation),
- flag,
- sign,
- condition: Some(UseDepCondition::Question),
- });
-
- let c = UseFlag::parser()
- .and(UseDepSign::parser().opt())
- .followed_by(tag("?"))
- .map(|(flag, sign)| UseDep {
- negate: None,
- flag,
- sign,
- condition: Some(UseDepCondition::Question),
- });
-
- let d = UseFlag::parser()
- .and(UseDepSign::parser().opt())
- .preceded_by(tag("!"))
- .followed_by(tag("="))
- .map(|(flag, sign)| UseDep {
- negate: Some(UseDepNegate::Exclamation),
- flag,
- sign,
- condition: Some(UseDepCondition::Eq),
- });
-
- let e = UseFlag::parser()
- .and(UseDepSign::parser().opt())
- .followed_by(tag("="))
- .map(|(flag, sign)| UseDep {
- negate: None,
- flag,
- sign,
- condition: Some(UseDepCondition::Eq),
- });
-
- let f = UseFlag::parser()
- .and(UseDepSign::parser().opt())
- .map(|(flag, sign)| UseDep {
- negate: None,
- flag,
- sign,
- condition: None,
- });
-
- a.or(b).or(c).or(d).or(e).or(f)
- }
-}
-
-impl<'a> Parseable<'a, &'a str> for Atom {
- type Parser = impl Parser<&'a str, Output = Self>;
-
- fn parser() -> Self::Parser {
- let usedeps = || {
- UseDep::parser()
- .separated_by(tag(","))
- .at_least(1)
- .delimited_by(tag("["), tag("]"))
- .opt()
- };
-
- let without_version = Blocker::parser()
- .opt()
- .and(Category::parser())
- .and(Name::parser().preceded_by(tag("/")))
- .and(Slot::parser().preceded_by(tag(":")).opt())
- .and(Repo::parser().preceded_by(tag("::")).opt())
- .and(usedeps())
- .map(
- |(((((blocker, category), name), slot), repo), usedeps)| Atom {
- blocker,
- category,
- name,
- version: None,
- slot,
- repo,
- usedeps: usedeps.unwrap_or(Vec::new()),
- },
- );
-
- let with_version = Blocker::parser()
- .opt()
- .and(VersionOperator::parser())
- .and(Category::parser())
- .and(Name::parser().preceded_by(tag("/")))
- .and(Version::parser().preceded_by(tag("-")))
- .and(tag("*").map(|_| Wildcard).opt())
- .and(Slot::parser().preceded_by(tag(":")).opt())
- .and(Repo::parser().preceded_by(tag("::")).opt())
- .and(usedeps())
- .verify_output(
- |((((((((_, version_operator), _), _), version), star), _), _), _)| {
- matches!(
- (version_operator, star),
- (VersionOperator::Eq, Some(_) | None) | (_, None)
- ) && matches!((version.build_id(), star), (Some(_), None) | (None, _))
- },
- )
- .map(
- |(
- (
- ((((((blocker, version_operator), category), name), version), star), slot),
- repo,
- ),
- usedeps,
- )| {
- Atom {
- blocker,
- category,
- name,
- version: Some((version_operator, version, star)),
- slot,
- repo,
- usedeps: usedeps.unwrap_or(Vec::new()),
- }
- },
- );
-
- with_version.or(without_version)
- }
-}
-
-impl<'a> Parseable<'a, &'a str> for Cp {
- type Parser = impl Parser<&'a str, Output = Self>;
-
- fn parser() -> Self::Parser {
- Category::parser()
- .and(Name::parser().preceded_by(tag("/")))
- .map(|(category, name)| Cp { category, name })
- }
-}
-
-impl<'a> Parseable<'a, &'a str> for Cpv {
- type Parser = impl Parser<&'a str, Output = Self>;
-
- fn parser() -> Self::Parser {
- Category::parser()
- .and(Name::parser().preceded_by(tag("/")))
- .and(Version::parser().preceded_by(tag("-")))
- .and(Slot::parser().preceded_by(tag(":")).opt())
- .map(|(((category, name), version), slot)| Cpv {
- category,
- name,
- version,
- slot,
- })
- }
-}
-
-#[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::parser().check_finished(it).unwrap();
- }
-
- #[test]
- fn test_name() {
- let it = InputIter::new("foo-1-bar-1.0.0");
-
- match Name::parser().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=[use,use=,!use=,use?,!use?,-use,use(+),use(-)]",
- );
-
- Atom::parser().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::parser().check_finished(it).unwrap();
- }
-
- #[test]
- fn test_atom_with_star_in_non_empty_slot() {
- let it = InputIter::new("foo/bar:*/subslot");
-
- assert!(Atom::parser().check_finished(it).is_err());
- }
-
- #[test]
- fn test_invalid_usedep() {
- let it = InputIter::new("foo-bar:slot/sub=[!use]");
-
- assert!(Atom::parser().check_finished(it).is_err());
- }
-
- #[test]
- fn test_empty_slot() {
- let it = InputIter::new("=dev-ml/uucp-17*:");
-
- assert!(Atom::parser().check_finished(it).is_err());
- }
-
- #[test]
- fn test_usedep_with_underscore() {
- let it = InputIter::new("foo/bar[use_dep]");
-
- Atom::parser().check_finished(it).unwrap();
- }
-
- #[test]
- fn test_version_with_uppercase_letter() {
- let it = InputIter::new("=foo/bar-1.0.0V");
-
- assert!(Atom::parser().check_finished(it).is_err());
- }
-
- #[test]
- fn test_version_with_version_operator_without_version() {
- let it = InputIter::new("=foo/bar");
-
- assert!(Atom::parser().check_finished(it).is_err());
- }
-
- #[test]
- fn test_version_with_version_without_version_operator() {
- let it = InputIter::new("foo/bar-1.0.0");
-
- assert!(Atom::parser().check_finished(it).is_err());
- }
-
- #[test]
- fn test_atom_with_eq_version_operator() {
- let it = InputIter::new("=foo/bar-1.0.0");
-
- Atom::parser().check_finished(it).unwrap();
- }
-
- #[test]
- fn test_atom_with_star_in_version() {
- let it = InputIter::new("=foo/bar-1.2*");
-
- Atom::parser().check_finished(it).unwrap();
- }
-
- #[test]
- fn test_atom_with_star_in_version_without_eq_version_operator() {
- let it = InputIter::new(">=foo/bar-1.2*");
-
- assert!(Atom::parser().check_finished(it).is_err());
- }
-
- #[test]
- fn test_atom_with_trailing_dash_and_letter() {
- let it = InputIter::new("dev-db/mysql-connector-c");
-
- Atom::parser().check_finished(it).unwrap();
- }
-
- #[test]
- fn test_cpv_with_slot() {
- let it = InputIter::new("foo/bar-1.0:slot/sub=");
-
- Cpv::parser().check_finished(it).unwrap();
- }
-
- #[test]
- fn test_cpv_without_version_but_trailing_almost_version() {
- let it = InputIter::new("dev-perl/mod-p-2.3_");
-
- assert!(Cpv::parser().parse_finished(it).is_err());
- }
-
- #[test]
- fn test_empty_slot_with_operator() {
- let it = InputIter::new("foo/bar:=");
-
- Atom::parser().check_finished(it).unwrap();
- }
-
- #[test]
- fn test_with_repo() {
- let it = InputIter::new("=foo/bar-1.0.0:slot/sub=::gentoo[a,b,c]");
-
- Atom::parser().check_finished(it).unwrap();
- }
-
- #[test]
- fn test_against_fuzzer_false_positives() {
- let atoms = [
- "media-libs/libsdl2[haptitick(+),sound(+)vd,eio(+)]",
- "=kde-frameworks/kcodecs-6.19*86",
- "=dev-ml/stdio-0.17*t:=[ocamlopt?]",
- ">=dev-libs/libgee-0-8.5:0..8=",
- "<dev-haskell/wai-3.3:=[]",
- ">=kde-frameworks/kcrash-2.16.0:6*",
- "0-f/merreka+m::k+",
- "iev-a/h:/n=",
- "=dev-ml/stdio-0-17*:=[ocamlopt?]",
- ];
-
- for atom in atoms {
- assert!(
- Atom::parser().check_finished(InputIter::new(atom)).is_err(),
- "{atom}"
- );
- }
- }
-}