diff --git a/.gitignore b/.gitignore index 4b047b8f..85d2f86b 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,6 @@ Cargo.lock # Graphiz *.dot + +# Idea +.idea \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 57648ee4..fbbb6826 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ rust: - beta - nightly os: - - osx + # - osx - linux addons: apt: @@ -29,11 +29,3 @@ script: travis-cargo test travis-cargo bench travis-cargo doc -- --no-deps -after_success: - - touch target/doc/index.html - - travis-cargo --only nightly doc-upload - - travis-cargo coveralls --no-sudo --verify -env: - global: - - TRAVIS_CARGO_NIGHTLY_FEATURE="nightly" - - secure: "DKEIK2vaRCzWGkEv6fpPhkmszMQBD0lTP5PeNBQ4C6vtRWAGtHdqYxFuXtCIwE264tFYj9zoSZo9Bz+fWCOgvX2nQZwX6MRnMB+jaG+8jo0GynQs8L3Ldwj0MgJJ19+teEa5sR3IGZL94BGbG4bMPoUepTJRrGVXMXry38VFLUjLVBSB3pim7ivc2CWr++Y7s5Rdzrf26IrfjSSHHewW/acNZnSsdrivVt6VfziYVJFEEs6VSmR7Camgoc0VNjQLEP2fMYbnbMuQJ+ftQTYpdbdCoH2R+e3VFMLCpdIccE8RycA2mX1KRJXJSGXmtAhQ30brx1IoStHjtLct0NSQd86SVSjRlkDmyG6hGxehWA1V06HnUSLlJMw0hZT07jVpewmjcWfZAWm5Vg/jeC3qWLo/anVpTiH80CUukkCUZyXkKC6ePFfb84Cf8dgdEvxchUKIH6T6Q+/yePkmn2tKepXadgNgHpoJ6Yp89jJl1e+lEFgh1hw2Wy1rsAgwbKFwZ3/fGz6pDqm+vhfelmQhLHKdwERFIoFOhSjizjajD1CbHO+aZQjznguYO34x/MZ70ZyVB2MiIkn4ujsZ6U41KCS51QQa1XIZOVlNmcMx+GmZLg8eC/H/pwSKaM4LS4websJMjwhRw7EU227YzfefSW6krtushgrWa9s8pVacYT8=" diff --git a/Cargo.toml b/Cargo.toml index c8633deb..4f0d5ba6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,11 +17,6 @@ categories = ["visualization"] travis-ci = { repository = "adjivas/ml", branch = "master" } appveyor = { repository = "adjivas/ml", branch = "master", service = "github" } -[[bin]] -name = "ml" -doc = false -test = false - [lib] test = false doctest = true @@ -36,11 +31,9 @@ unstable = [] # for building with unstable features on stable Rust debug = [] # for building with debug messages travis = ["lints", "nightly"] # for building with travis-cargo -[dependencies.syntex_syntax] -version = "0.59" - -[dependencies.syntex_errors] -version = "0.59" +[dependencies] +syn = { features = [ "full", "default", "extra-traits", "derive", "parsing", "printing", "clone-impls", "proc-macro" ], version = "1.0" } +quote = "1.0" [dependencies.rustc-serialize] version = "0.3" diff --git a/src/core/item/mod.rs b/src/core/item/mod.rs index 88194faf..7055894c 100644 --- a/src/core/item/mod.rs +++ b/src/core/item/mod.rs @@ -1,52 +1,43 @@ -pub mod relation; -pub mod state; +use std::rc::Rc; +use std::{iter, slice}; -pub use self::state::ItemState; +use itertools::Itertools; -use std::{slice, iter}; -use std::rc::Rc; +use module::path::ModulePath; -use ::syntex_syntax::{ptr, ast}; -use ::itertools::Itertools; +pub use self::state::ItemState; -use ::module::path::ModulePath; +pub mod relation; +pub mod state; /// The structure Item is a iterable collection of abstract elements. - #[derive(Debug, Clone)] -pub struct Item <'a> { +pub struct Item<'a> { /// Iterator. - it: iter::Peekable, Rc)>>, + it: iter::Peekable)>>, } -impl <'a>From, Rc)>>> for Item<'a> { - +impl<'a> From)>>> for Item<'a> { /// The constructor method `from` returns a typed and iterable collection of abstract element. - fn from(iter: iter::Peekable, Rc)>>) -> Item { - Item { - it: iter, - } + fn from(iter: iter::Peekable)>>) -> Item { + Item { it: iter } } } -impl <'a>Iterator for Item<'a> { +impl<'a> Iterator for Item<'a> { type Item = ItemState<'a>; /// The method `next` will returns the first abstract elements defined like a structure, /// enumeration or trait. fn next(&mut self) -> Option> { - self.it.next().and_then(|item| { - let mut list: Vec<&'a (ptr::P, Rc)> = vec!(item); - - list.extend(self.it.peeking_take_while(|&&(ref item, _): (&&'a (ptr::P, Rc))| { - if let ast::ItemKind::Impl(..) = item.node { - true - } else { - false - } - }) - .collect::, Rc)>>()); - Some(ItemState::from(list)) + self.it.next().map(|item| { + let mut list: Vec<&'a (syn::Item, Rc)> = vec![item]; + + list.extend( + self.it + .peeking_take_while(|(item, _)| matches!(item, syn::Item::Impl(_))), + ); + ItemState::from(list) }) } } diff --git a/src/core/item/relation.rs b/src/core/item/relation.rs index 6cdb1a8f..2feb017d 100644 --- a/src/core/item/relation.rs +++ b/src/core/item/relation.rs @@ -1,6 +1,6 @@ -use super::ItemState; +use dot::{ArrowShape, Fill, Side}; -use ::dot::{Fill, ArrowShape, Side}; +use super::ItemState; /// The enumeration `Relation` is the relationship specification from [UML 2.5](http://www.omg.org/spec/UML/2.5) without generalization. #[derive(Debug, Copy, Clone)] @@ -14,21 +14,20 @@ pub enum Relation { } impl Relation { - /// The method `as_style` returns a stylized arrow (See *Table B.2 UML Edges* from [UML 2.5](http://www.omg.org/spec/UML/2.5). pub fn as_style(&self) -> ArrowShape { match self { - &Relation::Association => ArrowShape::Vee(Side::Both), - &Relation::Dependency => ArrowShape::Vee(Side::Both), - &Relation::Aggregation => ArrowShape::Diamond(Fill::Open, Side::Both), - &Relation::Composition => ArrowShape::Diamond(Fill::Filled, Side::Both), - &Relation::Realization => ArrowShape::Normal(Fill::Open, Side::Both), - &Relation::None => ArrowShape::NoArrow, + Relation::Association => ArrowShape::Vee(Side::Both), + Relation::Dependency => ArrowShape::Vee(Side::Both), + Relation::Aggregation => ArrowShape::Diamond(Fill::Open, Side::Both), + Relation::Composition => ArrowShape::Diamond(Fill::Filled, Side::Both), + Relation::Realization => ArrowShape::Normal(Fill::Open, Side::Both), + Relation::None => ArrowShape::NoArrow, } } } -impl <'a>From<(&'a ItemState<'a>, &'a ItemState<'a>)> for Relation { +impl<'a> From<(&'a ItemState<'a>, &'a ItemState<'a>)> for Relation { fn from((left, right): (&'a ItemState<'a>, &'a ItemState<'a>)) -> Relation { if left.is_composition(right) { Relation::Composition diff --git a/src/core/item/state/abstraction/enumerate.rs b/src/core/item/state/abstraction/enumerate.rs index ef074498..0f3221de 100644 --- a/src/core/item/state/abstraction/enumerate.rs +++ b/src/core/item/state/abstraction/enumerate.rs @@ -1,71 +1,89 @@ use std::fmt; use std::rc::Rc; -use ::syntex_syntax::print::pprust::ty_to_string; -use ::syntex_syntax::{codemap, symbol, ast}; +use dot::escape_html; +use quote::ToTokens; +use syn::{Field, TypeParam, Variant}; -use ::module::path::ModulePath; - -use ::dot::escape_html; +use module::path::ModulePath; /// The structure `Enum` is a enumerate abstract element. - #[derive(Debug, Eq, PartialEq, Clone)] pub struct Enum<'a> { pub path: Rc, /// Visibility - pub vis: &'a ast::Visibility, - pub name: symbol::InternedString, - pub params: Vec, - pub variants: Vec<(symbol::InternedString, Vec)>, + pub vis: &'a syn::Visibility, + pub name: String, + pub params: Vec, + pub variants: Vec<(String, Vec)>, } -impl <'a>From<((&'a ast::Item, &'a Vec, &'a Vec), Rc)> for Enum<'a> { - fn from(((item, ty_params, variants), path): ((&'a ast::Item, &'a Vec, &'a Vec), Rc)) -> Enum<'a> { +impl<'a> + From<( + (&'a syn::ItemEnum, &'a Vec, &'a Vec), + Rc, + )> for Enum<'a> +{ + fn from( + ((item, ty_params, variants), path): ( + (&'a syn::ItemEnum, &'a Vec, &'a Vec), + Rc, + ), + ) -> Enum<'a> { Enum { - path: path, + path, vis: &item.vis, - name: item.ident.name.as_str(), - params: ty_params.iter() - .map(|&ast::TyParam {attrs: _, ident: ast::Ident {name, ..}, ..}| name.as_str()) - .collect::>(), - variants: variants.iter() - .map(|&codemap::Spanned {node: ast::Variant_ {name: ast::Ident {name, ..}, attrs: _, ref data, ..}, ..}| { - if let &ast::VariantData::Tuple(ref struct_field, _) = data { - (name.as_str(), - struct_field.iter() - .filter_map(|&ast::StructField { span: _, ident: _, vis: _, id: _, ref ty, .. }| Some(ty_to_string(&ty))) - .collect::>()) - } else { - (name.as_str(), Vec::new()) - } - }) - .collect::)>>(), + name: item.ident.to_string(), + params: ty_params + .iter() + .map(|param| param.ident.to_string()) + .collect(), + variants: variants + .iter() + .map(|Variant { ident, fields, .. }| { + ( + ident.to_string(), + if fields.is_empty() { + vec![] + } else { + fields + .iter() + .map(|Field { ty, .. }| ty.to_token_stream().to_string()) + .collect() + }, + ) + }) + .collect(), } } } -impl <'a>fmt::Display for Enum<'a> { +impl<'a> fmt::Display for Enum<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.variants.is_empty() { - write!(f, "<<<Enumeration>>>\n{name}", name = self.name) + write!( + f, + "<<<Enumeration>>>\n{name}", + name = self.name + ) } else { - write!(f, "<<<Enumeration>>>\n{name}|{variants}", + write!( + f, + "<<<Enumeration>>>\n{name}|{variants}", name = self.name, - variants = escape_html(self.variants.iter() - .map(|&(ref name, ref struct_field): &(symbol::InternedString, Vec)| - if struct_field.is_empty() { - format!("{}", name) - } else { - format!("{}({})", name, struct_field.join(", ")) - } - ) - .collect::>() - .join("\n") - .as_str()), + variants = escape_html( + self.variants + .iter() + .map(|(name, struct_field)| if struct_field.is_empty() { + name.to_owned() + } else { + format!("{}({})", name, struct_field.join(", ")) + }) + .collect::>() + .join("\n") + .as_str() + ), ) } } } - - diff --git a/src/core/item/state/abstraction/extend.rs b/src/core/item/state/abstraction/extend.rs index 21ab97bc..2d01bc1d 100644 --- a/src/core/item/state/abstraction/extend.rs +++ b/src/core/item/state/abstraction/extend.rs @@ -1,64 +1,126 @@ use std::fmt; -use std::ops::Deref; use std::rc::Rc; -use ::syntex_syntax::print::pprust::ty_to_string; -use ::syntex_syntax::{symbol, ast}; +use dot::escape_html; +use quote::ToTokens; +use syn::{ + FnArg, ItemTrait, PatType, Receiver, ReturnType, Signature, TraitItem, TraitItemMethod, + TypeParam, Visibility, +}; -use ::module::path::ModulePath; - -use ::dot::escape_html; +use module::path::ModulePath; #[derive(Debug, Eq, PartialEq, Clone)] pub struct Trait<'a> { pub path: Rc, /// Visibility - pub vis: &'a ast::Visibility, - pub name: symbol::InternedString, - pub params: Vec, - pub items: Vec<(symbol::InternedString, Vec, String)>, + pub vis: &'a Visibility, + pub name: String, + pub params: Vec, + pub items: Vec<(String, Vec, String)>, } -impl <'a>From<((&'a ast::Item, &'a Vec, &'a Vec), Rc)> for Trait<'a> { - fn from(((item, ty_params, trait_item), path): ((&'a ast::Item, &'a Vec, &'a Vec), Rc)) -> Trait<'a> { +impl<'a> + From<( + (&'a ItemTrait, &'a Vec, &'a Vec), + Rc, + )> for Trait<'a> +{ + fn from( + ((item, ty_params, trait_item), path): ( + (&'a ItemTrait, &'a Vec, &'a Vec), + Rc, + ), + ) -> Trait<'a> { Trait { - path: path, + path, vis: &item.vis, - name: item.ident.name.as_str(), - params: ty_params.iter() - .map(|&ast::TyParam {attrs: _, ident: ast::Ident {name, ..}, ..}| name.as_str()) - .collect::>(), - items: trait_item.iter() - .filter_map(|&ast::TraitItem {id: _, ident: ast::Ident {name, ..}, attrs: _, ref node, ..}| - if let &ast::TraitItemKind::Method(ast::MethodSig { unsafety: _, constness: _, abi: _, ref decl, ..}, _) = node { - if let &ast::FnDecl {ref inputs, output: ast::FunctionRetTy::Ty(ref ty), ..} = decl.deref() { - Some((name.as_str(), inputs.iter().map(|input| ty_to_string(&input.ty)).collect::>(), ty_to_string(&ty))) - } else { - None - } - } else { - None - } - ) - .collect::, String)>>() + name: item.ident.to_string(), + params: ty_params + .iter() + .map(|TypeParam { ident, .. }| ident.to_string()) + .collect(), + items: trait_item + .iter() + .filter_map(|item: &TraitItem| { + if let TraitItem::Method(TraitItemMethod { + sig: + Signature { + ident, + inputs, + output: ReturnType::Type(_, output), + .. + }, + .. + }) = item + { + Some(( + ident.to_string(), + inputs + .iter() + .map(|input| { + dbg!(input); + match input { + FnArg::Typed(PatType { ty, .. }) => { + ty.to_token_stream().to_string() + } + FnArg::Receiver(Receiver { + reference, + mutability, + .. + }) => { + // FIXME + let (r1, r2) = match reference { + Some((and, lifetime)) => ( + Some(and.to_token_stream().to_string()), + Some(lifetime.to_token_stream().to_string()), + ), + None => (None, None), + }; + let mutability = + Some(mutability.to_token_stream().to_string()); + let s = Some(" Self".into()); + [r1, r2, mutability, s] + .iter() + .flatten() + .map(ToString::to_string) + .collect() + } + } + }) + .collect(), + output.to_token_stream().to_string(), + )) + } else { + None + } + }) + .collect::, String)>>(), } } } -impl <'a>fmt::Display for Trait<'a> { +impl<'a> fmt::Display for Trait<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "<<<Trait>>>\n{name}|{items}", - name = self.name, - items = escape_html(self.items.iter() - .map(|&(ref name, ref ty, ref ret): &(symbol::InternedString, Vec, String)| - format!("{name}({ty}) -> {ret}", - name = name, - ty = ty.join(", "), - ret = ret - )) - .collect::>() - .join("\n") - .as_str()) + write!( + f, + "<<<Trait>>>\n{name}|{items}", + name = self.name, + items = escape_html( + self.items + .iter() + .map( + |&(ref name, ref ty, ref ret): &(String, Vec, String)| format!( + "{name}({ty}) -> {ret}", + name = name, + ty = ty.join(", "), + ret = ret + ) + ) + .collect::>() + .join("\n") + .as_str() + ) ) } } diff --git a/src/core/item/state/abstraction/mod.rs b/src/core/item/state/abstraction/mod.rs index a990ae0d..b105bd01 100644 --- a/src/core/item/state/abstraction/mod.rs +++ b/src/core/item/state/abstraction/mod.rs @@ -1,40 +1,34 @@ -pub mod extend; -pub mod structure; -pub mod enumerate; - use std::fmt; use std::vec; -use std::rc::Rc; - -use ::syntex_syntax::symbol; -use ::core::ast; -use ::module::path::ModulePath; +use syn::Visibility; +use self::enumerate::Enum; use self::extend::Trait; use self::structure::Struct; -use self::enumerate::Enum; -/// The structure `Abstract` is a enumerate for abstract element types or none. +pub mod enumerate; +pub mod extend; +pub mod structure; +/// The structure `Abstract` is a enumerate for abstract element types or none. #[derive(Debug, Eq, PartialEq, Clone)] -pub enum Abstract <'a> { +pub enum Abstract<'a> { Trait(Trait<'a>), Struct(Struct<'a>), Enum(Enum<'a>), None, } -impl <'a> Abstract <'a> { - +impl<'a> Abstract<'a> { /// The method `as_name` returns the name of the abstract element /// or else declare a panic. - pub fn as_name(&self) -> Option<&symbol::InternedString> { + pub fn as_name(&self) -> Option<&String> { match self { - &Abstract::Trait(Trait { vis: _, ref name, ..}) => Some(name), - &Abstract::Struct(Struct { vis: _, ref name, ..}) => Some(name), - &Abstract::Enum(Enum { vis: _, ref name, ..}) => Some(name), - &Abstract::None => None, + Abstract::Trait(Trait { name, .. }) => Some(name), + Abstract::Struct(Struct { name, .. }) => Some(name), + Abstract::Enum(Enum { name, .. }) => Some(name), + Abstract::None => None, } } } @@ -45,60 +39,35 @@ impl<'a> IntoIterator for &'a Abstract<'a> { fn into_iter(self) -> Self::IntoIter { match self { - &Abstract::Struct(Struct {path: _, vis: _, name: _, fields: ref ty_field}) => { - ty_field.iter() - .map(|&(_, _, ref ty): &'a (&'a ast::Visibility, symbol::InternedString, String)| ty) - .collect::>() - .into_iter() - }, - &Abstract::Enum(Enum {path: _, vis: _, name: _, params: _, variants: ref ty_multi_field}) => { - ty_multi_field.iter() - .map(|&(_, ref ty_field): &'a (symbol::InternedString, Vec)| - ty_field.iter() - .map(|ty: &'a String| ty) - .collect::>()) - .collect::>>() - .concat() - .into_iter() - }, - _ => { - Vec::default().into_iter() - }, + Abstract::Struct(Struct { fields, .. }) => fields + .iter() + .map(|&(_, _, ref ty): &'a (&'a Visibility, String, String)| ty) + .collect::>() + .into_iter(), + Abstract::Enum(Enum { variants, .. }) => variants + .iter() + .map(|(_, ty_field)| ty_field.iter().collect::>()) + .collect::>>() + .concat() + .into_iter(), + _ => Vec::default().into_iter(), } } } -impl <'a> Default for Abstract<'a> { +impl<'a> Default for Abstract<'a> { fn default() -> Abstract<'a> { Abstract::None } } -impl <'a>From<((&'a ast::Item, &'a Vec, &'a Vec), Rc)> for Abstract<'a> { - fn from(arguments: ((&'a ast::Item, &'a Vec, &'a Vec), Rc)) -> Abstract<'a> { - Abstract::Trait(Trait::from(arguments)) - } -} - -impl <'a>From<((&'a ast::Item, &'a Vec), Rc)> for Abstract<'a> { - fn from(arguments: ((&'a ast::Item, &'a Vec), Rc)) -> Abstract<'a> { - Abstract::Struct(Struct::from(arguments)) - } -} - -impl <'a>From<((&'a ast::Item, &'a Vec, &'a Vec), Rc)> for Abstract<'a> { - fn from(arguments: ((&'a ast::Item, &'a Vec, &'a Vec), Rc)) -> Abstract<'a> { - Abstract::Enum(Enum::from(arguments)) - } -} - -impl <'a>fmt::Display for Abstract<'a> { +impl<'a> fmt::Display for Abstract<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - &Abstract::Struct(ref item) => write!(f, "{}", item), - &Abstract::Enum(ref item) => write!(f, "{}", item), - &Abstract::Trait(ref item) => write!(f, "{}", item), - &Abstract::None => Err(fmt::Error), + Abstract::Struct(item) => write!(f, "{}", item), + Abstract::Enum(item) => write!(f, "{}", item), + Abstract::Trait(item) => write!(f, "{}", item), + Abstract::None => Err(fmt::Error), } } } diff --git a/src/core/item/state/abstraction/structure.rs b/src/core/item/state/abstraction/structure.rs index 6bac5616..8ef55afb 100644 --- a/src/core/item/state/abstraction/structure.rs +++ b/src/core/item/state/abstraction/structure.rs @@ -1,56 +1,76 @@ use std::fmt; +use std::fmt::{Debug, Formatter}; use std::rc::Rc; -use ::syntex_syntax::print::pprust::ty_to_string; -use ::syntex_syntax::{symbol, ast}; +use dot::escape_html; +use quote::ToTokens; +use syn::{Field, ItemStruct, Visibility}; -use ::module::path::ModulePath; - -use ::dot::escape_html; +use module::path::ModulePath; /// The structure `Struct` is a structure abstract element. - #[derive(Debug, Eq, PartialEq, Clone)] pub struct Struct<'a> { pub path: Rc, /// Visibility - pub vis: &'a ast::Visibility, - pub name: symbol::InternedString, - pub fields: Vec<(&'a ast::Visibility, symbol::InternedString, String)>, + pub vis: &'a Visibility, + pub name: String, + pub fields: Vec<(&'a Visibility, String, String)>, } -impl <'a>From<((&'a ast::Item, &'a Vec), Rc)> for Struct<'a> { - fn from(((item, struct_field), path): ((&'a ast::Item, &'a Vec), Rc)) -> Struct<'a> { +impl<'a> From<((&'a ItemStruct, &'a Vec), Rc)> for Struct<'a> { + fn from( + ((item, struct_field), path): ((&'a ItemStruct, &'a Vec), Rc), + ) -> Struct<'a> { Struct { - path: path, + path, vis: &item.vis, - name: item.ident.name.as_str(), - fields: struct_field.iter() - .filter_map(|&ast::StructField { span: _, ident, ref vis, id: _, ref ty, .. }| - ident.and_then(|ast::Ident {name, ..}| Some((vis, name.as_str(), ty_to_string(&ty))))) - .collect::>() + name: item.ident.to_string(), + fields: struct_field + .iter() + .enumerate() + .map(|(index, Field { vis, ident, ty, .. })| { + let ident = if let Some(ident) = ident { + ident.to_string() + } else { + index.to_string() + }; + (vis, ident, ty.to_token_stream().to_string()) + }) + .collect(), } } } -impl <'a>fmt::Display for Struct<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl<'a> fmt::Display for Struct<'a> { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { if self.fields.is_empty() { - write!(f, "<<<Structure>>>\n{name}", name = self.name) + write!( + f, + "<<<Structure>>>\n{name}", + name = self.name + ) } else { - write!(f, "<<<Structure>>>\n{name}|{fields}", + write!( + f, + "<<<Structure>>>\n{name}|{fields}", name = self.name, - fields = escape_html(self.fields.iter() - .map(|&(ref vis, ref name, ref ty): &(&ast::Visibility, symbol::InternedString, String)| - if ast::Visibility::Public.eq(vis) { - format!("+ {name}: {ty}", name = name, ty = ty) - } else { - format!("- {name}: {ty}", name = name, ty = ty) - } - ) - .collect::>() - .join("\n") - .as_str()), + fields = escape_html( + self.fields + .iter() + .map( + |&(ref vis, ref name, ref ty): &(&Visibility, String, String)| { + if let Visibility::Public(_) = vis { + format!("+ {name}: {ty}", name = name, ty = ty) + } else { + format!("- {name}: {ty}", name = name, ty = ty) + } + } + ) + .collect::>() + .join("\n") + .as_str() + ), ) } } diff --git a/src/core/item/state/implem.rs b/src/core/item/state/implem.rs index 8a4a50eb..f8c5f9f7 100644 --- a/src/core/item/state/implem.rs +++ b/src/core/item/state/implem.rs @@ -1,25 +1,26 @@ -use super::DEFAULT_FUNC; - -use std::ops::Deref; use std::fmt; -use ::syntex_syntax::print::pprust::ty_to_string; -use ::syntex_syntax::symbol::InternedString; -use ::syntex_syntax::ast; +use dot::escape_html; +use quote::ToTokens; +use syn::punctuated::Punctuated; +use syn::token::Colon2; +use syn::{ + AngleBracketedGenericArguments, FnArg, GenericArgument, ImplItem, ImplItemMethod, + PathArguments, PathSegment, ReturnType, Signature, +}; -use ::dot::escape_html; +use super::DEFAULT_FUNC; /// The structure `Implem` is a collection of methods and tyes for an abstract element. - #[derive(Default, Debug, Clone, Eq, PartialEq)] pub struct Implem { - ty: Vec<(InternedString, Vec)>, + ty: Vec<(String, Vec)>, /// method's name, arguments, result. - method: Vec<(InternedString, Vec, Option)>, + method: Vec<(String, Vec, Option)>, } impl Implem { - pub fn is_realization(&self, ty_name: &String) -> bool { + pub fn is_realization(&self, ty_name: &str) -> bool { if let Some(&(ref name, _)) = self.ty.first() { name.to_string().eq(ty_name) } else { @@ -27,81 +28,143 @@ impl Implem { } } - pub fn is_association(&self, ty_name: &String) -> bool { - self.method.iter() - .any(|&(_, _, ref result): &(InternedString, Vec, Option)| - if let &Some(ref ret) = result { - ret.split(|at| "<[(;, )]>".contains(at)) - .any(|ty| ty.eq(ty_name)) - } else { - false - } - ) + pub fn is_association(&self, ty_name: &str) -> bool { + self.method.iter().any(|(_, _, result)| { + if let Some(ret) = result { + ret.split(|at| "<[(;, )]>".contains(at)) + .any(|ty| ty.eq(ty_name)) + } else { + false + } + }) } - pub fn is_dependency(&self, _: &String) -> bool { + pub fn is_dependency(&self, _: &str) -> bool { false /*self.method.iter() - .any(|&( _, ref arg, _): &(InternedString, Vec, Option)| - arg.iter().any(|ty| ty.ends_with(name)))*/ + .any(|&( _, ref arg, _): &(String, Vec, Option)| + arg.iter().any(|ty| ty.ends_with(name)))*/ } } -impl From<(Vec<(InternedString, Vec)>, Vec<(InternedString, Vec, Option)>)> for Implem { - fn from((ty, method): (Vec<(InternedString, Vec)>, Vec<(InternedString, Vec, Option)>)) -> Implem { - Implem { - ty: ty, - method: method, - } +impl + From<( + Vec<(String, Vec)>, + Vec<(String, Vec, Option)>, + )> for Implem +{ + fn from( + (ty, method): ( + Vec<(String, Vec)>, + Vec<(String, Vec, Option)>, + ), + ) -> Implem { + Implem { ty, method } } } -impl <'a> From<(&'a Vec, &'a Vec)> for Implem { - fn from((segments, impl_item): (&'a Vec, &'a Vec)) -> Implem { - Implem::from((segments.iter() - .map(|&ast::PathSegment { identifier: ast::Ident {name, ..}, span: _, ref parameters }| { - if let &Some(ref path) = parameters { - if let &ast::PathParameters::AngleBracketed( - ast::AngleBracketedParameterData { lifetimes: _, ref types, .. } - ) = path.deref() { - (name.as_str(), types.iter().map(|ty| ty_to_string(&ty)).collect::>()) - } else { - (name.as_str(), Vec::new()) - } - } else { - (name.as_str(), Vec::new()) - } - }) - .collect::)>>(), - impl_item.iter() - .filter_map(|&ast::ImplItem { id: _, ident: ast::Ident {name, ..}, vis: _, defaultness: _, attrs: _, ref node, ..}| - if let &ast::ImplItemKind::Method(ast::MethodSig { unsafety: _, constness: _, abi: _, ref decl, .. }, _) = node { - if let ast::FunctionRetTy::Ty(ref ty) = decl.output { - Some((name.as_str(), decl.inputs.iter().map(|arg| ty_to_string(&arg.ty)).collect::>(), Some(ty_to_string(&ty)))) - } else { - Some((name.as_str(), decl.inputs.iter().map(|arg| ty_to_string(&arg.ty)).collect::>(), None)) - } - } else { - None - } - ).collect::, Option)>>())) +impl<'a> From<(&'a Punctuated, &'a Vec)> for Implem { + fn from( + (segments, impl_item): (&'a Punctuated, &'a Vec), + ) -> Implem { + Implem::from(( + segments + .iter() + .map( + |PathSegment { + ident, arguments, .. + }| { + if let PathArguments::AngleBracketed(AngleBracketedGenericArguments { + args, + .. + }) = arguments + { + ( + ident.to_string(), + args.iter() + .filter_map(|ty| { + if let GenericArgument::Type(ty) = ty { + Some(ty.to_token_stream().to_string()) + } else { + None + } + }) + .collect::>(), + ) + } else { + (ident.to_string(), Vec::new()) + } + }, + ) + .collect::)>>(), + impl_item + .iter() + .filter_map(|item| { + if let ImplItem::Method(ImplItemMethod { + sig: + Signature { + ident, + inputs, + output, + .. + }, + .. + }) = item + { + let inputs = inputs + .iter() + .filter_map(|arg| { + if let FnArg::Typed(ty) = arg { + Some(ty.ty.to_token_stream().to_string()) + } else { + None + } + }) + .collect(); + if let ReturnType::Type(_, ref ty) = output { + Some(( + ident.to_string(), + inputs, + Some(ty.to_token_stream().to_string()), + )) + } else { + Some((ident.to_string(), inputs, None)) + } + } else { + None + } + }) + .collect::, Option)>>(), + )) } } impl fmt::Display for Implem { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{item}", - item = escape_html(self.method.iter() - .map(|&(ref name, ref args, ref result): &(InternedString, Vec, Option)| { - if let &Some(ref ret) = result { - format!("{}{}({}) -> {}", DEFAULT_FUNC, name, args.join(", "), ret) - } else { - format!("{}{}({})", DEFAULT_FUNC, name, args.join(", ")) - } - }) - .collect::>() - .join("\n") - .as_str())) + write!( + f, + "{item}", + item = escape_html( + self.method + .iter() + .map( + |&(ref name, ref args, ref result): &( + String, + Vec, + Option + )| { + if let Some(ret) = result { + format!("{}{}({}) -> {}", DEFAULT_FUNC, name, args.join(", "), ret) + } else { + format!("{}{}({})", DEFAULT_FUNC, name, args.join(", ")) + } + } + ) + .collect::>() + .join("\n") + .as_str() + ) + ) /*if let Some(&(ref name, ref template)) = self.ty.last() { if template.is_empty() { write!(f, "{name}", name = name.to_string()) diff --git a/src/core/item/state/method.rs b/src/core/item/state/method.rs index ae2a6903..388efe0a 100644 --- a/src/core/item/state/method.rs +++ b/src/core/item/state/method.rs @@ -1,99 +1,176 @@ -use super::DEFAULT_FUNC; - -use std::ops::Deref; use std::fmt; use std::rc::Rc; -use ::syntex_syntax::print::pprust::{ty_to_string, arg_to_string}; -use ::syntex_syntax::symbol::InternedString; -use ::syntex_syntax::ast; +use dot::escape_html; +use quote::ToTokens; +use syn::{ + punctuated::Punctuated, token::Comma, FnArg, ImplItem, ImplItemMethod, ReturnType, Signature, + Type, Visibility, +}; -use ::module::path::ModulePath; +use module::path::ModulePath; -use ::dot::escape_html; +use super::DEFAULT_FUNC; /// The structure `Method` is a collection of methods from a abstract element. - #[derive(Default, Debug, Clone, Eq, PartialEq)] -pub struct Method <'a> { +pub struct Method<'a> { /// visibility, method's name, arguments, result. - func: Vec<(&'a ast::Visibility, InternedString, Vec, Option)>, + func: Vec<( + &'a Visibility, + String, + &'a Punctuated, + Option<&'a Box>, + )>, path: Rc, } -impl <'a> Method <'a> { - pub fn is_association(&self, ty_name: &String) -> bool { - self.func.iter() - .any(|&(_, _, _, ref result): &(&'a ast::Visibility, InternedString, Vec, Option)| - if let &Some(ref ret) = result { - ret.split(|at| "<[(;, )]>".contains(at)) - .any(|ty| ty.eq(ty_name)) - } else { - false - } - ) +impl<'a> Method<'a> { + pub fn is_association(&self, ty_name: &str) -> bool { + self.func.iter().any(|(_, _, _, result)| { + if let Some(ret) = result { + // TODO: replace by checking if ret (which is of type Type) + // TODO: contains ty_name anywhere + ret.to_token_stream() + .to_string() + .split(|at| "<[(;, )]>".contains(at)) + .any(|ty| ty.eq(ty_name)) + } else { + false + } + }) } - pub fn is_dependency(&self, name: &String) -> bool { - self.func.iter() - .any(|&(_, _, ref arg, _): &(&'a ast::Visibility, InternedString, Vec, Option)| - arg.iter().any(|ty| ty.ends_with(name))) + pub fn is_dependency(&self, name: &str) -> bool { + self.func.iter().any(|(_, _, arg, _)| { + arg.iter() + .filter_map(|ty| { + if let FnArg::Typed(ty) = ty { + Some(ty) + } else { + None + } + }) + .any(|ty| ty.ty.to_token_stream().to_string().ends_with(name)) + }) } } -impl <'a> From<(Vec<(&'a ast::Visibility, InternedString, Vec, Option)>, Rc)> for Method<'a> { - fn from((func, path): (Vec<(&'a ast::Visibility, InternedString, Vec, Option)>, Rc)) -> Method<'a> { - Method { - func: func, - path: path, - } +impl<'a> + From<( + Vec<( + &'a Visibility, + String, + &'a Punctuated, + Option<&'a Box>, + )>, + Rc, + )> for Method<'a> +{ + fn from( + (func, path): ( + Vec<( + &'a Visibility, + String, + &'a Punctuated, + Option<&'a Box>, + )>, + Rc, + ), + ) -> Method<'a> { + Method { func, path } } } -impl <'a> From<(&'a Vec, Rc)> for Method<'a> { - fn from((impl_item, path): (&'a Vec, Rc)) -> Method<'a> { - Method::from((impl_item.iter() - .filter_map(|&ast::ImplItem {id: _, ident: ast::Ident { name, ..}, ref vis, defaultness: _, attrs: _, ref node, .. }| { - if let &ast::ImplItemKind::Method(ast::MethodSig {unsafety: _, constness: _, abi: _, ref decl, ..}, _) = node { - if let &ast::FnDecl {ref inputs, output: ast::FunctionRetTy::Ty(ref ty), ..} = decl.deref() { - Some((vis, name.as_str(), inputs.iter().map(|ref arg| arg_to_string(&arg)).collect::>(), Some(ty_to_string(&ty)))) - } else if let &ast::FnDecl {ref inputs, output: ast::FunctionRetTy::Default(_), ..} = decl.deref() { - Some((vis, name.as_str(), inputs.iter().map(|ref arg| arg_to_string(&arg)).collect::>(), None)) - } else { - None - } - } else { - None - } - }) - .collect::, Option)>>(), - path)) +impl<'a> From<(&'a Vec, Rc)> for Method<'a> { + fn from((impl_item, path): (&'a Vec, Rc)) -> Method<'a> { + Method::from(( + impl_item + .iter() + .filter_map(|item| { + if let ImplItem::Method(ImplItemMethod { + ref vis, ref sig, .. + }) = item + { + if let Signature { + ident, + inputs, + output: ReturnType::Default, + .. + } = sig + { + Some((vis, ident.to_string(), inputs, None)) + } else if let Signature { + ident, + inputs, + output: ReturnType::Type(_, ref output), + .. + } = sig + { + Some((vis, ident.to_string(), inputs, Some(output))) + } else { + None + } + } else { + None + } + }) + .collect::>(), + path, + )) } } -impl <'a>fmt::Display for Method<'a> { +impl<'a> fmt::Display for Method<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{item}", - item = escape_html(self.func.iter() - .map(|&(ref vis, ref name, ref inputs, ref ty)| - match (vis, ty) { - (&&ast::Visibility::Public, &Some(ref ty)) => { - format!("+{}{}({}) -> {}", DEFAULT_FUNC, name, inputs.iter().map(|arg| arg.to_string()).collect::>().join(", "), ty) - }, - (&&ast::Visibility::Public, &None) => { - format!("+{}{}({})", DEFAULT_FUNC, name, inputs.iter().map(|arg| arg.to_string()).collect::>().join(", ")) - }, - (_, &Some(ref ty)) => { - format!("-{}{}({}) -> {}", DEFAULT_FUNC, name, inputs.iter().map(|arg| arg.to_string()).collect::>().join(", "), ty) - }, - (_, &None) => { - format!("-{}{}({})", DEFAULT_FUNC, name, inputs.iter().map(|arg| arg.to_string()).collect::>().join(", ")) - }, - } - ) - .collect::>() - .join("\n") - .as_str()) + write!( + f, + "{item}", + item = escape_html( + self.func + .iter() + .map(|&(vis, ref name, ref inputs, ty)| { + match (vis, ty) { + (Visibility::Public(_), Some(ty)) => { + format!( + "+{}{}({}) -> {}", + DEFAULT_FUNC, + name, + inputs.to_token_stream().to_string(), + ty.to_token_stream().to_string() + ) + } + (Visibility::Public(_), None) => { + format!( + "+{}{}({})", + DEFAULT_FUNC, + name, + inputs.to_token_stream().to_string() + ) + } + (_, Some(ty)) => { + format!( + "-{}{}({}) -> {}", + DEFAULT_FUNC, + name, + inputs.to_token_stream().to_string(), + ty.to_token_stream().to_string() + ) + } + (_, None) => { + format!( + "-{}{}({})", + DEFAULT_FUNC, + name, + inputs.to_token_stream().to_string() + ) + } + } + }) + .collect::>() + .join("\n") + .as_str() + ) ) } } diff --git a/src/core/item/state/mod.rs b/src/core/item/state/mod.rs index 195e012b..6d449c38 100644 --- a/src/core/item/state/mod.rs +++ b/src/core/item/state/mod.rs @@ -1,26 +1,25 @@ -pub mod abstraction; -pub mod implem; -pub mod method; +use std::fmt; +use std::ops::BitOr; +use std::rc::Rc; -#[cfg(not(feature = "fn-emilgardis"))] -const DEFAULT_FUNC: &'static str = " "; -#[cfg(feature = "fn-emilgardis")] -const DEFAULT_FUNC: &'static str = " fn "; +use syn::{Field, Item, ItemEnum, ItemImpl, ItemTrait, Path, TraitItem, TypeParam, Variant}; -use self::abstraction::Abstract; -use self::implem::Implem; -use self::method::Method; +use module::path::ModulePath; use super::relation::Relation; -use std::ops::BitOr; -use std::fmt; -use std::rc::Rc; +use self::abstraction::Abstract; +use self::implem::Implem; +use self::method::Method; -use ::syntex_syntax::symbol::InternedString; -use ::syntex_syntax::{ptr, ast}; +pub mod abstraction; +pub mod implem; +pub mod method; -use ::module::path::ModulePath; +#[cfg(not(feature = "fn-emilgardis"))] +const DEFAULT_FUNC: &str = " "; +#[cfg(feature = "fn-emilgardis")] +const DEFAULT_FUNC: &str = " fn "; /// The structure `ItemState` describes an abstract element with a collections of methodes /// and implementations. @@ -34,49 +33,53 @@ pub struct ItemState<'a> { implem: Vec, } -impl <'a> ItemState <'a> { - +impl<'a> ItemState<'a> { pub fn is_none(&self) -> bool { self.node.eq(&Abstract::None) } pub fn is_association(&self, rhs: &ItemState<'a>) -> bool { - if let Some(ref name) = self.as_name() { - let ref ty_name: String = name.to_string(); - - rhs.method.iter() - .any(|func| func.is_association(ty_name)) - .bitor(rhs.implem.iter() - .any(|implem| implem.is_association(&ty_name))) + if let Some(ty_name) = self.as_name() { + rhs.method + .iter() + .any(|func| func.is_association(ty_name)) + .bitor( + rhs.implem + .iter() + .any(|implem| implem.is_association(ty_name)), + ) } else { false } } pub fn is_dependency(&self, rhs: &ItemState<'a>) -> bool { - if let Some(ref name) = self.as_name() { - let ref ty_name: String = name.to_string(); - - rhs.method.iter() - .any(|method| method.is_dependency(&ty_name)) - .bitor(self.implem.iter() - .any(|implem| implem.is_dependency(&ty_name))) + if let Some(ty_name) = self.as_name() { + rhs.method + .iter() + .any(|method| method.is_dependency(ty_name)) + .bitor( + self.implem + .iter() + .any(|implem| implem.is_dependency(ty_name)), + ) } else { false } } pub fn is_aggregation(&self, rhs: &ItemState<'a>) -> bool { - if let Some(ref name) = self.as_name() { - let mut ty_name_mut: String = String::from("*mut "); - let mut ty_name_const: String = String::from("*const "); - + if let Some(name) = self.as_name() { + let mut ty_name_mut: String = String::from("* mut "); + let mut ty_name_const: String = String::from("* const "); + ty_name_mut.push_str(&name); ty_name_const.push_str(&name); - rhs.node.into_iter() - .any(|attribut: &String| - attribut.split(|at| "<[(;,)]>".contains(at)) - .any(|ty| ty_name_mut.eq(ty).bitor(ty_name_const.eq(ty)))) + rhs.node.into_iter().any(|attribut: &String| { + attribut + .split(|at| "<[(;,)]>".contains(at)) + .any(|ty| ty_name_mut.eq(ty).bitor(ty_name_const.eq(ty))) + }) } else { false } @@ -86,10 +89,11 @@ impl <'a> ItemState <'a> { if let Some(ref name) = self.as_name() { let ty_name: String = name.to_string(); - rhs.node.into_iter() - .any(|attribut: &String| - attribut.split(|at| "<[(;,)]>".contains(at)) - .any(|ty| ty.eq(&ty_name))) + rhs.node.into_iter().any(|attribut: &String| { + attribut + .split(|at| "<[(;,)]>".contains(at)) + .any(|ty| ty.eq(&ty_name)) + }) } else { false } @@ -99,8 +103,9 @@ impl <'a> ItemState <'a> { if let Some(ref name) = self.as_name() { let ty_name: String = name.to_string(); - rhs.implem.iter() - .any(|implem| implem.is_realization(&ty_name)) + rhs.implem + .iter() + .any(|implem| implem.is_realization(&ty_name)) } else { false } @@ -114,7 +119,7 @@ impl <'a> ItemState <'a> { .bitor(self.is_realization(rhs)) } - pub fn as_name(&self) -> Option<&InternedString> { + pub fn as_name(&self) -> Option<&String> { self.node.as_name() } @@ -123,72 +128,114 @@ impl <'a> ItemState <'a> { } } -impl <'a>From<(Abstract<'a>, Vec<&'a (ptr::P, Rc)>)> for ItemState<'a> { - fn from((node, properties): (Abstract<'a>, Vec<&'a (ptr::P, Rc)>)) -> ItemState<'a> { +impl<'a> From<(Abstract<'a>, Vec<&'a (Item, Rc)>)> for ItemState<'a> { + fn from((node, properties): (Abstract<'a>, Vec<&'a (Item, Rc)>)) -> ItemState<'a> { ItemState { - node: node, - method: properties.iter() - .filter_map(|&&(ref item, ref path): (&&'a (ptr::P, Rc))| - if let ast::ItemKind::Impl(_, _, _, _, None, _, ref impl_item) = item.node { - Some(Method::from((impl_item, Rc::clone(path)))) - } else { - None + node, + method: properties + .iter() + .filter_map(|(item, path)| match item { + Item::Impl(ItemImpl { trait_, items, .. }) if trait_.is_none() => { + Some(Method::from((items, Rc::clone(path)))) } - ) - .collect::>(), - implem: properties.iter() - .filter_map(|&&(ref item, _): (&&'a (ptr::P, Rc))| - if let ast::ItemKind::Impl(_, _, _, _, Some(ast::TraitRef {path: ast::Path {span: _, ref segments}, ..}), _, ref impl_item) = item.node { - Some(Implem::from((segments, impl_item))) + _ => None, + }) + .collect(), + implem: properties + .iter() + .filter_map(|(item, _)| { + if let Item::Impl(ItemImpl { + trait_: Some((_, Path { segments, .. }, _)), + items, + .. + }) = item + { + Some(Implem::from((segments, items))) } else { None } - ) - .collect::>() + }) + .collect(), } } } -impl <'a>From, Rc)>> for ItemState<'a> { - fn from(state: Vec<&'a (ptr::P, Rc)>) -> ItemState<'a> { - state.split_first().and_then(|(&&(ref item, ref path), properties): (&&'a (ptr::P, Rc), &[&'a (ptr::P, Rc)])| { - match &item.node { - /// Trait. - &ast::ItemKind::Trait(_, ast::Generics {lifetimes: _, ref ty_params, ..}, _, ref trait_item) => { - let kind: (&'a ast::Item, &'a Vec, &'a Vec) = (item, ty_params, trait_item); - let kind: (Abstract, Vec<&'a (ptr::P, Rc)>) = (Abstract::from((kind, Rc::clone(path))), properties.to_vec()); - Some(ItemState::from(kind)) - }, - /// Structure with variables. - &ast::ItemKind::Struct(ast::VariantData::Struct(ref struct_field, _), ..) => { - let kind: (&'a ast::Item, &'a Vec) = (item, struct_field); - let kind: (Abstract, Vec<&'a (ptr::P, Rc)>) = (Abstract::from((kind, Rc::clone(path))), properties.to_vec()); - Some(ItemState::from(kind)) - }, - /// Enumeration with variables. - &ast::ItemKind::Enum(ast::EnumDef {ref variants}, ast::Generics {lifetimes: _, ref ty_params, ..}) => { - let kind: (&'a ast::Item, &'a Vec, &'a Vec) = (item, ty_params, variants); - let kind: (Abstract, Vec<&'a (ptr::P, Rc)>) = (Abstract::from((kind, Rc::clone(path))), properties.to_vec()); - Some(ItemState::from(kind)) - }, - _ => None, - } - }).unwrap_or_default() +impl<'a> From)>> for ItemState<'a> { + fn from(state: Vec<&'a (Item, Rc)>) -> ItemState<'a> { + state + .split_first() + .and_then(|((item, path), properties)| { + match item { + // Trait. + Item::Trait(item) => { + let ItemTrait { + generics, items, .. + } = item; + let ty_params = + Box::leak(Box::new(generics.type_params().cloned().collect())); // FIXME + let kind: (_, &'a Vec, &'a Vec) = + (item, ty_params, items); + let kind: (Abstract, Vec<&'a (Item, Rc)>) = ( + Abstract::Trait((kind, Rc::clone(path)).into()), + properties.to_vec(), + ); + Some(ItemState::from(kind)) + } + // Structure with variables. + Item::Struct(item) => { + let fields = Box::leak(Box::new(item.fields.iter().cloned().collect())); // FIXME + let kind: (_, &Vec) = (item, fields); + let kind = ( + Abstract::Struct((kind, Rc::clone(path)).into()), + properties.to_vec(), + ); + Some(ItemState::from(kind)) + } + // Enumeration with variables. + Item::Enum(item) => { + let ItemEnum { + generics, variants, .. + } = item; + let ty_params = + Box::leak(Box::new(generics.type_params().cloned().collect())); // FIXME + let variants = Box::leak(Box::new(variants.iter().cloned().collect())); // FIXME + let kind: (&'a ItemEnum, &'a Vec, &'a Vec) = + (item, ty_params, variants); + let kind: (Abstract, Vec<&'a (Item, Rc)>) = ( + Abstract::Enum((kind, Rc::clone(path)).into()), + properties.to_vec(), + ); + Some(ItemState::from(kind)) + } + _ => None, + } + }) + .unwrap_or_default() } } -impl <'a>fmt::Display for ItemState<'a> { - +impl<'a> fmt::Display for ItemState<'a> { #[cfg(feature = "implem")] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{{{node}|{method}|{implem}}}", + write!( + f, + "{{{node}|{method}|{implem}}}", node = self.node, - method = self.method.iter() - .map(|ref methods| format!("{}", methods)) - .collect::>().join("\n").as_str(), - implem = self.implem.iter() - .map(|ref implem| format!("{}", implem)) - .collect::>().join("\n").as_str()) + method = self + .method + .iter() + .map(|ref methods| format!("{}", methods)) + .collect::>() + .join("\n") + .as_str(), + implem = self + .implem + .iter() + .map(|ref implem| format!("{}", implem)) + .collect::>() + .join("\n") + .as_str() + ) } #[cfg(not(feature = "implem"))] @@ -196,11 +243,18 @@ impl <'a>fmt::Display for ItemState<'a> { if self.method.is_empty() { write!(f, "{{{node}}}", node = self.node) } else { - write!(f, "{{{node}|{method}}}", + write!( + f, + "{{{node}|{method}}}", node = self.node, - method = self.method.iter() - .map(|ref methods| format!("{}", methods)) - .collect::>().join("\n").as_str()) + method = self + .method + .iter() + .map(|ref methods| format!("{}", methods)) + .collect::>() + .join("\n") + .as_str() + ) } } } diff --git a/src/core/mod.rs b/src/core/mod.rs index 43468179..860748e6 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,47 +1,47 @@ //! Core Syntax and definitions. -pub mod segment; -pub mod item; - -use self::segment::Segment; -use self::item::{Item, ItemState}; -use self::item::relation::Relation; - -use std::{slice, iter}; use std::borrow::Cow; use std::ops::BitOr; use std::rc::Rc; +use std::{iter, slice}; + +use dot::{Arrow, Edges, GraphWalk, Id, LabelText, Labeller, Nodes, Style}; +use itertools::Itertools; + +use module::path::ModulePath; -use ::syntex_syntax::{ptr, ast}; -use ::dot::{Nodes, Edges, Arrow, Style, GraphWalk, Labeller, LabelText, Id}; -use ::itertools::Itertools; +use self::item::relation::Relation; +use self::item::{Item, ItemState}; +use self::segment::Segment; -use ::module::path::ModulePath; +pub mod item; +pub mod segment; #[derive(Debug, Clone)] -pub struct ListItem <'a> { +pub struct ListItem<'a> { parse: Item<'a>, } -impl <'a> From> for ListItem <'a> { - fn from(parse: Item<'a>) -> ListItem <'a> { - ListItem { - parse: parse, - } +impl<'a> From> for ListItem<'a> { + fn from(parse: Item<'a>) -> ListItem<'a> { + ListItem { parse } } } -impl <'a> From, Rc)>>> for ListItem <'a> { - fn from(list: iter::Peekable, Rc)>>) -> ListItem <'a> { +impl<'a> From)>>> for ListItem<'a> { + fn from(list: iter::Peekable)>>) -> ListItem<'a> { ListItem::from(Item::from(list)) } } -impl <'a>Iterator for ListItem<'a> { +impl<'a> Iterator for ListItem<'a> { type Item = ItemState<'a>; fn next(&mut self) -> Option> { - self.parse.by_ref().skip_while(|state| state.is_none()).next() + self.parse + .by_ref() + .skip_while(|state| state.is_none()) + .next() } } @@ -58,25 +58,26 @@ impl<'a> Labeller<'a, ItemState<'a>, Segment<'a>> for ListItem<'a> { } fn node_shape(&'a self, _node: &ItemState<'a>) -> Option> { - Some(LabelText::LabelStr(Cow::from(format!("record")))) + Some(LabelText::LabelStr(Cow::from("record"))) } fn node_label(&'a self, state: &ItemState<'a>) -> LabelText<'a> { - LabelText::LabelStr(format!("{}", state).into()) + LabelText::LabelStr(state.to_string().into()) } - fn edge_end_arrow(&'a self, ref seg: &Segment<'a>) -> Arrow { - match ( - seg.left.as_arrow(&seg.right), - seg.right.as_arrow(&seg.left) - ) { + fn edge_end_arrow(&'a self, seg: &Segment<'a>) -> Arrow { + match (seg.left.as_arrow(&seg.right), seg.right.as_arrow(&seg.left)) { (Relation::Association, Relation::Association) => Arrow::none(), (edge_left, _) => Arrow::from_arrow(edge_left.as_style()), } } - fn edge_style(&'a self, ref seg: &Segment<'a>) -> Style { - if seg.left.is_realization(&seg.right).bitor(seg.left.is_dependency(&seg.right)) { + fn edge_style(&'a self, seg: &Segment<'a>) -> Style { + if seg + .left + .is_realization(&seg.right) + .bitor(seg.left.is_dependency(&seg.right)) + { Style::Dashed } else { Style::None @@ -88,24 +89,37 @@ impl<'a> GraphWalk<'a, ItemState<'a>, Segment<'a>> for ListItem<'a> { fn nodes(&'a self) -> Nodes<'a, ItemState<'a>> { Cow::Owned(self.clone().collect::>>()) } - + fn edges(&'a self) -> Edges<'a, Segment<'a>> { let items = self.clone().collect::>>(); - Cow::Owned(items.iter() - .map(|item| items.iter() - .filter(|rhs| item.ne(rhs)) - .filter(|rhs| item.is_relation(rhs)) - .map(|rhs| Segment::from((item.clone(), rhs.clone()))) - .collect::>>()) - .collect::>>>() - .concat() - .into_iter() - .unique() - .collect::>>()) + let dbg = items.iter().map(|s| format!("{:?}", s)).collect::>(); + println!("{:#?}", dbg); + + let items = items + .iter() + .map(|item| { + items + .iter() + .filter(|rhs| item.ne(rhs)) + .filter(|rhs| item.is_relation(rhs)) + .map(|rhs| Segment::from((item.clone(), rhs.clone()))) + .collect::>>() + }) + .collect::>>>() + .concat() + .into_iter() + .unique() + .collect::>>(); + + Cow::Owned(items) } - fn source(&self, seg: &Segment<'a>) -> ItemState<'a> { seg.left.clone() } + fn source(&self, seg: &Segment<'a>) -> ItemState<'a> { + seg.left.clone() + } - fn target(&self, seg: &Segment<'a>) -> ItemState<'a> { seg.right.clone() } + fn target(&self, seg: &Segment<'a>) -> ItemState<'a> { + seg.right.clone() + } } diff --git a/src/core/segment.rs b/src/core/segment.rs index ecf01f29..91360026 100644 --- a/src/core/segment.rs +++ b/src/core/segment.rs @@ -1,5 +1,5 @@ -use std::ops::{BitOr, BitAnd}; use std::hash::{Hash, Hasher}; +use std::ops::{BitAnd, BitOr}; use super::ItemState; @@ -11,25 +11,21 @@ pub struct Segment<'a> { pub right: ItemState<'a>, } -impl <'a> From<(ItemState<'a>, ItemState<'a>)> for Segment <'a> { +impl<'a> From<(ItemState<'a>, ItemState<'a>)> for Segment<'a> { fn from((left, right): (ItemState<'a>, ItemState<'a>)) -> Segment<'a> { - Segment { - left: left, - right: right, - } + Segment { left, right } } } -impl <'a> Hash for Segment <'a> { - fn hash(&self, _: &mut H) { - } +impl<'a> Hash for Segment<'a> { + fn hash(&self, _: &mut H) {} } -impl <'a> PartialEq for Segment <'a> { +impl<'a> PartialEq for Segment<'a> { fn eq(&self, rhs: &Segment) -> bool { - self.left.eq(&rhs.left) - .bitand(self.right.eq(&rhs.right)) - .bitor(self.left.eq(&rhs.right) - .bitand(self.right.eq(&rhs.left))) + self.left + .eq(&rhs.left) + .bitand(self.right.eq(&rhs.right)) + .bitor(self.left.eq(&rhs.right).bitand(self.right.eq(&rhs.left))) } } diff --git a/src/lib.rs b/src/lib.rs index 3f66c809..53441af8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,7 @@ -#![crate_name="mml"] -#![crate_type= "lib"] - +#![crate_name = "mml"] +#![crate_type = "lib"] #![doc(html_root_url = "https://docs.rs/mml/0.1.41")] - #![cfg_attr(feature = "nightly", feature(plugin))] - #![cfg_attr(feature = "lints", plugin(clippy))] #![cfg_attr(feature = "lints", deny(warnings))] #![cfg_attr(not(any(feature = "lints", feature = "nightly")), deny())] @@ -19,62 +16,93 @@ //! ![uml](ml.svg) -extern crate syntex_syntax; -extern crate syntex_errors; +extern crate dot; extern crate itertools; +extern crate quote; +extern crate syn; extern crate walkdir; -extern crate dot; - -pub mod prelude; -pub mod module; -pub mod core; -use std::process::{Command, Stdio}; -use std::io::{self, Write, Read}; -use std::path::Path; -use std::fs::{self, File}; use std::ffi::OsStr; +use std::fs::{self, File}; +use std::io::{self, Read, Write}; +use std::path::Path; +use std::process::{Command, Stdio}; use std::rc::Rc; +use std::result::Result::Err; -use syntex_errors::emitter::ColorConfig; -use syntex_errors::Handler; - -use syntex_syntax::codemap::{CodeMap, FilePathMapping}; -use syntex_syntax::parse::{self, ParseSess}; -use syntex_syntax::{ast, ptr}; - +use syn::parse_file; use walkdir::WalkDir; + use core::ListItem; -use module::Module; use module::path::ModulePath; +use module::Module; + +pub mod core; +pub mod module; +pub mod prelude; /// The default name of *graph/dot* file. -pub const DEFAULT_NAME_DOT: &'static str = "ml.dot"; +pub const DEFAULT_NAME_DOT: &str = "ml.dot"; /// The default name of *image/svg* file. -pub const DEFAULT_NAME_PNG: &'static str = "ml.svg"; +pub const DEFAULT_NAME_PNG: &str = "ml.svg"; + +#[derive(Debug)] +pub enum Error { + IoError(io::Error), + SynError(syn::Error), +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + let mut debug_struct = f.debug_struct("MlError"); + + match self { + Error::IoError(err) => { + debug_struct.field("err", &err.to_string()); + } + Error::SynError(err) => { + debug_struct.field("err", &err.to_string()); + } + } + + debug_struct.finish() + } +} + +impl std::error::Error for Error {} + +impl From for Error { + fn from(err: io::Error) -> Self { + Error::IoError(err) + } +} + +impl From for Error { + fn from(err: syn::Error) -> Self { + Error::SynError(err) + } +} /// The function `file2crate` returns a syntex module. -fn file2crate>(path: P) -> io::Result { - let codemap = Rc::new(CodeMap::new(FilePathMapping::empty())); - let tty_handler = - Handler::with_tty_emitter(ColorConfig::Auto, true, false, Some(codemap.clone())); - let parse_session: ParseSess = ParseSess::with_span_handler(tty_handler, codemap.clone()); - let parse = parse::parse_crate_from_file(path.as_ref(), &parse_session); - let ast: ast::Crate = parse.unwrap(); - - Ok(ast) +fn file2crate>(path: P) -> Result { + let mut content = String::new(); + let _ = File::open(path)?.read_to_string(&mut content)?; + match parse_file(&content) { + Ok(file) => Ok(file), + Err(err) => Err(err.into()), + } } /// The function `items2chars` returns a graph formated for *Graphiz/Dot*. -fn items2chars<'a>(modules: Vec) -> io::Result> { +fn items2chars(modules: Vec) -> Result, Error> { let mut f: Vec = Vec::new(); - let itt: Vec<(ptr::P, Rc)> = - modules.into_iter() - .flat_map(|s: Module| s.into_iter()) - .collect::, Rc)>>(); - let it: ListItem = ListItem::from(itt.as_slice().into_iter().peekable()); + let iter: Vec<(syn::Item, Rc)> = modules + .into_iter() + .flat_map(|s: Module| s.into_iter()) + .collect::)>>(); + let iter: ListItem = ListItem::from(iter.iter().peekable()); - dot::render(&it, &mut f).and_then(|()| Ok(f)) + dot::render(&iter, &mut f).map(|()| f).map_err(Into::into) } /// The function `rs2dot` returns graphed file module. @@ -87,8 +115,13 @@ fn items2chars<'a>(modules: Vec) -> io::Result> { /// let _ = mml::rs2dot("src/lib.rs"); /// } /// ``` -pub fn rs2dot<'a, P: AsRef>(path: P) -> io::Result> { - file2crate(path.as_ref()).and_then(|parse: ast::Crate| items2chars(vec![Module::from((parse.module.items, path.as_ref().to_path_buf()))])) +pub fn rs2dot>(path: P) -> Result, Error> { + file2crate(path.as_ref()).and_then(|parse: syn::File| { + items2chars(vec![Module::from(( + parse.items, + path.as_ref().to_path_buf(), + ))]) + }) } /// The function `src2dot` returns graphed repository of modules. @@ -101,34 +134,43 @@ pub fn rs2dot<'a, P: AsRef>(path: P) -> io::Result> { /// let _ = mml::src2dot("src"); /// } /// ``` -pub fn src2dot<'a, P: AsRef>(path: P) -> io::Result> { - items2chars(WalkDir::new(path).into_iter() - .filter_map(|entry: Result| entry.ok()) - .filter(|entry| entry.file_type().is_file()) - .filter_map(|entry: walkdir::DirEntry| { - let path: &Path = entry.path(); - - if path.extension().eq(&Some(OsStr::new("rs"))) { - file2crate(path).ok().and_then(|parse| Some(Module::from((parse.module.items, path.to_path_buf())))) - } else { - None - } - }) - .collect::>()) +pub fn src2dot>(path: P) -> Result, Error> { + items2chars( + WalkDir::new(path) + .into_iter() + .filter_map(|entry: Result| entry.ok()) + .filter(|entry| entry.file_type().is_file()) + .filter_map(|entry: walkdir::DirEntry| { + let path: &Path = entry.path(); + + if path.extension().eq(&Some(OsStr::new("rs"))) { + file2crate(path) + .ok() + .map(|file| Module::from((file.items, path.to_path_buf()))) + } else { + None + } + }) + .collect::>(), + ) + .map_err(Into::into) } /// The function `content2svg` returns structured vector graphics content of modules. -fn content2svg(buf: Vec) -> io::Result> { - Command::new("dot").arg("-Tsvg") - .stdin(Stdio::piped()).stdout(Stdio::piped()) - .spawn() - .and_then(|child| { - let mut ret = vec![]; - - child.stdin.unwrap().write_all(buf.as_slice()).unwrap(); - child.stdout.unwrap().read_to_end(&mut ret).unwrap(); - Ok(ret) - }) +pub fn content2svg(buf: Vec) -> Result, Error> { + Command::new("dot") + .arg("-Tsvg") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .map(|child| { + let mut ret = vec![]; + + child.stdin.unwrap().write_all(buf.as_slice()).unwrap(); + child.stdout.unwrap().read_to_end(&mut ret).unwrap(); + ret + }) + .map_err(Into::into) } /// The function `rs2svg` returns structured vector graphics file modules. @@ -141,8 +183,8 @@ fn content2svg(buf: Vec) -> io::Result> { /// let _ = mml::rs2svg("src/lib.rs"); /// } /// ``` -pub fn rs2svg>(path: P) -> io::Result> { - rs2dot(path).and_then(|buf| content2svg(buf)) +pub fn rs2svg>(path: P) -> Result, Error> { + rs2dot(path).and_then(|buf| content2svg(buf).map_err(Into::into)) } /// The function `src2svg` returns structured vector graphics repository of modules. @@ -155,8 +197,8 @@ pub fn rs2svg>(path: P) -> io::Result> { /// let _ = mml::src2svg("src"); /// } /// ``` -pub fn src2svg>(path: P) -> io::Result> { - src2dot(path).and_then(|buf| content2svg(buf)) +pub fn src2svg>(path: P) -> Result, Error> { + src2dot(path).and_then(|buf| content2svg(buf).map_err(Into::into)) } /// The function `src2both` creates two files formated like a graph/dot and a structured vector graphics. @@ -171,7 +213,7 @@ pub fn src2svg>(path: P) -> io::Result> { /// let _ = mml::src2both("src", dest.replace("-", "_").as_str()); /// } /// ``` -pub fn src2both>(src: P, dest: P) -> io::Result<()> { +pub fn src2both>(src: P, dest: P) -> Result<(), Error> { let _ = fs::create_dir_all(dest.as_ref())?; let mut file_dot = File::create(dest.as_ref().join(DEFAULT_NAME_DOT))?; let mut file_svg = File::create(dest.as_ref().join(DEFAULT_NAME_PNG))?; diff --git a/src/module/mod.rs b/src/module/mod.rs index 77df6312..c9e375fd 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -1,44 +1,46 @@ -use std::rc::Rc; -use std::path::PathBuf; use std::ffi::OsString; +use std::path::PathBuf; +use std::rc::Rc; use std::vec; -use syntex_syntax::{ast, ptr}; - +extern crate syn; pub mod path; use self::path::ModulePath; +use syn::Item; #[derive(Default, Debug, Clone)] pub struct Module { - pub list: Vec>, + pub list: Vec, pub path: ModulePath, } -impl From<(Vec>, PathBuf)> for Module { - fn from((list, mut path): (Vec>, PathBuf)) -> Module { +impl From<(Vec, PathBuf)> for Module { + fn from((list, mut path): (Vec, PathBuf)) -> Module { path.set_extension(""); Module { - list: list, + list, path: ModulePath { - path: path.components() - .skip(1) - .map(|comp| comp.as_os_str().to_os_string()) - .collect::>(), + path: path + .components() + .skip(1) + .map(|comp| comp.as_os_str().to_os_string()) + .collect::>(), }, } } } impl IntoIterator for Module { - type Item = (ptr::P, Rc); - type IntoIter = vec::IntoIter<(ptr::P, Rc)>; + type Item = (Item, Rc); + type IntoIter = vec::IntoIter<(Item, Rc)>; fn into_iter(self) -> Self::IntoIter { - let ref rc: Rc = Rc::new(self.path); - self.list.into_iter() - .map(|item| (item, Rc::clone(rc))) - .collect::, Rc)>>() - .into_iter() + let rc = &Rc::new(self.path); + self.list + .into_iter() + .map(|item| (item, Rc::clone(rc))) + .collect::)>>() + .into_iter() } } diff --git a/src/prelude.rs b/src/prelude.rs index 4b26c19c..7912b6b0 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,15 +1,15 @@ //! The purpose of this module is to provide reexports of core traits so that they can be then //! glob-imported all at once. -pub use ::DEFAULT_NAME_DOT; -pub use ::DEFAULT_NAME_PNG; -pub use ::core::segment::Segment; -pub use ::core::item::Item; -pub use ::core::item::relation::Relation; -pub use ::core::item::state::ItemState; -pub use ::core::item::state::method::Method; -pub use ::core::item::state::implem::Implem; -pub use ::core::item::state::abstraction::Abstract; -pub use ::core::item::state::abstraction::extend::Trait; -pub use ::core::item::state::abstraction::structure::Struct; -pub use ::core::item::state::abstraction::enumerate::Enum; +pub use core::item::relation::Relation; +pub use core::item::state::abstraction::enumerate::Enum; +pub use core::item::state::abstraction::extend::Trait; +pub use core::item::state::abstraction::structure::Struct; +pub use core::item::state::abstraction::Abstract; +pub use core::item::state::implem::Implem; +pub use core::item::state::method::Method; +pub use core::item::state::ItemState; +pub use core::item::Item; +pub use core::segment::Segment; +pub use DEFAULT_NAME_DOT; +pub use DEFAULT_NAME_PNG; diff --git a/tests/aggregation.rs b/tests/aggregation.rs index 14eb5728..ac7ecfef 100644 --- a/tests/aggregation.rs +++ b/tests/aggregation.rs @@ -1,6 +1,10 @@ #![allow(dead_code)] extern crate mml; +use std::fs::File; +use std::io::Write; +use std::path::Path; + struct Amut { b: *mut B, } @@ -9,19 +13,35 @@ struct Aconst { b: *const B, } -struct B { -} +struct B {} #[test] fn test_aggregation() { - assert_eq!( - String::from_utf8(mml::rs2dot("tests/aggregation.rs").unwrap()).unwrap(), - r#"digraph ml { - ndAmut[label="{<<<Structure>>>\nAmut|- b: *mut B}"][shape="record"]; - ndAconst[label="{<<<Structure>>>\nAconst|- b: *const B}"][shape="record"]; + let vec = mml::rs2dot("tests/aggregation.rs").unwrap(); + let target = r#"digraph ml { + ndAmut[label="{<<<Structure>>>\nAmut|- b: * mut B}"][shape="record"]; + ndAconst[label="{<<<Structure>>>\nAconst|- b: * const B}"][shape="record"]; ndB[label="{<<<Structure>>>\nB}"][shape="record"]; ndB -> ndAmut[label=""][arrowhead="odiamond"]; ndB -> ndAconst[label=""][arrowhead="odiamond"]; } -"#); +"#; + + let path = "target/test/aggregation"; + if let Err(_) = std::fs::create_dir_all(path) { + if !Path::new(path).is_dir() { + panic!("Could not create directory!") + } + } + + File::create("target/test/aggregation/aggregation.svg") + .unwrap() + .write(&mml::content2svg(vec.clone()).unwrap()) + .unwrap(); + File::create("target/test/aggregation/aggregation_target.svg") + .unwrap() + .write(&mml::content2svg(target.to_string().into_bytes()).unwrap()) + .unwrap(); + + assert_eq!(String::from_utf8(vec).unwrap(), target); } diff --git a/tests/association.rs b/tests/association.rs index 7f69b13d..d4a23d00 100644 --- a/tests/association.rs +++ b/tests/association.rs @@ -1,46 +1,61 @@ #![allow(dead_code)] extern crate mml; -struct A { -} +use std::fs::File; +use std::io::Write; +use std::path::Path; + +struct A {} impl A { fn b() -> B { - B { - } + B {} } } -struct Ab { -} +struct Ab {} impl Ab { fn b() -> B { - B { - } + B {} } } -struct B { -} +struct B {} impl B { fn a() -> Ab { - Ab { - } + Ab {} } } #[test] fn test_association() { - assert_eq!( - String::from_utf8(mml::rs2dot("tests/association.rs").unwrap()).unwrap(), - r#"digraph ml { + let vec = mml::rs2dot("tests/association.rs").unwrap(); + let target = r#"digraph ml { ndA[label="{<<<Structure>>>\nA|- b() -> B}"][shape="record"]; ndAb[label="{<<<Structure>>>\nAb|- b() -> B}"][shape="record"]; ndB[label="{<<<Structure>>>\nB|- a() -> Ab}"][shape="record"]; ndAb -> ndB[label=""][arrowhead="none"]; ndB -> ndA[label=""][arrowhead="vee"]; } -"#); +"#; + + let path = "target/test/association"; + if let Err(_) = std::fs::create_dir_all(path) { + if !Path::new(path).is_dir() { + panic!("Could not create directory!") + } + } + + File::create("target/test/association/association.svg") + .unwrap() + .write(&mml::content2svg(vec.clone()).unwrap()) + .unwrap(); + File::create("target/test/association/association_target.svg") + .unwrap() + .write(&mml::content2svg(target.to_string().into_bytes()).unwrap()) + .unwrap(); + + assert_eq!(String::from_utf8(vec).unwrap(), target); } diff --git a/tests/composition.rs b/tests/composition.rs index e3873959..2cee4add 100644 --- a/tests/composition.rs +++ b/tests/composition.rs @@ -2,21 +2,41 @@ extern crate mml; +use std::fs::File; +use std::io::Write; +use std::path::Path; + struct A { b: B, } -struct B { -} +struct B {} #[test] fn test_composition() { - assert_eq!( - String::from_utf8(mml::rs2dot("tests/composition.rs").unwrap()).unwrap(), - r#"digraph ml { + let vec = mml::rs2dot("tests/composition.rs").unwrap(); + let target = r#"digraph ml { ndA[label="{<<<Structure>>>\nA|- b: B}"][shape="record"]; ndB[label="{<<<Structure>>>\nB}"][shape="record"]; ndB -> ndA[label=""][arrowhead="diamond"]; } -"#); +"#; + + let path = "target/test/composition/"; + if let Err(_) = std::fs::create_dir_all(path) { + if !Path::new(path).is_dir() { + panic!("Could not create directory!") + } + } + + File::create("target/test/composition/composition.svg") + .unwrap() + .write(&mml::content2svg(vec.clone()).unwrap()) + .unwrap(); + File::create("target/test/composition/composition_target.svg") + .unwrap() + .write(&mml::content2svg(target.to_string().into_bytes()).unwrap()) + .unwrap(); + + assert_eq!(String::from_utf8(vec).unwrap(), target); } diff --git a/tests/dependency.rs b/tests/dependency.rs index c74620e9..4c3e140b 100644 --- a/tests/dependency.rs +++ b/tests/dependency.rs @@ -1,25 +1,43 @@ #![allow(dead_code, unused_variables)] extern crate mml; -struct A { -} +use std::fs::File; +use std::io::Write; +use std::path::Path; + +struct A {} impl A { - fn b(b: &B) { - } + fn b(b: &B) {} } -struct B { -} +struct B {} #[test] fn test_dependency() { - assert_eq!( - String::from_utf8(mml::rs2dot("tests/dependency.rs").unwrap()).unwrap(), - r#"digraph ml { - ndA[label="{<<<Structure>>>\nA|- b(b: &B)}"][shape="record"]; + let vec = mml::rs2dot("tests/dependency.rs").unwrap(); + let target = r#"digraph ml { + ndA[label="{<<<Structure>>>\nA|- b(b : & B)}"][shape="record"]; ndB[label="{<<<Structure>>>\nB}"][shape="record"]; ndB -> ndA[label=""][style="dashed"][arrowhead="vee"]; } -"#); +"#; + + let path = "target/test/dependency"; + if let Err(_) = std::fs::create_dir_all(path) { + if !Path::new(path).is_dir() { + panic!("Could not create directory!") + } + } + + File::create("target/test/dependency/dependency.svg") + .unwrap() + .write(&mml::content2svg(vec.clone()).unwrap()) + .unwrap(); + File::create("target/test/dependency/dependency_target.svg") + .unwrap() + .write(&mml::content2svg(target.to_string().into_bytes()).unwrap()) + .unwrap(); + + assert_eq!(String::from_utf8(vec).unwrap(), target); } diff --git a/tests/realization.rs b/tests/realization.rs index 4c58ee0c..9a662c04 100644 --- a/tests/realization.rs +++ b/tests/realization.rs @@ -2,31 +2,44 @@ extern crate mml; use std::fmt::Debug; +use std::fs::File; +use std::io::Write; +use std::path::Path; #[derive(Debug)] -struct A where T: Debug { +struct A +where + T: Debug, +{ a: T, } -impl A where T: Debug { +impl A +where + T: Debug, +{ fn a(a: T) -> Self { - A { - a: a, - } + A { a } } } -impl B for A where T: Debug { +impl B for A +where + T: Debug, +{ fn a(&self) -> Option { None } } -trait B : Debug where T: Debug { +trait B: Debug +where + T: Debug, +{ fn a(&self) -> Option; } -impl B { +impl dyn B { fn a(&self) -> Option { None } @@ -34,11 +47,29 @@ impl B { #[test] fn test_realization() { - assert_eq!(String::from_utf8(mml::rs2dot("tests/realization.rs").unwrap()).unwrap(), - r#"digraph ml { - ndA[label="{<<<Structure>>>\nA|- a: T|- a(a: T) -> Self}"][shape="record"]; - ndB[label="{<<<Trait>>>\nB|a(&Self) -> Option<T>|- a(&self) -> Option<T>}"][shape="record"]; + let vec = mml::rs2dot("tests/realization.rs").unwrap(); + let target = r#"digraph ml { + ndA[label="{<<<Structure>>>\nA|- a: T|- a(a : T) -> Self}"][shape="record"]; + ndB[label="{<<<Trait>>>\nB|a(& Self) -> Option < T >|- a(& self) -> Option < T >}"][shape="record"]; ndB -> ndA[label=""][style="dashed"][arrowhead="onormal"]; } -"#); +"#; + + let path = "target/test/realization"; + if let Err(_) = std::fs::create_dir_all(path) { + if !Path::new(path).is_dir() { + panic!("Could not create directory!") + } + } + + File::create("target/test/realization/realization.svg") + .unwrap() + .write(&mml::content2svg(vec.clone()).unwrap()) + .unwrap(); + File::create("target/test/realization/realization_target.svg") + .unwrap() + .write(&mml::content2svg(target.to_string().into_bytes()).unwrap()) + .unwrap(); + + assert_eq!(String::from_utf8(vec).unwrap(), target); }