use std::fmt::{self, Debug, Write};
use indexmap::IndexMap;
use crate::docs::Docs;
use crate::formatter::Formatter;
use crate::function::Function;
use crate::import::Import;
use crate::item::Item;
use crate::module::Module;
use crate::r#enum::Enum;
use crate::r#impl::Impl;
use crate::r#struct::Struct;
use crate::r#trait::Trait;
use crate::type_alias::TypeAlias;
#[derive(Debug, Clone)]
pub struct Scope {
docs: Option<Docs>,
imports: IndexMap<String, IndexMap<String, Import>>,
items: Vec<Item>,
}
impl Scope {
pub fn new() -> Self {
Scope {
docs: None,
imports: IndexMap::new(),
items: vec![],
}
}
pub fn import(&mut self, path: &str, ty: &str) -> &mut Import {
let ty = ty.split("::").next().unwrap_or(ty);
self.imports
.entry(path.to_string())
.or_insert(IndexMap::new())
.entry(ty.to_string())
.or_insert_with(|| Import::new(path, ty))
}
pub fn new_module(&mut self, name: &str) -> &mut Module {
self.push_module(Module::new(name));
match *self.items.last_mut().unwrap() {
Item::Module(ref mut v) => v,
_ => unreachable!(),
}
}
pub fn get_module_mut<Q: ?Sized>(&mut self, name: &Q) -> Option<&mut Module>
where
String: PartialEq<Q>,
{
self.items
.iter_mut()
.filter_map(|item| match item {
&mut Item::Module(ref mut module) if module.name == *name => Some(module),
_ => None,
})
.next()
}
pub fn get_module<Q: ?Sized>(&self, name: &Q) -> Option<&Module>
where
String: PartialEq<Q>,
{
self.items
.iter()
.filter_map(|item| match item {
&Item::Module(ref module) if module.name == *name => Some(module),
_ => None,
})
.next()
}
pub fn get_or_new_module(&mut self, name: &str) -> &mut Module {
if self.get_module(name).is_some() {
self.get_module_mut(name).unwrap()
} else {
self.new_module(name)
}
}
pub fn push_module(&mut self, item: Module) -> &mut Self {
assert!(self.get_module(&item.name).is_none());
self.items.push(Item::Module(item));
self
}
pub fn new_struct(&mut self, name: &str) -> &mut Struct {
self.push_struct(Struct::new(name));
match *self.items.last_mut().unwrap() {
Item::Struct(ref mut v) => v,
_ => unreachable!(),
}
}
pub fn push_struct(&mut self, item: Struct) -> &mut Self {
self.items.push(Item::Struct(item));
self
}
pub fn new_fn(&mut self, name: &str) -> &mut Function {
self.push_fn(Function::new(name));
match *self.items.last_mut().unwrap() {
Item::Function(ref mut v) => v,
_ => unreachable!(),
}
}
pub fn push_fn(&mut self, item: Function) -> &mut Self {
self.items.push(Item::Function(item));
self
}
pub fn new_trait(&mut self, name: impl Into<String>) -> &mut Trait {
self.push_trait(Trait::new(name));
match *self.items.last_mut().unwrap() {
Item::Trait(ref mut v) => v,
_ => unreachable!(),
}
}
pub fn push_trait(&mut self, item: Trait) -> &mut Self {
self.items.push(Item::Trait(item));
self
}
pub fn new_enum(&mut self, name: impl Into<String>) -> &mut Enum {
self.push_enum(Enum::new(name));
match *self.items.last_mut().unwrap() {
Item::Enum(ref mut v) => v,
_ => unreachable!(),
}
}
pub fn push_enum(&mut self, item: Enum) -> &mut Self {
self.items.push(Item::Enum(item));
self
}
pub fn new_impl(&mut self, target: &str) -> &mut Impl {
self.push_impl(Impl::new(target));
match *self.items.last_mut().unwrap() {
Item::Impl(ref mut v) => v,
_ => unreachable!(),
}
}
pub fn push_impl(&mut self, item: Impl) -> &mut Self {
self.items.push(Item::Impl(item));
self
}
pub fn raw(&mut self, val: impl Into<String>) -> &mut Self {
self.items.push(Item::Raw(val.into()));
self
}
pub fn new_type_alias(
&mut self,
name: impl Into<String>,
target: impl Into<String>,
) -> &mut TypeAlias {
self.push_type_alias(TypeAlias::new(name.into(), target.into()));
match *self.items.last_mut().unwrap() {
Item::TypeAlias(ref mut v) => v,
_ => unreachable!(),
}
}
pub fn push_type_alias(&mut self, item: TypeAlias) -> &mut Self {
self.items.push(Item::TypeAlias(item));
self
}
pub fn to_string(&self) -> String {
let mut ret = String::new();
self.fmt(&mut Formatter::new(&mut ret)).unwrap();
if ret.as_bytes().last() == Some(&b'\n') {
ret.pop();
}
ret
}
pub fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
self.fmt_imports(fmt)?;
if !self.imports.is_empty() {
write!(fmt, "\n")?;
}
for (i, item) in self.items.iter().enumerate() {
if i != 0 {
write!(fmt, "\n")?;
}
match *item {
Item::Module(ref v) => v.fmt(fmt)?,
Item::Struct(ref v) => v.fmt(fmt)?,
Item::Function(ref v) => v.fmt(false, fmt)?,
Item::Trait(ref v) => v.fmt(fmt)?,
Item::Enum(ref v) => v.fmt(fmt)?,
Item::Impl(ref v) => v.fmt(fmt)?,
Item::Raw(ref v) => {
write!(fmt, "{}\n", v)?;
}
Item::TypeAlias(ref v) => v.fmt(fmt)?,
}
}
Ok(())
}
fn fmt_imports(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
let mut visibilities = vec![];
for (_, imports) in &self.imports {
for (_, import) in imports {
if !visibilities.contains(&import.vis) {
visibilities.push(import.vis.clone());
}
}
}
let mut tys = vec![];
for vis in &visibilities {
for (path, imports) in &self.imports {
tys.clear();
for (ty, import) in imports {
if *vis == import.vis {
tys.push(ty);
}
}
if !tys.is_empty() {
if let Some(ref vis) = *vis {
write!(fmt, "{} ", vis)?;
}
write!(fmt, "use {}::", path)?;
if tys.len() > 1 {
write!(fmt, "{{")?;
for (i, ty) in tys.iter().enumerate() {
if i != 0 {
write!(fmt, ", ")?;
}
write!(fmt, "{}", ty)?;
}
write!(fmt, "}};\n")?;
} else if tys.len() == 1 {
write!(fmt, "{};\n", tys[0])?;
}
}
}
}
Ok(())
}
}