use entity::Entity;
use failure::{err_msg, Fallible};
use glob::glob;
use language_type::LanguageType;
use std::{
cell::{Ref, RefCell, RefMut}, fmt::{Display, Formatter, Result}, marker::PhantomData,
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(&mut self, description: &str) {
self.description = description
.split('\n')
.map(|desc| {
String::from(
desc.trim_left()
.trim_left_matches('*')
.trim_left_matches('/')
.trim_left(),
)
})
.filter(|ref desc| !desc.is_empty() && desc.as_str() != "**")
.map(|desc| {
if desc.chars().next() == Some('#') {
desc.replace(" ", "")
} else {
desc
}
})
.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: 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: Vec::new(),
description: None,
}
}
pub fn set_return_type(&mut self, ftype: &str) -> Fallible<()> {
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(|| err_msg("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);
}
}
pub fn set_arguments(&mut self, arguments: &[Argument]) {
self.arguments = arguments.into();
}
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Enum {
pub name: String,
pub etype: Option<String>,
pub arguments: Vec<Argument>,
}
impl Enum {
pub fn new<S: Into<String>>(name: S) -> Self {
Self {
name: name.into(),
etype: None,
arguments: Vec::new(),
}
}
pub fn set_arguments(&mut self, arguments: &[Argument]) {
self.arguments = arguments.into();
}
}
#[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()
}
}
impl<T> Display for ProjectFile<T>
where
T: LanguageType,
{
fn fmt(&self, f: &mut Formatter) -> Result {
if let Some(path) = self.path.to_str() {
return write!(f, "{}", path);
}
write!(f, "Unable to stringify filename")
}
}
#[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]) -> Fallible<()> {
if !project_dir.exists() || !project_dir.is_dir() {
return Err(format_err!(
"The given project dir '{}' does not exist.",
project_dir
.to_str()
.ok_or_else(|| err_msg("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) -> Fallible<()> {
T::extract_entities(&self)
}
}