use std::collections::VecDeque;
use proc_macro2::{Delimiter, Ident, Span, TokenStream, TokenTree};
use crate::ParseResult;
use crate::util::kv_parser::KvValue;
use crate::util::{KvParser, bail, delimiter_opening_char, is_punct};
pub struct ListParser {
lists: VecDeque<KvValue>,
span_close: Span,
}
impl ListParser {
pub(crate) fn new_from_kv(
parser: &mut KvParser,
key: &str,
delimiter: Delimiter,
) -> ParseResult<Option<Self>> {
let mut tokens = match parser.handle_any_entry(key) {
None => return Ok(None),
Some((key, None)) => {
return Ok(Some(Self {
lists: VecDeque::new(),
span_close: key.span(),
}));
}
Some((_, Some(tokens))) => tokens.into_tokens(),
};
if tokens.len() > 1 {
return bail!(&tokens[1], "unexpected expression");
}
Ok(Some(Self::new_from_tree(tokens.remove(0), delimiter)?))
}
pub fn new_from_tree(tree: TokenTree, delimiter: Delimiter) -> ParseResult<Self> {
let group = match tree {
TokenTree::Group(group) => group,
_ => return bail!(tree, "expected list of items"),
};
if group.delimiter() != delimiter {
let expected = delimiter_opening_char(delimiter);
let got = delimiter_opening_char(group.delimiter());
return bail!(group.span_open(), "expected `{expected}`, got `{got}`");
}
let trees: Vec<TokenTree> = group.stream().into_iter().collect();
let raw_lists = trees
.split_inclusive(|tree| is_punct(tree, ','))
.collect::<Vec<_>>();
let list_len = raw_lists.len();
let mut lists = Vec::new();
for (i, list) in raw_lists.into_iter().enumerate() {
let is_last = i == list_len - 1;
if list.is_empty() {
break;
}
if !is_last && list.len() == 1 {
let list_stream = list.iter().cloned().collect::<TokenStream>();
return bail!(list_stream, "expected expression, found `,`");
}
let end = if is_last { list.len() } else { list.len() - 1 };
lists.push(KvValue::new((&list[..end]).into()));
}
Ok(Self {
lists: lists.into(),
span_close: group.span_close(),
})
}
fn pop_next(&mut self) -> Option<KvValue> {
self.lists.pop_front()
}
pub(crate) fn peek(&self) -> Option<&KvValue> {
self.lists.front()
}
pub(crate) fn next_expr(&mut self) -> ParseResult<TokenStream> {
match self.pop_next() {
Some(kv) => kv.expr(),
None => bail!(self.span_close, "expected expression"),
}
}
pub(crate) fn next_ident(&mut self) -> ParseResult<Option<Ident>> {
self.pop_next().map(|kv| kv.ident()).transpose()
}
pub fn try_next_ident(&mut self) -> ParseResult<Option<Ident>> {
let Some(kv) = self.peek() else {
return Ok(None);
};
let id = kv.as_ident()?;
_ = self.pop_next();
Ok(Some(id))
}
pub fn next_allowed_ident(&mut self, allowed_ids: &[&str]) -> ParseResult<Option<Ident>> {
let Some(next_id) = self.try_next_ident()? else {
return Ok(None);
};
for id in allowed_ids {
if next_id == id {
return Ok(Some(next_id));
}
}
let allowed_values = allowed_ids.join(",");
bail!(next_id, "expected one of: \"{allowed_values}\"")
}
pub(crate) fn try_next_key_value(&mut self) -> Option<(Ident, KvValue)> {
let kv = self.peek()?;
match kv.as_key_value() {
Ok((key, value)) => {
_ = self.pop_next();
Some((key, value))
}
_ => None,
}
}
pub(crate) fn next_key_optional_value(
&mut self,
) -> ParseResult<Option<(Ident, Option<KvValue>)>> {
if let Some((key, value)) = self.try_next_key_value() {
return Ok(Some((key, Some(value))));
}
match self.try_next_ident() {
Ok(opt) => Ok(opt.map(|k| (k, None))),
Err(err) => bail!(err.span(), "expected `key [= value]`"),
}
}
pub(crate) fn next_allowed_key_optional_value(
&mut self,
allowed_flag_keys: &[&str],
allowed_kv_keys: &[&str],
) -> ParseResult<Option<(Ident, Option<KvValue>)>> {
let allowed_keys = || {
let allowed_flag_keys = allowed_flag_keys.join(",");
let allowed_kv_keys = allowed_kv_keys.join(",");
[allowed_flag_keys, allowed_kv_keys].join(",")
};
match self.next_key_optional_value()? {
Some((key, None)) if !allowed_flag_keys.contains(&key.to_string().as_str()) => {
if allowed_kv_keys.contains(&key.to_string().as_str()) {
return bail!(key, "`{key}` requires a value `{key} = VALUE`");
}
bail!(key, "expected one of \"{}\"", allowed_keys())
}
Some((key, Some(_))) if !allowed_kv_keys.contains(&key.to_string().as_str()) => {
if allowed_flag_keys.contains(&key.to_string().as_str()) {
return bail!(key, "key `{key}` mustn't have a value");
}
bail!(key, "expected one of \"{}\"", allowed_keys())
}
key_maybe_value => Ok(key_maybe_value),
}
}
pub fn finish(&mut self) -> ParseResult<()> {
if let Some(kv) = self.pop_next() {
let stream: TokenStream = kv.expr()?;
return bail!(&stream, "unrecognized value `{stream}`");
}
Ok(())
}
}