Rename CharIter to Cursor (#667)

This better aligns with the analogous struct that we have in Ruff.
This commit is contained in:
Charlie Marsh 2023-12-15 16:57:59 -05:00 committed by GitHub
parent 620f73b38b
commit 875c9a635e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 126 additions and 125 deletions

View file

@ -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),

View file

@ -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)
}