boltbuild 0.1.0

BoltBuild is a programmable build system.
Documentation
use super::Node;
use mlua::prelude::{LuaError, LuaString};
use mlua::{Error, FromLua, Lua, MetaMethod, UserData, UserDataMethods, UserDataRef, Value};
use std::io::{Read, Write};
use std::ops::Deref;
use std::path::PathBuf;

impl UserData for Node {
    fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
        fields.add_field_method_get("parent", |_lua, this| {
            let mut result = this.clone();
            result.path.pop();
            Ok(result)
        })
    }

    fn add_methods<'lua, M: UserDataMethods<Self>>(methods: &mut M) {
        methods.add_meta_method(MetaMethod::ToString, |_lua, this: &Node, ()| {
            Ok(this.abs_path().to_string_lossy().to_string())
        });
        methods.add_method("make_node", |_lua, this, lua_path: String| {
            Ok(this.make_node(&PathBuf::from(lua_path)))
        });
        methods.add_method("abs_path", |_lua, this, ()| {
            Ok(this.path.to_string_lossy().to_string())
        });
        methods.add_method("extension", |_lua, this, ()| {
            if let Some(ext) = this.path.extension() {
                Ok(ext.to_string_lossy().to_string())
            } else {
                Ok("".to_string())
            }
        });
        methods.add_method("change_ext", |_lua, this, new_ext: String| {
            let mut node = this.clone();
            node.change_ext(new_ext.as_str());
            Ok(node)
        });
        methods.add_method("path_from", |_, this: &Node, base: UserDataRef<Node>| {
            use std::path::Component;
            assert!(this.path.is_absolute());
            assert!(base.path.is_absolute());

            let mut ita = this.path.components();
            let mut itb = base.path.components();
            let mut comps: Vec<Component> = vec![];
            loop {
                match (ita.next(), itb.next()) {
                    (None, None) => break,
                    (Some(a), None) => {
                        comps.push(a);
                        comps.extend(ita.by_ref());
                        break;
                    }
                    (None, _) => comps.push(Component::ParentDir),
                    (Some(a), Some(b)) if comps.is_empty() && a == b => (),
                    (Some(a), Some(Component::CurDir)) => comps.push(a),
                    (Some(_), Some(Component::ParentDir)) => {
                        return Err(Error::RuntimeError(String::from(
                            "can't get relative path from {} to {}",
                        )))
                    }
                    (Some(a), Some(_)) => {
                        comps.push(Component::ParentDir);
                        for _ in itb {
                            comps.push(Component::ParentDir);
                        }
                        comps.push(a);
                        comps.extend(ita.by_ref());
                        break;
                    }
                }
            }

            Ok(String::from(
                PathBuf::from_iter(comps.iter().map(|c| c.as_os_str())).to_string_lossy(),
            ))
        });
        methods.add_method("name", |_lua, this: &Node, ()| {
            Ok(this
                .path
                .file_name()
                .map(|x| x.to_string_lossy().to_string()))
        });
        methods.add_method("basename", |_lua, this: &Node, ()| {
            Ok(this
                .path
                .with_extension("")
                .file_name()
                .map(|x| x.to_string_lossy().to_string()))
        });
        methods.add_method("is_dir", |_lua, this: &Node, ()| Ok(this.is_dir()));
        methods.add_method("is_file", |_lua, this: &Node, ()| Ok(this.is_file()));
        methods.add_method("read_link", |_lua, this: &Node, ()| Ok(this.read_link()));
        methods.add_method("canonicalize", |_lua, this: &Node, ()| {
            Ok(this.canonicalize())
        });
        methods.add_method("delete", |_lua, this: &Node, ()| {
            this.delete()
                .map_err(|x| LuaError::RuntimeError(x.to_string()))
        });
        methods.add_method("try_delete", |_lua, this: &Node, ()| {
            this.delete().or(Ok(()))
        });
        methods.add_method("mkdir", |_lua, this: &Node, ()| {
            this.mkdir()
                .map_err(|x| LuaError::RuntimeError(x.to_string()))
        });
        methods.add_method("read", |_lua, this: &Node, ()| {
            let mut content = Vec::new();
            std::fs::File::create(&this.path)?.read_to_end(&mut content)?;
            _lua.create_string(content)
        });
        methods.add_method("write", |_lua, this: &Node, content: LuaString| {
            std::fs::File::create(&this.path)?.write_all(content.as_bytes().deref())?;
            Ok(())
        });
    }
}

impl FromLua for Node {
    fn from_lua(value: Value, _lua: &Lua) -> mlua::Result<Self> {
        match value {
            Value::UserData(userdata) => Ok(userdata.borrow::<Node>()?.clone()),
            _ => Err(Error::FromLuaConversionError {
                from: value.type_name(),
                to: "Node".to_string(),
                message: None,
            }),
        }
    }
}