#![allow(clippy::module_inception)]
use core::cmp::Ordering;
use core::hash;
use core::iter::FromIterator;
use core::mem;
use core::slice;
use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::{self, Vec};
use crate::fmt;
use crate::lang::{Lang, LangSupportsEval};
use crate::tokens::{FormatInto, Item, Kind, Register};
pub struct Tokens<L = ()>
where
L: Lang,
{
items: Vec<(usize, Item<L>)>,
last_lang_item: usize,
}
impl<L> Tokens<L>
where
L: Lang,
{
pub fn new() -> Self {
Tokens {
items: Vec::new(),
last_lang_item: 0,
}
}
pub fn with_capacity(cap: usize) -> Self {
Tokens {
items: Vec::with_capacity(cap),
last_lang_item: 0,
}
}
#[inline]
pub fn iter(&self) -> Iter<'_, L> {
Iter {
iter: self.items.iter(),
}
}
pub fn append<T>(&mut self, tokens: T)
where
T: FormatInto<L>,
{
tokens.format_into(self)
}
pub fn extend<I>(&mut self, it: I)
where
I: IntoIterator<Item = Item<L>>,
{
let it = it.into_iter();
let (low, high) = it.size_hint();
self.items.reserve(high.unwrap_or(low));
for item in it {
self.item(item);
}
}
pub fn iter_lang(&self) -> IterLang<'_, L> {
IterLang {
items: &self.items,
pos: self.last_lang_item,
}
}
pub fn register<T>(&mut self, tokens: T)
where
T: Register<L>,
{
tokens.register(self);
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn space(&mut self) {
if let Some((_, Item { kind: Kind::Space })) = self.items.last() {
return;
}
self.items.push((0, Item::space()));
}
pub fn push(&mut self) {
let item = loop {
let Some((o, item)) = self.items.pop() else {
break None;
};
match &item.kind {
Kind::Line => {
self.items.push((o, item));
return;
}
Kind::Space | Kind::Push => continue,
_ => break Some((o, item)),
}
};
self.items.extend(item);
self.items.push((0, Item::push()));
}
pub fn line(&mut self) {
let item = loop {
let Some((o, item)) = self.items.pop() else {
break None;
};
if matches!(item.kind, Kind::Line | Kind::Push) {
continue;
}
break Some((o, item));
};
self.items.extend(item);
self.items.push((0, Item::line()));
}
pub fn indent(&mut self) {
self.indentation(1);
}
pub fn unindent(&mut self) {
self.indentation(-1);
}
pub fn format(
&self,
out: &mut fmt::Formatter<'_>,
config: &L::Config,
format: &L::Format,
) -> fmt::Result {
out.format_items(&self.items, config, format)
}
pub(crate) fn item(&mut self, item: Item<L>) {
match item.kind {
Kind::Push => self.push(),
Kind::Line => self.line(),
Kind::Space => self.space(),
Kind::Indentation(n) => self.indentation(n),
Kind::Lang(item) => self.lang_item(item),
Kind::Register(item) => self.lang_item_register(item),
other => self.items.push((0, Item::new(other))),
}
}
pub(crate) fn lang_item(&mut self, item: Box<L::Item>) {
self.items.push((self.last_lang_item, Item::lang(item)));
self.last_lang_item = self.items.len();
}
pub(crate) fn lang_item_register(&mut self, item: Box<L::Item>) {
self.items.push((self.last_lang_item, Item::register(item)));
self.last_lang_item = self.items.len();
}
pub fn format_file(&self, out: &mut fmt::Formatter<'_>, config: &L::Config) -> fmt::Result {
L::format_file(self, out, config)?;
out.write_trailing_line()?;
Ok(())
}
fn indentation(&mut self, mut n: i16) {
let item = loop {
let Some((o, item)) = self.items.pop() else {
break None;
};
match &item.kind {
Kind::Push => continue,
Kind::Space => continue,
Kind::Line => continue,
Kind::Indentation(u) => n += u,
_ => break Some((o, item)),
}
};
self.items.extend(item);
if n != 0 {
self.items.push((0, Item::new(Kind::Indentation(n))));
}
}
}
impl<L> Default for Tokens<L>
where
L: Lang,
{
fn default() -> Self {
Self::new()
}
}
impl<L> Tokens<L>
where
L: LangSupportsEval,
{
#[doc(hidden)]
#[inline]
pub fn lang_supports_eval(&self) {}
}
impl<L> Tokens<L>
where
L: Lang,
L::Config: Default,
{
pub fn to_file_string(&self) -> fmt::Result<String> {
let mut w = fmt::FmtWriter::new(String::new());
let fmt = fmt::Config::from_lang::<L>();
let mut formatter = w.as_formatter(&fmt);
let config = L::Config::default();
self.format_file(&mut formatter, &config)?;
Ok(w.into_inner())
}
pub fn to_string(&self) -> fmt::Result<String> {
let mut w = fmt::FmtWriter::new(String::new());
let fmt = fmt::Config::from_lang::<L>();
let mut formatter = w.as_formatter(&fmt);
let config = L::Config::default();
let format = L::Format::default();
self.format(&mut formatter, &config, &format)?;
Ok(w.into_inner())
}
pub fn to_file_vec(&self) -> fmt::Result<Vec<String>> {
let mut w = fmt::VecWriter::new();
let fmt = fmt::Config::from_lang::<L>();
let mut formatter = w.as_formatter(&fmt);
let config = L::Config::default();
self.format_file(&mut formatter, &config)?;
Ok(w.into_vec())
}
pub fn to_vec(&self) -> fmt::Result<Vec<String>> {
let mut w = fmt::VecWriter::new();
let fmt = fmt::Config::from_lang::<L>();
let mut formatter = w.as_formatter(&fmt);
let config = L::Config::default();
let format = L::Format::default();
self.format(&mut formatter, &config, &format)?;
Ok(w.into_vec())
}
}
impl<L> PartialEq<Tokens<L>> for Tokens<L>
where
L: Lang,
L::Item: PartialEq,
{
#[inline]
fn eq(&self, other: &Tokens<L>) -> bool {
self.items == other.items
}
}
impl<L> PartialEq<Vec<Item<L>>> for Tokens<L>
where
L: Lang,
L::Item: PartialEq,
{
#[inline]
fn eq(&self, other: &Vec<Item<L>>) -> bool {
self == &other[..]
}
}
impl<L> PartialEq<Tokens<L>> for Vec<Item<L>>
where
L: Lang,
L::Item: PartialEq,
{
#[inline]
fn eq(&self, other: &Tokens<L>) -> bool {
other == &self[..]
}
}
impl<L> PartialEq<[Item<L>]> for Tokens<L>
where
L: Lang,
L::Item: PartialEq,
{
#[inline]
fn eq(&self, other: &[Item<L>]) -> bool {
self.iter().eq(other)
}
}
impl<L, const N: usize> PartialEq<[Item<L>; N]> for Tokens<L>
where
L: Lang,
L::Item: PartialEq,
{
#[inline]
fn eq(&self, other: &[Item<L>; N]) -> bool {
self == &other[..]
}
}
impl<L> PartialEq<Tokens<L>> for [Item<L>]
where
L: Lang,
L::Item: PartialEq,
{
#[inline]
fn eq(&self, other: &Tokens<L>) -> bool {
self.iter().eq(other.iter())
}
}
impl<L> Eq for Tokens<L>
where
L: Lang,
L::Item: Eq,
{
}
impl<L> PartialOrd<Tokens<L>> for Tokens<L>
where
L: Lang,
L::Item: PartialOrd,
{
#[inline]
fn partial_cmp(&self, other: &Tokens<L>) -> Option<Ordering> {
self.items.iter().partial_cmp(other.items.iter())
}
}
impl<L> Ord for Tokens<L>
where
L: Lang,
L::Item: Ord,
{
#[inline]
fn cmp(&self, other: &Tokens<L>) -> Ordering {
self.items.iter().cmp(other.items.iter())
}
}
pub struct IntoIter<L>
where
L: Lang,
{
iter: vec::IntoIter<(usize, Item<L>)>,
}
impl<L> Iterator for IntoIter<L>
where
L: Lang,
{
type Item = Item<L>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
Some(self.iter.next()?.1)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
impl<L> IntoIterator for Tokens<L>
where
L: Lang,
{
type Item = Item<L>;
type IntoIter = IntoIter<L>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
IntoIter {
iter: self.items.into_iter(),
}
}
}
pub struct Iter<'a, L>
where
L: Lang,
{
iter: slice::Iter<'a, (usize, Item<L>)>,
}
impl<'a, L> Iterator for Iter<'a, L>
where
L: Lang,
{
type Item = &'a Item<L>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
Some(&self.iter.next()?.1)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
impl<'a, L> IntoIterator for &'a Tokens<L>
where
L: Lang,
{
type Item = &'a Item<L>;
type IntoIter = Iter<'a, L>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a, L> FromIterator<&'a Item<L>> for Tokens<L>
where
L: Lang,
L::Item: Clone,
{
fn from_iter<I: IntoIterator<Item = &'a Item<L>>>(iter: I) -> Self {
let it = iter.into_iter();
let (low, high) = it.size_hint();
let mut tokens = Self::with_capacity(high.unwrap_or(low));
tokens.extend(it.cloned());
tokens
}
}
impl<L> FromIterator<Item<L>> for Tokens<L>
where
L: Lang,
{
fn from_iter<I: IntoIterator<Item = Item<L>>>(iter: I) -> Self {
let it = iter.into_iter();
let (low, high) = it.size_hint();
let mut tokens = Self::with_capacity(high.unwrap_or(low));
tokens.extend(it);
tokens
}
}
impl<L> core::fmt::Debug for Tokens<L>
where
L: Lang,
L::Item: core::fmt::Debug,
{
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_list().entries(&self.items).finish()
}
}
impl<L> Clone for Tokens<L>
where
L: Lang,
L::Item: Clone,
{
#[inline]
fn clone(&self) -> Self {
Self {
items: self.items.clone(),
last_lang_item: self.last_lang_item,
}
}
}
impl<L> hash::Hash for Tokens<L>
where
L: Lang,
L::Item: hash::Hash,
{
#[inline]
fn hash<H>(&self, state: &mut H)
where
H: hash::Hasher,
{
self.items.hash(state);
self.last_lang_item.hash(state);
}
}
pub struct IterLang<'a, L>
where
L: Lang,
{
items: &'a [(usize, Item<L>)],
pos: usize,
}
impl<'a, L> Iterator for IterLang<'a, L>
where
L: Lang,
{
type Item = &'a L::Item;
fn next(&mut self) -> Option<Self::Item> {
let pos = mem::take(&mut self.pos);
if pos == 0 {
return None;
}
match self.items.get(pos - 1)? {
(
prev,
Item {
kind: Kind::Lang(item) | Kind::Register(item),
},
) => {
self.pos = *prev;
Some(item)
}
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use core::fmt::Write as _;
use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;
use crate as genco;
use crate::fmt;
use crate::{quote, Tokens};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct Import(u32);
impl_lang! {
Lang {
type Config = ();
type Format = ();
type Item = Any;
}
Import(Import) {
fn format(&self, out: &mut fmt::Formatter<'_>, _: &(), _: &()) -> fmt::Result {
write!(out, "{}", self.0)
}
}
}
#[test]
fn test_walk_custom() {
let toks: Tokens<Lang> = quote! {
1:1 $(Import(1)) 1:2
bar
2:1 2:2 $(quote!(3:1 3:2)) $(Import(2))
$(String::from("nope"))
};
let mut output: Vec<_> = toks.iter_lang().cloned().collect();
output.sort();
let expected: Vec<Any> = vec![Import(1).into(), Import(2).into()];
assert_eq!(expected, output);
}
}