1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
use crate::ast::ASTNode;
use crate::ast::SchemaFile;
use std::convert::TryFrom;
use std::ffi::OsStr;
use std::path::Path;
use std::path::PathBuf;
use walkdir::WalkDir;

#[derive(Debug, Eq, PartialEq, Default)]
pub struct ASTBuilder {
    directories: Vec<PathBuf>,
}

#[allow(dead_code)]
impl ASTBuilder {
    pub fn build(self) -> AST {
        self.directories
            .into_iter()
            .map(|d| {
                WalkDir::new(&d)
                    .follow_links(true)
                    .into_iter()
                    .filter_map(|e| e.ok())
                    .map(|e| {
                        e.path()
                            .to_str()
                            .map(|s| s.to_string())
                            .ok_or("Can't tranform into &str")
                    })
                    .filter_map(Result::ok)
                    .map(PathBuf::from)
                    .filter(|p| p.extension() == Some(OsStr::new("schema")))
                    .map(|p| (SchemaFile::try_from(p.clone()), p))
                    .map(|(schemas, buf)| match schemas {
                        Ok(data) => Ok(data),
                        Err(e) => {
                            eprintln!("{}: {:?}", e, buf);
                            Err(())
                        }
                    })
                    .filter_map(Result::ok)
                    .collect::<Vec<_>>()
            })
            .flatten()
            .fold(AST::default(), |acc, val| {
                acc.merge_schema(&val, &val.package_name)
            })
    }

    pub fn with_directory<P: AsRef<Path>>(mut self, path: P) -> Self {
        let path = path.as_ref().to_path_buf();
        self.directories.push(path);
        self
    }
}

#[derive(Debug, Eq, PartialEq, Default)]
pub struct AST {
    pub inner: Vec<ASTNode>,
}

impl AST {
    pub fn generate<P: AsRef<Path> + Clone>(&self, path: P) -> Result<(), std::io::Error> {
        let path_clone = path.clone();
        if path_clone.as_ref().exists() {
            std::fs::remove_dir_all(path)?;
        }
        for node in &self.inner {
            node.generate_node(path_clone.clone())?;
        }
        ASTNode::generate_mod_rs(&self.inner, path_clone)
    }

    fn merge_schema<T: AsRef<str>>(self, schema: &SchemaFile, path: &[T]) -> Self {
        if !path.is_empty() {
            let is_path_present = self
                .inner
                .iter()
                .map(|n| match n {
                    ASTNode::SchemaNode(_) => panic!("SchemaFile shouldn't be at the root of AST"),
                    ASTNode::PackageNode(pn) => pn.name == *path[0].as_ref(),
                })
                .fold(false, |acc, val| acc | val);
            if is_path_present {
                AST {
                    inner: self
                        .inner
                        .into_iter()
                        .map(|n| n.merge_schema(schema, path))
                        .collect::<Vec<ASTNode>>(),
                }
            } else {
                let mut inner = self.inner;
                inner.push(ASTNode::package_schema(schema, path));
                AST { inner }
            }
        } else {
            panic!("SchemaFile does not have a package name");
        }
    }
}