use std::{cell::RefCell, rc::Rc};
use regex::Regex;
#[derive(Debug, PartialEq, Clone)]
pub enum NodeName {
Literal(String),
Pattern(String),
}
#[derive(Debug, Clone)]
pub enum Node {
Dir(Rc<RefCell<DirNode>>),
File(Rc<RefCell<FileNode>>),
}
#[derive(Debug, Clone)]
pub struct DirNode {
pub name: NodeName,
pub children: Vec<Node>,
pub required: bool,
pub allow_defined_only: bool,
pub excluded: Vec<Regex>,
}
#[derive(Debug, Clone)]
pub struct FileNode {
pub name: NodeName,
pub required: bool,
}
pub struct ModelBuilder {
current: Node,
parent_stack: Vec<ModelBuilder>,
}
impl ModelBuilder {
pub fn new_dir(name: impl Into<String>) -> Self {
Self {
current: DirNode::new(
NodeName::Literal(name.into()),
vec![],
false,
false,
vec![],
),
parent_stack: vec![],
}
}
pub fn new_dir_pattern(pattern: impl Into<String>) -> Self {
Self {
current: DirNode::new(
NodeName::Pattern(pattern.into()),
vec![],
false,
false,
vec![],
),
parent_stack: vec![],
}
}
pub fn new_file(name: impl Into<String>) -> Self {
Self {
current: FileNode::new(NodeName::Literal(name.into()), false),
parent_stack: vec![],
}
}
pub fn new_file_pattern(pattern: impl Into<String>) -> Self {
Self {
current: FileNode::new(NodeName::Pattern(pattern.into()), false),
parent_stack: vec![],
}
}
pub fn required(self, required: bool) -> Self {
match &self.current {
Node::Dir(dir_rc) => {
dir_rc.borrow_mut().required = required;
}
Node::File(file_rc) => {
file_rc.borrow_mut().required = required;
}
};
self
}
pub fn allow_defined_only(self, allow_defined_only: bool) -> Self {
if let Node::Dir(dir_rc) = &self.current {
dir_rc.borrow_mut().allow_defined_only = allow_defined_only;
} else {
panic!("Cannot set allow_defined_only on a file node");
}
self
}
pub fn exclude_patterns(self, patterns: Vec<impl Into<String>>) -> Self {
if let Node::Dir(dir_rc) = &self.current {
let mut dir = dir_rc.borrow_mut();
for pattern in patterns {
dir.excluded.push(Regex::new(&pattern.into()).unwrap());
}
} else {
panic!("Cannot add excluded patterns to a file node");
}
self
}
pub fn add_file(self, name: impl Into<String>, required: bool) -> Self {
if let Node::Dir(dir_rc) = &self.current {
let file_node = FileNode::new(NodeName::Literal(name.into()), required);
dir_rc.borrow_mut().children.push(file_node);
} else {
panic!("Cannot add a child to a file node");
}
self
}
pub fn add_file_pattern(self, pattern: impl Into<String>, required: bool) -> Self {
if let Node::Dir(dir_rc) = &self.current {
let file_node = FileNode::new(NodeName::Pattern(pattern.into()), required);
dir_rc.borrow_mut().children.push(file_node);
} else {
panic!("Cannot add a child to a file node");
}
self
}
pub fn add_dir(self, name: impl Into<String>, required: bool) -> Self {
if let Node::Dir(dir_rc) = &self.current {
let mut child_builder = Self::new_dir(name);
child_builder = child_builder.required(required);
let child_node = child_builder.current.clone();
dir_rc.borrow_mut().children.push(child_node);
let mut new_builder = child_builder;
new_builder.parent_stack.push(self);
new_builder
} else {
panic!("Cannot add a child to a file node");
}
}
pub fn add_dir_pattern(self, pattern: impl Into<String>, required: bool) -> Self {
if let Node::Dir(dir_rc) = &self.current {
let mut child_builder = Self::new_dir_pattern(pattern);
child_builder = child_builder.required(required);
let child_node = child_builder.current.clone();
dir_rc.borrow_mut().children.push(child_node);
let mut new_builder = child_builder;
new_builder.parent_stack.push(self);
new_builder
} else {
panic!("Cannot add a child to a file node");
}
}
pub fn up(mut self) -> Self {
if !self.parent_stack.is_empty() {
self.parent_stack.pop().expect("Parent stack is empty")
} else {
panic!("Cannot move up from the root builder");
}
}
pub fn build(self) -> Node {
self.current
}
}
impl Clone for ModelBuilder {
fn clone(&self) -> Self {
Self {
current: self.current.clone(),
parent_stack: self.parent_stack.clone(),
}
}
}
impl DirNode {
#[allow(clippy::new_ret_no_self)]
pub fn new(
name: NodeName,
children: Vec<Node>,
required: bool,
allow_defined_only: bool,
excluded: Vec<Regex>,
) -> Node {
Node::Dir(Rc::new(RefCell::new(Self {
name,
children,
required,
allow_defined_only,
excluded,
})))
}
}
impl FileNode {
#[allow(clippy::new_ret_no_self)]
pub fn new(name: NodeName, required: bool) -> Node {
Node::File(Rc::new(RefCell::new(Self { name, required })))
}
}