use crate::formatter::{FmtWriter, IoWriter};
use crate::{
FormatTokens, Formatter, FormatterConfig, Item, Lang, LangItem, RegisterTokens, VecWriter,
};
use std::fmt;
use std::io;
use std::iter::FromIterator;
use std::result;
use std::vec;
#[derive(Default)]
pub struct Tokens<L = ()>
where
L: Lang,
{
pub(crate) elements: Vec<Item<L>>,
}
impl<L> fmt::Debug for Tokens<L>
where
L: Lang,
{
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_list().entries(self.elements.iter()).finish()
}
}
impl<L> Tokens<L>
where
L: Lang,
{
pub fn new() -> Tokens<L> {
Tokens {
elements: Vec::new(),
}
}
pub fn append<T>(&mut self, tokens: T)
where
T: FormatTokens<L>,
{
tokens.format_tokens(self)
}
pub fn extend<I>(&mut self, it: I)
where
I: IntoIterator<Item = Item<L>>,
{
self.elements.extend(it.into_iter());
}
pub fn walk_imports(&self) -> WalkImports<'_, L> {
WalkImports {
queue: self.elements.iter(),
}
}
pub fn register<T>(&mut self, tokens: T)
where
T: RegisterTokens<L>,
{
tokens.register_tokens(self);
}
pub fn is_empty(&self) -> bool {
self.elements.is_empty()
}
pub fn spacing(&mut self) {
match self.elements.last() {
Some(Item::Spacing) => return,
None => return,
_ => (),
}
self.elements.push(Item::Spacing);
}
pub fn push(&mut self) {
match self.elements.last() {
Some(Item::Push) | Some(Item::Line) => return,
_ => (),
}
self.elements.push(Item::Push);
}
pub fn push_line(&mut self) {
let mut it = self.elements.iter().rev();
let last = it.next();
let ntl = it.next();
match (ntl, last) {
(Some(Item::Push), Some(Item::Line)) => (),
(_, Some(Item::Push)) => {
self.elements.push(Item::Line);
}
(_, Some(..)) => {
self.elements.push(Item::Push);
self.elements.push(Item::Line);
}
_ => (),
}
}
pub fn indent(&mut self) {
self.elements.push(Item::Indent);
}
pub fn unindent(&mut self) {
self.elements.push(Item::Unindent);
}
}
impl<L> Clone for Tokens<L>
where
L: Lang,
{
fn clone(&self) -> Self {
Self {
elements: self.elements.clone(),
}
}
}
impl<L> IntoIterator for Tokens<L>
where
L: Lang,
{
type Item = Item<L>;
type IntoIter = vec::IntoIter<Item<L>>;
fn into_iter(self) -> Self::IntoIter {
self.elements.into_iter()
}
}
impl<L: Lang> Tokens<L> {
pub fn format(&self, out: &mut Formatter, config: &mut L::Config, level: usize) -> fmt::Result {
for element in &self.elements {
element.format(out, config, level)?;
}
Ok(())
}
pub fn to_file_string_with(
self,
mut config: L::Config,
format_config: FormatterConfig,
) -> result::Result<String, fmt::Error> {
let mut w = FmtWriter::new(String::new());
{
let mut formatter = Formatter::new(&mut w, format_config);
L::write_file(self, &mut formatter, &mut config, 0usize)?;
}
Ok(w.into_writer())
}
pub fn to_string_with(
self,
mut config: L::Config,
format_config: FormatterConfig,
) -> result::Result<String, fmt::Error> {
let mut w = FmtWriter::new(String::new());
{
let mut formatter = Formatter::new(&mut w, format_config);
self.format(&mut formatter, &mut config, 0usize)?;
}
Ok(w.into_writer())
}
pub fn to_file_vec_with(
self,
mut config: L::Config,
format_config: FormatterConfig,
) -> result::Result<Vec<String>, fmt::Error> {
let mut w = VecWriter::new();
{
let mut formatter = Formatter::new(&mut w, format_config);
L::write_file(self, &mut formatter, &mut config, 0usize)?;
}
Ok(w.into_vec())
}
pub fn to_fmt_writer_with<W>(
self,
writer: W,
mut config: L::Config,
format_config: FormatterConfig,
) -> result::Result<(), fmt::Error>
where
W: fmt::Write,
{
let mut w = FmtWriter::new(writer);
{
let mut formatter = Formatter::new(&mut w, format_config);
L::write_file(self, &mut formatter, &mut config, 0usize)?;
formatter.new_line_unless_empty()?;
}
Ok(())
}
pub fn to_io_writer_with<W>(
self,
writer: W,
mut config: L::Config,
format_config: FormatterConfig,
) -> result::Result<(), fmt::Error>
where
W: io::Write,
{
let mut w = IoWriter::new(writer);
{
let mut formatter = Formatter::new(&mut w, format_config);
L::write_file(self, &mut formatter, &mut config, 0usize)?;
formatter.new_line_unless_empty()?;
}
Ok(())
}
}
impl<C: Default, L: Lang<Config = C>> Tokens<L> {
pub fn to_file_string(self) -> result::Result<String, fmt::Error> {
self.to_file_string_with(L::Config::default(), FormatterConfig::from_lang::<L>())
}
pub fn to_string(self) -> result::Result<String, fmt::Error> {
self.to_string_with(L::Config::default(), FormatterConfig::from_lang::<L>())
}
pub fn to_file_vec(self) -> result::Result<Vec<String>, fmt::Error> {
self.to_file_vec_with(L::Config::default(), FormatterConfig::from_lang::<L>())
}
pub fn to_fmt_writer<W>(self, writer: W) -> result::Result<(), fmt::Error>
where
W: fmt::Write,
{
self.to_fmt_writer_with(
writer,
L::Config::default(),
FormatterConfig::from_lang::<L>(),
)
}
pub fn to_io_writer<W>(self, writer: W) -> result::Result<(), fmt::Error>
where
W: io::Write,
{
self.to_io_writer_with(
writer,
L::Config::default(),
FormatterConfig::from_lang::<L>(),
)
}
}
impl<'a, L> FromIterator<&'a Item<L>> for Tokens<L>
where
L: Lang,
{
fn from_iter<I: IntoIterator<Item = &'a Item<L>>>(iter: I) -> Tokens<L> {
Tokens {
elements: iter.into_iter().map(Clone::clone).collect(),
}
}
}
impl<L> FromIterator<Item<L>> for Tokens<L>
where
L: Lang,
{
fn from_iter<I: IntoIterator<Item = Item<L>>>(iter: I) -> Tokens<L> {
Tokens {
elements: iter.into_iter().collect(),
}
}
}
pub struct WalkImports<'a, L>
where
L: Lang,
{
queue: std::slice::Iter<'a, Item<L>>,
}
impl<'a, L> Iterator for WalkImports<'a, L>
where
L: Lang,
{
type Item = &'a L::Import;
fn next(&mut self) -> Option<Self::Item> {
while let Some(next) = self.queue.next() {
let import = match next {
Item::LangBox(item) => item.as_import(),
Item::Registered(item) => item.as_import(),
_ => continue,
};
if let Some(import) = import {
return Some(import);
}
}
None
}
}
#[cfg(test)]
mod tests {
use crate as genco;
use crate::{quote, Formatter, LangItem, Tokens};
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
struct Import(u32);
impl_lang_item!(Import, Lang);
impl LangItem<Lang> for Import {
fn format(&self, out: &mut Formatter, _: &mut (), _: usize) -> fmt::Result {
use std::fmt::Write as _;
write!(out, "{}", self.0)
}
fn as_import(&self) -> Option<&Self> {
Some(self)
}
}
#[derive(Clone, Copy)]
struct Lang(());
impl crate::Lang for Lang {
type Config = ();
type Import = Import;
}
#[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 output: Vec<_> = toks.walk_imports().cloned().collect();
let expected = vec![Import(1), Import(2)];
assert_eq!(expected, output);
}
}