use core::fmt::Write as _;
use alloc::collections::{BTreeMap, BTreeSet, VecDeque};
use crate::fmt;
use crate::tokens::ItemStr;
const SEP: &str = "::";
pub type Tokens = crate::Tokens<Rust>;
impl_lang! {
pub Rust {
type Config = Config;
type Format = Format;
type Item = Import;
fn write_quoted(out: &mut fmt::Formatter<'_>, input: &str) -> fmt::Result {
for c in input.chars() {
match c {
'\n' => out.write_str("\\n")?,
'\r' => out.write_str("\\r")?,
'\t' => out.write_str("\\t")?,
'\\' => out.write_str("\\\\")?,
'\0' => out.write_str("\\0")?,
'"' => out.write_str("\\\"")?,
c if !c.is_control() => out.write_char(c)?,
c if (c as u32) < 0x80 => {
write!(out, "\\x{:02x}", c as u32)?;
}
c => {
write!(out, "\\u{{{:04x}}}", c as u32)?;
}
};
}
Ok(())
}
fn format_file(
tokens: &Tokens,
out: &mut fmt::Formatter<'_>,
config: &Self::Config,
) -> fmt::Result {
let mut imports: Tokens = Tokens::new();
Self::imports(&mut imports, config, tokens);
let format = Format::default();
imports.format(out, config, &format)?;
tokens.format(out, config, &format)?;
Ok(())
}
}
Import(Import) {
fn format(&self, out: &mut fmt::Formatter<'_>, config: &Config, _: &Format) -> fmt::Result {
match &self.module {
Module::Module {
import: Some(ImportMode::Direct),
..
} => {
self.write_direct(out)?;
}
Module::Module {
import: Some(ImportMode::Qualified),
module,
} => {
self.write_prefixed(out, module)?;
}
Module::Module {
import: None,
module,
} => match &config.default_import {
ImportMode::Direct => self.write_direct(out)?,
ImportMode::Qualified => self.write_prefixed(out, module)?,
},
Module::Aliased {
alias: ref module, ..
} => {
out.write_str(module)?;
out.write_str(SEP)?;
out.write_str(&self.name)?;
}
}
Ok(())
}
}
}
#[derive(Debug, Default)]
pub struct Format {}
#[derive(Debug)]
pub struct Config {
default_import: ImportMode,
}
impl Config {
pub fn with_default_import(self, default_import: ImportMode) -> Self {
Self { default_import }
}
}
impl Default for Config {
fn default() -> Self {
Config {
default_import: ImportMode::Direct,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ImportMode {
Direct,
Qualified,
}
#[derive(Debug, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
enum Module {
Module {
import: Option<ImportMode>,
module: ItemStr,
},
Aliased { module: ItemStr, alias: ItemStr },
}
impl Module {
fn into_module_aliased<A>(self, alias: A) -> Self
where
A: Into<ItemStr>,
{
match self {
Self::Module { module, .. } => Self::Aliased {
module,
alias: alias.into(),
},
other => other,
}
}
fn into_aliased(self) -> Self {
match self {
Self::Module { module, .. } => Self::Module {
import: Some(ImportMode::Direct),
module,
},
other => other,
}
}
fn direct(self) -> Self {
match self {
Self::Module { module, .. } => Self::Module {
module,
import: Some(ImportMode::Direct),
},
other => other,
}
}
fn qualified(self) -> Self {
match self {
Self::Module { module, .. } => Self::Module {
module,
import: Some(ImportMode::Qualified),
},
other => other,
}
}
}
#[derive(Debug, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
pub struct Import {
module: Module,
name: ItemStr,
alias: Option<ItemStr>,
}
impl Import {
pub fn with_alias<A: Into<ItemStr>>(self, alias: A) -> Self {
Self {
module: self.module.into_aliased(),
alias: Some(alias.into()),
..self
}
}
pub fn with_module_alias<A: Into<ItemStr>>(self, alias: A) -> Self {
Self {
module: self.module.into_module_aliased(alias),
..self
}
}
pub fn qualified(self) -> Self {
Self {
module: self.module.qualified(),
..self
}
}
pub fn direct(self) -> Self {
Self {
module: self.module.direct(),
..self
}
}
fn write_direct(&self, out: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(alias) = &self.alias {
out.write_str(alias)
} else {
out.write_str(&self.name)
}
}
fn write_prefixed(&self, out: &mut fmt::Formatter<'_>, module: &ItemStr) -> fmt::Result {
if let Some(module) = module.rsplit(SEP).next() {
out.write_str(module)?;
out.write_str(SEP)?;
}
out.write_str(&self.name)?;
Ok(())
}
}
impl Rust {
fn imports(out: &mut Tokens, config: &Config, tokens: &Tokens) {
use alloc::collections::btree_set;
use crate as genco;
use crate::quote_in;
let mut modules = BTreeMap::<&ItemStr, Import>::new();
let mut queue = VecDeque::new();
for import in tokens.iter_lang() {
queue.push_back(import);
}
while let Some(import) = queue.pop_front() {
match &import.module {
Module::Module {
module,
import: Some(ImportMode::Direct),
} => {
let module = modules.entry(module).or_default();
module.names.insert((&import.name, import.alias.as_ref()));
}
Module::Module {
module,
import: Some(ImportMode::Qualified),
} => {
let module = modules.entry(module).or_default();
module.self_import = true;
}
Module::Module {
module,
import: None,
} => match config.default_import {
ImportMode::Direct => {
let module = modules.entry(module).or_default();
module.names.insert((&import.name, import.alias.as_ref()));
}
ImportMode::Qualified => {
let module = modules.entry(module).or_default();
module.self_import = true;
}
},
Module::Aliased { module, alias } => {
let module = modules.entry(module).or_default();
module.self_aliases.insert(alias);
}
}
}
let mut has_any = false;
for (m, module) in modules {
let mut render = module.iter(m);
if let Some(first) = render.next() {
has_any = true;
out.push();
if let Some(second) = render.next() {
quote_in! { *out =>
use $m::{$(ref o =>
first.render(o);
quote_in!(*o => , $(ref o => second.render(o)));
for item in render {
quote_in!(*o => , $(ref o => item.render(o)));
}
)};
};
} else {
match first {
RenderItem::SelfImport => {
quote_in!(*out => use $m;);
}
RenderItem::SelfAlias { alias } => {
quote_in!(*out => use $m as $alias;);
}
RenderItem::Name {
name,
alias: Some(alias),
} => {
quote_in!(*out => use $m::$name as $alias;);
}
RenderItem::Name { name, alias: None } => {
quote_in!(*out => use $m::$name;);
}
}
}
}
}
if has_any {
out.line();
}
return;
#[derive(Debug, Default)]
struct Import<'a> {
self_import: bool,
self_aliases: BTreeSet<&'a ItemStr>,
names: BTreeSet<(&'a ItemStr, Option<&'a ItemStr>)>,
}
impl<'a> Import<'a> {
fn iter(self, module: &'a str) -> ImportedIter<'a> {
ImportedIter {
module,
self_import: self.self_import,
self_aliases: self.self_aliases.into_iter(),
names: self.names.into_iter(),
}
}
}
struct ImportedIter<'a> {
module: &'a str,
self_import: bool,
self_aliases: btree_set::IntoIter<&'a ItemStr>,
names: btree_set::IntoIter<(&'a ItemStr, Option<&'a ItemStr>)>,
}
impl<'a> Iterator for ImportedIter<'a> {
type Item = RenderItem<'a>;
fn next(&mut self) -> Option<Self::Item> {
if core::mem::take(&mut self.self_import) {
if self.module.split(SEP).count() > 1 {
return Some(RenderItem::SelfImport);
}
}
if let Some(alias) = self.self_aliases.next() {
return Some(RenderItem::SelfAlias { alias });
}
if let Some((name, alias)) = self.names.next() {
return Some(RenderItem::Name { name, alias });
}
None
}
}
#[derive(Clone, Copy)]
enum RenderItem<'a> {
SelfImport,
SelfAlias {
alias: &'a ItemStr,
},
Name {
name: &'a ItemStr,
alias: Option<&'a ItemStr>,
},
}
impl RenderItem<'_> {
fn render(self, out: &mut Tokens) {
match self {
Self::SelfImport => {
quote_in!(*out => self);
}
Self::SelfAlias { alias } => {
quote_in!(*out => self as $alias);
}
Self::Name {
name,
alias: Some(alias),
} => {
quote_in!(*out => $name as $alias);
}
Self::Name { name, alias: None } => {
quote_in!(*out => $name);
}
}
}
}
}
}
pub fn import<M, N>(module: M, name: N) -> Import
where
M: Into<ItemStr>,
N: Into<ItemStr>,
{
Import {
module: Module::Module {
import: None,
module: module.into(),
},
name: name.into(),
alias: None,
}
}