boltbuild 0.1.0

BoltBuild is a programmable build system.
Documentation
use crate::environment::EnvironmentValue;
use serde::{Deserialize, Serialize};
use std::ffi::OsStr;
use std::fmt::Display;
use std::fs;
use std::path::{Component, Path, PathBuf};

mod lua_binding;

#[derive(Clone, Serialize, Deserialize, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub(crate) struct Node {
    path: PathBuf,
}

impl Node {
    pub(crate) fn from(path: &Path) -> Node {
        Node {
            path: normalize_path(path),
        }
    }

    fn new() -> Node {
        Node {
            path: PathBuf::new(),
        }
    }

    pub(crate) fn path(&self) -> &PathBuf {
        &self.path
    }

    pub(crate) fn abs_path(&self) -> &Path {
        self.path.as_path()
    }

    pub(crate) fn make_node(&self, path: &PathBuf) -> Node {
        let mut result = Node {
            path: self.path.clone(),
        };
        result.path.push(path);
        result.path = normalize_path(&result.path);
        result
    }

    pub(crate) fn parent(&self) -> Node {
        let mut result = Node {
            path: self.path.clone(),
        };
        result.path.pop();
        result
    }

    pub(crate) fn is_dir(&self) -> bool {
        self.path.is_dir()
    }

    pub(crate) fn is_file(&self) -> bool {
        self.path.is_file()
    }

    pub(crate) fn read_link(&self) -> Self {
        let mut rel_path = self.path.clone();
        while let Ok(path) = fs::read_link(&rel_path) {
            rel_path.pop();
            rel_path.push(path);
        }
        Node::from(&rel_path)
    }

    pub(crate) fn canonicalize(&self) -> Self {
        let mut result = Self::new();
        for c in self.path.iter() {
            result.path.push(c);
            result = result.read_link();
        }
        result
    }

    pub(crate) fn mkdir(&self) -> std::io::Result<()> {
        fs::create_dir_all(&self.path)
    }

    pub(crate) fn delete(&self) -> std::io::Result<()> {
        fs::remove_file(&self.path)
    }

    pub(crate) fn name(&self) -> String {
        self.path
            .file_name()
            .unwrap_or(OsStr::new(""))
            .to_string_lossy()
            .to_string()
    }

    pub(crate) fn change_ext(&mut self, extension: &str) {
        self.path.set_extension(extension);
    }
}

impl Display for Node {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.path.to_string_lossy())
    }
}

fn normalize_path(path: &Path) -> PathBuf {
    let mut components = path.components().peekable();
    let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
        components.next();
        PathBuf::from(c.as_os_str())
    } else {
        PathBuf::new()
    };

    for component in components {
        match component {
            Component::Prefix(..) => unreachable!(),
            Component::RootDir => {
                ret.push(component.as_os_str());
            }
            Component::CurDir => {}
            Component::ParentDir => {
                ret.pop();
            }
            Component::Normal(c) => {
                ret.push(c);
            }
        }
    }
    ret
}

impl From<&Node> for EnvironmentValue {
    fn from(value: &Node) -> Self {
        EnvironmentValue::Node(value.clone())
    }
}

impl From<&Vec<Node>> for EnvironmentValue {
    fn from(value: &Vec<Node>) -> Self {
        EnvironmentValue::Vec(
            value
                .iter()
                .map(|x| x.into())
                .collect::<Vec<EnvironmentValue>>(),
        )
    }
}