#![allow(clippy::module_inception)]
use crate::fmt;
use crate::lang::{Lang, LangSupportsEval};
use crate::tokens::{FormatInto, Item, Register};
use std::cmp;
use std::iter::FromIterator;
use std::mem;
use std::slice;
use std::vec;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Tokens<L = ()>
where
L: Lang,
{
items: Vec<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,
}
}
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 walk_imports(&self) -> WalkImports<'_, L> {
WalkImports {
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::Space) = self.items.last() {
return;
}
self.items.push(Item::Space);
}
pub fn push(&mut self) {
let item = loop {
match self.items.pop() {
Some(Item::Line) => {
self.items.push(Item::Line);
return;
}
Some(Item::Space | Item::Push) => continue,
item => break item,
}
};
self.items.extend(item);
self.items.push(Item::Push);
}
pub fn line(&mut self) {
let item = loop {
match self.items.pop() {
Some(Item::Line) | Some(Item::Push) => continue,
item => break item,
}
};
self.items.extend(item);
self.items.push(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 {
Item::Push => self.push(),
Item::Line => self.line(),
Item::Space => self.space(),
Item::Indentation(n) => self.indentation(n),
Item::Lang(_, item) => self.lang_item(item),
Item::Register(_, item) => self.lang_item_register(item),
other => self.items.push(other),
}
}
pub(crate) fn lang_item(&mut self, item: Box<L::Item>) {
self.items
.push(crate::tokens::Item::Lang(self.last_lang_item, item));
self.last_lang_item = self.items.len();
}
pub(crate) fn lang_item_register(&mut self, item: Box<L::Item>) {
self.items
.push(crate::tokens::Item::Register(self.last_lang_item, 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 {
match self.items.pop() {
Some(Item::Push) => continue,
Some(Item::Space) => continue,
Some(Item::Line) => continue,
Some(Item::Indentation(u)) => n += u,
item => break item,
}
};
self.items.extend(item);
if n != 0 {
self.items.push(Item::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> cmp::PartialEq<Vec<Item<L>>> for Tokens<L>
where
L: Lang,
{
fn eq(&self, other: &Vec<Item<L>>) -> bool {
self.items == *other
}
}
impl<L> cmp::PartialEq<Tokens<L>> for Vec<Item<L>>
where
L: Lang,
{
fn eq(&self, other: &Tokens<L>) -> bool {
*self == other.items
}
}
impl<L> cmp::PartialEq<[Item<L>]> for Tokens<L>
where
L: Lang,
{
fn eq(&self, other: &[Item<L>]) -> bool {
&*self.items == other
}
}
impl<L> cmp::PartialEq<Tokens<L>> for [Item<L>]
where
L: Lang,
{
fn eq(&self, other: &Tokens<L>) -> bool {
self == &*other.items
}
}
pub struct IntoIter<L>
where
L: Lang,
{
iter: vec::IntoIter<Item<L>>,
}
impl<L> Iterator for IntoIter<L>
where
L: Lang,
{
type Item = Item<L>;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
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>;
fn into_iter(self) -> Self::IntoIter {
IntoIter {
iter: self.items.into_iter(),
}
}
}
pub struct Iter<'a, L>
where
L: Lang,
{
iter: slice::Iter<'a, Item<L>>,
}
impl<'a, L> Iterator for Iter<'a, L>
where
L: Lang,
{
type Item = &'a Item<L>;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
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,
{
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
}
}
pub struct WalkImports<'a, L>
where
L: Lang,
{
items: &'a [Item<L>],
pos: usize,
}
impl<'a, L> Iterator for WalkImports<'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;
}
let item = self.items.get(pos - 1)?;
let (prev, item) = match item {
Item::Lang(prev, item) => (prev, item),
Item::Register(prev, item) => (prev, item),
_ => return None,
};
self.pos = *prev;
Some(item)
}
}
#[cfg(test)]
mod tests {
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 = Import;
}
Import {
fn format(&self, out: &mut fmt::Formatter<'_>, _: &(), _: &()) -> fmt::Result {
use std::fmt::Write as _;
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.walk_imports().cloned().collect();
output.sort();
let expected = vec![Import(1), Import(2)];
assert_eq!(expected, output);
}
}