boltbuild 0.1.0

BoltBuild is a programmable build system.
Documentation
use super::{Task, TaskHandle};
use crate::context::Context;
use crate::node::Node;
use mlua::prelude::LuaError;
use mlua::{AnyUserData, FromLua, UserData, UserDataFields, UserDataMethods, Value};

impl UserData for TaskHandle {
    fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
        fields.add_field_function_get("driver", |_lua, this: AnyUserData| {
            let context = this.user_value::<AnyUserData>()?;
            context.borrow_mut_scoped::<Context, _>(|context| {
                let index = this.borrow::<TaskHandle>()?.0;
                Ok(context.tasks[index].driver.clone())
            })?
        });
        fields.add_field_function_get("inputs", |_lua, this: AnyUserData| {
            let context = this.user_value::<AnyUserData>()?;
            context.borrow_mut_scoped::<Context, _>(|context| {
                let index = this.borrow::<TaskHandle>()?.0;
                Ok(context.tasks[index].inputs.clone())
            })?
        });
        fields.add_field_function_get("outputs", |_lua, this: AnyUserData| {
            let context = this.user_value::<AnyUserData>()?;
            context.borrow_mut_scoped::<Context, _>(|context| {
                let index = this.borrow::<TaskHandle>()?.0;
                Ok(context.tasks[index].outputs.clone())
            })?
        });
        fields.add_field_function_get("generator", |_lua, this: AnyUserData| {
            let context = this.user_value::<AnyUserData>()?;
            context.borrow_mut_scoped::<Context, _>(|context| {
                let index = this.borrow::<TaskHandle>()?.0;
                Ok(context.tasks[index].generator.clone())
            })?
        });
        fields.add_field_function_get("group", |_lua, this: AnyUserData| {
            let context = this.user_value::<AnyUserData>()?;
            context.borrow_mut_scoped::<Context, _>(|context| {
                let index = this.borrow::<TaskHandle>()?.0;
                Ok(context.tasks[index].group.clone())
            })?
        });
        fields.add_field_function_get("env", |_lua, this: AnyUserData| {
            let context = this.user_value::<AnyUserData>()?;
            context.borrow_mut_scoped::<Context, _>(|context| {
                let index = this.borrow::<TaskHandle>()?.0;
                Ok(context.tasks[index].env.clone())
            })?
        });
    }

    fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
        methods.add_function_mut("add_input", |lua, (this, nodes): (AnyUserData, Value)| {
            let context = this.user_value::<AnyUserData>()?;
            context.borrow_mut_scoped::<Context, _>(|context| {
                let index = this.borrow::<TaskHandle>()?.0;

                let nodes = match &nodes {
                    Value::Table(_) => Vec::<Node>::from_lua(nodes.clone(), lua)?,
                    Value::UserData(d) => vec![d.borrow::<Node>()?.clone()],
                    _ => {
                        return Err(LuaError::RuntimeError(
                            "inputs should be a node or a list of nodes".to_string(),
                        ))
                    }
                };

                let mut dependencies = Vec::new();
                for node in &nodes {
                    if let Some(&producer) = context.products.get(node.path()) {
                        dependencies.push((producer, index, format!("dependency on {}", node)));
                    }
                }

                context.add_dependencies(dependencies, None)?;

                let hasher = &mut context.signatures[index];
                for input in &nodes {
                    hasher.update(input.path().to_string_lossy().as_bytes());
                }
                let task = &mut context.tasks[index];
                task.inputs.extend(nodes);

                Ok(())
            })?
        });

        methods.add_function_mut("add_output", |lua, (this, nodes): (AnyUserData, Value)| {
            let context = this.user_value::<AnyUserData>()?;
            context.borrow_mut_scoped::<Context, _>(|context| {
                let index = this.borrow::<TaskHandle>()?.0;

                let nodes = match &nodes {
                    Value::Table(_) => Vec::<Node>::from_lua(nodes.clone(), lua)?,
                    Value::UserData(d) => vec![d.borrow::<Node>()?.clone()],
                    _ => {
                        return Err(LuaError::RuntimeError(
                            "outputs should be a node or a list of nodes".to_string(),
                        ))
                    }
                };

                context.declare_products(&nodes, Vec::new(), index, None)?;

                let hasher = &mut context.signatures[index];
                for output in &nodes {
                    hasher.update(output.path().to_string_lossy().as_bytes());
                }
                let task = &mut context.tasks[index];
                task.outputs.extend(nodes);
                Ok(())
            })?
        });

        methods.add_function_mut(
            "set_run_before",
            |_lua, (this, other): (AnyUserData, AnyUserData)| {
                let context = this.user_value::<AnyUserData>()?;
                context.borrow_mut_scoped::<Context, _>(|context| {
                    let index = this.borrow::<TaskHandle>()?.0;
                    let other = other.borrow::<TaskHandle>()?.0;

                    context.add_dependencies(vec![(index, other, "run before".to_string())], None)
                })?
            },
        );

        methods.add_function_mut(
            "set_run_after",
            |_lua, (this, other): (AnyUserData, AnyUserData)| {
                let context = this.user_value::<AnyUserData>()?;
                context.borrow_mut_scoped::<Context, _>(|context| {
                    let index = this.borrow::<TaskHandle>()?.0;
                    let other = other.borrow::<TaskHandle>()?.0;

                    context.add_dependencies(vec![(other, index, "run after".to_string())], None)
                })?
            },
        );

        methods.add_function(
            "run_command",
            |_lua, (this, command): (AnyUserData, String)| {
                let context = this.user_value::<AnyUserData>()?;
                context.borrow_mut_scoped::<Context, _>(|context| {
                    let index = this.borrow::<TaskHandle>()?.0;
                    Ok(context.tasks[index].run_command(command.as_str(), Vec::new()))
                })?
            },
        );
    }
}

impl UserData for Task {
    fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
        fields.add_field_method_get("driver", |_lua, this| Ok(this.driver.clone()));
        fields.add_field_method_get("inputs", |_lua, this| Ok(this.inputs.clone()));
        fields.add_field_method_get("outputs", |_lua, this| Ok(this.outputs.clone()));
        fields.add_field_method_get("generator", |_lua, this| Ok(this.generator.clone()));
        fields.add_field_method_get("group", |_lua, this| Ok(this.group.clone()));
        fields.add_field_method_get("env", |_lua, this| Ok(this.env.clone()));
    }

    fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
        methods.add_method("run_command", |_lua, this, command: String| {
            Ok(this.run_command(command.as_str(), Vec::new()))
        });
    }
}