diff --git a/Cargo.lock b/Cargo.lock index a259d8e3d7..8271285949 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1091,7 +1091,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "nom", + "nom 7.1.3", ] [[package]] @@ -3277,6 +3277,24 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "nom-language" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2de2bc5b451bfedaef92c90b8939a8fff5770bdcc1fafd6239d086aab8fa6b29" +dependencies = [ + "nom 8.0.0", +] + [[package]] name = "ntapi" version = "0.4.1" @@ -4278,7 +4296,8 @@ dependencies = [ "insta", "itertools 0.14.0", "lazy-regex", - "nom", + "nom 8.0.0", + "nom-language", "pathdiff", "purl", "rand 0.9.1", @@ -4654,7 +4673,7 @@ version = "2.0.13" dependencies = [ "archspec", "libloading", - "nom", + "nom 8.0.0", "once_cell", "plist", "rattler_conda_types", diff --git a/Cargo.toml b/Cargo.toml index 5d2c007f6d..7804ddc14e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -103,7 +103,8 @@ md-5 = "0.10.6" memchr = "2.7.4" memmap2 = "0.9.5" netrc-rs = "0.1.2" -nom = "7.1.3" +nom = "8.0.0" +nom-language = "0.1.0" num_cpus = "1.16.0" opendal = { version = "0.53.3", default-features = false } once_cell = "1.21.3" diff --git a/crates/rattler_conda_types/Cargo.toml b/crates/rattler_conda_types/Cargo.toml index 9e99724266..4c1a05a2d7 100644 --- a/crates/rattler_conda_types/Cargo.toml +++ b/crates/rattler_conda_types/Cargo.toml @@ -23,6 +23,7 @@ hex = { workspace = true } itertools = { workspace = true } lazy-regex = { workspace = true } nom = { workspace = true } +nom-language = { workspace = true } purl = { workspace = true, features = ["serde"] } rattler_digest = { workspace = true, default-features = false, features = [ "serde", diff --git a/crates/rattler_conda_types/src/match_spec/parse.rs b/crates/rattler_conda_types/src/match_spec/parse.rs index d682ed70de..a88b2ce99a 100644 --- a/crates/rattler_conda_types/src/match_spec/parse.rs +++ b/crates/rattler_conda_types/src/match_spec/parse.rs @@ -8,7 +8,7 @@ use nom::{ error::{context, ContextError, ParseError}, multi::{separated_list0, separated_list1}, sequence::{delimited, preceded, separated_pair, terminated}, - Finish, IResult, + Finish, IResult, Parser, }; use rattler_digest::{parse_digest_from_hex, Md5, Sha256}; use smallvec::SmallVec; @@ -150,13 +150,13 @@ type BracketVec<'a> = SmallVec<[(&'a str, &'a str); 2]>; /// A parse combinator to filter whitespace if front and after another parser. fn whitespace_enclosed<'a, F, O, E: ParseError<&'a str>>( mut inner: F, -) -> impl FnMut(&'a str) -> IResult<&'a str, O, E> +) -> impl Parser<&'a str, Output = O, Error = E> where - F: FnMut(&'a str) -> IResult<&'a str, O, E>, + F: Parser<&'a str, Output = O, Error = E>, { move |input: &'a str| { let (input, _) = multispace0(input)?; - let (input, o2) = inner(input)?; + let (input, o2) = inner.parse(input)?; multispace0(input).map(|(i, _)| (i, o2)) } } @@ -168,7 +168,8 @@ fn parse_bracket_list(input: &str) -> Result, ParseMatchSpecError whitespace_enclosed(context( "key", take_while(|c: char| c.is_alphanumeric() || c == '_' || c == '-'), - ))(input) + )) + .parse(input) } /// Parses a value in a bracket string. @@ -181,22 +182,23 @@ fn parse_bracket_list(input: &str) -> Result, ParseMatchSpecError delimited(char('['), take_until("]"), char(']')), take_till1(|c| c == ',' || c == ']' || c == '\'' || c == '"'), )), - ))(input) + )) + .parse(input) } /// Parses a `key=value` pair fn parse_key_value(input: &str) -> IResult<&str, (&str, &str)> { - separated_pair(parse_key, char('='), parse_value)(input) + separated_pair(parse_key, char('='), parse_value).parse(input) } /// Parses a list of `key=value` pairs separated by commas fn parse_key_value_list(input: &str) -> IResult<&str, Vec<(&str, &str)>> { - separated_list0(whitespace_enclosed(char(',')), parse_key_value)(input) + separated_list0(whitespace_enclosed(char(',')), parse_key_value).parse(input) } /// Parses an entire bracket string fn parse_bracket_list(input: &str) -> IResult<&str, Vec<(&str, &str)>> { - delimited(char('['), parse_key_value_list, char(']'))(input) + delimited(char('['), parse_key_value_list, char(']')).parse(input) } match parse_bracket_list(input).finish() { @@ -240,14 +242,15 @@ pub fn parse_extras(input: &str) -> Result, ParseMatchSpecError> { multispace0, take_while1(|c: char| c.is_alphanumeric() || c == '_' || c == '-'), multispace0, - )(i) + ) + .parse(i) } fn parse_features(i: &str) -> IResult<&str, Vec> { - separated_list1(char(','), map(parse_feature_name, |s: &str| s.to_string()))(i) + separated_list1(char(','), map(parse_feature_name, |s: &str| s.to_string())).parse(i) } - match all_consuming(parse_features)(input).finish() { + match all_consuming(parse_features).parse(input).finish() { Ok((_remaining, features)) => Ok(features), Err(_e) => Err(ParseMatchSpecError::InvalidBracket), } @@ -392,7 +395,7 @@ fn split_version_and_build( ) -> impl FnMut(&'a str) -> IResult<&'a str, &'a str, E> { move |input: &'a str| { if strictness == Lenient { - alt((parse_special_equality, recognize_constraint))(input) + alt((parse_special_equality, recognize_constraint)).parse(input) } else { recognize_constraint(input) } @@ -406,7 +409,8 @@ fn split_version_and_build( alt(( delimited(tag("("), parse_version_group(strictness), tag(")")), maybe_recognize_lenient_constraint(strictness), - ))(input) + )) + .parse(input) } } @@ -417,7 +421,8 @@ fn split_version_and_build( recognize(separated_list1( whitespace_enclosed(one_of(",|")), parse_version_constraint_or_group(strictness), - ))(input) + )) + .parse(input) } } @@ -437,7 +442,8 @@ fn split_version_and_build( recognize(preceded( tag("="), alt((version_followed_by_glob, just_star)), - ))(input) + )) + .parse(input) } fn parse_version_and_build_separator<'a, E: ParseError<&'a str> + ContextError<&'a str>>( @@ -445,9 +451,9 @@ fn split_version_and_build( ) -> impl FnMut(&'a str) -> IResult<&'a str, &'a str, E> { move |input: &'a str| { if strictness == Lenient { - terminated(parse_version_group(strictness), opt(one_of(" =")))(input) + terminated(parse_version_group(strictness), opt(one_of(" ="))).parse(input) } else { - terminated(parse_version_group(strictness), space0)(input) + terminated(parse_version_group(strictness), space0).parse(input) } } } @@ -470,9 +476,9 @@ fn split_version_and_build( build_string.is_empty().not().then_some(build_string), )) } - Err(nom::error::VerboseError { .. }) => Err(ParseMatchSpecError::InvalidVersionAndBuild( - input.to_string(), - )), + Err(nom_language::error::VerboseError { .. }) => Err( + ParseMatchSpecError::InvalidVersionAndBuild(input.to_string()), + ), } } /// Parse version and build string. diff --git a/crates/rattler_conda_types/src/package/has_prefix.rs b/crates/rattler_conda_types/src/package/has_prefix.rs index 6a4d1e4d62..2aed1b4302 100644 --- a/crates/rattler_conda_types/src/package/has_prefix.rs +++ b/crates/rattler_conda_types/src/package/has_prefix.rs @@ -1,12 +1,3 @@ -use crate::{package::paths::FileMode, package::PackageFile}; -use nom::{ - branch::alt, - bytes::complete::{tag, tag_no_case, take_till1}, - character::complete::multispace1, - combinator::{all_consuming, map, value}, - sequence::{preceded, terminated, tuple}, - IResult, -}; use std::{ borrow::Cow, hint::black_box, @@ -15,6 +6,17 @@ use std::{ sync::OnceLock, }; +use nom::{ + branch::alt, + bytes::complete::{tag, tag_no_case, take_till1}, + character::complete::multispace1, + combinator::{all_consuming, map, value}, + sequence::{preceded, terminated}, + IResult, Parser, +}; + +use crate::package::{paths::FileMode, PackageFile}; + /// Representation of an entry in `info/has_prefix`. #[derive(Debug, Clone, PartialEq, Eq)] pub struct HasPrefixEntry { @@ -29,7 +31,8 @@ pub struct HasPrefixEntry { /// Representation of the `info/has_prefix` file in older package archives. #[derive(Debug, Clone, PartialEq, Eq)] pub struct HasPrefix { - /// A list of files in the package that contain the `prefix` (and need prefix replacement). + /// A list of files in the package that contain the `prefix` (and need + /// prefix replacement). pub files: Vec, } @@ -48,11 +51,13 @@ impl PackageFile for HasPrefix { } } -/// Returns the default placeholder path. Although this is just a constant it is constructed at -/// runtime. This ensures that the string itself is not present in the binary when compiled. The -/// reason we want that is that conda-build (and friends) tries to replace this placeholder in the -/// binary to point to the actual path in the installed conda environment. In this case we don't -/// want to that so we deliberately break up the string and reconstruct it at runtime. +/// Returns the default placeholder path. Although this is just a constant it is +/// constructed at runtime. This ensures that the string itself is not present +/// in the binary when compiled. The reason we want that is that conda-build +/// (and friends) tries to replace this placeholder in the binary to point to +/// the actual path in the installed conda environment. In this case we don't +/// want to that so we deliberately break up the string and reconstruct it at +/// runtime. fn placeholder_string() -> &'static str { static PLACEHOLDER: OnceLock = OnceLock::new(); PLACEHOLDER @@ -70,22 +75,24 @@ impl FromStr for HasPrefixEntry { type Err = std::io::Error; fn from_str(s: &str) -> Result { - /// Parses ` ` and fails if there is more input. + /// Parses ` ` and fails if there is more + /// input. fn prefix_file_mode_path(buf: &str) -> IResult<&str, HasPrefixEntry> { all_consuming(map( - tuple(( + ( possibly_quoted_string, multispace1, file_mode, multispace1, possibly_quoted_string, - )), + ), |(prefix, _, file_mode, _, path)| HasPrefixEntry { prefix: Cow::Owned(prefix.into_owned()), file_mode, relative_path: PathBuf::from(&*path), }, - ))(buf) + )) + .parse(buf) } /// Parses "" and fails if there is more input. @@ -94,7 +101,8 @@ impl FromStr for HasPrefixEntry { prefix: Cow::Borrowed(placeholder_string()), file_mode: FileMode::Text, relative_path: PathBuf::from(&*path), - }))(buf) + })) + .parse(buf) } /// Parses "text|binary" as a [`FileMode`] @@ -102,7 +110,8 @@ impl FromStr for HasPrefixEntry { alt(( value(FileMode::Text, tag_no_case("text")), value(FileMode::Binary, tag_no_case("binary")), - ))(buf) + )) + .parse(buf) } /// Parses either a quoted or an unquoted string. @@ -110,7 +119,8 @@ impl FromStr for HasPrefixEntry { alt(( map(quoted_string, Cow::Owned), map(take_till1(char::is_whitespace), Cow::Borrowed), - ))(buf) + )) + .parse(buf) } /// Parses a quoted string and delimited '\"' @@ -132,10 +142,11 @@ impl FromStr for HasPrefixEntry { } let qs = preceded(tag("\""), in_quotes); - terminated(qs, tag("\""))(buf) + terminated(qs, tag("\"")).parse(buf) } - alt((prefix_file_mode_path, only_path))(s) + alt((prefix_file_mode_path, only_path)) + .parse(s) .map(|(_, res)| res) .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string())) } @@ -143,9 +154,10 @@ impl FromStr for HasPrefixEntry { #[cfg(test)] mod test { + use std::{borrow::Cow, path::PathBuf, str::FromStr}; + use super::*; use crate::package::FileMode; - use std::{borrow::Cow, path::PathBuf, str::FromStr}; #[test] fn test_placeholder() { diff --git a/crates/rattler_conda_types/src/version/parse.rs b/crates/rattler_conda_types/src/version/parse.rs index 71a4527c33..3fe90a8c32 100644 --- a/crates/rattler_conda_types/src/version/parse.rs +++ b/crates/rattler_conda_types/src/version/parse.rs @@ -8,7 +8,7 @@ use nom::character::complete::{alpha1, char, digit1, one_of}; use nom::combinator::{map, opt, value}; use nom::error::{ErrorKind, FromExternalError, ParseError}; use nom::sequence::terminated; -use nom::IResult; +use nom::{IResult, Parser}; use smallvec::SmallVec; use std::{ convert::Into, @@ -110,7 +110,7 @@ impl<'i> FromExternalError<&'i str, ParseVersionErrorKind> for ParseVersionError /// Parses the epoch part of a version. This is a number followed by `'!'` at the start of the /// version string. pub fn epoch_parser(input: &str) -> IResult<&str, u64, ParseVersionErrorKind> { - let (rest, digits) = terminated(digit1, char('!'))(input)?; + let (rest, digits) = terminated(digit1, char('!')).parse(input)?; let epoch = digits .parse() .map_err(ParseVersionErrorKind::EpochMustBeInteger) @@ -139,7 +139,8 @@ fn component_parser<'i>(input: &'i str) -> IResult<&'i str, Component, ParseVers map(alpha1, |alpha: &'i str| { Component::Iden(alpha.to_lowercase().into_boxed_str()) }), - ))(input) + )) + .parse(input) } /// Parses a version segment from a list of components. @@ -168,7 +169,7 @@ fn segment_parser<'i>( // Loop until we can't find any more components loop { - let (remaining, component) = match opt(component_parser)(rest) { + let (remaining, component) = match opt(component_parser).parse(rest) { Ok((i, o)) => (i, o), Err(e) => { // Remove any components that we may have added. @@ -205,7 +206,8 @@ fn trailing_dash_underscore_parser( dash_or_underscore: Option, ) -> IResult<&str, (Option, Option), ParseVersionErrorKind> { // Parse a - or _. Return early if it cannot be found. - let (rest, Some(separator)) = opt(one_of::<_, _, (&str, ErrorKind)>("-_"))(input) + let (rest, Some(separator)) = opt(one_of::<_, _, (&str, ErrorKind)>("-_")) + .parse(input) .map_err(|e| e.map(|(_, kind)| ParseVersionErrorKind::Nom(kind)))? else { return Ok((input, (None, dash_or_underscore))); @@ -250,7 +252,7 @@ fn version_part_parser<'i>( // Iterate over any additional segments that we find. let result = loop { // Parse a version segment separator. - let (rest, separator) = match opt(one_of("-._"))(input) { + let (rest, separator) = match opt(one_of("-._")).parse(input) { Ok((_, None)) => { // No additional separator found, exit early. return Ok((input, dash_or_underscore)); @@ -361,7 +363,7 @@ pub fn version_parser(input: &str) -> IResult<&str, Version, ParseVersionErrorKi } // Parse an optional epoch. - let (input, epoch) = opt(epoch_parser)(input)?; + let (input, epoch) = opt(epoch_parser).parse(input)?; if let Some(epoch) = epoch { components.push(epoch.into()); flags = flags.with_has_epoch(true); diff --git a/crates/rattler_conda_types/src/version_spec/parse.rs b/crates/rattler_conda_types/src/version_spec/parse.rs index 33e7abf8e2..39c98bad81 100644 --- a/crates/rattler_conda_types/src/version_spec/parse.rs +++ b/crates/rattler_conda_types/src/version_spec/parse.rs @@ -4,8 +4,7 @@ use nom::{ character::complete::char, combinator::opt, error::{ErrorKind, ParseError}, - sequence::tuple, - IResult, + IResult, Parser, }; use thiserror::Error; @@ -100,7 +99,7 @@ fn regex_constraint_parser( ) -> impl FnMut(&str) -> IResult<&str, Constraint, ParseConstraintError> { move |input: &str| { let (_rest, (preceder, _, terminator)) = - tuple((opt(char('^')), take_while(|c| c != '$'), opt(char('$'))))(input)?; + (opt(char('^')), take_while(|c| c != '$'), opt(char('$'))).parse(input)?; match (preceder, terminator) { (None, None) => Err(nom::Err::Error(ParseConstraintError::UnterminatedRegex)), (_, None) | (None, _) => { @@ -118,7 +117,7 @@ fn any_constraint_parser( strictness: ParseStrictness, ) -> impl FnMut(&str) -> IResult<&str, Constraint, ParseConstraintError> { move |input: &str| { - let (remaining, (_, trailing)) = tuple((tag("*"), opt(tag(".*"))))(input)?; + let (remaining, (_, trailing)) = (tag("*"), opt(tag(".*"))).parse(input)?; // `*.*` is not allowed in strict mode if trailing.is_some() && strictness == ParseStrictness::Strict { @@ -333,7 +332,8 @@ pub fn constraint_parser( regex_constraint_parser(strictness), any_constraint_parser(strictness), logical_constraint_parser(strictness), - ))(input) + )) + .parse(input) } } diff --git a/crates/rattler_conda_types/src/version_spec/version_tree.rs b/crates/rattler_conda_types/src/version_spec/version_tree.rs index a6a29ed2f7..1251074dbb 100644 --- a/crates/rattler_conda_types/src/version_spec/version_tree.rs +++ b/crates/rattler_conda_types/src/version_spec/version_tree.rs @@ -5,11 +5,12 @@ use nom::{ bytes::complete::{tag, take_while}, character::complete::{alpha1, digit1, multispace0, u32}, combinator::{all_consuming, cut, map, not, opt, recognize, value}, - error::{context, convert_error, ContextError, ParseError}, + error::{context, ContextError, ParseError}, multi::{many0, separated_list1}, - sequence::{delimited, preceded, terminated, tuple}, - IResult, + sequence::{delimited, preceded, terminated}, + IResult, Parser, }; +use nom_language::error::convert_error; use thiserror::Error; use crate::version_spec::{ @@ -58,14 +59,15 @@ fn parse_operator<'a, E: ParseError<&'a str>>( VersionOperators::StrictRange(StrictRangeOperator::Compatible), tag("~="), ), - ))(input) + )) + .parse(input) } /// Recognizes the version epoch fn parse_version_epoch<'a, E: ParseError<&'a str> + ContextError<&'a str>>( input: &'a str, ) -> Result<(&'a str, u32), nom::Err> { - terminated(u32, tag("!"))(input) + terminated(u32, tag("!")).parse(input) } /// A parser that recognizes a version @@ -81,9 +83,9 @@ pub(crate) fn recognize_version<'a, E: ParseError<&'a str> + ContextError<&'a st let num = digit1; let glob = tag("*"); if allow_glob { - alt((ident, num, glob))(input) + alt((ident, num, glob)).parse(input) } else { - alt((ident, num))(input) + alt((ident, num)).parse(input) } } } @@ -93,18 +95,19 @@ pub(crate) fn recognize_version<'a, E: ParseError<&'a str> + ContextError<&'a st allow_glob: bool, ) -> impl FnMut(&'a str) -> IResult<&'a str, &'a str, E> { move |input: &'a str| { - recognize(tuple(( + recognize(( recognize_version_component(allow_glob), many0(preceded( opt(take_while(|c: char| c == '.' || c == '-' || c == '_')), recognize_version_component(allow_glob), )), - )))(input) + )) + .parse(input) } } move |input: &'a str| { - recognize(tuple(( + recognize(( // Optional version epoch opt(context("epoch", parse_version_epoch)), // Version components @@ -114,7 +117,8 @@ pub(crate) fn recognize_version<'a, E: ParseError<&'a str> + ContextError<&'a st tag("+"), cut(context("local", recognize_version_components(allow_glob))), )), - )))(input) + )) + .parse(input) } } @@ -130,7 +134,8 @@ pub(crate) fn recognize_version_with_star<'a, E: ParseError<&'a str> + ContextEr ), // Just a * tag("*"), - ))(input) + )) + .parse(input) } /// A parser that recognized a constraint but does not actually parse it. @@ -151,7 +156,8 @@ pub(crate) fn recognize_constraint<'a, E: ParseError<&'a str> + ContextError<&'a )), cut(context("version", recognize_version_with_star)), )), - ))(input) + )) + .parse(input) } impl<'a> TryFrom<&'a str> for VersionTree<'a> { @@ -171,7 +177,8 @@ impl<'a> TryFrom<&'a str> for VersionTree<'a> { map(recognize_constraint, |constraint| { VersionTree::Term(constraint) }), - ))(input) + )) + .parse(input) } /// Given multiple version tree components, flatten the structure as @@ -199,7 +206,8 @@ impl<'a> TryFrom<&'a str> for VersionTree<'a> { input: &'a str, ) -> Result<(&'a str, VersionTree<'a>), nom::Err> { let (rest, group) = - separated_list1(delimited(multispace0, tag(","), multispace0), parse_term)(input)?; + separated_list1(delimited(multispace0, tag(","), multispace0), parse_term) + .parse(input)?; Ok((rest, flatten_group(LogicalOperator::And, group))) } @@ -210,11 +218,12 @@ impl<'a> TryFrom<&'a str> for VersionTree<'a> { let (rest, group) = separated_list1( delimited(multispace0, tag("|"), multispace0), parse_and_group, - )(input)?; + ) + .parse(input)?; Ok((rest, flatten_group(LogicalOperator::Or, group))) } - match all_consuming(parse_or_group)(input) { + match all_consuming(parse_or_group).parse(input) { Ok((_, tree)) => Ok(tree), Err(nom::Err::Error(e) | nom::Err::Failure(e)) => { Err(ParseVersionTreeError::ParseError(convert_error(input, e))) diff --git a/crates/rattler_virtual_packages/src/linux.rs b/crates/rattler_virtual_packages/src/linux.rs index b78eb5484a..95e91d0b6f 100644 --- a/crates/rattler_virtual_packages/src/linux.rs +++ b/crates/rattler_virtual_packages/src/linux.rs @@ -80,14 +80,16 @@ fn parse_linux_version(version_str: &str) -> Result Option<&str> { use nom::character::complete::{char, digit1}; use nom::combinator::{opt, recognize}; - use nom::sequence::{pair, tuple}; - let result: Result<_, nom::Err>> = recognize(tuple(( + use nom::sequence::pair; + use nom::Parser; + let result: Result<_, nom::Err>> = recognize(( digit1, char('.'), digit1, opt(pair(char('.'), digit1)), opt(pair(char('.'), digit1)), - )))(version_str); + )) + .parse(version_str); let (_rest, version_part) = result.ok()?; Some(version_part) diff --git a/js-rattler/Cargo.lock b/js-rattler/Cargo.lock index 12e3ac37d3..50ec2d822b 100644 --- a/js-rattler/Cargo.lock +++ b/js-rattler/Cargo.lock @@ -1369,12 +1369,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "miniz_oxide" version = "0.8.8" @@ -1397,12 +1391,20 @@ dependencies = [ [[package]] name = "nom" -version = "7.1.3" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" dependencies = [ "memchr", - "minimal-lexical", +] + +[[package]] +name = "nom-language" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2de2bc5b451bfedaef92c90b8939a8fff5770bdcc1fafd6239d086aab8fa6b29" +dependencies = [ + "nom", ] [[package]] @@ -1819,6 +1821,7 @@ dependencies = [ "itertools", "lazy-regex", "nom", + "nom-language", "purl", "rattler_digest", "rattler_macros",