From b147d967d45092d6e289483e48ad4a91e3b89a14 Mon Sep 17 00:00:00 2001 From: John Turner Date: Fri, 7 Nov 2025 20:57:48 +0000 Subject: [PATCH] put version and version operator in the same Option in the Atom struct All atoms must either have a version with a version operator, or have no version and no version operator. Putting these in the same Option helps encode that into the type system. --- src/atom/mod.rs | 16 ++++++++++---- src/atom/parsers.rs | 51 ++++++++++++++++++++++++++++++--------------- 2 files changed, 46 insertions(+), 21 deletions(-) diff --git a/src/atom/mod.rs b/src/atom/mod.rs index ffb9c61..eb7537b 100644 --- a/src/atom/mod.rs +++ b/src/atom/mod.rs @@ -106,15 +106,23 @@ pub struct UseDep { #[derive(Clone, Debug, Get)] pub struct Atom { blocker: Option, - version_operator: Option, category: Category, name: Name, - version: Option, + version: Option<(VersionOperator, Version)>, slot: Option, #[get(kind = "deref")] usedeps: Vec, } +impl Atom { + pub fn version_operator(&self) -> Option { + match self.version { + Some((operator, _)) => Some(operator), + None => None, + } + } +} + impl fmt::Display for Blocker { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -299,7 +307,7 @@ impl fmt::Display for Atom { write!(f, "{blocker}")?; } - if let Some(version_operator) = self.version_operator.as_ref() { + if let Some(version_operator) = self.version_operator().as_ref() { write!(f, "{version_operator}")?; } @@ -307,7 +315,7 @@ impl fmt::Display for Atom { write!(f, "/")?; write!(f, "{}", self.name)?; - if let Some(version) = self.version.as_ref() { + if let Some((_, version)) = self.version.as_ref() { write!(f, "-{version}")?; } diff --git a/src/atom/parsers.rs b/src/atom/parsers.rs index f8a9f25..60f9223 100644 --- a/src/atom/parsers.rs +++ b/src/atom/parsers.rs @@ -253,36 +253,52 @@ impl<'a> Parseable<'a, &'a str> for Atom { type Parser = impl Parser<&'a str, Output = Self>; fn parser() -> Self::Parser { - Blocker::parser() + let usedeps = || { + UseDep::parser() + .separated_by(tag(",")) + .many() + .delimited_by(tag("["), tag("]")) + .opt() + }; + + let without_version = Blocker::parser() .opt() - .and(VersionOperator::parser().opt()) .and(Category::parser()) .and(Name::parser().preceded_by(tag("/"))) - .and(Version::parser().preceded_by(tag("-")).opt()) .and(Slot::parser().preceded_by(tag(":")).opt()) - .and( - UseDep::parser() - .separated_by(tag(",")) - .many() - .delimited_by(tag("["), tag("]")) - .opt(), - ) + .and(usedeps()) + .map(|((((blocker, category), name), slot), usedeps)| Atom { + blocker, + category, + name, + version: None, + slot, + 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(Slot::parser().preceded_by(tag(":")).opt()) + .and(usedeps()) .map( |((((((blocker, version_operator), category), name), version), slot), usedeps)| { Atom { blocker, - version_operator, category, name, - version, + version: Some((version_operator, version)), slot, usedeps: usedeps.unwrap_or(Vec::new()), } }, ) - .verify_output(|atom| match (&atom.version_operator, &atom.version) { - (Some(VersionOperator::Eq), Some(_)) => true, - (Some(_), Some(version)) + .verify_output(|atom| match &atom.version { + Some((VersionOperator::Eq, _)) => true, + Some((_, version)) if !version .numbers() .iter() @@ -290,9 +306,10 @@ impl<'a> Parseable<'a, &'a str> for Atom { { true } - (None, None) => true, _ => false, - }) + }); + + with_version.or(without_version) } }