mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 02:48:17 +00:00
Rename CharIter
to Cursor
(#667)
This better aligns with the analogous struct that we have in Ruff.
This commit is contained in:
parent
620f73b38b
commit
875c9a635e
2 changed files with 126 additions and 125 deletions
|
@ -381,13 +381,13 @@ impl FromStr for Requirement {
|
|||
|
||||
/// Parse a [Dependency Specifier](https://packaging.python.org/en/latest/specifications/dependency-specifiers/)
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
parse(&mut CharIter::new(input))
|
||||
parse(&mut Cursor::new(input))
|
||||
}
|
||||
}
|
||||
|
||||
impl Requirement {
|
||||
/// Parse a [Dependency Specifier](https://packaging.python.org/en/latest/specifications/dependency-specifiers/)
|
||||
pub fn parse(input: &mut CharIter) -> Result<Self, Pep508Error> {
|
||||
pub fn parse(input: &mut Cursor) -> Result<Self, Pep508Error> {
|
||||
parse(input)
|
||||
}
|
||||
}
|
||||
|
@ -401,15 +401,16 @@ pub enum VersionOrUrl {
|
|||
Url(VerbatimUrl),
|
||||
}
|
||||
|
||||
/// A `Vec<char>` and an index inside of it. Like [String], but with utf-8 aware indexing
|
||||
pub struct CharIter<'a> {
|
||||
/// A [`Cursor`] over a string.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Cursor<'a> {
|
||||
input: &'a str,
|
||||
chars: Chars<'a>,
|
||||
/// char-based (not byte-based) position
|
||||
pos: usize,
|
||||
}
|
||||
|
||||
impl<'a> CharIter<'a> {
|
||||
impl<'a> Cursor<'a> {
|
||||
/// Convert from `&str`
|
||||
pub fn new(input: &'a str) -> Self {
|
||||
Self {
|
||||
|
@ -518,11 +519,11 @@ impl<'a> CharIter<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_name(chars: &mut CharIter) -> Result<PackageName, Pep508Error> {
|
||||
fn parse_name(cursor: &mut Cursor) -> Result<PackageName, Pep508Error> {
|
||||
// https://peps.python.org/pep-0508/#names
|
||||
// ^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$ with re.IGNORECASE
|
||||
let mut name = String::new();
|
||||
if let Some((index, char)) = chars.next() {
|
||||
if let Some((index, char)) = cursor.next() {
|
||||
if matches!(char, 'A'..='Z' | 'a'..='z' | '0'..='9') {
|
||||
name.push(char);
|
||||
} else {
|
||||
|
@ -532,7 +533,7 @@ fn parse_name(chars: &mut CharIter) -> Result<PackageName, Pep508Error> {
|
|||
)),
|
||||
start: index,
|
||||
len: 1,
|
||||
input: chars.copy_chars(),
|
||||
input: cursor.copy_chars(),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
@ -540,24 +541,24 @@ fn parse_name(chars: &mut CharIter) -> Result<PackageName, Pep508Error> {
|
|||
message: Pep508ErrorSource::String("Empty field is not allowed for PEP508".to_string()),
|
||||
start: 0,
|
||||
len: 1,
|
||||
input: chars.copy_chars(),
|
||||
input: cursor.copy_chars(),
|
||||
});
|
||||
}
|
||||
|
||||
loop {
|
||||
match chars.peek() {
|
||||
match cursor.peek() {
|
||||
Some((index, char @ ('A'..='Z' | 'a'..='z' | '0'..='9' | '.' | '-' | '_'))) => {
|
||||
name.push(char);
|
||||
chars.next();
|
||||
cursor.next();
|
||||
// [.-_] can't be the final character
|
||||
if chars.peek().is_none() && matches!(char, '.' | '-' | '_') {
|
||||
if cursor.peek().is_none() && matches!(char, '.' | '-' | '_') {
|
||||
return Err(Pep508Error {
|
||||
message: Pep508ErrorSource::String(format!(
|
||||
"Package name must end with an alphanumeric character, not '{char}'"
|
||||
)),
|
||||
start: index,
|
||||
len: 1,
|
||||
input: chars.copy_chars(),
|
||||
input: cursor.copy_chars(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -570,15 +571,15 @@ fn parse_name(chars: &mut CharIter) -> Result<PackageName, Pep508Error> {
|
|||
}
|
||||
|
||||
/// parses extras in the `[extra1,extra2] format`
|
||||
fn parse_extras(chars: &mut CharIter) -> Result<Option<Vec<ExtraName>>, Pep508Error> {
|
||||
let Some(bracket_pos) = chars.eat('[') else {
|
||||
fn parse_extras(cursor: &mut Cursor) -> Result<Option<Vec<ExtraName>>, Pep508Error> {
|
||||
let Some(bracket_pos) = cursor.eat('[') else {
|
||||
return Ok(None);
|
||||
};
|
||||
let mut extras = Vec::new();
|
||||
|
||||
loop {
|
||||
// wsp* before the identifier
|
||||
chars.eat_whitespace();
|
||||
cursor.eat_whitespace();
|
||||
let mut buffer = String::new();
|
||||
let early_eof_error = Pep508Error {
|
||||
message: Pep508ErrorSource::String(
|
||||
|
@ -587,11 +588,11 @@ fn parse_extras(chars: &mut CharIter) -> Result<Option<Vec<ExtraName>>, Pep508Er
|
|||
),
|
||||
start: bracket_pos,
|
||||
len: 1,
|
||||
input: chars.copy_chars(),
|
||||
input: cursor.copy_chars(),
|
||||
};
|
||||
|
||||
// First char of the identifier
|
||||
match chars.next() {
|
||||
match cursor.next() {
|
||||
// letterOrDigit
|
||||
Some((_, alphanumeric @ ('a'..='z' | 'A'..='Z' | '0'..='9'))) => {
|
||||
buffer.push(alphanumeric);
|
||||
|
@ -603,7 +604,7 @@ fn parse_extras(chars: &mut CharIter) -> Result<Option<Vec<ExtraName>>, Pep508Er
|
|||
)),
|
||||
start: pos,
|
||||
len: 1,
|
||||
input: chars.copy_chars(),
|
||||
input: cursor.copy_chars(),
|
||||
});
|
||||
}
|
||||
None => return Err(early_eof_error),
|
||||
|
@ -613,13 +614,13 @@ fn parse_extras(chars: &mut CharIter) -> Result<Option<Vec<ExtraName>>, Pep508Er
|
|||
// identifier_end = letterOrDigit | (('-' | '_' | '.' )* letterOrDigit)
|
||||
// identifier_end*
|
||||
buffer.push_str(
|
||||
&chars
|
||||
&cursor
|
||||
.take_while(
|
||||
|char| matches!(char, 'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_' | '.'),
|
||||
)
|
||||
.0,
|
||||
);
|
||||
match chars.peek() {
|
||||
match cursor.peek() {
|
||||
Some((pos, char)) if char != ',' && char != ']' && !char.is_whitespace() => {
|
||||
return Err(Pep508Error {
|
||||
message: Pep508ErrorSource::String(format!(
|
||||
|
@ -627,15 +628,15 @@ fn parse_extras(chars: &mut CharIter) -> Result<Option<Vec<ExtraName>>, Pep508Er
|
|||
)),
|
||||
start: pos,
|
||||
len: 1,
|
||||
input: chars.copy_chars(),
|
||||
input: cursor.copy_chars(),
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
// wsp* after the identifier
|
||||
chars.eat_whitespace();
|
||||
cursor.eat_whitespace();
|
||||
// end or next identifier?
|
||||
match chars.next() {
|
||||
match cursor.next() {
|
||||
Some((_, ',')) => {
|
||||
extras.push(
|
||||
ExtraName::new(buffer)
|
||||
|
@ -656,7 +657,7 @@ fn parse_extras(chars: &mut CharIter) -> Result<Option<Vec<ExtraName>>, Pep508Er
|
|||
)),
|
||||
start: pos,
|
||||
len: 1,
|
||||
input: chars.copy_chars(),
|
||||
input: cursor.copy_chars(),
|
||||
});
|
||||
}
|
||||
None => return Err(early_eof_error),
|
||||
|
@ -666,31 +667,31 @@ fn parse_extras(chars: &mut CharIter) -> Result<Option<Vec<ExtraName>>, Pep508Er
|
|||
Ok(Some(extras))
|
||||
}
|
||||
|
||||
fn parse_url(chars: &mut CharIter) -> Result<VersionOrUrl, Pep508Error> {
|
||||
fn parse_url(cursor: &mut Cursor) -> Result<VersionOrUrl, Pep508Error> {
|
||||
// wsp*
|
||||
chars.eat_whitespace();
|
||||
cursor.eat_whitespace();
|
||||
// <URI_reference>
|
||||
let (url, start, len) = chars.take_while(|char| !char.is_whitespace());
|
||||
let (url, start, len) = cursor.take_while(|char| !char.is_whitespace());
|
||||
if url.is_empty() {
|
||||
return Err(Pep508Error {
|
||||
message: Pep508ErrorSource::String("Expected URL".to_string()),
|
||||
start,
|
||||
len,
|
||||
input: chars.copy_chars(),
|
||||
input: cursor.copy_chars(),
|
||||
});
|
||||
}
|
||||
let url = VerbatimUrl::parse(url).map_err(|err| Pep508Error {
|
||||
message: Pep508ErrorSource::UrlError(err),
|
||||
start,
|
||||
len,
|
||||
input: chars.copy_chars(),
|
||||
input: cursor.copy_chars(),
|
||||
})?;
|
||||
Ok(VersionOrUrl::Url(url))
|
||||
}
|
||||
|
||||
/// PEP 440 wrapper
|
||||
fn parse_specifier(
|
||||
chars: &mut CharIter,
|
||||
cursor: &mut Cursor,
|
||||
buffer: &str,
|
||||
start: usize,
|
||||
end: usize,
|
||||
|
@ -699,7 +700,7 @@ fn parse_specifier(
|
|||
message: Pep508ErrorSource::String(err),
|
||||
start,
|
||||
len: end - start,
|
||||
input: chars.copy_chars(),
|
||||
input: cursor.copy_chars(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -708,22 +709,22 @@ fn parse_specifier(
|
|||
/// ```text
|
||||
/// version_one (wsp* ',' version_one)*
|
||||
/// ```
|
||||
fn parse_version_specifier(chars: &mut CharIter) -> Result<Option<VersionOrUrl>, Pep508Error> {
|
||||
let mut start = chars.get_pos();
|
||||
fn parse_version_specifier(cursor: &mut Cursor) -> Result<Option<VersionOrUrl>, Pep508Error> {
|
||||
let mut start = cursor.get_pos();
|
||||
let mut specifiers = Vec::new();
|
||||
let mut buffer = String::new();
|
||||
let requirement_kind = loop {
|
||||
match chars.peek() {
|
||||
match cursor.peek() {
|
||||
Some((end, ',')) => {
|
||||
let specifier = parse_specifier(chars, &buffer, start, end)?;
|
||||
let specifier = parse_specifier(cursor, &buffer, start, end)?;
|
||||
specifiers.push(specifier);
|
||||
buffer.clear();
|
||||
chars.next();
|
||||
cursor.next();
|
||||
start = end + 1;
|
||||
}
|
||||
Some((_, ';')) | None => {
|
||||
let end = chars.get_pos();
|
||||
let specifier = parse_specifier(chars, &buffer, start, end)?;
|
||||
let end = cursor.get_pos();
|
||||
let specifier = parse_specifier(cursor, &buffer, start, end)?;
|
||||
specifiers.push(specifier);
|
||||
break Some(VersionOrUrl::VersionSpecifier(
|
||||
specifiers.into_iter().collect(),
|
||||
|
@ -731,7 +732,7 @@ fn parse_version_specifier(chars: &mut CharIter) -> Result<Option<VersionOrUrl>,
|
|||
}
|
||||
Some((_, char)) => {
|
||||
buffer.push(char);
|
||||
chars.next();
|
||||
cursor.next();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -744,26 +745,26 @@ fn parse_version_specifier(chars: &mut CharIter) -> Result<Option<VersionOrUrl>,
|
|||
/// '(' version_one (wsp* ',' version_one)* ')'
|
||||
/// ```
|
||||
fn parse_version_specifier_parentheses(
|
||||
chars: &mut CharIter,
|
||||
cursor: &mut Cursor,
|
||||
) -> Result<Option<VersionOrUrl>, Pep508Error> {
|
||||
let brace_pos = chars.get_pos();
|
||||
chars.next();
|
||||
let brace_pos = cursor.get_pos();
|
||||
cursor.next();
|
||||
// Makes for slightly better error underline
|
||||
chars.eat_whitespace();
|
||||
let mut start = chars.get_pos();
|
||||
cursor.eat_whitespace();
|
||||
let mut start = cursor.get_pos();
|
||||
let mut specifiers = Vec::new();
|
||||
let mut buffer = String::new();
|
||||
let requirement_kind = loop {
|
||||
match chars.next() {
|
||||
match cursor.next() {
|
||||
Some((end, ',')) => {
|
||||
let specifier =
|
||||
parse_specifier(chars, &buffer, start, end)?;
|
||||
parse_specifier(cursor, &buffer, start, end)?;
|
||||
specifiers.push(specifier);
|
||||
buffer.clear();
|
||||
start = end + 1;
|
||||
}
|
||||
Some((end, ')')) => {
|
||||
let specifier = parse_specifier(chars, &buffer, start, end)?;
|
||||
let specifier = parse_specifier(cursor, &buffer, start, end)?;
|
||||
specifiers.push(specifier);
|
||||
break Some(VersionOrUrl::VersionSpecifier(specifiers.into_iter().collect()));
|
||||
}
|
||||
|
@ -772,7 +773,7 @@ fn parse_version_specifier_parentheses(
|
|||
message: Pep508ErrorSource::String("Missing closing parenthesis (expected ')', found end of dependency specification)".to_string()),
|
||||
start: brace_pos,
|
||||
len: 1,
|
||||
input: chars.copy_chars(),
|
||||
input: cursor.copy_chars(),
|
||||
}),
|
||||
}
|
||||
};
|
||||
|
@ -780,7 +781,7 @@ fn parse_version_specifier_parentheses(
|
|||
}
|
||||
|
||||
/// Parse a [dependency specifier](https://packaging.python.org/en/latest/specifications/dependency-specifiers)
|
||||
fn parse(chars: &mut CharIter) -> Result<Requirement, Pep508Error> {
|
||||
fn parse(cursor: &mut Cursor) -> Result<Requirement, Pep508Error> {
|
||||
// Technically, the grammar is:
|
||||
// ```text
|
||||
// name_req = name wsp* extras? wsp* versionspec? wsp* quoted_marker?
|
||||
|
@ -794,50 +795,50 @@ fn parse(chars: &mut CharIter) -> Result<Requirement, Pep508Error> {
|
|||
// Where the extras start with '[' if any, then we have '@', '(' or one of the version comparison
|
||||
// operators. Markers start with ';' if any
|
||||
// wsp*
|
||||
chars.eat_whitespace();
|
||||
cursor.eat_whitespace();
|
||||
// name
|
||||
let name = parse_name(chars)?;
|
||||
let name = parse_name(cursor)?;
|
||||
// wsp*
|
||||
chars.eat_whitespace();
|
||||
cursor.eat_whitespace();
|
||||
// extras?
|
||||
let extras = parse_extras(chars)?;
|
||||
let extras = parse_extras(cursor)?;
|
||||
// wsp*
|
||||
chars.eat_whitespace();
|
||||
cursor.eat_whitespace();
|
||||
|
||||
// ( url_req | name_req )?
|
||||
let requirement_kind = match chars.peek_char() {
|
||||
let requirement_kind = match cursor.peek_char() {
|
||||
Some('@') => {
|
||||
chars.next();
|
||||
Some(parse_url(chars)?)
|
||||
cursor.next();
|
||||
Some(parse_url(cursor)?)
|
||||
}
|
||||
Some('(') => parse_version_specifier_parentheses(chars)?,
|
||||
Some('<' | '=' | '>' | '~' | '!') => parse_version_specifier(chars)?,
|
||||
Some('(') => parse_version_specifier_parentheses(cursor)?,
|
||||
Some('<' | '=' | '>' | '~' | '!') => parse_version_specifier(cursor)?,
|
||||
Some(';') | None => None,
|
||||
Some(other) => {
|
||||
return Err(Pep508Error {
|
||||
message: Pep508ErrorSource::String(format!(
|
||||
"Expected one of `@`, `(`, `<`, `=`, `>`, `~`, `!`, `;`, found `{other}`"
|
||||
)),
|
||||
start: chars.get_pos(),
|
||||
start: cursor.get_pos(),
|
||||
len: 1,
|
||||
input: chars.copy_chars(),
|
||||
input: cursor.copy_chars(),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// wsp*
|
||||
chars.eat_whitespace();
|
||||
cursor.eat_whitespace();
|
||||
// quoted_marker?
|
||||
let marker = if chars.peek_char() == Some(';') {
|
||||
let marker = if cursor.peek_char() == Some(';') {
|
||||
// Skip past the semicolon
|
||||
chars.next();
|
||||
Some(marker::parse_markers_impl(chars)?)
|
||||
cursor.next();
|
||||
Some(marker::parse_markers_impl(cursor)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
// wsp*
|
||||
chars.eat_whitespace();
|
||||
if let Some((pos, char)) = chars.next() {
|
||||
cursor.eat_whitespace();
|
||||
if let Some((pos, char)) = cursor.next() {
|
||||
return Err(Pep508Error {
|
||||
message: Pep508ErrorSource::String(if marker.is_none() {
|
||||
format!(r#"Expected end of input or ';', found '{char}'"#)
|
||||
|
@ -846,7 +847,7 @@ fn parse(chars: &mut CharIter) -> Result<Requirement, Pep508Error> {
|
|||
}),
|
||||
start: pos,
|
||||
len: 1,
|
||||
input: chars.copy_chars(),
|
||||
input: cursor.copy_chars(),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -897,7 +898,7 @@ mod tests {
|
|||
parse_markers_impl, MarkerExpression, MarkerOperator, MarkerTree, MarkerValue,
|
||||
MarkerValueString, MarkerValueVersion,
|
||||
};
|
||||
use crate::{CharIter, Requirement, VerbatimUrl, VersionOrUrl};
|
||||
use crate::{Cursor, Requirement, VerbatimUrl, VersionOrUrl};
|
||||
|
||||
fn assert_err(input: &str, error: &str) {
|
||||
assert_eq!(Requirement::from_str(input).unwrap_err().to_string(), error);
|
||||
|
@ -1159,7 +1160,7 @@ mod tests {
|
|||
#[test]
|
||||
fn test_marker_parsing() {
|
||||
let marker = r#"python_version == "2.7" and (sys_platform == "win32" or (os_name == "linux" and implementation_name == 'cpython'))"#;
|
||||
let actual = parse_markers_impl(&mut CharIter::new(marker)).unwrap();
|
||||
let actual = parse_markers_impl(&mut Cursor::new(marker)).unwrap();
|
||||
let expected = MarkerTree::And(vec![
|
||||
MarkerTree::Expression(MarkerExpression {
|
||||
l_value: MarkerValue::MarkerEnvVersion(MarkerValueVersion::PythonVersion),
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
//! outcomes. This implementation tries to carefully validate everything and emit warnings whenever
|
||||
//! bogus comparisons with unintended semantics are made.
|
||||
|
||||
use crate::{CharIter, Pep508Error, Pep508ErrorSource};
|
||||
use crate::{Cursor, Pep508Error, Pep508ErrorSource};
|
||||
use pep440_rs::{Version, VersionSpecifier};
|
||||
#[cfg(feature = "pyo3")]
|
||||
use pyo3::{
|
||||
|
@ -875,7 +875,7 @@ impl FromStr for MarkerExpression {
|
|||
type Err = Pep508Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut chars = CharIter::new(s);
|
||||
let mut chars = Cursor::new(s);
|
||||
let expression = parse_marker_key_op_value(&mut chars)?;
|
||||
chars.eat_whitespace();
|
||||
if let Some((pos, unexpected)) = chars.next() {
|
||||
|
@ -1111,20 +1111,20 @@ impl Display for MarkerTree {
|
|||
/// version_cmp = wsp* <'<=' | '<' | '!=' | '==' | '>=' | '>' | '~=' | '==='>
|
||||
/// marker_op = version_cmp | (wsp* 'in') | (wsp* 'not' wsp+ 'in')
|
||||
/// ```
|
||||
fn parse_marker_operator(chars: &mut CharIter) -> Result<MarkerOperator, Pep508Error> {
|
||||
fn parse_marker_operator(cursor: &mut Cursor) -> Result<MarkerOperator, Pep508Error> {
|
||||
let (operator, start, len) =
|
||||
chars.take_while(|char| !char.is_whitespace() && char != '\'' && char != '"');
|
||||
cursor.take_while(|char| !char.is_whitespace() && char != '\'' && char != '"');
|
||||
if operator == "not" {
|
||||
// 'not' wsp+ 'in'
|
||||
match chars.next() {
|
||||
match cursor.next() {
|
||||
None => {
|
||||
return Err(Pep508Error {
|
||||
message: Pep508ErrorSource::String(
|
||||
"Expected whitespace after 'not', found end of input".to_string(),
|
||||
),
|
||||
start: chars.get_pos(),
|
||||
start: cursor.get_pos(),
|
||||
len: 1,
|
||||
input: chars.copy_chars(),
|
||||
input: cursor.copy_chars(),
|
||||
})
|
||||
}
|
||||
Some((_, whitespace)) if whitespace.is_whitespace() => {}
|
||||
|
@ -1135,13 +1135,13 @@ fn parse_marker_operator(chars: &mut CharIter) -> Result<MarkerOperator, Pep508E
|
|||
)),
|
||||
start: pos,
|
||||
len: 1,
|
||||
input: chars.copy_chars(),
|
||||
input: cursor.copy_chars(),
|
||||
})
|
||||
}
|
||||
};
|
||||
chars.eat_whitespace();
|
||||
chars.next_expect_char('i', chars.get_pos())?;
|
||||
chars.next_expect_char('n', chars.get_pos())?;
|
||||
cursor.eat_whitespace();
|
||||
cursor.next_expect_char('i', cursor.get_pos())?;
|
||||
cursor.next_expect_char('n', cursor.get_pos())?;
|
||||
return Ok(MarkerOperator::NotIn);
|
||||
}
|
||||
MarkerOperator::from_str(&operator).map_err(|_| Pep508Error {
|
||||
|
@ -1150,7 +1150,7 @@ fn parse_marker_operator(chars: &mut CharIter) -> Result<MarkerOperator, Pep508E
|
|||
)),
|
||||
start,
|
||||
len,
|
||||
input: chars.copy_chars(),
|
||||
input: cursor.copy_chars(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1158,31 +1158,31 @@ fn parse_marker_operator(chars: &mut CharIter) -> Result<MarkerOperator, Pep508E
|
|||
/// '`os_name`', '`sys_platform`', '`platform_release`', '`platform_system`', '`platform_version`',
|
||||
/// '`platform_machine`', '`platform_python_implementation`', '`implementation_name`',
|
||||
/// '`implementation_version`', 'extra'
|
||||
fn parse_marker_value(chars: &mut CharIter) -> Result<MarkerValue, Pep508Error> {
|
||||
fn parse_marker_value(cursor: &mut Cursor) -> Result<MarkerValue, Pep508Error> {
|
||||
// > User supplied constants are always encoded as strings with either ' or " quote marks. Note
|
||||
// > that backslash escapes are not defined, but existing implementations do support them. They
|
||||
// > are not included in this specification because they add complexity and there is no observable
|
||||
// > need for them today. Similarly we do not define non-ASCII character support: all the runtime
|
||||
// > variables we are referencing are expected to be ASCII-only.
|
||||
match chars.peek() {
|
||||
match cursor.peek() {
|
||||
None => Err(Pep508Error {
|
||||
message: Pep508ErrorSource::String(
|
||||
"Expected marker value, found end of dependency specification".to_string(),
|
||||
),
|
||||
start: chars.get_pos(),
|
||||
start: cursor.get_pos(),
|
||||
len: 1,
|
||||
input: chars.copy_chars(),
|
||||
input: cursor.copy_chars(),
|
||||
}),
|
||||
// It can be a string ...
|
||||
Some((start_pos, quotation_mark @ ('"' | '\''))) => {
|
||||
chars.next();
|
||||
let (value, _, _) = chars.take_while(|c| c != quotation_mark);
|
||||
chars.next_expect_char(quotation_mark, start_pos)?;
|
||||
cursor.next();
|
||||
let (value, _, _) = cursor.take_while(|c| c != quotation_mark);
|
||||
cursor.next_expect_char(quotation_mark, start_pos)?;
|
||||
Ok(MarkerValue::string_value(value))
|
||||
}
|
||||
// ... or it can be a keyword
|
||||
Some(_) => {
|
||||
let (key, start, len) = chars.take_while(|char| {
|
||||
let (key, start, len) = cursor.take_while(|char| {
|
||||
!char.is_whitespace() && !['>', '=', '<', '!', '~', ')'].contains(&char)
|
||||
});
|
||||
MarkerValue::from_str(&key).map_err(|_| Pep508Error {
|
||||
|
@ -1191,7 +1191,7 @@ fn parse_marker_value(chars: &mut CharIter) -> Result<MarkerValue, Pep508Error>
|
|||
)),
|
||||
start,
|
||||
len,
|
||||
input: chars.copy_chars(),
|
||||
input: cursor.copy_chars(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1200,16 +1200,16 @@ fn parse_marker_value(chars: &mut CharIter) -> Result<MarkerValue, Pep508Error>
|
|||
/// ```text
|
||||
/// marker_var:l marker_op:o marker_var:r
|
||||
/// ```
|
||||
fn parse_marker_key_op_value(chars: &mut CharIter) -> Result<MarkerExpression, Pep508Error> {
|
||||
chars.eat_whitespace();
|
||||
let lvalue = parse_marker_value(chars)?;
|
||||
chars.eat_whitespace();
|
||||
fn parse_marker_key_op_value(cursor: &mut Cursor) -> Result<MarkerExpression, Pep508Error> {
|
||||
cursor.eat_whitespace();
|
||||
let lvalue = parse_marker_value(cursor)?;
|
||||
cursor.eat_whitespace();
|
||||
// "not in" and "in" must be preceded by whitespace. We must already have matched a whitespace
|
||||
// when we're here because other `parse_marker_key` would have pulled the characters in and
|
||||
// errored
|
||||
let operator = parse_marker_operator(chars)?;
|
||||
chars.eat_whitespace();
|
||||
let rvalue = parse_marker_value(chars)?;
|
||||
let operator = parse_marker_operator(cursor)?;
|
||||
cursor.eat_whitespace();
|
||||
let rvalue = parse_marker_value(cursor)?;
|
||||
Ok(MarkerExpression {
|
||||
l_value: lvalue,
|
||||
operator,
|
||||
|
@ -1221,14 +1221,14 @@ fn parse_marker_key_op_value(chars: &mut CharIter) -> Result<MarkerExpression, P
|
|||
/// marker_expr = marker_var:l marker_op:o marker_var:r -> (o, l, r)
|
||||
/// | wsp* '(' marker:m wsp* ')' -> m
|
||||
/// ```
|
||||
fn parse_marker_expr(chars: &mut CharIter) -> Result<MarkerTree, Pep508Error> {
|
||||
chars.eat_whitespace();
|
||||
if let Some(start_pos) = chars.eat('(') {
|
||||
let marker = parse_marker_or(chars)?;
|
||||
chars.next_expect_char(')', start_pos)?;
|
||||
fn parse_marker_expr(cursor: &mut Cursor) -> Result<MarkerTree, Pep508Error> {
|
||||
cursor.eat_whitespace();
|
||||
if let Some(start_pos) = cursor.eat('(') {
|
||||
let marker = parse_marker_or(cursor)?;
|
||||
cursor.next_expect_char(')', start_pos)?;
|
||||
Ok(marker)
|
||||
} else {
|
||||
Ok(MarkerTree::Expression(parse_marker_key_op_value(chars)?))
|
||||
Ok(MarkerTree::Expression(parse_marker_key_op_value(cursor)?))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1236,31 +1236,31 @@ fn parse_marker_expr(chars: &mut CharIter) -> Result<MarkerTree, Pep508Error> {
|
|||
/// marker_and = marker_expr:l wsp* 'and' marker_expr:r -> ('and', l, r)
|
||||
/// | marker_expr:m -> m
|
||||
/// ```
|
||||
fn parse_marker_and(chars: &mut CharIter) -> Result<MarkerTree, Pep508Error> {
|
||||
parse_marker_op(chars, "and", MarkerTree::And, parse_marker_expr)
|
||||
fn parse_marker_and(cursor: &mut Cursor) -> Result<MarkerTree, Pep508Error> {
|
||||
parse_marker_op(cursor, "and", MarkerTree::And, parse_marker_expr)
|
||||
}
|
||||
|
||||
/// ```text
|
||||
/// marker_or = marker_and:l wsp* 'or' marker_and:r -> ('or', l, r)
|
||||
/// | marker_and:m -> m
|
||||
/// ```
|
||||
fn parse_marker_or(chars: &mut CharIter) -> Result<MarkerTree, Pep508Error> {
|
||||
parse_marker_op(chars, "or", MarkerTree::Or, parse_marker_and)
|
||||
fn parse_marker_or(cursor: &mut Cursor) -> Result<MarkerTree, Pep508Error> {
|
||||
parse_marker_op(cursor, "or", MarkerTree::Or, parse_marker_and)
|
||||
}
|
||||
|
||||
/// Parses both `marker_and` and `marker_or`
|
||||
fn parse_marker_op(
|
||||
chars: &mut CharIter,
|
||||
cursor: &mut Cursor,
|
||||
op: &str,
|
||||
op_constructor: fn(Vec<MarkerTree>) -> MarkerTree,
|
||||
parse_inner: fn(&mut CharIter) -> Result<MarkerTree, Pep508Error>,
|
||||
parse_inner: fn(&mut Cursor) -> Result<MarkerTree, Pep508Error>,
|
||||
) -> Result<MarkerTree, Pep508Error> {
|
||||
// marker_and or marker_expr
|
||||
let first_element = parse_inner(chars)?;
|
||||
let first_element = parse_inner(cursor)?;
|
||||
// wsp*
|
||||
chars.eat_whitespace();
|
||||
cursor.eat_whitespace();
|
||||
// Check if we're done here instead of invoking the whole vec allocating loop
|
||||
if matches!(chars.peek_char(), None | Some(')')) {
|
||||
if matches!(cursor.peek_char(), None | Some(')')) {
|
||||
return Ok(first_element);
|
||||
}
|
||||
|
||||
|
@ -1268,13 +1268,13 @@ fn parse_marker_op(
|
|||
expressions.push(first_element);
|
||||
loop {
|
||||
// wsp*
|
||||
chars.eat_whitespace();
|
||||
cursor.eat_whitespace();
|
||||
// ('or' marker_and) or ('and' marker_or)
|
||||
let (maybe_op, _start, _len) = chars.peek_while(|c| !c.is_whitespace());
|
||||
let (maybe_op, _start, _len) = cursor.peek_while(|c| !c.is_whitespace());
|
||||
match maybe_op {
|
||||
value if value == op => {
|
||||
chars.take_while(|c| !c.is_whitespace());
|
||||
let expression = parse_inner(chars)?;
|
||||
cursor.take_while(|c| !c.is_whitespace());
|
||||
let expression = parse_inner(cursor)?;
|
||||
expressions.push(expression);
|
||||
}
|
||||
_ => {
|
||||
|
@ -1292,10 +1292,10 @@ fn parse_marker_op(
|
|||
/// ```text
|
||||
/// marker = marker_or
|
||||
/// ```
|
||||
pub(crate) fn parse_markers_impl(chars: &mut CharIter) -> Result<MarkerTree, Pep508Error> {
|
||||
let marker = parse_marker_or(chars)?;
|
||||
chars.eat_whitespace();
|
||||
if let Some((pos, unexpected)) = chars.next() {
|
||||
pub(crate) fn parse_markers_impl(cursor: &mut Cursor) -> Result<MarkerTree, Pep508Error> {
|
||||
let marker = parse_marker_or(cursor)?;
|
||||
cursor.eat_whitespace();
|
||||
if let Some((pos, unexpected)) = cursor.next() {
|
||||
// If we're here, both parse_marker_or and parse_marker_and returned because the next
|
||||
// character was neither "and" nor "or"
|
||||
return Err(Pep508Error {
|
||||
|
@ -1303,8 +1303,8 @@ pub(crate) fn parse_markers_impl(chars: &mut CharIter) -> Result<MarkerTree, Pep
|
|||
"Unexpected character '{unexpected}', expected 'and', 'or' or end of input"
|
||||
)),
|
||||
start: pos,
|
||||
len: chars.chars.clone().count(),
|
||||
input: chars.copy_chars(),
|
||||
len: cursor.chars.clone().count(),
|
||||
input: cursor.copy_chars(),
|
||||
});
|
||||
};
|
||||
Ok(marker)
|
||||
|
@ -1313,7 +1313,7 @@ pub(crate) fn parse_markers_impl(chars: &mut CharIter) -> Result<MarkerTree, Pep
|
|||
/// Parses markers such as `python_version < '3.8'` or
|
||||
/// `python_version == "3.10" and (sys_platform == "win32" or (os_name == "linux" and implementation_name == 'cpython'))`
|
||||
fn parse_markers(markers: &str) -> Result<MarkerTree, Pep508Error> {
|
||||
let mut chars = CharIter::new(markers);
|
||||
let mut chars = Cursor::new(markers);
|
||||
parse_markers_impl(&mut chars)
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue