mod modifier;
mod utils;
pub use self::modifier::Modifier;
pub use self::utils::DocComment;
use super::cons::Cons;
use super::custom::Custom;
use super::formatter::Formatter;
use super::into_tokens::IntoTokens;
use super::tokens::Tokens;
use std::fmt::{self, Write};
static SEP: &'static str = ".";
pub static DART_CORE: &'static str = "dart:core";
pub const INT: Dart<'static> = Dart::BuiltIn { name: "int" };
pub const DOUBLE: Dart<'static> = Dart::BuiltIn { name: "double" };
pub const BOOL: Dart<'static> = Dart::BuiltIn { name: "bool" };
#[derive(Default, Debug, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
pub struct Type<'el> {
path: Option<Cons<'el>>,
alias: Option<Cons<'el>>,
name: Option<Cons<'el>>,
arguments: Vec<Dart<'el>>,
}
#[derive(Debug, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
pub enum Dart<'el> {
BuiltIn {
name: &'static str,
},
Void,
Dynamic,
Type(Type<'el>),
}
into_tokens_impl_from!(Dart<'el>, Dart<'el>);
into_tokens_impl_from!(&'el Dart<'el>, Dart<'el>);
#[derive(Debug, Default)]
pub struct Config {}
impl crate::Config for Config {}
impl<'el> Dart<'el> {
fn imports<'a, 'b: 'a>(
input: &'b Tokens<'a, Dart<'el>>,
_: &mut Config,
) -> Tokens<'a, Dart<'el>> {
use crate::quoted::Quoted;
use std::collections::BTreeSet;
let mut modules = BTreeSet::new();
for custom in input.walk_custom() {
if let Dart::Type(ref ty) = *custom {
if let Some(path) = ty.path.as_ref() {
if path.as_ref() == DART_CORE {
continue;
}
modules.insert((path.as_ref(), ty.alias.as_ref().map(AsRef::as_ref)));
}
}
}
if modules.is_empty() {
return toks!();
}
let mut o = toks!();
for (name, alias) in modules {
if let Some(alias) = alias {
o.push(toks!("import ", name.quoted(), " as ", alias, ";"));
} else {
o.push(toks!("import ", name.quoted(), ";"));
}
}
return o;
}
pub fn alias(&self, alias: impl Into<Cons<'el>>) -> Dart<'el> {
match *self {
Dart::Type(ref ty) => Dart::Type(Type {
alias: Some(alias.into()),
..ty.clone()
}),
ref dart => dart.clone(),
}
}
pub fn name(&self, name: impl Into<Cons<'el>>) -> Dart<'el> {
match *self {
Dart::Type(ref ty) => Dart::Type(Type {
name: Some(name.into()),
..ty.clone()
}),
ref dart => dart.clone(),
}
}
pub fn with_arguments(&self, arguments: Vec<Dart<'el>>) -> Dart<'el> {
match *self {
Dart::Type(ref ty) => Dart::Type(Type {
arguments: arguments,
..ty.clone()
}),
ref dart => dart.clone(),
}
}
pub fn arguments(&self) -> Option<&[Dart<'el>]> {
use self::Dart::*;
match *self {
Type(ref ty) => Some(&ty.arguments),
_ => None,
}
}
pub fn is_built_in(&self) -> bool {
use self::Dart::*;
match *self {
BuiltIn { .. } => true,
_ => false,
}
}
pub fn raw(&self) -> Dart<'el> {
match *self {
Dart::Type(ref ty) => Dart::Type(Type {
arguments: vec![],
alias: None,
..ty.clone()
}),
ref other => other.clone(),
}
}
pub fn is_core(&self) -> bool {
use self::Dart::*;
let ty = match *self {
Type(ref ty) => ty,
BuiltIn { .. } => return true,
Void => return true,
Dynamic => return true,
};
match ty.path.as_ref() {
Some(path) => path.as_ref() == DART_CORE,
None => false,
}
}
pub fn is_generic(&self) -> bool {
self.arguments().map(|a| !a.is_empty()).unwrap_or(false)
}
}
impl<'el> Custom for Dart<'el> {
type Config = Config;
fn format(&self, out: &mut Formatter, config: &mut Self::Config, level: usize) -> fmt::Result {
use self::Dart::*;
match *self {
BuiltIn { ref name, .. } => {
out.write_str(name.as_ref())?;
}
Void => out.write_str("void")?,
Dynamic => out.write_str("dynamic")?,
Type(ref ty) => {
if let Some(ref name) = ty.name {
if let Some(ref alias) = ty.alias {
out.write_str(alias.as_ref())?;
out.write_str(SEP)?;
}
out.write_str(name.as_ref())?;
if !ty.arguments.is_empty() {
out.write_str("<")?;
let mut it = ty.arguments.iter().peekable();
while let Some(argument) = it.next() {
argument.format(out, config, level + 1)?;
if it.peek().is_some() {
out.write_str(", ")?;
}
}
out.write_str(">")?;
}
}
}
}
Ok(())
}
fn quote_string(out: &mut Formatter, input: &str) -> fmt::Result {
out.write_char('"')?;
for c in input.chars() {
match c {
'\t' => out.write_str("\\t")?,
'\u{0007}' => out.write_str("\\b")?,
'\n' => out.write_str("\\n")?,
'\r' => out.write_str("\\r")?,
'\u{0014}' => out.write_str("\\f")?,
'\'' => out.write_str("\\'")?,
'"' => out.write_str("\\\"")?,
'\\' => out.write_str("\\\\")?,
c => out.write_char(c)?,
}
}
out.write_char('"')?;
Ok(())
}
fn write_file<'a>(
tokens: Tokens<'a, Self>,
out: &mut Formatter,
config: &mut Self::Config,
level: usize,
) -> fmt::Result {
let mut toks: Tokens<Self> = Tokens::new();
toks.push_unless_empty(Self::imports(&tokens, config));
toks.push_ref(&tokens);
toks.join_line_spacing().format(out, config, level)
}
}
pub fn imported<'a, P: Into<Cons<'a>>>(path: P) -> Dart<'a> {
Dart::Type(Type {
path: Some(path.into()),
..Type::default()
})
}
pub fn local<'el, N: Into<Cons<'el>>>(name: N) -> Dart<'el> {
Dart::Type(Type {
name: Some(name.into()),
..Type::default()
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dart::Dart;
use crate::quoted::Quoted;
use crate::tokens::Tokens;
#[test]
fn test_builtin() {
assert!(INT.is_built_in());
assert!(DOUBLE.is_built_in());
assert!(BOOL.is_built_in());
assert!(!Dart::Void.is_built_in());
}
#[test]
fn test_string() {
let mut toks: Tokens<Dart> = Tokens::new();
toks.append("hello \n world".quoted());
assert_eq!("\"hello \\n world\"", toks.to_string().unwrap().as_str());
}
#[test]
fn test_imported() {
let import = imported("package:http/http.dart");
let import2 = imported("package:http/http.dart");
let import_alias = imported("package:http/http.dart").alias("h2");
let import_relative = imported("../http.dart");
let toks = toks![
import.name("a"),
import2.name("b"),
import_alias.name("c"),
import_relative.name("d"),
]
.join_spacing();
let expected = vec![
"import \"../http.dart\";",
"import \"package:http/http.dart\";",
"import \"package:http/http.dart\" as h2;",
"",
"a b h2.c d",
"",
];
assert_eq!(
Ok(expected.join("\n").as_str()),
toks.to_file().as_ref().map(|s| s.as_str())
);
}
}