use entity::Entity;
use error::*;
use glob::glob;
use language_type::LanguageType;
use std::cell::{Ref, RefCell, RefMut};
use std::marker::PhantomData;
use std::path::PathBuf;
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Description {
pub description: Vec<String>,
}
impl Description {
pub fn new() -> Self {
Self { description: Vec::new() }
}
pub fn set_description(&mut self, description: &str) {
self.description = description
.split('\n')
.map(|fd| {
String::from(
fd.trim_left()
.trim_left_matches('*')
.trim_left_matches('/')
.trim_left(),
)
})
.filter(|ref c| !c.is_empty() && c.as_str() != "**")
.collect();
}
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Argument {
pub name: String,
pub atype: Option<String>,
pub value: Option<String>,
}
impl Argument {
pub fn new<S: Into<String>>(name: S, atype: Option<S>) -> Self {
Argument {
name: name.into(),
atype: atype.map(S::into),
value: None,
}
}
pub fn set_value(&mut self, value: &str) {
self.value = Some(String::from(value));
}
}
#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Function {
pub name: String,
pub return_type: Option<String>,
pub arguments: Option<Vec<Argument>>,
pub description: Option<Description>,
}
impl Function {
pub fn new<S: Into<String>>(name: S) -> Self {
Self {
name: name.into(),
return_type: None,
arguments: None,
description: None,
}
}
pub fn set_return_type(&mut self, ftype: &str) -> Result<()> {
if ftype.is_empty() {
self.return_type = None;
} else {
let ftype_vec: Vec<&str> = ftype.split('(').collect();
self.return_type = Some(String::from(
ftype_vec
.get(0)
.ok_or_else(|| "Function type can not be parsed from signature.")?
.trim_right(),
));
}
Ok(())
}
pub fn set_description(&mut self, description: &str) {
if self.description.is_none() {
self.description = Some(Description::new());
}
if let Some(desc) = &mut self.description {
desc.set_description(description);
}
}
pub fn set_arguments(&mut self, arguments: &[Argument]) {
if arguments.is_empty() {
self.arguments = None;
} else {
self.arguments = Some(arguments.into());
}
}
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Enum {
pub name: String,
pub etype: Option<String>,
pub arguments: Option<Vec<Argument>>,
}
impl Enum {
pub fn new<S: Into<String>>(name: S) -> Self {
Self {
name: name.into(),
etype: None,
arguments: None,
}
}
pub fn set_arguments(&mut self, arguments: &[Argument]) {
if arguments.is_empty() {
self.arguments = None;
} else {
self.arguments = Some(arguments.into());
}
}
pub fn push_argument(&mut self, argument: Argument) {
if self.arguments.is_none() {
self.arguments = Some(Vec::new());
}
if let Some(arguments) = &mut self.arguments {
arguments.push(argument);
}
}
}
#[derive(Default, Clone, Debug)]
pub struct ProjectFile<T> {
pub pf_type: PhantomData<T>,
pub path: PathBuf,
pub entities: RefCell<Vec<Entity>>,
}
impl<T> ProjectFile<T>
where
T: LanguageType,
{
pub fn new<S: Into<PathBuf>>(path: S) -> Self {
ProjectFile {
pf_type: PhantomData,
path: path.into(),
entities: RefCell::new(Vec::new()),
}
}
pub fn entities(&self) -> Ref<Vec<Entity>> {
self.entities.borrow()
}
pub fn entities_mut(&self) -> RefMut<Vec<Entity>> {
self.entities.borrow_mut()
}
pub fn add_entity(&self, entity: Entity) {
self.entities_mut().push(entity);
}
}
#[derive(Default, Debug)]
pub struct Analysis<T>
where
T: LanguageType,
{
pub file_types: &'static [&'static str],
pub project_files: RefCell<Vec<ProjectFile<T>>>,
}
impl<T> Analysis<T>
where
T: LanguageType,
{
pub fn new() -> Self {
Analysis {
file_types: T::file_types(),
project_files: RefCell::new(Vec::new()),
}
}
pub fn project_files(&self) -> Ref<Vec<ProjectFile<T>>> {
self.project_files.borrow()
}
pub fn project_files_mut(&self) -> RefMut<Vec<ProjectFile<T>>> {
self.project_files.borrow_mut()
}
pub fn collect_sources(&self, project_dir: &PathBuf, search_dirs: &[String]) -> Result<()> {
if !project_dir.exists() || !project_dir.is_dir() {
return Err(Error::from(format!(
"The given project dir '{}' does not exist.",
project_dir.to_str().ok_or_else(
|| "Unable to stringify project dir path.",
)?
)));
}
for src_dir in search_dirs {
for ext in self.file_types {
for entry in glob(
project_dir
.join(src_dir)
.join("**")
.join(String::from("*.") + ext)
.to_str()
.unwrap_or("."),
)?
{
self.project_files_mut().push(ProjectFile::new(entry?));
}
}
}
Ok(())
}
pub fn extract_entities(&self) -> Result<()> {
T::extract_entities(&self)
}
}