<commit_before>use runner::Runner;
use glob;
use regex::{Captures, Regex};
use runtime::lua;
use runtime::{Runtime, RuntimeResult};
use std::env;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use term;

/// Expands global and environment variables inside a given string.
pub fn expand_string(input: &str, runtime: Runtime) -> String {
    // Replace anything that looks like a variable expansion.
    let pattern = Regex::new(r"\$(\w+)").unwrap();

    pattern.replace_all(input, |caps: &Captures| {
        let name = caps.at(1).unwrap_or("");

        // Attempt to match a variable definition, or fallback to the original string.
        if let Ok(value) = env::var(name) {
            value.to_string()
        } else {
            let value = if runtime.state().get_global(name) != lua::Type::Nil {
                runtime.state().check_string(-1).to_string()
            } else {
                caps.at(0).unwrap_or("").to_string()
            };

            runtime.state().pop(1);
            value
        }
    })
}

/// Sets the current working directory.
fn change_dir(runtime: Runtime) -> RuntimeResult {
    let path = runtime.state().check_string(1).to_string();

    if env::set_current_dir(path).is_err() {
        Err("failed to change directory".into())
    } else {
        Ok(0)
    }
}

/// Defines a new rule.
///
/// # Lua arguments
/// * `pattern: string`      - The name of the task.
/// * `description: string`  - A description of the task. (Optional)
/// * `dependencies: table`  - A list of task names that the rule depends on. (Optional)
/// * `func: function`       - A function that should be called when the rule is run. (Optional)
fn create_rule(mut runtime: Runtime) -> RuntimeResult {
    let runner: &mut Runner = runtime.reg_get("runner").unwrap();

    let pattern = runtime.state().check_string(1).to_string();
    let desc = if runtime.state().is_string(2) {
        Some(runtime.state().check_string(2).to_string())
    } else {
        None
    };

    // Read the dependency table.
    runtime.state().check_type(3, lua::Type::Table);
    let deps: Vec<String> = runtime.iter(3)
        .map(|item| item.value().unwrap())
        .collect();

    // Third argument is the task function.
    let func = runtime.state().type_of(4).and_then(|t| {
        if t == lua::Type::Function {
            // Get a portable reference to the task function.
            Some(runtime.state().reference(lua::REGISTRYINDEX))
        } else {
            None
        }
    });

    runner.create_rule(pattern, desc, deps, func);
    Ok(0)
}

/// Creates a new task.
///
/// # Lua arguments
/// * `name: string`         - The name of the task.
/// * `description: string`  - A description of the task. (Optional)
/// * `dependencies: table`  - A list of task names that the task depends on. (Optional)
/// * `func: function`       - A function that should be called when the task is run.
fn create_task(mut runtime: Runtime) -> RuntimeResult {
    let runner: &mut Runner = runtime.reg_get("runner").unwrap();

    let name = runtime.state().check_string(1).to_string();
    let desc = if runtime.state().is_string(2) {
        Some(runtime.state().check_string(2).to_string())
    } else {
        None
    };

    // Read the dependency table.
    runtime.state().check_type(3, lua::Type::Table);
    let deps: Vec<String> = runtime.iter(3)
        .map(|item| item.value().unwrap())
        .collect();

    // Get a portable reference to the task function.
    runtime.state().check_type(4, lua::Type::Function);
    let func = runtime.state().reference(lua::REGISTRYINDEX);

    runner.create_task(name, desc, deps, func);
    Ok(0)
}

/// Gets the current working directory.
fn current_dir(runtime: Runtime) -> RuntimeResult {
    Ok(env::current_dir()
        .map(|dir| {
            runtime.state().push(dir.to_str());
            1
        })
        .unwrap_or(0))
}

/// Executes a shell command with a given list of arguments.
fn execute(runtime: Runtime) -> RuntimeResult {
    //let runner = Runner::from_runtime(runtime.clone());

    // Create a command for the given program name.
    let mut command = Command::new(runtime.state().check_string(1));

    // Set the current directory.
    if let Ok(dir) = env::current_dir() {
        command.current_dir(dir);
    }

    // For each other parameter given, add it as a shell argument.
    for i in 2..runtime.state().get_top()+1 {
        // Expand each argument as we go.
        command.arg(expand_string(runtime.state().check_string(i), runtime.clone()));
    }

    // If we're not inside a task, just inherit standard output streams.
    command.stdout(Stdio::inherit());
    command.stderr(Stdio::inherit());

    // Run the command, piping output to stdout and returning the exit code.
    command.output().map_err(|e| {
        format!("failed to execute process: {}", e).into()
    }).and_then(|output| {
        runtime.state().push_number(output.status.code().unwrap_or(1) as f64);
        Ok(1)
    })
}

/// Expands global and environment variables inside a given string.
fn expand(runtime: Runtime) -> RuntimeResult {
    // Get the input string.
    let input = runtime.state().check_string(1).to_string();

    // Expand the string.
    let result = expand_string(&input, runtime.clone());

    // Return the result.
    runtime.state().push_string(&result);
    Ok(1)
}

/// Exports an environment variable.
///
/// # Lua arguments
/// * `key: string` - The variable name.
/// * `value: string` - The value to set.
fn export(runtime: Runtime) -> RuntimeResult {
    let key = runtime.state().check_string(1).to_string();
    let value = runtime.state().check_string(2).to_string();
    let expanded = expand_string(&value, runtime.clone());

    env::set_var(key, expanded);
    Ok(0)
}

/// Searches for paths matching a pattern.
///
/// # Lua arguments
/// * `pattern: string` - The glob pattern to match.
fn glob(mut runtime: Runtime) -> RuntimeResult {
    // Get the pattern as the first argument.
    let pattern = runtime.state().check_string(1).to_string();

    // Make the pattern absolute if it isn't already.
    let mut full_path = PathBuf::from(pattern);
    if full_path.is_relative() {
        let path = full_path;
        full_path = env::current_dir().unwrap();
        full_path.push(&path);
    }

    // Create a table of values to return.
    runtime.state().new_table();

    // Get an iterator for the glob result and return a Lua iterator.
    if let Ok(mut iter) = glob::glob(&full_path.to_str().unwrap()) {
        runtime.push_closure(Box::new(move |runtime: Runtime| {
            loop {
                match iter.next() {
                    Some(Ok(path)) => {
                        // Turn the path into a string.
                        if let Some(path) = path.to_str() {
                            // Push the path onto the return value list.
                            runtime.state().push(path);
                            return Ok(1);
                        } else {
                            warn!("bad characters in path from glob");
                        }
                    }
                    Some(Err(_)) => {
                        // if the path matched but was unreadable,
                        // thereby preventing its contents from matching
                        warn!("unreadable path in glob");
                        // just continue the loop until we find a path we care about
                    }
                    None => {
                        trace!("reached end of iterator");
                        runtime.state().push_nil();
                        return Ok(1);
                    }
                }
            }
        }));
        Ok(1)
    } else {
        warn!("invalid glob pattern");
        Ok(0)
    }
}

/// Parses an input table of options and merges it with a table of default values.
fn options(runtime: Runtime) -> RuntimeResult {
    // If the input table is nil, just use the defaults table as the result.
    if runtime.state().is_nil(1) {
        runtime.state().push_value(2);
    } else {
        // Copy the input table
        runtime.state().push_value(1);
        // Create a table to be used as the input's metatable
        runtime.state().new_table();
        // Set __index in the metatable to be the defaults table
        runtime.state().push("__index");
        runtime.state().push_value(2);
        runtime.state().set_table(-3);
        runtime.state().set_metatable(-2);
    }

    Ok(1)
}

/// Prints text to the console output.
///
/// # Lua arguments
/// * `str: string` - The string to print.
fn print(runtime: Runtime) -> RuntimeResult {
    let runner = Runner::from_runtime(runtime.clone());
    let string = runtime.state().check_string(1).to_string();
    let string = expand_string(&string, runtime.clone());
    let mut out = term::stdout().unwrap();

    if !runner.stack.is_empty() {
        for line in string.lines() {
            let name = runner.stack.front().unwrap();
            out.fg(term::color::GREEN).unwrap();
            write!(out, "{:9} ", format!("[{}]", name)).unwrap();
            out.reset().unwrap();

            writeln!(out, "{}", line).unwrap();
        }
    } else {
        writeln!(out, "{}", string).unwrap();
    }

    Ok(0)
}

/// Sets the default task.
///
/// # Lua arguments
/// * `name: string` - The name of the task to set as default.
fn set_default_task(mut runtime: Runtime) -> RuntimeResult {
    let runner: &mut Runner = runtime.reg_get("runner").unwrap();

    // Get the task name as the first argument.
    let name = runtime.state().check_string(1).to_string();

    // Set the default task to the given name.
    runner.default_task = Some(name);

    Ok(0)
}

/// Returns the current version of Rote as a string.
fn version(runtime: Runtime) -> RuntimeResult {
    runtime.state().push_string(::ROTE_VERSION);
    Ok(1)
}

/// Makes the standard Rote module functions available in the runtime.
pub fn open_lib(mut runtime: Runtime) {
    // Load the module functions.
    runtime.register_lib(&[
        ("change_dir", change_dir),
        ("create_rule", create_rule),
        ("create_task", create_task),
        ("current_dir", current_dir),
        ("execute", execute),
        ("expand", expand),
        ("export", export),
        ("glob", glob),
        ("options", options),
        ("print", print),
        ("set_default_task", set_default_task),
        ("version", version),
    ]);
    runtime.state().set_global("rote");

    // Define some global aliases.
    runtime.register_fn("exec", execute);
    runtime.register_fn("export", export);
    runtime.register_fn("glob", glob);
    runtime.register_fn("print", print);
}
<commit_msg>Handle result<commit_after>use runner::Runner;
use glob;
use regex::{Captures, Regex};
use runtime::lua;
use runtime::{Runtime, RuntimeResult};
use std::env;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use term;

/// Expands global and environment variables inside a given string.
pub fn expand_string(input: &str, runtime: Runtime) -> String {
    // Replace anything that looks like a variable expansion.
    let pattern = Regex::new(r"\$(\w+)").unwrap();

    pattern.replace_all(input, |caps: &Captures| {
        let name = caps.at(1).unwrap_or("");

        // Attempt to match a variable definition, or fallback to the original string.
        if let Ok(value) = env::var(name) {
            value.to_string()
        } else {
            let value = if runtime.state().get_global(name) != lua::Type::Nil {
                runtime.state().check_string(-1).to_string()
            } else {
                caps.at(0).unwrap_or("").to_string()
            };

            runtime.state().pop(1);
            value
        }
    })
}

/// Sets the current working directory.
fn change_dir(runtime: Runtime) -> RuntimeResult {
    let path = runtime.state().check_string(1).to_string();

    if env::set_current_dir(path).is_err() {
        Err("failed to change directory".into())
    } else {
        Ok(0)
    }
}

/// Defines a new rule.
///
/// # Lua arguments
/// * `pattern: string`      - The name of the task.
/// * `description: string`  - A description of the task. (Optional)
/// * `dependencies: table`  - A list of task names that the rule depends on. (Optional)
/// * `func: function`       - A function that should be called when the rule is run. (Optional)
fn create_rule(mut runtime: Runtime) -> RuntimeResult {
    let runner: &mut Runner = runtime.reg_get("runner").unwrap();

    let pattern = runtime.state().check_string(1).to_string();
    let desc = if runtime.state().is_string(2) {
        Some(runtime.state().check_string(2).to_string())
    } else {
        None
    };

    // Read the dependency table.
    runtime.state().check_type(3, lua::Type::Table);
    let deps: Vec<String> = runtime.iter(3)
        .map(|item| item.value().unwrap())
        .collect();

    // Third argument is the task function.
    let func = runtime.state().type_of(4).and_then(|t| {
        if t == lua::Type::Function {
            // Get a portable reference to the task function.
            Some(runtime.state().reference(lua::REGISTRYINDEX))
        } else {
            None
        }
    });

    runner.create_rule(pattern, desc, deps, func);
    Ok(0)
}

/// Creates a new task.
///
/// # Lua arguments
/// * `name: string`         - The name of the task.
/// * `description: string`  - A description of the task. (Optional)
/// * `dependencies: table`  - A list of task names that the task depends on. (Optional)
/// * `func: function`       - A function that should be called when the task is run.
fn create_task(mut runtime: Runtime) -> RuntimeResult {
    let runner: &mut Runner = runtime.reg_get("runner").unwrap();

    let name = runtime.state().check_string(1).to_string();
    let desc = if runtime.state().is_string(2) {
        Some(runtime.state().check_string(2).to_string())
    } else {
        None
    };

    // Read the dependency table.
    runtime.state().check_type(3, lua::Type::Table);
    let deps: Vec<String> = runtime.iter(3)
        .map(|item| item.value().unwrap())
        .collect();

    // Get a portable reference to the task function.
    runtime.state().check_type(4, lua::Type::Function);
    let func = runtime.state().reference(lua::REGISTRYINDEX);

    runner.create_task(name, desc, deps, func);
    Ok(0)
}

/// Gets the current working directory.
fn current_dir(runtime: Runtime) -> RuntimeResult {
    Ok(env::current_dir()
        .map(|dir| {
            runtime.state().push(dir.to_str());
            1
        })
        .unwrap_or(0))
}

/// Executes a shell command with a given list of arguments.
fn execute(runtime: Runtime) -> RuntimeResult {
    //let runner = Runner::from_runtime(runtime.clone());

    // Create a command for the given program name.
    let mut command = Command::new(runtime.state().check_string(1));

    // Set the current directory.
    if let Ok(dir) = env::current_dir() {
        command.current_dir(dir);
    }

    // For each other parameter given, add it as a shell argument.
    for i in 2..runtime.state().get_top()+1 {
        // Expand each argument as we go.
        command.arg(expand_string(runtime.state().check_string(i), runtime.clone()));
    }

    // If we're not inside a task, just inherit standard output streams.
    command.stdout(Stdio::inherit());
    command.stderr(Stdio::inherit());

    // Run the command, piping output to stdout and returning the exit code.
    command.output().map_err(|e| {
        format!("failed to execute process: {}", e).into()
    }).and_then(|output| {
        runtime.state().push_number(output.status.code().unwrap_or(1) as f64);
        Ok(1)
    })
}

/// Expands global and environment variables inside a given string.
fn expand(runtime: Runtime) -> RuntimeResult {
    // Get the input string.
    let input = runtime.state().check_string(1).to_string();

    // Expand the string.
    let result = expand_string(&input, runtime.clone());

    // Return the result.
    runtime.state().push_string(&result);
    Ok(1)
}

/// Exports an environment variable.
///
/// # Lua arguments
/// * `key: string` - The variable name.
/// * `value: string` - The value to set.
fn export(runtime: Runtime) -> RuntimeResult {
    let key = runtime.state().check_string(1).to_string();
    let value = runtime.state().check_string(2).to_string();
    let expanded = expand_string(&value, runtime.clone());

    env::set_var(key, expanded);
    Ok(0)
}

/// Searches for paths matching a pattern.
///
/// # Lua arguments
/// * `pattern: string` - The glob pattern to match.
fn glob(mut runtime: Runtime) -> RuntimeResult {
    // Get the pattern as the first argument.
    let pattern = runtime.state().check_string(1).to_string();

    // Make the pattern absolute if it isn't already.
    let mut full_path = PathBuf::from(pattern);
    if full_path.is_relative() {
        let path = full_path;
        full_path = env::current_dir().unwrap();
        full_path.push(&path);
    }

    // Create a table of values to return.
    runtime.state().new_table();

    // Get an iterator for the glob result and return a Lua iterator.
    if let Ok(mut iter) = glob::glob(&full_path.to_str().unwrap()) {
        runtime.push_closure(Box::new(move |runtime: Runtime| {
            loop {
                match iter.next() {
                    Some(Ok(path)) => {
                        // Turn the path into a string.
                        if let Some(path) = path.to_str() {
                            // Push the path onto the return value list.
                            runtime.state().push(path);
                            return Ok(1);
                        } else {
                            warn!("bad characters in path from glob");
                        }
                    }
                    Some(Err(_)) => {
                        // if the path matched but was unreadable,
                        // thereby preventing its contents from matching
                        warn!("unreadable path in glob");
                        // just continue the loop until we find a path we care about
                    }
                    None => {
                        trace!("reached end of iterator");
                        runtime.state().push_nil();
                        return Ok(1);
                    }
                }
            }
        }));
        Ok(1)
    } else {
        warn!("invalid glob pattern");
        Ok(0)
    }
}

/// Parses an input table of options and merges it with a table of default values.
fn options(runtime: Runtime) -> RuntimeResult {
    // If the input table is nil, just use the defaults table as the result.
    if runtime.state().is_nil(1) {
        runtime.state().push_value(2);
    } else {
        // Copy the input table
        runtime.state().push_value(1);
        // Create a table to be used as the input's metatable
        runtime.state().new_table();
        // Set __index in the metatable to be the defaults table
        runtime.state().push("__index");
        runtime.state().push_value(2);
        runtime.state().set_table(-3);
        runtime.state().set_metatable(-2);
    }

    Ok(1)
}

/// Prints text to the console output.
///
/// # Lua arguments
/// * `str: string` - The string to print.
fn print(runtime: Runtime) -> RuntimeResult {
    let runner = Runner::from_runtime(runtime.clone()).unwrap();
    let string = runtime.state().check_string(1).to_string();
    let string = expand_string(&string, runtime.clone());
    let mut out = term::stdout().unwrap();

    if !runner.stack.is_empty() {
        for line in string.lines() {
            let name = runner.stack.front().unwrap();
            out.fg(term::color::GREEN).unwrap();
            write!(out, "{:9} ", format!("[{}]", name)).unwrap();
            out.reset().unwrap();

            writeln!(out, "{}", line).unwrap();
        }
    } else {
        writeln!(out, "{}", string).unwrap();
    }

    Ok(0)
}

/// Sets the default task.
///
/// # Lua arguments
/// * `name: string` - The name of the task to set as default.
fn set_default_task(mut runtime: Runtime) -> RuntimeResult {
    let runner: &mut Runner = runtime.reg_get("runner").unwrap();

    // Get the task name as the first argument.
    let name = runtime.state().check_string(1).to_string();

    // Set the default task to the given name.
    runner.default_task = Some(name);

    Ok(0)
}

/// Returns the current version of Rote as a string.
fn version(runtime: Runtime) -> RuntimeResult {
    runtime.state().push_string(::ROTE_VERSION);
    Ok(1)
}

/// Makes the standard Rote module functions available in the runtime.
pub fn open_lib(mut runtime: Runtime) {
    // Load the module functions.
    runtime.register_lib(&[
        ("change_dir", change_dir),
        ("create_rule", create_rule),
        ("create_task", create_task),
        ("current_dir", current_dir),
        ("execute", execute),
        ("expand", expand),
        ("export", export),
        ("glob", glob),
        ("options", options),
        ("print", print),
        ("set_default_task", set_default_task),
        ("version", version),
    ]);
    runtime.state().set_global("rote");

    // Define some global aliases.
    runtime.register_fn("exec", execute);
    runtime.register_fn("export", export);
    runtime.register_fn("glob", glob);
    runtime.register_fn("print", print);
}
<|endoftext|>


<commit_before>use std::cell::{Ref, RefCell, RefMut};
use std::rc::Rc;

use super::scope::Scope;
use super::typ::{Generic, GenericConstraint, Type, Variable};
use super::{TypeError, TypeResult};

// Extract the variable type within a type.
fn unwrap_variable(typ: &Type) -> &Rc<RefCell<Variable>> {
    match typ {
        Type::Variable(variable) => variable,
        other @ _ => unreachable!("Not a Variable: {:?}", other),
    }
}

/// Unify a variable (mutable) generic with another generic.
pub fn unify_variable_generic_with_generic(
    destination: &Type,
    source: &Generic,
    scope: Scope,
) -> TypeResult<()> {
    let destination = unwrap_variable(destination);

    enum Action {
        AddCallableConstraint(Vec<Type>, Type),
        AddPropertyConstraint(String, Type),
        None,
    }

    for constraint in source.constraints.iter() {
        use Action::*;
        use GenericConstraint::*;

        let action = {
            // Get an immutable borrow while we determine what to do.
            let destination = Ref::map(destination.borrow(), Variable::unwrap_generic);

            match constraint {
                Property(source_property) => {
                    // If the property already exists then unify their types,
                    // otherwise add it to the left side.
                    if let Some(destination_property) =
                        destination.get_property(&source_property.name)
                    {
                        unify(
                            &destination_property.typ,
                            &source_property.typ,
                            scope.clone(),
                        )?;
                        None
                    } else {
                        AddPropertyConstraint(
                            source_property.name.clone(),
                            source_property.typ.clone(),
                        )
                    }
                }
                Callable(source_callable) => {
                    if let Some(destination_callable) = destination.get_callable() {
                        if destination_callable.arguments.len() != source_callable.arguments.len() {
                            return Err(TypeError::ArgumentLengthMismatch {
                                expected: destination_callable.arguments.clone(),
                                got: source_callable.arguments.clone(),
                            });
                        }
                        for (destination_argument, source_argument) in destination_callable
                            .arguments
                            .iter()
                            .zip(source_callable.arguments.iter())
                        {
                            unify(destination_argument, source_argument, scope.clone())?;
                        }
                        unify(
                            &destination_callable.retrn,
                            &source_callable.retrn,
                            scope.clone(),
                        )?;
                        None
                    } else {
                        AddCallableConstraint(
                            source_callable.arguments.clone(),
                            source_callable.retrn.clone(),
                        )
                    }
                }
            }
        };

        let destination = &mut *RefMut::map(destination.borrow_mut(), Variable::unwrap_mut_generic);
        match action {
            AddCallableConstraint(arguments, retrn) => {
                destination.add_callable_constraint(arguments, retrn)
            }
            AddPropertyConstraint(name, typ) => destination.add_property_constraint(name, typ),
            None => (),
        }
    }
    Ok(())
}

pub fn unify(typ1: &Type, typ2: &Type, scope: Scope) -> TypeResult<()> {
    if typ1 == typ2 {
        return Ok(());
    }

    if let Type::Variable(var2) = typ2 {
        if let Variable::Substitute { substitute, .. } = &*var2.borrow() {
            return unify(typ1, substitute, scope);
        }
    }

    if let Type::Variable(var1) = typ1 {
        enum Action {
            // Make `typ1` a substitute for another type.
            Substitute(Type),
            // Used to unify a different type with `typ2` (eg. if `typ1` is a
            // substitute).
            Reunify(Type),
            // Unify both variable generics.
            UnifyGenerics,
        }
        use Action::*;

        let action = match &*var1.borrow() {
            Variable::Generic { .. } => {
                // WARNING: These if-elses rely on implicit returns and
                //   therefore *must* stay exhaustive.
                if let Type::Variable(var2) = typ2 {
                    // If this is a generic and the other type is unbound then
                    // update the other type to be a substitute for this type.
                    if var2.borrow().is_unbound() {
                        *var2.borrow_mut() = Variable::Substitute {
                            scope,
                            substitute: Box::new(typ1.clone()),
                        };
                        return Ok(());
                    // If it's also a variable generic then we can attempt to
                    // union the two sets of generic constraints.
                    } else if var2.borrow().is_generic() {
                        UnifyGenerics
                    } else {
                        return Err(TypeError::TypeMismatch {
                            expected: typ1.clone(),
                            got: typ2.clone(),
                        });
                    }
                } else {
                    return Err(TypeError::TypeMismatch {
                        expected: typ1.clone(),
                        got: typ2.clone(),
                    });
                }
            }
            // If we're a substitute then unify whatever we're substituted with
            // with `typ2`.
            Variable::Substitute { substitute, .. } => Reunify(*substitute.clone()),
            // If we're unbound then inherit whatever the other type is.
            Variable::Unbound { .. } => Substitute(typ2.clone()),
        };

        return match action {
            Substitute(typ) => {
                *var1.borrow_mut() = Variable::Substitute {
                    scope,
                    substitute: Box::new(typ),
                };
                Ok(())
            }
            Reunify(typ) => unify(&typ, typ2, scope),
            UnifyGenerics => {
                let var2 = unwrap_variable(typ2);
                // If both types are variable generics then first merge the
                // right into the left.
                {
                    let generic = Ref::map(var2.borrow(), Variable::unwrap_generic);
                    unify_variable_generic_with_generic(typ1, &generic, scope.clone())?;
                }
                // Then make the right a substitute for the left.
                *var2.borrow_mut() = Variable::Substitute {
                    scope,
                    substitute: Box::new(typ1.clone()),
                };
                Ok(())
            }
        };
    }

    // If the other type is a variable then unify with it as the first term so
    // that we can reuse the logic above.
    if let Type::Variable(_) = &typ2 {
        return unify(typ2, typ1, scope).map_err(|err| err.reverse());
    }

    match (typ1, typ2) {
        (Type::Func(func1), Type::Func(func2)) => {
            if func1.arity() != func2.arity() {
                return Err(TypeError::TypeMismatch {
                    expected: typ1.clone(),
                    got: typ2.clone(),
                });
            }
            let func1_arguments = func1.arguments.borrow();
            let func2_arguments = func2.arguments.borrow();
            for (func1_argument, func2_argument) in
                func1_arguments.iter().zip(func2_arguments.iter())
            {
                unify(func1_argument, func2_argument, scope.clone())?;
            }
            let func1_return = &*func1.retrn.borrow();
            let func2_return = &*func2.retrn.borrow();
            return unify(&func1_return, &func2_return, scope);
        }
        _ => (),
    }

    Err(TypeError::CannotUnify {
        first: typ1.clone(),
        second: typ2.clone(),
    })
}
<commit_msg>Start implementation of checking generic constraints against objects<commit_after>use std::cell::{Ref, RefCell, RefMut};
use std::rc::Rc;

use super::scope::Scope;
use super::typ::{Generic, GenericConstraint, Object, Type, Variable};
use super::{TypeError, TypeResult};

// Extract the variable type within a type.
fn unwrap_variable(typ: &Type) -> &Rc<RefCell<Variable>> {
    match typ {
        Type::Variable(variable) => variable,
        other @ _ => unreachable!("Not a Variable: {:?}", other),
    }
}

/// Unify a variable (mutable) generic with another generic.
pub fn unify_variable_generic_with_generic(
    destination: &Type,
    source: &Generic,
    scope: Scope,
) -> TypeResult<()> {
    let destination = unwrap_variable(destination);

    enum Action {
        AddCallableConstraint(Vec<Type>, Type),
        AddPropertyConstraint(String, Type),
        None,
    }

    for constraint in source.constraints.iter() {
        use Action::*;
        use GenericConstraint::*;

        let action = {
            // Get an immutable borrow while we determine what to do.
            let destination = Ref::map(destination.borrow(), Variable::unwrap_generic);

            match constraint {
                Property(source_property) => {
                    // If the property already exists then unify their types,
                    // otherwise add it to the left side.
                    if let Some(destination_property) =
                        destination.get_property(&source_property.name)
                    {
                        unify(
                            &destination_property.typ,
                            &source_property.typ,
                            scope.clone(),
                        )?;
                        None
                    } else {
                        AddPropertyConstraint(
                            source_property.name.clone(),
                            source_property.typ.clone(),
                        )
                    }
                }
                Callable(source_callable) => {
                    if let Some(destination_callable) = destination.get_callable() {
                        if destination_callable.arguments.len() != source_callable.arguments.len() {
                            return Err(TypeError::ArgumentLengthMismatch {
                                expected: destination_callable.arguments.clone(),
                                got: source_callable.arguments.clone(),
                            });
                        }
                        for (destination_argument, source_argument) in destination_callable
                            .arguments
                            .iter()
                            .zip(source_callable.arguments.iter())
                        {
                            unify(destination_argument, source_argument, scope.clone())?;
                        }
                        unify(
                            &destination_callable.retrn,
                            &source_callable.retrn,
                            scope.clone(),
                        )?;
                        None
                    } else {
                        AddCallableConstraint(
                            source_callable.arguments.clone(),
                            source_callable.retrn.clone(),
                        )
                    }
                }
            }
        };

        let destination = &mut *RefMut::map(destination.borrow_mut(), Variable::unwrap_mut_generic);
        match action {
            AddCallableConstraint(arguments, retrn) => {
                destination.add_callable_constraint(arguments, retrn)
            }
            AddPropertyConstraint(name, typ) => destination.add_property_constraint(name, typ),
            None => (),
        }
    }
    Ok(())
}

/// Check if an object satisfies a generic's constraints.
fn object_satisfies_constraints(generic: &Generic, object: &Rc<Object>) -> TypeResult<()> {
    if generic.constraints.is_empty() {
        return Ok(());
    }
    // TODO: Actually check each constraint against the object.
    Err(TypeError::InternalError {
        message: format!(
            "Object doesn't satisfy constraints:\nobject: {:?}\nconstraints: {:?}",
            object, generic.constraints,
        ),
    })
}

pub fn unify(typ1: &Type, typ2: &Type, scope: Scope) -> TypeResult<()> {
    if typ1 == typ2 {
        return Ok(());
    }

    if let Type::Variable(var2) = typ2 {
        if let Variable::Substitute { substitute, .. } = &*var2.borrow() {
            return unify(typ1, substitute, scope);
        }
    }

    if let Type::Variable(var1) = typ1 {
        enum Action {
            // Make `typ1` a substitute for another type.
            Substitute(Type),
            // Used to unify a different type with `typ2` (eg. if `typ1` is a
            // substitute).
            Reunify(Type),
            // Unify both variable generics.
            UnifyGenerics,
            UnifyGenericWithObject(Rc<Object>),
        }
        use Action::*;

        let action = match &*var1.borrow() {
            Variable::Generic { .. } => {
                // WARNING: These branches rely on implicit returns and
                //   therefore *must* stay exhaustive.
                match typ2 {
                    Type::Object(object2) => UnifyGenericWithObject(object2.clone()),
                    Type::Variable(var2) => {
                        // If this is a generic and the other type is unbound
                        // then update the other type to be a substitute for
                        // this type.
                        if var2.borrow().is_unbound() {
                            *var2.borrow_mut() = Variable::Substitute {
                                scope,
                                substitute: Box::new(typ1.clone()),
                            };
                            return Ok(());
                        // If it's also a variable generic then we can attempt to
                        // union the two sets of generic constraints.
                        } else if var2.borrow().is_generic() {
                            UnifyGenerics
                        } else {
                            return Err(TypeError::TypeMismatch {
                                expected: typ1.clone(),
                                got: typ2.clone(),
                            });
                        }
                    }
                    _ => {
                        return Err(TypeError::TypeMismatch {
                            expected: typ1.clone(),
                            got: typ2.clone(),
                        })
                    }
                }
            }
            // If we're a substitute then unify whatever we're substituted with
            // with `typ2`.
            Variable::Substitute { substitute, .. } => Reunify(*substitute.clone()),
            // If we're unbound then inherit whatever the other type is.
            Variable::Unbound { .. } => Substitute(typ2.clone()),
        };

        return match action {
            Substitute(typ) => {
                *var1.borrow_mut() = Variable::Substitute {
                    scope,
                    substitute: Box::new(typ),
                };
                Ok(())
            }
            Reunify(typ) => unify(&typ, typ2, scope),
            UnifyGenerics => {
                let var2 = unwrap_variable(typ2);
                // If both types are variable generics then first merge the
                // right into the left.
                {
                    let generic = Ref::map(var2.borrow(), Variable::unwrap_generic);
                    unify_variable_generic_with_generic(typ1, &generic, scope.clone())?;
                }
                // Then make the right a substitute for the left.
                *var2.borrow_mut() = Variable::Substitute {
                    scope,
                    substitute: Box::new(typ1.clone()),
                };
                Ok(())
            }
            UnifyGenericWithObject(object) => {
                {
                    // First ensure the object satisfies our constraints.
                    let generic = Ref::map(var1.borrow(), Variable::unwrap_generic);
                    object_satisfies_constraints(&generic, &object)?;
                }
                // If the constraints are all satisfied then we can substitute
                // ourselves for the object.
                *var1.borrow_mut() = Variable::Substitute {
                    scope,
                    substitute: Box::new(Type::Object(object)),
                };
                Ok(())
            }
        };
    }

    // If the other type is a variable then unify with it as the first term so
    // that we can reuse the logic above.
    if let Type::Variable(_) = &typ2 {
        return unify(typ2, typ1, scope).map_err(|err| err.reverse());
    }

    match (typ1, typ2) {
        (Type::Func(func1), Type::Func(func2)) => {
            if func1.arity() != func2.arity() {
                return Err(TypeError::TypeMismatch {
                    expected: typ1.clone(),
                    got: typ2.clone(),
                });
            }
            let func1_arguments = func1.arguments.borrow();
            let func2_arguments = func2.arguments.borrow();
            for (func1_argument, func2_argument) in
                func1_arguments.iter().zip(func2_arguments.iter())
            {
                unify(func1_argument, func2_argument, scope.clone())?;
            }
            let func1_return = &*func1.retrn.borrow();
            let func2_return = &*func2.retrn.borrow();
            return unify(&func1_return, &func2_return, scope);
        }
        _ => (),
    }

    Err(TypeError::CannotUnify {
        first: typ1.clone(),
        second: typ2.clone(),
    })
}
<|endoftext|>


<commit_before>#![warn(rust_2018_idioms)]
#![allow(clippy::suspicious_else_formatting)]

#[macro_use]
extern crate tracing;

use std::collections::hash_map::{Entry, HashMap};

use conduit::{box_error, Handler, HandlerResult, Method, RequestExt};
use route_recognizer::{Match, Params, Router};

#[derive(Default)]
pub struct RouteBuilder {
    routers: HashMap<Method, Router<WrappedHandler>>,
}

#[derive(Clone, Copy)]
pub struct RoutePattern(&'static str);

impl RoutePattern {
    pub fn pattern(&self) -> &str {
        self.0
    }
}

struct WrappedHandler {
    pattern: RoutePattern,
    handler: Box<dyn Handler>,
}

impl conduit::Handler for WrappedHandler {
    fn call(&self, request: &mut dyn RequestExt) -> HandlerResult {
        self.handler.call(request)
    }
}

#[derive(Debug, thiserror::Error)]
pub enum RouterError {
    #[error("Invalid method")]
    UnknownMethod,
    #[error("Path not found")]
    PathNotFound,
}

impl RouteBuilder {
    pub fn new() -> Self {
        Self {
            routers: HashMap::new(),
        }
    }

    #[instrument(level = "trace", skip(self))]
    fn recognize<'a>(
        &'a self,
        method: &Method,
        path: &str,
    ) -> Result<Match<&WrappedHandler>, RouterError> {
        match self.routers.get(method) {
            Some(router) => router.recognize(path).or(Err(RouterError::PathNotFound)),
            None => Err(RouterError::UnknownMethod),
        }
    }

    #[instrument(level = "trace", skip(self, handler))]
    pub fn map<H: Handler>(
        &mut self,
        method: Method,
        pattern: &'static str,
        handler: H,
    ) -> &mut Self {
        let router = match self.routers.entry(method) {
            Entry::Occupied(e) => e.into_mut(),
            Entry::Vacant(e) => e.insert(Router::new()),
        };
        let wrapped_handler = WrappedHandler {
            pattern: RoutePattern(pattern),
            handler: Box::new(handler),
        };
        router.add(pattern, wrapped_handler);
        self
    }

    pub fn get<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut Self {
        self.map(Method::GET, pattern, handler)
    }

    pub fn post<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut Self {
        self.map(Method::POST, pattern, handler)
    }

    pub fn put<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut Self {
        self.map(Method::PUT, pattern, handler)
    }

    pub fn delete<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut Self {
        self.map(Method::DELETE, pattern, handler)
    }

    pub fn head<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut Self {
        self.map(Method::HEAD, pattern, handler)
    }
}

impl conduit::Handler for RouteBuilder {
    #[instrument(level = "trace", skip(self, request))]
    fn call(&self, request: &mut dyn RequestExt) -> HandlerResult {
        let mut m = {
            let method = request.method();
            let path = request.path();

            match self.recognize(&method, path) {
                Ok(m) => m,
                Err(e) => {
                    info!("{}", e);
                    return Err(box_error(e));
                }
            }
        };

        // We don't have `pub` access to the fields to destructure `Params`, so swap with an empty
        // value to avoid an allocation.
        let mut params = Params::new();
        std::mem::swap(m.params_mut(), &mut params);

        let pattern = m.handler().pattern;
        debug!(pattern = pattern.0, "matching route handler found");

        let extensions = request.mut_extensions();
        extensions.insert(pattern);
        extensions.insert(params);

        let span = trace_span!("handler", pattern = pattern.0);
        span.in_scope(|| m.handler().call(request))
    }
}

pub trait RequestParams<'a> {
    fn params(self) -> &'a Params;
}

impl<'a> RequestParams<'a> for &'a (dyn RequestExt + 'a) {
    fn params(self) -> &'a Params {
        self.extensions().get::<Params>().expect("Missing params")
    }
}

#[cfg(test)]
mod tests {
    use super::{RequestParams, RouteBuilder, RoutePattern};

    use conduit::{Body, Handler, Method, Response, StatusCode};
    use conduit_test::{MockRequest, ResponseExt};

    lazy_static::lazy_static! {
        static ref TRACING: () = {
            tracing_subscriber::FmtSubscriber::builder()
                .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
                .with_span_events(tracing_subscriber::fmt::format::FmtSpan::FULL)
                .with_test_writer()
                .init();
        };
    }

    #[test]
    fn basic_get() {
        lazy_static::initialize(&TRACING);

        let router = test_router();
        let mut req = MockRequest::new(Method::GET, "/posts/1");
        let res = router.call(&mut req).expect("No response");

        assert_eq!(res.status(), StatusCode::OK);
        assert_eq!(*res.into_cow(), b"1, GET, /posts/:id"[..]);
    }

    #[test]
    fn basic_post() {
        lazy_static::initialize(&TRACING);

        let router = test_router();
        let mut req = MockRequest::new(Method::POST, "/posts/10");
        let res = router.call(&mut req).expect("No response");

        assert_eq!(res.status(), StatusCode::OK);
        assert_eq!(*res.into_cow(), b"10, POST, /posts/:id"[..]);
    }

    #[test]
    fn path_not_found() {
        lazy_static::initialize(&TRACING);

        let router = test_router();
        let mut req = MockRequest::new(Method::POST, "/nonexistent");
        let err = router.call(&mut req).err().unwrap();

        assert_eq!(err.to_string(), "Path not found");
    }

    #[test]
    fn unknown_method() {
        lazy_static::initialize(&TRACING);

        let router = test_router();
        let mut req = MockRequest::new(Method::DELETE, "/posts/1");
        let err = router.call(&mut req).err().unwrap();

        assert_eq!(err.to_string(), "Invalid method");
    }

    #[test]
    fn catch_all() {
        lazy_static::initialize(&TRACING);

        let mut router = RouteBuilder::new();
        router.get("/*", test_handler);

        let mut req = MockRequest::new(Method::GET, "/foo");
        let res = router.call(&mut req).expect("No response");
        assert_eq!(res.status(), StatusCode::OK);
        assert_eq!(*res.into_cow(), b", GET, /*"[..]);
    }

    fn test_router() -> RouteBuilder {
        let mut router = RouteBuilder::new();
        router.post("/posts/:id", test_handler);
        router.get("/posts/:id", test_handler);
        router
    }

    fn test_handler(req: &mut dyn conduit::RequestExt) -> conduit::HttpResult {
        let res = vec![
            req.params().find("id").unwrap_or("").to_string(),
            format!("{:?}", req.method()),
            req.extensions()
                .get::<RoutePattern>()
                .unwrap()
                .pattern()
                .to_string(),
        ];

        let bytes = res.join(", ").into_bytes();
        Response::builder().body(Body::from_vec(bytes))
    }
}
<commit_msg>Fix a clippy warning<commit_after>#![warn(rust_2018_idioms)]
#![allow(clippy::suspicious_else_formatting)]

#[macro_use]
extern crate tracing;

use std::collections::hash_map::{Entry, HashMap};

use conduit::{box_error, Handler, HandlerResult, Method, RequestExt};
use route_recognizer::{Match, Params, Router};

#[derive(Default)]
pub struct RouteBuilder {
    routers: HashMap<Method, Router<WrappedHandler>>,
}

#[derive(Clone, Copy)]
pub struct RoutePattern(&'static str);

impl RoutePattern {
    pub fn pattern(&self) -> &str {
        self.0
    }
}

struct WrappedHandler {
    pattern: RoutePattern,
    handler: Box<dyn Handler>,
}

impl conduit::Handler for WrappedHandler {
    fn call(&self, request: &mut dyn RequestExt) -> HandlerResult {
        self.handler.call(request)
    }
}

#[derive(Debug, thiserror::Error)]
pub enum RouterError {
    #[error("Invalid method")]
    UnknownMethod,
    #[error("Path not found")]
    PathNotFound,
}

impl RouteBuilder {
    pub fn new() -> Self {
        Self {
            routers: HashMap::new(),
        }
    }

    #[instrument(level = "trace", skip(self))]
    fn recognize<'a>(
        &'a self,
        method: &Method,
        path: &str,
    ) -> Result<Match<&WrappedHandler>, RouterError> {
        match self.routers.get(method) {
            Some(router) => router.recognize(path).or(Err(RouterError::PathNotFound)),
            None => Err(RouterError::UnknownMethod),
        }
    }

    #[instrument(level = "trace", skip(self, handler))]
    pub fn map<H: Handler>(
        &mut self,
        method: Method,
        pattern: &'static str,
        handler: H,
    ) -> &mut Self {
        let router = match self.routers.entry(method) {
            Entry::Occupied(e) => e.into_mut(),
            Entry::Vacant(e) => e.insert(Router::new()),
        };
        let wrapped_handler = WrappedHandler {
            pattern: RoutePattern(pattern),
            handler: Box::new(handler),
        };
        router.add(pattern, wrapped_handler);
        self
    }

    pub fn get<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut Self {
        self.map(Method::GET, pattern, handler)
    }

    pub fn post<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut Self {
        self.map(Method::POST, pattern, handler)
    }

    pub fn put<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut Self {
        self.map(Method::PUT, pattern, handler)
    }

    pub fn delete<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut Self {
        self.map(Method::DELETE, pattern, handler)
    }

    pub fn head<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut Self {
        self.map(Method::HEAD, pattern, handler)
    }
}

impl conduit::Handler for RouteBuilder {
    #[instrument(level = "trace", skip(self, request))]
    fn call(&self, request: &mut dyn RequestExt) -> HandlerResult {
        let mut m = {
            let method = request.method();
            let path = request.path();

            match self.recognize(method, path) {
                Ok(m) => m,
                Err(e) => {
                    info!("{}", e);
                    return Err(box_error(e));
                }
            }
        };

        // We don't have `pub` access to the fields to destructure `Params`, so swap with an empty
        // value to avoid an allocation.
        let mut params = Params::new();
        std::mem::swap(m.params_mut(), &mut params);

        let pattern = m.handler().pattern;
        debug!(pattern = pattern.0, "matching route handler found");

        let extensions = request.mut_extensions();
        extensions.insert(pattern);
        extensions.insert(params);

        let span = trace_span!("handler", pattern = pattern.0);
        span.in_scope(|| m.handler().call(request))
    }
}

pub trait RequestParams<'a> {
    fn params(self) -> &'a Params;
}

impl<'a> RequestParams<'a> for &'a (dyn RequestExt + 'a) {
    fn params(self) -> &'a Params {
        self.extensions().get::<Params>().expect("Missing params")
    }
}

#[cfg(test)]
mod tests {
    use super::{RequestParams, RouteBuilder, RoutePattern};

    use conduit::{Body, Handler, Method, Response, StatusCode};
    use conduit_test::{MockRequest, ResponseExt};

    lazy_static::lazy_static! {
        static ref TRACING: () = {
            tracing_subscriber::FmtSubscriber::builder()
                .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
                .with_span_events(tracing_subscriber::fmt::format::FmtSpan::FULL)
                .with_test_writer()
                .init();
        };
    }

    #[test]
    fn basic_get() {
        lazy_static::initialize(&TRACING);

        let router = test_router();
        let mut req = MockRequest::new(Method::GET, "/posts/1");
        let res = router.call(&mut req).expect("No response");

        assert_eq!(res.status(), StatusCode::OK);
        assert_eq!(*res.into_cow(), b"1, GET, /posts/:id"[..]);
    }

    #[test]
    fn basic_post() {
        lazy_static::initialize(&TRACING);

        let router = test_router();
        let mut req = MockRequest::new(Method::POST, "/posts/10");
        let res = router.call(&mut req).expect("No response");

        assert_eq!(res.status(), StatusCode::OK);
        assert_eq!(*res.into_cow(), b"10, POST, /posts/:id"[..]);
    }

    #[test]
    fn path_not_found() {
        lazy_static::initialize(&TRACING);

        let router = test_router();
        let mut req = MockRequest::new(Method::POST, "/nonexistent");
        let err = router.call(&mut req).err().unwrap();

        assert_eq!(err.to_string(), "Path not found");
    }

    #[test]
    fn unknown_method() {
        lazy_static::initialize(&TRACING);

        let router = test_router();
        let mut req = MockRequest::new(Method::DELETE, "/posts/1");
        let err = router.call(&mut req).err().unwrap();

        assert_eq!(err.to_string(), "Invalid method");
    }

    #[test]
    fn catch_all() {
        lazy_static::initialize(&TRACING);

        let mut router = RouteBuilder::new();
        router.get("/*", test_handler);

        let mut req = MockRequest::new(Method::GET, "/foo");
        let res = router.call(&mut req).expect("No response");
        assert_eq!(res.status(), StatusCode::OK);
        assert_eq!(*res.into_cow(), b", GET, /*"[..]);
    }

    fn test_router() -> RouteBuilder {
        let mut router = RouteBuilder::new();
        router.post("/posts/:id", test_handler);
        router.get("/posts/:id", test_handler);
        router
    }

    fn test_handler(req: &mut dyn conduit::RequestExt) -> conduit::HttpResult {
        let res = vec![
            req.params().find("id").unwrap_or("").to_string(),
            format!("{:?}", req.method()),
            req.extensions()
                .get::<RoutePattern>()
                .unwrap()
                .pattern()
                .to_string(),
        ];

        let bytes = res.join(", ").into_bytes();
        Response::builder().body(Body::from_vec(bytes))
    }
}
<|endoftext|>


<commit_before>#![warn(rust_2018_idioms)]

#[macro_use]
extern crate tracing;

use std::collections::hash_map::{Entry, HashMap};

use conduit::{box_error, Handler, HandlerResult, Method, RequestExt};
use route_recognizer::{Match, Params, Router};

#[derive(Default)]
pub struct RouteBuilder {
    routers: HashMap<Method, Router<WrappedHandler>>,
}

#[derive(Clone, Copy)]
pub struct RoutePattern(&'static str);

impl RoutePattern {
    pub fn pattern(&self) -> &str {
        self.0
    }
}

struct WrappedHandler {
    pattern: RoutePattern,
    handler: Box<dyn Handler>,
}

impl conduit::Handler for WrappedHandler {
    fn call(&self, request: &mut dyn RequestExt) -> HandlerResult {
        self.handler.call(request)
    }
}

#[derive(Debug, thiserror::Error)]
pub enum RouterError {
    #[error("Invalid method")]
    UnknownMethod,
    #[error("Path not found")]
    PathNotFound,
}

impl RouteBuilder {
    pub fn new() -> Self {
        Self {
            routers: HashMap::new(),
        }
    }

    #[instrument(level = "trace", skip(self))]
    #[allow(clippy::suspicious_else_formatting)]
    fn recognize<'a>(
        &'a self,
        method: &Method,
        path: &str,
    ) -> Result<Match<&WrappedHandler>, RouterError> {
        match self.routers.get(method) {
            Some(router) => router.recognize(path).or(Err(RouterError::PathNotFound)),
            None => Err(RouterError::UnknownMethod),
        }
    }

    #[instrument(level = "trace", skip(self, handler))]
    pub fn map<H: Handler>(
        &mut self,
        method: Method,
        pattern: &'static str,
        handler: H,
    ) -> &mut Self {
        let router = match self.routers.entry(method) {
            Entry::Occupied(e) => e.into_mut(),
            Entry::Vacant(e) => e.insert(Router::new()),
        };
        let wrapped_handler = WrappedHandler {
            pattern: RoutePattern(pattern),
            handler: Box::new(handler),
        };
        router.add(pattern, wrapped_handler);
        self
    }

    pub fn get<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut Self {
        self.map(Method::GET, pattern, handler)
    }

    pub fn post<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut Self {
        self.map(Method::POST, pattern, handler)
    }

    pub fn put<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut Self {
        self.map(Method::PUT, pattern, handler)
    }

    pub fn delete<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut Self {
        self.map(Method::DELETE, pattern, handler)
    }

    pub fn head<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut Self {
        self.map(Method::HEAD, pattern, handler)
    }
}

impl conduit::Handler for RouteBuilder {
    #[instrument(level = "trace", skip(self, request))]
    fn call(&self, request: &mut dyn RequestExt) -> HandlerResult {
        let mut m = {
            let method = request.method();
            let path = request.path();

            match self.recognize(&method, path) {
                Ok(m) => m,
                Err(e) => {
                    info!("{}", e);
                    return Err(box_error(e));
                }
            }
        };

        // We don't have `pub` access to the fields to destructure `Params`, so swap with an empty
        // value to avoid an allocation.
        let mut params = Params::new();
        std::mem::swap(m.params_mut(), &mut params);

        let pattern = m.handler().pattern;
        debug!(pattern = pattern.0, "matching route handler found");

        let extensions = request.mut_extensions();
        extensions.insert(pattern);
        extensions.insert(params);

        let span = trace_span!("handler", pattern = pattern.0);
        span.in_scope(|| m.handler().call(request))
    }
}

pub trait RequestParams<'a> {
    fn params(self) -> &'a Params;
}

impl<'a> RequestParams<'a> for &'a (dyn RequestExt + 'a) {
    fn params(self) -> &'a Params {
        self.extensions().get::<Params>().expect("Missing params")
    }
}

#[cfg(test)]
mod tests {
    use super::{RequestParams, RouteBuilder, RoutePattern};

    use conduit::{Body, Handler, Method, Response, StatusCode};
    use conduit_test::{MockRequest, ResponseExt};

    lazy_static::lazy_static! {
        static ref TRACING: () = {
            tracing_subscriber::FmtSubscriber::builder()
                .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
                .with_span_events(tracing_subscriber::fmt::format::FmtSpan::FULL)
                .with_test_writer()
                .init();
        };
    }

    #[test]
    fn basic_get() {
        lazy_static::initialize(&TRACING);

        let router = test_router();
        let mut req = MockRequest::new(Method::GET, "/posts/1");
        let res = router.call(&mut req).expect("No response");

        assert_eq!(res.status(), StatusCode::OK);
        assert_eq!(*res.into_cow(), b"1, GET, /posts/:id"[..]);
    }

    #[test]
    fn basic_post() {
        lazy_static::initialize(&TRACING);

        let router = test_router();
        let mut req = MockRequest::new(Method::POST, "/posts/10");
        let res = router.call(&mut req).expect("No response");

        assert_eq!(res.status(), StatusCode::OK);
        assert_eq!(*res.into_cow(), b"10, POST, /posts/:id"[..]);
    }

    #[test]
    fn path_not_found() {
        lazy_static::initialize(&TRACING);

        let router = test_router();
        let mut req = MockRequest::new(Method::POST, "/nonexistent");
        let err = router.call(&mut req).err().unwrap();

        assert_eq!(err.to_string(), "Path not found");
    }

    #[test]
    fn unknown_method() {
        lazy_static::initialize(&TRACING);

        let router = test_router();
        let mut req = MockRequest::new(Method::DELETE, "/posts/1");
        let err = router.call(&mut req).err().unwrap();

        assert_eq!(err.to_string(), "Invalid method");
    }

    #[test]
    fn catch_all() {
        lazy_static::initialize(&TRACING);

        let mut router = RouteBuilder::new();
        router.get("/*", test_handler);

        let mut req = MockRequest::new(Method::GET, "/foo");
        let res = router.call(&mut req).expect("No response");
        assert_eq!(res.status(), StatusCode::OK);
        assert_eq!(*res.into_cow(), b", GET, /*"[..]);
    }

    fn test_router() -> RouteBuilder {
        let mut router = RouteBuilder::new();
        router.post("/posts/:id", test_handler);
        router.get("/posts/:id", test_handler);
        router
    }

    fn test_handler(req: &mut dyn conduit::RequestExt) -> conduit::HttpResult {
        let res = vec![
            req.params().find("id").unwrap_or("").to_string(),
            format!("{:?}", req.method()),
            req.extensions()
                .get::<RoutePattern>()
                .unwrap()
                .pattern()
                .to_string(),
        ];

        let bytes = res.join(", ").into_bytes();
        Response::builder().body(Body::from_vec(bytes))
    }
}
<commit_msg>Allow `clippy::suspicious_else_formatting`<commit_after>#![warn(rust_2018_idioms)]
#![allow(clippy::suspicious_else_formatting)]

#[macro_use]
extern crate tracing;

use std::collections::hash_map::{Entry, HashMap};

use conduit::{box_error, Handler, HandlerResult, Method, RequestExt};
use route_recognizer::{Match, Params, Router};

#[derive(Default)]
pub struct RouteBuilder {
    routers: HashMap<Method, Router<WrappedHandler>>,
}

#[derive(Clone, Copy)]
pub struct RoutePattern(&'static str);

impl RoutePattern {
    pub fn pattern(&self) -> &str {
        self.0
    }
}

struct WrappedHandler {
    pattern: RoutePattern,
    handler: Box<dyn Handler>,
}

impl conduit::Handler for WrappedHandler {
    fn call(&self, request: &mut dyn RequestExt) -> HandlerResult {
        self.handler.call(request)
    }
}

#[derive(Debug, thiserror::Error)]
pub enum RouterError {
    #[error("Invalid method")]
    UnknownMethod,
    #[error("Path not found")]
    PathNotFound,
}

impl RouteBuilder {
    pub fn new() -> Self {
        Self {
            routers: HashMap::new(),
        }
    }

    #[instrument(level = "trace", skip(self))]
    fn recognize<'a>(
        &'a self,
        method: &Method,
        path: &str,
    ) -> Result<Match<&WrappedHandler>, RouterError> {
        match self.routers.get(method) {
            Some(router) => router.recognize(path).or(Err(RouterError::PathNotFound)),
            None => Err(RouterError::UnknownMethod),
        }
    }

    #[instrument(level = "trace", skip(self, handler))]
    pub fn map<H: Handler>(
        &mut self,
        method: Method,
        pattern: &'static str,
        handler: H,
    ) -> &mut Self {
        let router = match self.routers.entry(method) {
            Entry::Occupied(e) => e.into_mut(),
            Entry::Vacant(e) => e.insert(Router::new()),
        };
        let wrapped_handler = WrappedHandler {
            pattern: RoutePattern(pattern),
            handler: Box::new(handler),
        };
        router.add(pattern, wrapped_handler);
        self
    }

    pub fn get<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut Self {
        self.map(Method::GET, pattern, handler)
    }

    pub fn post<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut Self {
        self.map(Method::POST, pattern, handler)
    }

    pub fn put<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut Self {
        self.map(Method::PUT, pattern, handler)
    }

    pub fn delete<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut Self {
        self.map(Method::DELETE, pattern, handler)
    }

    pub fn head<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut Self {
        self.map(Method::HEAD, pattern, handler)
    }
}

impl conduit::Handler for RouteBuilder {
    #[instrument(level = "trace", skip(self, request))]
    fn call(&self, request: &mut dyn RequestExt) -> HandlerResult {
        let mut m = {
            let method = request.method();
            let path = request.path();

            match self.recognize(&method, path) {
                Ok(m) => m,
                Err(e) => {
                    info!("{}", e);
                    return Err(box_error(e));
                }
            }
        };

        // We don't have `pub` access to the fields to destructure `Params`, so swap with an empty
        // value to avoid an allocation.
        let mut params = Params::new();
        std::mem::swap(m.params_mut(), &mut params);

        let pattern = m.handler().pattern;
        debug!(pattern = pattern.0, "matching route handler found");

        let extensions = request.mut_extensions();
        extensions.insert(pattern);
        extensions.insert(params);

        let span = trace_span!("handler", pattern = pattern.0);
        span.in_scope(|| m.handler().call(request))
    }
}

pub trait RequestParams<'a> {
    fn params(self) -> &'a Params;
}

impl<'a> RequestParams<'a> for &'a (dyn RequestExt + 'a) {
    fn params(self) -> &'a Params {
        self.extensions().get::<Params>().expect("Missing params")
    }
}

#[cfg(test)]
mod tests {
    use super::{RequestParams, RouteBuilder, RoutePattern};

    use conduit::{Body, Handler, Method, Response, StatusCode};
    use conduit_test::{MockRequest, ResponseExt};

    lazy_static::lazy_static! {
        static ref TRACING: () = {
            tracing_subscriber::FmtSubscriber::builder()
                .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
                .with_span_events(tracing_subscriber::fmt::format::FmtSpan::FULL)
                .with_test_writer()
                .init();
        };
    }

    #[test]
    fn basic_get() {
        lazy_static::initialize(&TRACING);

        let router = test_router();
        let mut req = MockRequest::new(Method::GET, "/posts/1");
        let res = router.call(&mut req).expect("No response");

        assert_eq!(res.status(), StatusCode::OK);
        assert_eq!(*res.into_cow(), b"1, GET, /posts/:id"[..]);
    }

    #[test]
    fn basic_post() {
        lazy_static::initialize(&TRACING);

        let router = test_router();
        let mut req = MockRequest::new(Method::POST, "/posts/10");
        let res = router.call(&mut req).expect("No response");

        assert_eq!(res.status(), StatusCode::OK);
        assert_eq!(*res.into_cow(), b"10, POST, /posts/:id"[..]);
    }

    #[test]
    fn path_not_found() {
        lazy_static::initialize(&TRACING);

        let router = test_router();
        let mut req = MockRequest::new(Method::POST, "/nonexistent");
        let err = router.call(&mut req).err().unwrap();

        assert_eq!(err.to_string(), "Path not found");
    }

    #[test]
    fn unknown_method() {
        lazy_static::initialize(&TRACING);

        let router = test_router();
        let mut req = MockRequest::new(Method::DELETE, "/posts/1");
        let err = router.call(&mut req).err().unwrap();

        assert_eq!(err.to_string(), "Invalid method");
    }

    #[test]
    fn catch_all() {
        lazy_static::initialize(&TRACING);

        let mut router = RouteBuilder::new();
        router.get("/*", test_handler);

        let mut req = MockRequest::new(Method::GET, "/foo");
        let res = router.call(&mut req).expect("No response");
        assert_eq!(res.status(), StatusCode::OK);
        assert_eq!(*res.into_cow(), b", GET, /*"[..]);
    }

    fn test_router() -> RouteBuilder {
        let mut router = RouteBuilder::new();
        router.post("/posts/:id", test_handler);
        router.get("/posts/:id", test_handler);
        router
    }

    fn test_handler(req: &mut dyn conduit::RequestExt) -> conduit::HttpResult {
        let res = vec![
            req.params().find("id").unwrap_or("").to_string(),
            format!("{:?}", req.method()),
            req.extensions()
                .get::<RoutePattern>()
                .unwrap()
                .pattern()
                .to_string(),
        ];

        let bytes = res.join(", ").into_bytes();
        Response::builder().body(Body::from_vec(bytes))
    }
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate libarchive3_sys;

use self::libarchive3_sys::ffi;
use std::ffi::{CStr, CString};
use std::marker;
use std::ptr;
use std::io::{Result, Error, SeekFrom, Read, Seek, ErrorKind};
use std::error::Error as STDError;
use fs::{SeekableRead, SeekExt};
use std::cmp::min;
use std::path::PathBuf;
use std::sync::{Once, ONCE_INIT};

// libarchive needs locale to convert pathname.
fn setlocale_once() {
    static ONCE: Once = ONCE_INIT;
    ONCE.call_once(|| unsafe {
        libc::setlocale(libc::LC_ALL, CString::new("").unwrap().as_ptr());
    });
}

struct Proxy<R: SeekableRead> {
    r: R,
    buf: Vec<u8>,
    pos: u64,
}

impl<R: SeekableRead> Proxy<R> {
    fn new(r: R) -> Proxy<R> {
        let mut v = Vec::new();
        v.resize(4096, 0);
        Proxy {
            r: r,
            buf: v,
            pos: 0,
        }
    }

    fn read(&mut self) -> Result<&[u8]> {
        let n = self.r.read(&mut self.buf[..])?;
        self.pos += n as u64;
        Ok(&self.buf[..n])
    }

    fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
        self.pos = self.r.seek(pos)?;
        Ok(self.pos)
    }

    fn skip(&mut self, request: libc::int64_t) -> Result<libc::int64_t> {
        let now = self.r.seek(SeekFrom::Current(request))?;
        let skip = now - self.pos;
        self.pos = now;
        Ok(skip as libc::int64_t)
    }
}

pub struct Archive<R: SeekableRead> {
    raw: *mut ffi::Struct_archive,
    eof: bool,
    _proxy: Box<Proxy<R>>,
}

unsafe fn set_error(raw: *mut ffi::Struct_archive, e: Error) {
    let desc = CString::new(e.description()).unwrap();
    ffi::archive_set_error(raw, e.raw_os_error().unwrap_or(libc::EIO), desc.as_ptr());
}

unsafe fn error_string(raw: *mut ffi::Struct_archive) -> String {
    CStr::from_ptr(ffi::archive_error_string(raw)).to_str().unwrap().to_string()
}

unsafe extern "C" fn read_callback<R: SeekableRead>(raw: *mut ffi::Struct_archive,
                                                    client_data: *mut libc::c_void,
                                                    buffer: *mut *const libc::c_void)
                                                    -> libc::ssize_t {
    let proxy = (client_data as *mut Proxy<R>).as_mut().unwrap();
    let err;
    match proxy.read() {
        Ok(data) => {
            *buffer = data.as_ptr() as *const libc::c_void;
            return data.len() as libc::ssize_t;
        }
        Err(e) => err = e,
    };
    set_error(raw, err);
    -1
}

unsafe extern "C" fn skip_callback<R: SeekableRead>(raw: *mut ffi::Struct_archive,
                                                    client_data: *mut libc::c_void,
                                                    request: libc::int64_t)
                                                    -> libc::int64_t {
    let proxy = (client_data as *mut Proxy<R>).as_mut().unwrap();
    match proxy.skip(request) {
        Ok(n) => n,
        Err(e) => {
            // collect?
            set_error(raw, e);
            0
        }
    }
}

unsafe extern "C" fn seek_callback<R: SeekableRead>(raw: *mut ffi::Struct_archive,
                                                    client_data: *mut libc::c_void,
                                                    offset: libc::int64_t,
                                                    whence: libc::c_int)
                                                    -> libc::int64_t {
    let proxy = (client_data as *mut Proxy<R>).as_mut().unwrap();
    let pos = match whence {
        libc::SEEK_SET => SeekFrom::Start(offset as u64),
        libc::SEEK_CUR => SeekFrom::Current(offset),
        libc::SEEK_END => SeekFrom::End(offset),
        _ => unreachable!(),
    };
    match proxy.seek(pos) {
        Ok(n) => n as libc::int64_t,
        Err(e) => {
            // collect?
            set_error(raw, e);
            -1
        }
    }
}

impl<R: SeekableRead> Archive<R> {
    pub fn new(r: R) -> Self {
        setlocale_once();
        unsafe {
            let raw = ffi::archive_read_new();
            if raw.is_null() {
                panic!("oom");
            }
            if ffi::archive_read_support_format_all(raw) != ffi::ARCHIVE_OK {
                panic!("not support format");
            }
            if ffi::archive_read_support_filter_all(raw) != ffi::ARCHIVE_OK {
                panic!("not support filter");
            }
            if r.bidirectional() {
                if ffi::archive_read_set_seek_callback(raw, Some(seek_callback::<R>)) !=
                   ffi::ARCHIVE_OK {
                    panic!("failed to set seek");
                }
            } else {
                if ffi::archive_read_set_skip_callback(raw, Some(skip_callback::<R>)) !=
                   ffi::ARCHIVE_OK {
                    panic!("failed to set skip");
                }
            }
            let proxy = Box::into_raw(Box::new(Proxy::new(r)));
            if ffi::archive_read_open(raw,
                                      proxy as *mut libc::c_void,
                                      None,
                                      Some(read_callback::<R>),
                                      None) != ffi::ARCHIVE_OK {
                panic!("failed to open");
            }
            Archive {
                raw: raw,
                eof: false,
                _proxy: Box::from_raw(proxy),
            }
        }
    }

    fn next_entry_raw(&mut self) -> Option<Result<Entry>> {
        if self.eof {
            return None;
        }

        let mut entry = ptr::null_mut();
        loop {
            match unsafe { ffi::archive_read_next_header(self.raw, &mut entry) } {
                ffi::ARCHIVE_OK => break,
                ffi::ARCHIVE_WARN => {
                    warn!("archive_read_next_header: {}",
                          unsafe { error_string(self.raw) });
                    break;
                }
                ffi::ARCHIVE_EOF => {
                    self.eof = true;
                    return None;
                }
                ffi::ARCHIVE_RETRY => {
                    // failed but retryable.
                    warn!("archive_read_next_header: {}, retry.",
                          unsafe { error_string(self.raw) });
                    continue;
                }
                ffi::ARCHIVE_FATAL => {
                    return Some(Err(Error::new(ErrorKind::Other,
                                               unsafe { error_string(self.raw) })));
                }
                _ => unreachable!(),
            }

        }
        Some(Ok(Entry::new(entry)))
    }

    pub fn next_entry<'a>(&'a mut self) -> Option<Result<RefEntry<'a, R>>> {
        self.next_entry_raw().map(|r| r.map(|e| RefEntry::new(e)))
    }

    pub fn find_map<F, T>(mut self, f: F) -> Option<Result<T>>
        where F: Fn(&Entry) -> Option<T>
    {
        loop {
            match self.next_entry_raw() {
                Some(Ok(e)) => {
                    if let Some(x) = f(&e) {
                        return Some(Ok(x));
                    }
                }
                Some(Err(e)) => return Some(Err(e)),
                None => return None,
            }
        }
    }

    pub fn find_open<P>(mut self, p: P) -> Option<Result<Reader<R>>>
        where P: Fn(&Entry) -> bool
    {
        loop {
            match self.next_entry_raw() {
                Some(Ok(e)) => {
                    if p(&e) {
                        break;
                    }
                }
                Some(Err(e)) => return Some(Err(e)),
                None => return None,
            }
        }
        Some(Ok(Reader::new(self)))
    }
}

impl<R: SeekableRead> Drop for Archive<R> {
    fn drop(&mut self) {
        unsafe { ffi::archive_read_free(self.raw) };
    }
}

pub struct Reader<R: SeekableRead> {
    a: Archive<R>,
    buf: *const libc::c_void,
    read_pos: usize,
    buf_size: libc::size_t,
    offset: libc::off_t,
    eof: bool,
}

impl<R: SeekableRead> Reader<R> {
    fn new(a: Archive<R>) -> Reader<R> {
        Reader {
            a: a,
            buf: ptr::null(),
            read_pos: 0,
            buf_size: 0,
            offset: 0,
            eof: false,
        }
    }

    fn fill_gap(&mut self, buf: &mut [u8]) -> usize {
        if self.read_pos < self.offset as usize {
            let l = min(buf.len(), (self.offset as usize) - self.read_pos);
            for x in &mut buf[..l] {
                *x = 0;
            }
            self.read_pos += l;
            return l;
        }
        0
    }

    fn read_data_block(&mut self) -> Result<()> {
        if self.eof {
            return Ok(());
        }

        while self.offset as usize + self.buf_size as usize <= self.read_pos {
            match unsafe {
                ffi::archive_read_data_block(self.a.raw,
                                             &mut self.buf,
                                             &mut self.buf_size,
                                             &mut self.offset)
            } {
                ffi::ARCHIVE_OK => continue,
                ffi::ARCHIVE_WARN => {
                    warn!("archive_read_data_block: {}",
                          unsafe { error_string(self.a.raw) });
                    continue;
                }
                ffi::ARCHIVE_EOF => {
                    self.eof = true;
                    return Ok(());
                }
                ffi::ARCHIVE_RETRY => {
                    // failed but retryable.
                    warn!("archive_read_data_block: {}, retry",
                          unsafe { error_string(self.a.raw) });
                    continue;
                }
                ffi::ARCHIVE_FATAL => {
                    return Err(Error::new(ErrorKind::Other, unsafe { error_string(self.a.raw) }));
                }
                _ => unreachable!(),
            }
        }
        Ok(())
    }
}

impl<R: SeekableRead> SeekExt for Reader<R> {
    fn bidirectional(&self) -> bool {
        return false;
    }
}

impl<R: SeekableRead> Read for Reader<R> {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        self.read_data_block()?;
        let n = self.fill_gap(buf);
        if n > 0 {
            return Ok(n);
        }
        let begin = self.read_pos - self.offset as usize;
        let l = min(buf.len(), self.buf_size - begin);
        unsafe {
            let p = (self.buf as *const u8).offset(begin as isize);
            ptr::copy_nonoverlapping(p, buf.as_mut_ptr(), l);
        }
        self.read_pos += l;
        Ok(l)
    }
}

impl<R: SeekableRead> Seek for Reader<R> {
    fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
        match pos {
            SeekFrom::Start(n) => self.read_pos = n as usize,
            SeekFrom::End(n) => unimplemented!(),
            SeekFrom::Current(n) => self.read_pos += n as usize,
        }
        Ok(self.read_pos as u64)
    }
}

pub struct Entry {
    entry: *mut ffi::Struct_archive_entry,
}

impl Entry {
    fn new(entry: *mut ffi::Struct_archive_entry) -> Entry {
        Entry { entry: entry }
    }

    pub fn pathname(&self) -> PathBuf {
        let c_str = unsafe { CStr::from_ptr(ffi::archive_entry_pathname(self.entry)) };
        PathBuf::from(c_str.to_string_lossy().as_ref())
    }

    pub fn size(&self) -> libc::int64_t {
        unsafe { ffi::archive_entry_size(self.entry) }
    }

    pub fn filetype(&self) -> libc::mode_t {
        unsafe { ffi::archive_entry_filetype(self.entry) }
    }
}

pub struct RefEntry<'a, R: SeekableRead + 'a> {
    e: Entry,
    _m: marker::PhantomData<&'a R>,
}

impl<'a, R: SeekableRead> RefEntry<'a, R> {
    fn new(e: Entry) -> RefEntry<'a, R> {
        RefEntry {
            e: e,
            _m: marker::PhantomData,
        }
    }

    pub fn pathname(&self) -> PathBuf {
        self.e.pathname()
    }

    pub fn size(&self) -> libc::int64_t {
        self.e.size()
    }

    pub fn filetype(&self) -> libc::mode_t {
        self.e.filetype()
    }
}
<commit_msg>fix typo<commit_after>extern crate libc;
extern crate libarchive3_sys;

use self::libarchive3_sys::ffi;
use std::ffi::{CStr, CString};
use std::marker;
use std::ptr;
use std::io::{Result, Error, SeekFrom, Read, Seek, ErrorKind};
use std::error::Error as STDError;
use fs::{SeekableRead, SeekExt};
use std::cmp::min;
use std::path::PathBuf;
use std::sync::{Once, ONCE_INIT};

// libarchive needs locale to convert pathname.
fn setlocale_once() {
    static ONCE: Once = ONCE_INIT;
    ONCE.call_once(|| unsafe {
        libc::setlocale(libc::LC_ALL, CString::new("").unwrap().as_ptr());
    });
}

struct Proxy<R: SeekableRead> {
    r: R,
    buf: Vec<u8>,
    pos: u64,
}

impl<R: SeekableRead> Proxy<R> {
    fn new(r: R) -> Proxy<R> {
        let mut v = Vec::new();
        v.resize(4096, 0);
        Proxy {
            r: r,
            buf: v,
            pos: 0,
        }
    }

    fn read(&mut self) -> Result<&[u8]> {
        let n = self.r.read(&mut self.buf[..])?;
        self.pos += n as u64;
        Ok(&self.buf[..n])
    }

    fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
        self.pos = self.r.seek(pos)?;
        Ok(self.pos)
    }

    fn skip(&mut self, request: libc::int64_t) -> Result<libc::int64_t> {
        let now = self.r.seek(SeekFrom::Current(request))?;
        let skip = now - self.pos;
        self.pos = now;
        Ok(skip as libc::int64_t)
    }
}

pub struct Archive<R: SeekableRead> {
    raw: *mut ffi::Struct_archive,
    eof: bool,
    _proxy: Box<Proxy<R>>,
}

unsafe fn set_error(raw: *mut ffi::Struct_archive, e: Error) {
    let desc = CString::new(e.description()).unwrap();
    ffi::archive_set_error(raw, e.raw_os_error().unwrap_or(libc::EIO), desc.as_ptr());
}

unsafe fn error_string(raw: *mut ffi::Struct_archive) -> String {
    CStr::from_ptr(ffi::archive_error_string(raw)).to_str().unwrap().to_string()
}

unsafe extern "C" fn read_callback<R: SeekableRead>(raw: *mut ffi::Struct_archive,
                                                    client_data: *mut libc::c_void,
                                                    buffer: *mut *const libc::c_void)
                                                    -> libc::ssize_t {
    let proxy = (client_data as *mut Proxy<R>).as_mut().unwrap();
    let err;
    match proxy.read() {
        Ok(data) => {
            *buffer = data.as_ptr() as *const libc::c_void;
            return data.len() as libc::ssize_t;
        }
        Err(e) => err = e,
    };
    set_error(raw, err);
    -1
}

unsafe extern "C" fn skip_callback<R: SeekableRead>(raw: *mut ffi::Struct_archive,
                                                    client_data: *mut libc::c_void,
                                                    request: libc::int64_t)
                                                    -> libc::int64_t {
    let proxy = (client_data as *mut Proxy<R>).as_mut().unwrap();
    match proxy.skip(request) {
        Ok(n) => n,
        Err(e) => {
            // correct?
            set_error(raw, e);
            0
        }
    }
}

unsafe extern "C" fn seek_callback<R: SeekableRead>(raw: *mut ffi::Struct_archive,
                                                    client_data: *mut libc::c_void,
                                                    offset: libc::int64_t,
                                                    whence: libc::c_int)
                                                    -> libc::int64_t {
    let proxy = (client_data as *mut Proxy<R>).as_mut().unwrap();
    let pos = match whence {
        libc::SEEK_SET => SeekFrom::Start(offset as u64),
        libc::SEEK_CUR => SeekFrom::Current(offset),
        libc::SEEK_END => SeekFrom::End(offset),
        _ => unreachable!(),
    };
    match proxy.seek(pos) {
        Ok(n) => n as libc::int64_t,
        Err(e) => {
            // correct?
            set_error(raw, e);
            -1
        }
    }
}

impl<R: SeekableRead> Archive<R> {
    pub fn new(r: R) -> Self {
        setlocale_once();
        unsafe {
            let raw = ffi::archive_read_new();
            if raw.is_null() {
                panic!("oom");
            }
            if ffi::archive_read_support_format_all(raw) != ffi::ARCHIVE_OK {
                panic!("not support format");
            }
            if ffi::archive_read_support_filter_all(raw) != ffi::ARCHIVE_OK {
                panic!("not support filter");
            }
            if r.bidirectional() {
                if ffi::archive_read_set_seek_callback(raw, Some(seek_callback::<R>)) !=
                   ffi::ARCHIVE_OK {
                    panic!("failed to set seek");
                }
            } else {
                if ffi::archive_read_set_skip_callback(raw, Some(skip_callback::<R>)) !=
                   ffi::ARCHIVE_OK {
                    panic!("failed to set skip");
                }
            }
            let proxy = Box::into_raw(Box::new(Proxy::new(r)));
            if ffi::archive_read_open(raw,
                                      proxy as *mut libc::c_void,
                                      None,
                                      Some(read_callback::<R>),
                                      None) != ffi::ARCHIVE_OK {
                panic!("failed to open");
            }
            Archive {
                raw: raw,
                eof: false,
                _proxy: Box::from_raw(proxy),
            }
        }
    }

    fn next_entry_raw(&mut self) -> Option<Result<Entry>> {
        if self.eof {
            return None;
        }

        let mut entry = ptr::null_mut();
        loop {
            match unsafe { ffi::archive_read_next_header(self.raw, &mut entry) } {
                ffi::ARCHIVE_OK => break,
                ffi::ARCHIVE_WARN => {
                    warn!("archive_read_next_header: {}",
                          unsafe { error_string(self.raw) });
                    break;
                }
                ffi::ARCHIVE_EOF => {
                    self.eof = true;
                    return None;
                }
                ffi::ARCHIVE_RETRY => {
                    // failed but retryable.
                    warn!("archive_read_next_header: {}, retry.",
                          unsafe { error_string(self.raw) });
                    continue;
                }
                ffi::ARCHIVE_FATAL => {
                    return Some(Err(Error::new(ErrorKind::Other,
                                               unsafe { error_string(self.raw) })));
                }
                _ => unreachable!(),
            }

        }
        Some(Ok(Entry::new(entry)))
    }

    pub fn next_entry<'a>(&'a mut self) -> Option<Result<RefEntry<'a, R>>> {
        self.next_entry_raw().map(|r| r.map(|e| RefEntry::new(e)))
    }

    pub fn find_map<F, T>(mut self, f: F) -> Option<Result<T>>
        where F: Fn(&Entry) -> Option<T>
    {
        loop {
            match self.next_entry_raw() {
                Some(Ok(e)) => {
                    if let Some(x) = f(&e) {
                        return Some(Ok(x));
                    }
                }
                Some(Err(e)) => return Some(Err(e)),
                None => return None,
            }
        }
    }

    pub fn find_open<P>(mut self, p: P) -> Option<Result<Reader<R>>>
        where P: Fn(&Entry) -> bool
    {
        loop {
            match self.next_entry_raw() {
                Some(Ok(e)) => {
                    if p(&e) {
                        break;
                    }
                }
                Some(Err(e)) => return Some(Err(e)),
                None => return None,
            }
        }
        Some(Ok(Reader::new(self)))
    }
}

impl<R: SeekableRead> Drop for Archive<R> {
    fn drop(&mut self) {
        unsafe { ffi::archive_read_free(self.raw) };
    }
}

pub struct Reader<R: SeekableRead> {
    a: Archive<R>,
    buf: *const libc::c_void,
    read_pos: usize,
    buf_size: libc::size_t,
    offset: libc::off_t,
    eof: bool,
}

impl<R: SeekableRead> Reader<R> {
    fn new(a: Archive<R>) -> Reader<R> {
        Reader {
            a: a,
            buf: ptr::null(),
            read_pos: 0,
            buf_size: 0,
            offset: 0,
            eof: false,
        }
    }

    fn fill_gap(&mut self, buf: &mut [u8]) -> usize {
        if self.read_pos < self.offset as usize {
            let l = min(buf.len(), (self.offset as usize) - self.read_pos);
            for x in &mut buf[..l] {
                *x = 0;
            }
            self.read_pos += l;
            return l;
        }
        0
    }

    fn read_data_block(&mut self) -> Result<()> {
        if self.eof {
            return Ok(());
        }

        while self.offset as usize + self.buf_size as usize <= self.read_pos {
            match unsafe {
                ffi::archive_read_data_block(self.a.raw,
                                             &mut self.buf,
                                             &mut self.buf_size,
                                             &mut self.offset)
            } {
                ffi::ARCHIVE_OK => continue,
                ffi::ARCHIVE_WARN => {
                    warn!("archive_read_data_block: {}",
                          unsafe { error_string(self.a.raw) });
                    continue;
                }
                ffi::ARCHIVE_EOF => {
                    self.eof = true;
                    return Ok(());
                }
                ffi::ARCHIVE_RETRY => {
                    // failed but retryable.
                    warn!("archive_read_data_block: {}, retry",
                          unsafe { error_string(self.a.raw) });
                    continue;
                }
                ffi::ARCHIVE_FATAL => {
                    return Err(Error::new(ErrorKind::Other, unsafe { error_string(self.a.raw) }));
                }
                _ => unreachable!(),
            }
        }
        Ok(())
    }
}

impl<R: SeekableRead> SeekExt for Reader<R> {
    fn bidirectional(&self) -> bool {
        return false;
    }
}

impl<R: SeekableRead> Read for Reader<R> {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        self.read_data_block()?;
        let n = self.fill_gap(buf);
        if n > 0 {
            return Ok(n);
        }
        let begin = self.read_pos - self.offset as usize;
        let l = min(buf.len(), self.buf_size - begin);
        unsafe {
            let p = (self.buf as *const u8).offset(begin as isize);
            ptr::copy_nonoverlapping(p, buf.as_mut_ptr(), l);
        }
        self.read_pos += l;
        Ok(l)
    }
}

impl<R: SeekableRead> Seek for Reader<R> {
    fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
        match pos {
            SeekFrom::Start(n) => self.read_pos = n as usize,
            SeekFrom::End(n) => unimplemented!(),
            SeekFrom::Current(n) => self.read_pos += n as usize,
        }
        Ok(self.read_pos as u64)
    }
}

pub struct Entry {
    entry: *mut ffi::Struct_archive_entry,
}

impl Entry {
    fn new(entry: *mut ffi::Struct_archive_entry) -> Entry {
        Entry { entry: entry }
    }

    pub fn pathname(&self) -> PathBuf {
        let c_str = unsafe { CStr::from_ptr(ffi::archive_entry_pathname(self.entry)) };
        PathBuf::from(c_str.to_string_lossy().as_ref())
    }

    pub fn size(&self) -> libc::int64_t {
        unsafe { ffi::archive_entry_size(self.entry) }
    }

    pub fn filetype(&self) -> libc::mode_t {
        unsafe { ffi::archive_entry_filetype(self.entry) }
    }
}

pub struct RefEntry<'a, R: SeekableRead + 'a> {
    e: Entry,
    _m: marker::PhantomData<&'a R>,
}

impl<'a, R: SeekableRead> RefEntry<'a, R> {
    fn new(e: Entry) -> RefEntry<'a, R> {
        RefEntry {
            e: e,
            _m: marker::PhantomData,
        }
    }

    pub fn pathname(&self) -> PathBuf {
        self.e.pathname()
    }

    pub fn size(&self) -> libc::int64_t {
        self.e.size()
    }

    pub fn filetype(&self) -> libc::mode_t {
        self.e.filetype()
    }
}
<|endoftext|>