From 78398b7ebe114f56eacbe4445a11ff2dfd10dfe7 Mon Sep 17 00:00:00 2001 From: John Turner Date: Tue, 18 Nov 2025 01:44:45 +0000 Subject: [PATCH] support ::repo syntax --- src/atom/mod.rs | 4 +++ src/atom/parsers.rs | 65 ++++++++++++++++++++++++++++++++++----------- 2 files changed, 53 insertions(+), 16 deletions(-) diff --git a/src/atom/mod.rs b/src/atom/mod.rs index 3be42bb..54de7ee 100644 --- a/src/atom/mod.rs +++ b/src/atom/mod.rs @@ -103,6 +103,9 @@ pub enum UseDepCondition { Question, } +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Repo(String); + #[derive(Clone, Debug, PartialEq, Eq, Get)] pub struct UseDep { negate: Option, @@ -132,6 +135,7 @@ pub struct Atom { name: Name, version: Option<(VersionOperator, Version, Option)>, slot: Option, + repo: Option, #[get(kind = "deref")] usedeps: Vec, } diff --git a/src/atom/parsers.rs b/src/atom/parsers.rs index 7bcf5c6..59bd23f 100644 --- a/src/atom/parsers.rs +++ b/src/atom/parsers.rs @@ -5,7 +5,7 @@ use mon::{Parser, ParserIter, alphanumeric, r#if, numeric1, one_of, tag}; use crate::{ Parseable, atom::{ - Atom, Blocker, Category, Cp, Cpv, Name, Slot, SlotName, SlotOperator, UseDep, + Atom, Blocker, Category, Cp, Cpv, Name, Repo, Slot, SlotName, SlotOperator, UseDep, UseDepCondition, UseDepNegate, UseDepSign, Version, VersionNumber, VersionNumbers, VersionOperator, VersionSuffix, VersionSuffixKind, VersionSuffixes, Wildcard, }, @@ -201,6 +201,21 @@ impl<'a> Parseable<'a, &'a str> for UseDepSign { } } +//A slot name may contain any of the characters [A-Za-z0-9+_.-]. It must not begin with a hyphen, a dot or a plus sign. +impl<'a> Parseable<'a, &'a str> for Repo { + type Parser = impl Parser<&'a str, Output = Self>; + + fn parser() -> Self::Parser { + let start = alphanumeric().or(one_of("_".chars())); + let rest = alphanumeric().or(one_of("+_.-".chars())).repeated().many(); + + start + .and(rest) + .recognize() + .map(|output: &str| Repo(output.to_string())) + } +} + impl<'a> Parseable<'a, &'a str> for UseDep { type Parser = impl Parser<&'a str, Output = Self>; @@ -288,15 +303,19 @@ impl<'a> Parseable<'a, &'a str> for Atom { .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), usedeps)| Atom { - blocker, - category, - name, - version: None, - slot, - usedeps: usedeps.unwrap_or(Vec::new()), - }); + .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() @@ -306,16 +325,22 @@ impl<'a> Parseable<'a, &'a str> for Atom { .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), _), _), _), star), _), _)| { - matches!( - (version_operator, star), - (VersionOperator::Eq, Some(_) | None) | (_, None) - ) - }) + .verify_output( + |((((((((_, version_operator), _), _), _), star), _), _), _)| { + matches!( + (version_operator, star), + (VersionOperator::Eq, Some(_) | None) | (_, None) + ) + }, + ) .map( |( - ((((((blocker, version_operator), category), name), version), star), slot), + ( + ((((((blocker, version_operator), category), name), version), star), slot), + repo, + ), usedeps, )| { Atom { @@ -324,6 +349,7 @@ impl<'a> Parseable<'a, &'a str> for Atom { name, version: Some((version_operator, version, star)), slot, + repo, usedeps: usedeps.unwrap_or(Vec::new()), } }, @@ -502,6 +528,13 @@ mod test { 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 = [