<commit_before>use clap::{App, Arg};
use log::info;
use regex::Regex;
use std::ffi::OsStr;
use std::path::Path;

use inkwell::context::Context;
use inkwell::memory_buffer::MemoryBuffer;
use inkwell::module::Linkage;
use inkwell::module::Module;
use inkwell::types::{AnyType, FunctionType};
use inkwell::values::{AnyValue, BasicValue, BasicValueEnum};
use inkwell::values::{FunctionValue, GlobalValue, PointerValue};
use inkwell::AddressSpace;

fn main() {
    // Command line argument parsing (using clap)
    let matches = App::new("Test inkwell")
        // .version("0.1.0")
        // .author("")
        // .about("")
        .arg(
            Arg::with_name("initializers")
                .long("initializers")
                .short("i")
                .long("initializers")
                .help("Call initializers from main"),
        )
        .arg(
            Arg::with_name("seahorn")
                .long("seahorn")
                .short("s")
                .long("seahorn")
                .conflicts_with("initializers")
                .help("SeaHorn preparation (conflicts with --initializers)"),
        )
        .arg(
            Arg::with_name("verbosity")
                .short("v")
                .long("verbosity")
                .multiple(true)
                .help("Increase message verbosity"),
        )
        .arg(
            Arg::with_name("INPUT")
                .help("Input file name")
                .required(true)
                .index(1),
        )
        .arg(
            Arg::with_name("OUTPUT")
                .help("Output file name")
                .short("o")
                .long("output")
                .takes_value(true)
                .default_value("out"),
        )
        .get_matches();

    stderrlog::new()
        .verbosity(matches.occurrences_of("verbosity") as usize)
        .init()
        .unwrap();

    let path_in = matches
        .value_of("INPUT")
        .expect("ERROR: missing input file name argument.");
    let path_in = Path::new(path_in);

    let path_out = matches
        .value_of("OUTPUT")
        .expect("ERROR: missing output file name argument.");
    let path_out = Path::new(path_out);

    // Read the input file
    info!("Reading input from {}", path_in.to_str().unwrap());
    let memory_buffer =
        MemoryBuffer::create_from_file(path_in).expect("ERROR: failed to open file.");

    let context = Context::create();
    let mut module = context
        .create_module_from_ir(memory_buffer)
        .expect("ERROR: failed to create module.");

    if matches.is_present("initializers") {
        handle_initializers(&context, &mut module);
    }

    if matches.is_present("seahorn") {
        handle_main(&module);

        handle_panic(&module);

        replace_def_with_dec(
            &module,
            &Regex::new(r"^_ZN3std2io5stdio7_eprint17h[a-f0-9]{16}E$").unwrap(),
        );
        replace_def_with_dec(
            &module,
            &Regex::new(r"^_ZN3std2io5stdio6_print17h[a-f0-9]{16}E$").unwrap(),
        );
    }

    // Write output file
    info!("Writing output to {}", path_out.to_str().unwrap());
    if path_out.extension() == Some(OsStr::new("bc")) {
        // output bitcode
        // TODO: this function returns bool but the doc doesn't say anything about it.
        module.write_bitcode_to_path(path_out);
    } else {
        // output disassembled bitcode
        module
            .print_to_file(path_out)
            .expect("ERROR: failed to write to file.");
    }
}

////////////////////////////////////////////////////////////////
// Transformations associated with initializers
////////////////////////////////////////////////////////////////

fn handle_initializers(context: &Context, module: &mut Module) {
    if let Some(initializer) =
        collect_initializers(context, module, ".init_array", "__init_function")
    {
        info!(
            "Combined .init_array* initializers into '{}'",
            initializer.get_name().to_str().unwrap()
        );

        let main = module
            .get_function("main")
            .expect("Unable to find 'main' function");
        let mut args = get_fn_args(main);
        assert!(args.len() == 2); // We expect "i32 @main(i32 %0, i8** %1)"
        let i8_type = context.i8_type();
        let pi8_type = i8_type.ptr_type(AddressSpace::Generic);
        let ppi8_type = pi8_type.ptr_type(AddressSpace::Generic);
        args.push(ppi8_type.const_null().as_basic_value_enum());
        insert_call_at_head(context, initializer, args, main);
        info!(
            "Inserted call to '{}' into 'main'",
            initializer.get_name().to_str().unwrap()
        )
    } else {
        info!("No initializers to handle")
    }
}

/// Collect all the initializers in a section (whose name starts with 'prefix')
/// into a single function that calls all the initializers.
fn collect_initializers<'a>(
    context: &Context,
    module: &mut Module<'a>,
    prefix: &str,
    nm: &str,
) -> Option<FunctionValue<'a>> {
    let vs = collect_variables_in_section(module, prefix);
    for v in &vs {
        info!("Found initializer {:?}", v.get_name().to_str().unwrap());
    }

    let fps: Vec<PointerValue> = vs.iter().map(get_initializer_function).collect();

    if !fps.is_empty() {
        let fp = fps[0];

        // dereference the pointer type
        let fp_type = fp.get_type().get_element_type().into_function_type();
        info!("Initializer type {:?}", fp_type.print_to_string());

        Some(build_fanout(context, module, nm, fp_type, fps))
    } else {
        None
    }
}

/// Collect variables that are assigned to a section whose name matches prefix
fn collect_variables_in_section<'a>(module: &Module<'a>, prefix: &str) -> Vec<GlobalValue<'a>> {
    let mut vs = Vec::new();
    // todo: should implement an iterator for global values so that the following loop
    // becomes just iter().filter_map().filter_map().filter()
    let mut og = module.get_first_global();
    while let Some(g) = og {
        if let Some(s) = g.get_section() {
            if let Ok(s) = s.to_str() {
                if s.starts_with(prefix) {
                    vs.push(g)
                }
            }
        }
        og = g.get_next_global();
    }
    vs
}

/// Convert the contents of an initializer section to a function pointer.
///
/// Initializer sections contain structs where the first field is a function pointer cast to
/// some other type.
fn get_initializer_function<'a>(v: &GlobalValue<'a>) -> PointerValue<'a> {
    let i = v.get_initializer().unwrap().into_struct_value();
    assert!(i.get_num_operands() == 2); // expecting two fields in struct
    let i = i.get_operand(0).unwrap();
    assert!(i.get_num_operands() == 1); // expecting bitcast
    let fp = i.get_operand(0).unwrap();
    fp.into_pointer_value()
}

/// Given a list of functions of type 'ty', build a function that calls each function in order
///
/// Assumes (without checking) that return type is void
///
///     define void @fanout(i32 %0, i32 %1) {
///     entry:
///       call void @f1(i32 %0, i32 %1)
///       call void @f2(i32 %0, i32 %1)
///       call void @f3(i32 %0, i32 %1)
///       ret void
///     }
///
fn build_fanout<'a>(
    context: &Context,
    module: &mut Module<'a>,
    nm: &str,
    ty: FunctionType<'a>,
    fps: Vec<PointerValue<'a>>,
) -> FunctionValue<'a> {
    let function = module.add_function(nm, ty, None);
    let args = get_fn_args(function);
    let basic_block = context.append_basic_block(function, "entry");
    let builder = context.create_builder();
    builder.position_at_end(basic_block);

    for fp in fps {
        builder.build_call(fp, &args, "");
        builder.build_return(None);
        // println!("Built function {:?}", function)
    }

    function
}

fn get_fn_args<'a>(function: FunctionValue<'a>) -> Vec<BasicValueEnum<'a>> {
    (0..function.count_params())
        .map(|i| function.get_nth_param(i).unwrap())
        .collect()
}

fn insert_call_at_head<'a>(
    context: &Context,
    f: FunctionValue<'a>,
    args: Vec<BasicValueEnum<'a>>,
    insertee: FunctionValue<'a>,
) {
    let bb = insertee
        .get_first_basic_block()
        .expect("Unable to find function to insert function call into");
    let first_instruction = bb
        .get_first_instruction()
        .expect("Unable to find where to insert function call into function");
    let builder = context.create_builder();
    builder.position_before(&first_instruction);
    builder.build_call(f, &args, "");
}

////////////////////////////////////////////////////////////////
// Transformations associated with SeaHorn
////////////////////////////////////////////////////////////////

fn handle_main(module: &Module) {
    // Remove the main function rustc generates.
    if let Some(main) = module.get_function("main") {
        unsafe { main.delete(); }
        info!("Deleted 'main' (was added by rustc).");
    }

    // Change the linkage of mangled main function from internal to external.
    if let Some(main) = get_function(module, &Regex::new(r"4main17h[a-f0-9]{16}E$").unwrap()) {
        // main.set_linkage(Linkage::External);
        println!("MAIN: {}", main.get_name().to_str().unwrap());
    }
}

fn get_function<'ctx>(module: &'ctx Module, re: &Regex) -> Option<FunctionValue<'ctx>> {
    let mut op_fun = module.get_first_function();
    while let Some(fun) = op_fun {
        if re.is_match(fun.get_name().to_str()
                       .expect("ERROR: function name is not in valid UTF-8")) {
            return Some(fun);
        }
        op_fun = fun.get_next_function();
    }
    None
}

fn handle_panic(module: &Module) {
    // TODO: make "spanic" a CL arg.
    if let Some(spanic) = module.get_function("spanic") {
        if let Some(unwind) = module.get_function("rust_begin_unwind") {
            unwind.replace_all_uses_with(spanic);
            info!("Replaced panic handling ('rust_begin_unwind') with 'spanic'.");
        }
    }
}

fn replace_def_with_dec(module: &Module, re: &Regex) {
    if let Some(fun) = get_function(module, re) {
        for bb in fun.get_basic_blocks() {
            unsafe { bb.delete().unwrap(); }
        }
        fun.remove_personality_function();
        fun.set_linkage(Linkage::External);
        info!("Removed the implementation of '{}'.", fun.get_name().to_str().unwrap());
    }
}

////////////////////////////////////////////////////////////////
// End
////////////////////////////////////////////////////////////////
<commit_msg>Add '--test <TEST>'<commit_after>use clap::{App, Arg};
use log::info;
use regex::Regex;
use std::ffi::OsStr;
use std::path::Path;

use inkwell::context::Context;
use inkwell::memory_buffer::MemoryBuffer;
use inkwell::module::Linkage;
use inkwell::module::Module;
use inkwell::types::{AnyType, FunctionType};
use inkwell::values::{AnyValue, BasicValue, BasicValueEnum};
use inkwell::values::{FunctionValue, GlobalValue, PointerValue};
use inkwell::AddressSpace;

fn main() {
    // Command line argument parsing (using clap)
    let matches = App::new("Test inkwell")
        // .version("0.1.0")
        // .author("")
        // .about("")
        .arg(
            Arg::with_name("initializers")
                .short("i")
                .long("initializers")
                .help("Call initializers from main"),
        )
        .arg(
            Arg::with_name("seahorn")
                .short("s")
                .long("seahorn")
                .conflicts_with("initializers")
                .help("SeaHorn preparation (conflicts with --initializers)"),
        )
        .arg(
            Arg::with_name("TEST")
                .short("t")
                .long("test")
                .takes_value(true)
                .help("Select a specific test to run (instead of 'main')"),
        )
        .arg(
            Arg::with_name("verbosity")
                .short("v")
                .long("verbosity")
                .multiple(true)
                .help("Increase message verbosity"),
        )
        .arg(
            Arg::with_name("INPUT")
                .help("Input file name")
                .required(true)
                .index(1),
        )
        .arg(
            Arg::with_name("OUTPUT")
                .help("Output file name")
                .short("o")
                .long("output")
                .takes_value(true)
                .default_value("out"),
        )
        .get_matches();

    stderrlog::new()
        .verbosity(matches.occurrences_of("verbosity") as usize)
        .init()
        .unwrap();

    let path_in = matches
        .value_of("INPUT")
        .expect("ERROR: missing input file name argument.");
    let path_in = Path::new(path_in);

    let path_out = matches
        .value_of("OUTPUT")
        .expect("ERROR: missing output file name argument.");
    let path_out = Path::new(path_out);

    // Read the input file
    info!("Reading input from {}", path_in.to_str().unwrap());
    let memory_buffer =
        MemoryBuffer::create_from_file(path_in).expect("ERROR: failed to open file.");

    let context = Context::create();
    let mut module = context
        .create_module_from_ir(memory_buffer)
        .expect("ERROR: failed to create module.");

    if matches.is_present("initializers") {
        handle_initializers(&context, &mut module);
    }

    if matches.is_present("seahorn") {
        handle_main(&module, &matches.value_of("TEST"));

        handle_panic(&module);

        replace_def_with_dec(
            &module,
            &Regex::new(r"^_ZN3std2io5stdio7_eprint17h[a-f0-9]{16}E$").unwrap(),
        );
        replace_def_with_dec(
            &module,
            &Regex::new(r"^_ZN3std2io5stdio6_print17h[a-f0-9]{16}E$").unwrap(),
        );
    }

    // Write output file
    info!("Writing output to {}", path_out.to_str().unwrap());
    if path_out.extension() == Some(OsStr::new("bc")) {
        // output bitcode
        // TODO: this function returns bool but the doc doesn't say anything about it.
        module.write_bitcode_to_path(path_out);
    } else {
        // output disassembled bitcode
        module
            .print_to_file(path_out)
            .expect("ERROR: failed to write to file.");
    }
}

////////////////////////////////////////////////////////////////
// Transformations associated with initializers
////////////////////////////////////////////////////////////////

fn handle_initializers(context: &Context, module: &mut Module) {
    if let Some(initializer) =
        collect_initializers(context, module, ".init_array", "__init_function")
    {
        info!(
            "Combined .init_array* initializers into '{}'",
            initializer.get_name().to_str().unwrap()
        );

        let main = module
            .get_function("main")
            .expect("Unable to find 'main' function");
        let mut args = get_fn_args(main);
        assert!(args.len() == 2); // We expect "i32 @main(i32 %0, i8** %1)"
        let i8_type = context.i8_type();
        let pi8_type = i8_type.ptr_type(AddressSpace::Generic);
        let ppi8_type = pi8_type.ptr_type(AddressSpace::Generic);
        args.push(ppi8_type.const_null().as_basic_value_enum());
        insert_call_at_head(context, initializer, args, main);
        info!(
            "Inserted call to '{}' into 'main'",
            initializer.get_name().to_str().unwrap()
        )
    } else {
        info!("No initializers to handle")
    }
}

/// Collect all the initializers in a section (whose name starts with 'prefix')
/// into a single function that calls all the initializers.
fn collect_initializers<'a>(
    context: &Context,
    module: &mut Module<'a>,
    prefix: &str,
    nm: &str,
) -> Option<FunctionValue<'a>> {
    let vs = collect_variables_in_section(module, prefix);
    for v in &vs {
        info!("Found initializer {:?}", v.get_name().to_str().unwrap());
    }

    let fps: Vec<PointerValue> = vs.iter().map(get_initializer_function).collect();

    if !fps.is_empty() {
        let fp = fps[0];

        // dereference the pointer type
        let fp_type = fp.get_type().get_element_type().into_function_type();
        info!("Initializer type {:?}", fp_type.print_to_string());

        Some(build_fanout(context, module, nm, fp_type, fps))
    } else {
        None
    }
}

/// Collect variables that are assigned to a section whose name matches prefix
fn collect_variables_in_section<'a>(module: &Module<'a>, prefix: &str) -> Vec<GlobalValue<'a>> {
    let mut vs = Vec::new();
    // todo: should implement an iterator for global values so that the following loop
    // becomes just iter().filter_map().filter_map().filter()
    let mut og = module.get_first_global();
    while let Some(g) = og {
        if let Some(s) = g.get_section() {
            if let Ok(s) = s.to_str() {
                if s.starts_with(prefix) {
                    vs.push(g)
                }
            }
        }
        og = g.get_next_global();
    }
    vs
}

/// Convert the contents of an initializer section to a function pointer.
///
/// Initializer sections contain structs where the first field is a function pointer cast to
/// some other type.
fn get_initializer_function<'a>(v: &GlobalValue<'a>) -> PointerValue<'a> {
    let i = v.get_initializer().unwrap().into_struct_value();
    assert!(i.get_num_operands() == 2); // expecting two fields in struct
    let i = i.get_operand(0).unwrap();
    assert!(i.get_num_operands() == 1); // expecting bitcast
    let fp = i.get_operand(0).unwrap();
    fp.into_pointer_value()
}

/// Given a list of functions of type 'ty', build a function that calls each function in order
///
/// Assumes (without checking) that return type is void
///
///     define void @fanout(i32 %0, i32 %1) {
///     entry:
///       call void @f1(i32 %0, i32 %1)
///       call void @f2(i32 %0, i32 %1)
///       call void @f3(i32 %0, i32 %1)
///       ret void
///     }
///
fn build_fanout<'a>(
    context: &Context,
    module: &mut Module<'a>,
    nm: &str,
    ty: FunctionType<'a>,
    fps: Vec<PointerValue<'a>>,
) -> FunctionValue<'a> {
    let function = module.add_function(nm, ty, None);
    let args = get_fn_args(function);
    let basic_block = context.append_basic_block(function, "entry");
    let builder = context.create_builder();
    builder.position_at_end(basic_block);

    for fp in fps {
        builder.build_call(fp, &args, "");
        builder.build_return(None);
        // println!("Built function {:?}", function)
    }

    function
}

fn get_fn_args<'a>(function: FunctionValue<'a>) -> Vec<BasicValueEnum<'a>> {
    (0..function.count_params())
        .map(|i| function.get_nth_param(i).unwrap())
        .collect()
}

fn insert_call_at_head<'a>(
    context: &Context,
    f: FunctionValue<'a>,
    args: Vec<BasicValueEnum<'a>>,
    insertee: FunctionValue<'a>,
) {
    let bb = insertee
        .get_first_basic_block()
        .expect("Unable to find function to insert function call into");
    let first_instruction = bb
        .get_first_instruction()
        .expect("Unable to find where to insert function call into function");
    let builder = context.create_builder();
    builder.position_before(&first_instruction);
    builder.build_call(f, &args, "");
}

////////////////////////////////////////////////////////////////
// Transformations associated with SeaHorn
////////////////////////////////////////////////////////////////

fn handle_main(module: &Module, otest: &Option<&str>) {
    // Remove the main function rustc generates.
    if let Some(main) = module.get_function("main") {
        unsafe { main.delete(); }
        info!("Deleted 'main' (was added by rustc).");
    }

    let re = match otest {
        Some(test) => {
            Regex::new(&(test.len().to_string() + test + r"17h[a-f0-9]{16}E$")).unwrap()
        }
        None => Regex::new(r"4main17h[a-f0-9]{16}E$").unwrap()
    };

    // Print the entry point function name
    if let Some(fun) = get_function(module, &re) {
        // Change the linkage of mangled main function from internal to external.
        // fun.set_linkage(Linkage::External);
        println!("ENTRY: {}", fun.get_name().to_str().unwrap());
    }
}

fn get_function<'ctx>(module: &'ctx Module, re: &Regex) -> Option<FunctionValue<'ctx>> {
    let mut op_fun = module.get_first_function();
    while let Some(fun) = op_fun {
        if re.is_match(fun.get_name().to_str()
                       .expect("ERROR: function name is not in valid UTF-8")) {
            return Some(fun);
        }
        op_fun = fun.get_next_function();
    }
    None
}

fn handle_panic(module: &Module) {
    // TODO: make "spanic" a CL arg.
    if let Some(spanic) = module.get_function("spanic") {
        if let Some(unwind) = module.get_function("rust_begin_unwind") {
            unwind.replace_all_uses_with(spanic);
            info!("Replaced panic handling ('rust_begin_unwind') with 'spanic'.");
        }
    }
}

fn replace_def_with_dec(module: &Module, re: &Regex) {
    if let Some(fun) = get_function(module, re) {
        for bb in fun.get_basic_blocks() {
            unsafe { bb.delete().unwrap(); }
        }
        fun.remove_personality_function();
        fun.set_linkage(Linkage::External);
        info!("Removed the implementation of '{}'.", fun.get_name().to_str().unwrap());
    }
}

////////////////////////////////////////////////////////////////
// End
////////////////////////////////////////////////////////////////
<|endoftext|>


<commit_before><commit_msg>Add id reporting in imag-category<commit_after><|endoftext|>


<commit_before>use super::{Projector, Projection};
use geometry::RegularSpace;
use geometry::dimensions::{BoundedDimension, Continuous};
use ndarray::Array1;
use utils::cartesian_product;

/// Polynomial basis projector.
#[derive(Clone, Serialize, Deserialize)]
pub struct Polynomial {
    order: u8,
    limits: Vec<(f64, f64)>,
    exponents: Vec<Vec<i32>>,
}

impl Polynomial {
    pub fn new(order: u8, limits: Vec<(f64, f64)>) -> Self {
        let exponents = Polynomial::make_exponents(order, limits.len());

        Polynomial {
            order: order,
            limits: limits,
            exponents: exponents,
        }
    }

    pub fn from_space(order: u8, input_space: RegularSpace<Continuous>) -> Self {
        Polynomial::new(order, input_space.iter().map(|d| d.limits()).collect())
    }

    fn make_exponents(order: u8, dim: usize) -> Vec<Vec<i32>> {
        let dcs = vec![(0..(order+1)).map(|v| v as i32).collect::<Vec<i32>>(); dim];
        let mut exponents = cartesian_product(&dcs);

        exponents.sort_by(|a, b| b.partial_cmp(a).unwrap());
        exponents.dedup();

        exponents
    }
}

impl Projector<RegularSpace<Continuous>> for Polynomial {
    fn project(&self, input: &Vec<f64>) -> Projection {
        let scaled_state = input.iter().enumerate().map(|(i, v)| {
            (v - self.limits[i].0) / (self.limits[i].1 - self.limits[i].0)
        }).map(|v| 2.0*v - 1.0).collect::<Vec<f64>>();

        let activations = self.exponents.iter().map(|exps| {
            scaled_state.iter().zip(exps).map(|(v, e)| v.powi(*e)).product()
        });

        Projection::Dense(Array1::from_iter(activations))
    }

    fn dim(&self) -> usize {
        self.limits.len()
    }

    fn size(&self) -> usize {
        self.exponents.len()
    }

    fn activation(&self) -> usize {
        self.size()
    }

    fn equivalent(&self, other: &Self) -> bool {
        self.order == other.order && self.limits == other.limits
    }
}
<commit_msg>refactor: replace `Array1` with `Vector` in `Polynomial`<commit_after>use Vector;
use super::{Projector, Projection};
use geometry::RegularSpace;
use geometry::dimensions::{BoundedDimension, Continuous};
use utils::cartesian_product;

/// Polynomial basis projector.
#[derive(Clone, Serialize, Deserialize)]
pub struct Polynomial {
    order: u8,
    limits: Vec<(f64, f64)>,
    exponents: Vec<Vec<i32>>,
}

impl Polynomial {
    pub fn new(order: u8, limits: Vec<(f64, f64)>) -> Self {
        let exponents = Polynomial::make_exponents(order, limits.len());

        Polynomial {
            order: order,
            limits: limits,
            exponents: exponents,
        }
    }

    pub fn from_space(order: u8, input_space: RegularSpace<Continuous>) -> Self {
        Polynomial::new(order, input_space.iter().map(|d| d.limits()).collect())
    }

    fn make_exponents(order: u8, dim: usize) -> Vec<Vec<i32>> {
        let dcs = vec![(0..(order+1)).map(|v| v as i32).collect::<Vec<i32>>(); dim];
        let mut exponents = cartesian_product(&dcs);

        exponents.sort_by(|a, b| b.partial_cmp(a).unwrap());
        exponents.dedup();

        exponents
    }
}

impl Projector<RegularSpace<Continuous>> for Polynomial {
    fn project(&self, input: &Vec<f64>) -> Projection {
        let scaled_state = input.iter().enumerate().map(|(i, v)| {
            (v - self.limits[i].0) / (self.limits[i].1 - self.limits[i].0)
        }).map(|v| 2.0*v - 1.0).collect::<Vec<f64>>();

        let activations = self.exponents.iter().map(|exps| {
            scaled_state.iter().zip(exps).map(|(v, e)| v.powi(*e)).product()
        });

        Projection::Dense(Vector::from_iter(activations))
    }

    fn dim(&self) -> usize {
        self.limits.len()
    }

    fn size(&self) -> usize {
        self.exponents.len()
    }

    fn activation(&self) -> usize {
        self.size()
    }

    fn equivalent(&self, other: &Self) -> bool {
        self.order == other.order && self.limits == other.limits
    }
}
<|endoftext|>


<commit_before>export Msg, Prefetch, GetImage, Exit;
export ImageResponseMsg, ImageReady, ImageNotReady;
export ImageCacheTask;
export image_cache_task;

import image::base::{Image, load_from_memory, test_image_bin};
import std::net::url::url;
import util::url::{make_url, UrlMap, url_map};
import comm::{chan, port};
import task::spawn_listener;
import resource::resource_task;
import resource_task::ResourceTask;
import core::arc::arc;
import clone_arc = core::arc::clone;

enum Msg {
    Prefetch(url),
    GetImage(url, chan<ImageResponseMsg>),
    Exit
}

enum ImageResponseMsg {
    ImageReady(arc<~Image>),
    ImageNotReady
}

type ImageCacheTask = chan<Msg>;

fn image_cache_task(resource_task: ResourceTask) -> ImageCacheTask {
    do spawn_listener |from_client| {
        ImageCache {
            resource_task: resource_task,
            from_client: from_client,
            prefetch_map: url_map(),
            image_map: url_map()
        }.run();
    }
}

struct ImageCache {
    resource_task: ResourceTask;
    from_client: port<Msg>;
    prefetch_map: UrlMap<@PrefetchData>;
    image_map: UrlMap<@arc<~Image>>;
}

struct PrefetchData {
    response_port: port<resource_task::ProgressMsg>;
    mut data: ~[u8];
}

impl ImageCache {

    fn run() {

        loop {
            match self.from_client.recv() {
              Prefetch(url) => self.prefetch(url),
              GetImage(url, response) => self.get_image(url, response),
              Exit => break
            }
        }
    }

    /*priv*/ fn prefetch(url: url) {
        if self.image_map.contains_key(url) {
            // We've already decoded this image
            return
        }

        if self.prefetch_map.contains_key(url) {
            // We're already waiting for this image
            return
        }
        let response_port = port();
        self.resource_task.send(resource_task::Load(url, response_port.chan()));

        let prefetch_data = @PrefetchData {
            response_port: response_port,
            data: ~[]
        };

        self.prefetch_map.insert(url, prefetch_data);
    }

    /*priv*/ fn get_image(url: url, response: chan<ImageResponseMsg>) {
        match self.prefetch_map.find(url) {
          some(prefetch_data) => {

            let mut image_sent = false;

            while prefetch_data.response_port.peek() {
                match prefetch_data.response_port.recv() {
                  resource_task::Payload(data) => {
                    prefetch_data.data += data;
                  }
                  resource_task::Done(result::ok(*)) => {
                    // We've got the entire image binary
                    let mut data = ~[];
                    data <-> prefetch_data.data;
                    // FIXME: Need to do this in parallel
                    let image = @arc(~load_from_memory(data));
                    response.send(ImageReady(clone_arc(image)));
                    self.prefetch_map.remove(url);
                    self.image_map.insert(url, image);
                    image_sent = true;
                    break;
                  }
                  resource_task::Done(result::err(*)) => {
                    // FIXME: need to actually report the failed image load
                    self.prefetch_map.remove(url);
                    break;
                  }
                }
            }

            if !image_sent {
                response.send(ImageNotReady);
            }
          }
          none => {
            // FIXME: Probably faster to hit this map before the prefetch map
            match self.image_map.find(url) {
              some(image) => response.send(ImageReady(clone_arc(image))),
              none => fail ~"got a request for image data without prefetch"
            }
          }
        }
    }
}

#[test]
fn should_exit_on_request() {

    let mock_resource_task = do spawn_listener |from_client| {

        // infer me
        let from_client: port<resource_task::ControlMsg> = from_client;

        loop {
            match from_client.recv() {
              resource_task::Exit => break,
              _ => ()
            }
        }
    };

    let image_cache_task = image_cache_task(mock_resource_task);
    let url = make_url(~"file", none);

    image_cache_task.send(Exit);
    mock_resource_task.send(resource_task::Exit);
}

#[test]
#[should_fail]
fn should_fail_if_unprefetched_image_is_requested() {

    let mock_resource_task = do spawn_listener |from_client| {
    };

    let image_cache_task = image_cache_task(mock_resource_task);
    let url = make_url(~"file", none);

    let request = port();
    image_cache_task.send(GetImage(url, request.chan()));
    request.recv();
}

#[test]
fn should_request_url_from_resource_task_on_prefetch() {
    let url_requested = port();
    let url_requested_chan = url_requested.chan();

    let mock_resource_task = do spawn_listener |from_client| {

        // infer me
        let from_client: port<resource_task::ControlMsg> = from_client;

        loop {
            match from_client.recv() {
              resource_task::Load(url, _) => url_requested_chan.send(()),
              resource_task::Exit => break
            }
        }
    };

    let image_cache_task = image_cache_task(mock_resource_task);
    let url = make_url(~"file", none);

    image_cache_task.send(Prefetch(url));
    url_requested.recv();
    image_cache_task.send(Exit);
    mock_resource_task.send(resource_task::Exit);
}

#[test]
fn should_not_request_url_from_resource_task_on_multiple_prefetches() {
    let url_requested = port();
    let url_requested_chan = url_requested.chan();

    let mock_resource_task = do spawn_listener |from_client| {

        // infer me
        let from_client: port<resource_task::ControlMsg> = from_client;

        loop {
            match from_client.recv() {
              resource_task::Load(url, _) => url_requested_chan.send(()),
              resource_task::Exit => break
            }
        }
    };

    let image_cache_task = image_cache_task(mock_resource_task);
    let url = make_url(~"file", none);

    image_cache_task.send(Prefetch(url));
    image_cache_task.send(Prefetch(url));
    url_requested.recv();
    image_cache_task.send(Exit);
    mock_resource_task.send(resource_task::Exit);
    assert !url_requested.peek()
}

#[test]
fn should_return_image_not_ready_if_data_has_not_arrived() {
    let mock_resource_task = do spawn_listener |from_client| {

        // infer me
        let from_client: port<resource_task::ControlMsg> = from_client;

        loop {
            match from_client.recv() {
              resource_task::Exit => break,
              _ => ()
            }
        }
    };

    let image_cache_task = image_cache_task(mock_resource_task);
    let url = make_url(~"file", none);

    image_cache_task.send(Prefetch(url));
    let response_port = port();
    image_cache_task.send(GetImage(url, response_port.chan()));
    assert response_port.recv() == ImageNotReady;
    image_cache_task.send(Exit);
    mock_resource_task.send(resource_task::Exit);
}

#[test]
fn should_return_decoded_image_data_if_data_has_arrived() {

    let image_bin_sent = port();
    let image_bin_sent_chan = image_bin_sent.chan();

    let mock_resource_task = do spawn_listener |from_client| {

        // infer me
        let from_client: port<resource_task::ControlMsg> = from_client;

        loop {
            match from_client.recv() {
              resource_task::Load(_, response) => {
                response.send(resource_task::Payload(test_image_bin()));
                response.send(resource_task::Done(result::ok(())));
                image_bin_sent_chan.send(());
              }
              resource_task::Exit => break
            }
        }
    };

    let image_cache_task = image_cache_task(mock_resource_task);
    let url = make_url(~"file", none);

    image_cache_task.send(Prefetch(url));

    // Wait until our mock resource task has sent the image to the image cache
    image_bin_sent.recv();

    let response_port = port();
    image_cache_task.send(GetImage(url, response_port.chan()));
    match response_port.recv() {
      ImageReady(_) => (),
      _ => fail
    }

    image_cache_task.send(Exit);
    mock_resource_task.send(resource_task::Exit);
}

#[test]
fn should_return_decoded_image_data_for_multiple_requests() {

    let image_bin_sent = port();
    let image_bin_sent_chan = image_bin_sent.chan();

    let mock_resource_task = do spawn_listener |from_client| {

        // infer me
        let from_client: port<resource_task::ControlMsg> = from_client;

        loop {
            match from_client.recv() {
              resource_task::Load(_, response) => {
                response.send(resource_task::Payload(test_image_bin()));
                response.send(resource_task::Done(result::ok(())));
                image_bin_sent_chan.send(());
              }
              resource_task::Exit => break
            }
        }
    };

    let image_cache_task = image_cache_task(mock_resource_task);
    let url = make_url(~"file", none);

    image_cache_task.send(Prefetch(url));

    // Wait until our mock resource task has sent the image to the image cache
    image_bin_sent.recv();

    for iter::repeat(2) {
        let response_port = port();
        image_cache_task.send(GetImage(url, response_port.chan()));
        match response_port.recv() {
          ImageReady(_) => (),
          _ => fail
        }
    }

    image_cache_task.send(Exit);
    mock_resource_task.send(resource_task::Exit);
}

#[test]
fn should_not_request_image_from_resource_task_if_image_is_already_available() {

    let image_bin_sent = port();
    let image_bin_sent_chan = image_bin_sent.chan();

    let resource_task_exited = port();
    let resource_task_exited_chan = resource_task_exited.chan();

    let mock_resource_task = do spawn_listener |from_client| {

        // infer me
        let from_client: port<resource_task::ControlMsg> = from_client;

        loop {
            match from_client.recv() {
              resource_task::Load(_, response) => {
                response.send(resource_task::Payload(test_image_bin()));
                response.send(resource_task::Done(result::ok(())));
                image_bin_sent_chan.send(());
              }
              resource_task::Exit => {
                resource_task_exited_chan.send(());
                break
              }
            }
        }
    };

    let image_cache_task = image_cache_task(mock_resource_task);
    let url = make_url(~"file", none);

    image_cache_task.send(Prefetch(url));

    // Wait until our mock resource task has sent the image to the image cache
    image_bin_sent.recv();

    let response_port = port();
    image_cache_task.send(GetImage(url, response_port.chan()));
    match response_port.recv() {
      ImageReady(_) => (),
      _ => fail
    }

    image_cache_task.send(Prefetch(url));

    let response_port = port();
    image_cache_task.send(GetImage(url, response_port.chan()));
    response_port.recv();

    image_cache_task.send(Exit);
    mock_resource_task.send(resource_task::Exit);

    resource_task_exited.recv();

    // Our resource task should not have received another request for the image
    // because it's already cached
    assert !image_bin_sent.peek();
}

#[test]
fn should_return_not_ready_if_image_bin_cannot_be_fetched() {

    let image_bin_sent = port();
    let image_bin_sent_chan = image_bin_sent.chan();

    let mock_resource_task = do spawn_listener |from_client| {

        // infer me
        let from_client: port<resource_task::ControlMsg> = from_client;

        loop {
            match from_client.recv() {
              resource_task::Load(_, response) => {
                response.send(resource_task::Payload(test_image_bin()));
                // ERROR fetching image
                response.send(resource_task::Done(result::err(())));
                image_bin_sent_chan.send(());
              }
              resource_task::Exit => break
            }
        }
    };

    let image_cache_task = image_cache_task(mock_resource_task);
    let url = make_url(~"file", none);

    image_cache_task.send(Prefetch(url));

    // Wait until our mock resource task has sent the image to the image cache
    image_bin_sent.recv();

    let response_port = port();
    image_cache_task.send(GetImage(url, response_port.chan()));
    match response_port.recv() {
      ImageNotReady => (),
      _ => fail
    }

    image_cache_task.send(Exit);
    mock_resource_task.send(resource_task::Exit);
}
<commit_msg>Restructure ImageCache.get_image<commit_after>export Msg, Prefetch, GetImage, Exit;
export ImageResponseMsg, ImageReady, ImageNotReady;
export ImageCacheTask;
export image_cache_task;

import image::base::{Image, load_from_memory, test_image_bin};
import std::net::url::url;
import util::url::{make_url, UrlMap, url_map};
import comm::{chan, port};
import task::spawn_listener;
import resource::resource_task;
import resource_task::ResourceTask;
import core::arc::arc;
import clone_arc = core::arc::clone;

enum Msg {
    Prefetch(url),
    GetImage(url, chan<ImageResponseMsg>),
    Exit
}

enum ImageResponseMsg {
    ImageReady(arc<~Image>),
    ImageNotReady
}

type ImageCacheTask = chan<Msg>;

fn image_cache_task(resource_task: ResourceTask) -> ImageCacheTask {
    do spawn_listener |from_client| {
        ImageCache {
            resource_task: resource_task,
            from_client: from_client,
            prefetch_map: url_map(),
            image_map: url_map()
        }.run();
    }
}

struct ImageCache {
    resource_task: ResourceTask;
    from_client: port<Msg>;
    prefetch_map: UrlMap<@PrefetchData>;
    image_map: UrlMap<@arc<~Image>>;
}

struct PrefetchData {
    response_port: port<resource_task::ProgressMsg>;
    mut data: ~[u8];
}

impl ImageCache {

    fn run() {

        loop {
            match self.from_client.recv() {
              Prefetch(url) => self.prefetch(url),
              GetImage(url, response) => self.get_image(url, response),
              Exit => break
            }
        }
    }

    /*priv*/ fn prefetch(url: url) {
        if self.image_map.contains_key(url) {
            // We've already decoded this image
            return
        }

        if self.prefetch_map.contains_key(url) {
            // We're already waiting for this image
            return
        }
        let response_port = port();
        self.resource_task.send(resource_task::Load(url, response_port.chan()));

        let prefetch_data = @PrefetchData {
            response_port: response_port,
            data: ~[]
        };

        self.prefetch_map.insert(url, prefetch_data);
    }

    /*priv*/ fn get_image(url: url, response: chan<ImageResponseMsg>) {
        match self.image_map.find(url) {
          some(image) => {
            response.send(ImageReady(clone_arc(image)));
            return
          }
          none => ()
        }

        match self.prefetch_map.find(url) {
          some(prefetch_data) => {

            let mut image_sent = false;

            while prefetch_data.response_port.peek() {
                match prefetch_data.response_port.recv() {
                  resource_task::Payload(data) => {
                    prefetch_data.data += data;
                  }
                  resource_task::Done(result::ok(*)) => {
                    // We've got the entire image binary
                    let mut data = ~[];
                    data <-> prefetch_data.data;
                    // FIXME: Need to do this in parallel
                    let image = @arc(~load_from_memory(data));
                    response.send(ImageReady(clone_arc(image)));
                    self.prefetch_map.remove(url);
                    self.image_map.insert(url, image);
                    image_sent = true;
                    break;
                  }
                  resource_task::Done(result::err(*)) => {
                    // FIXME: need to actually report the failed image load
                    self.prefetch_map.remove(url);
                    break;
                  }
                }
            }

            if !image_sent {
                response.send(ImageNotReady);
            }
          }
          none => fail ~"got a request for image data without prefetch"
        }
    }
}

#[test]
fn should_exit_on_request() {

    let mock_resource_task = do spawn_listener |from_client| {

        // infer me
        let from_client: port<resource_task::ControlMsg> = from_client;

        loop {
            match from_client.recv() {
              resource_task::Exit => break,
              _ => ()
            }
        }
    };

    let image_cache_task = image_cache_task(mock_resource_task);
    let url = make_url(~"file", none);

    image_cache_task.send(Exit);
    mock_resource_task.send(resource_task::Exit);
}

#[test]
#[should_fail]
fn should_fail_if_unprefetched_image_is_requested() {

    let mock_resource_task = do spawn_listener |from_client| {
    };

    let image_cache_task = image_cache_task(mock_resource_task);
    let url = make_url(~"file", none);

    let request = port();
    image_cache_task.send(GetImage(url, request.chan()));
    request.recv();
}

#[test]
fn should_request_url_from_resource_task_on_prefetch() {
    let url_requested = port();
    let url_requested_chan = url_requested.chan();

    let mock_resource_task = do spawn_listener |from_client| {

        // infer me
        let from_client: port<resource_task::ControlMsg> = from_client;

        loop {
            match from_client.recv() {
              resource_task::Load(url, _) => url_requested_chan.send(()),
              resource_task::Exit => break
            }
        }
    };

    let image_cache_task = image_cache_task(mock_resource_task);
    let url = make_url(~"file", none);

    image_cache_task.send(Prefetch(url));
    url_requested.recv();
    image_cache_task.send(Exit);
    mock_resource_task.send(resource_task::Exit);
}

#[test]
fn should_not_request_url_from_resource_task_on_multiple_prefetches() {
    let url_requested = port();
    let url_requested_chan = url_requested.chan();

    let mock_resource_task = do spawn_listener |from_client| {

        // infer me
        let from_client: port<resource_task::ControlMsg> = from_client;

        loop {
            match from_client.recv() {
              resource_task::Load(url, _) => url_requested_chan.send(()),
              resource_task::Exit => break
            }
        }
    };

    let image_cache_task = image_cache_task(mock_resource_task);
    let url = make_url(~"file", none);

    image_cache_task.send(Prefetch(url));
    image_cache_task.send(Prefetch(url));
    url_requested.recv();
    image_cache_task.send(Exit);
    mock_resource_task.send(resource_task::Exit);
    assert !url_requested.peek()
}

#[test]
fn should_return_image_not_ready_if_data_has_not_arrived() {
    let mock_resource_task = do spawn_listener |from_client| {

        // infer me
        let from_client: port<resource_task::ControlMsg> = from_client;

        loop {
            match from_client.recv() {
              resource_task::Exit => break,
              _ => ()
            }
        }
    };

    let image_cache_task = image_cache_task(mock_resource_task);
    let url = make_url(~"file", none);

    image_cache_task.send(Prefetch(url));
    let response_port = port();
    image_cache_task.send(GetImage(url, response_port.chan()));
    assert response_port.recv() == ImageNotReady;
    image_cache_task.send(Exit);
    mock_resource_task.send(resource_task::Exit);
}

#[test]
fn should_return_decoded_image_data_if_data_has_arrived() {

    let image_bin_sent = port();
    let image_bin_sent_chan = image_bin_sent.chan();

    let mock_resource_task = do spawn_listener |from_client| {

        // infer me
        let from_client: port<resource_task::ControlMsg> = from_client;

        loop {
            match from_client.recv() {
              resource_task::Load(_, response) => {
                response.send(resource_task::Payload(test_image_bin()));
                response.send(resource_task::Done(result::ok(())));
                image_bin_sent_chan.send(());
              }
              resource_task::Exit => break
            }
        }
    };

    let image_cache_task = image_cache_task(mock_resource_task);
    let url = make_url(~"file", none);

    image_cache_task.send(Prefetch(url));

    // Wait until our mock resource task has sent the image to the image cache
    image_bin_sent.recv();

    let response_port = port();
    image_cache_task.send(GetImage(url, response_port.chan()));
    match response_port.recv() {
      ImageReady(_) => (),
      _ => fail
    }

    image_cache_task.send(Exit);
    mock_resource_task.send(resource_task::Exit);
}

#[test]
fn should_return_decoded_image_data_for_multiple_requests() {

    let image_bin_sent = port();
    let image_bin_sent_chan = image_bin_sent.chan();

    let mock_resource_task = do spawn_listener |from_client| {

        // infer me
        let from_client: port<resource_task::ControlMsg> = from_client;

        loop {
            match from_client.recv() {
              resource_task::Load(_, response) => {
                response.send(resource_task::Payload(test_image_bin()));
                response.send(resource_task::Done(result::ok(())));
                image_bin_sent_chan.send(());
              }
              resource_task::Exit => break
            }
        }
    };

    let image_cache_task = image_cache_task(mock_resource_task);
    let url = make_url(~"file", none);

    image_cache_task.send(Prefetch(url));

    // Wait until our mock resource task has sent the image to the image cache
    image_bin_sent.recv();

    for iter::repeat(2) {
        let response_port = port();
        image_cache_task.send(GetImage(url, response_port.chan()));
        match response_port.recv() {
          ImageReady(_) => (),
          _ => fail
        }
    }

    image_cache_task.send(Exit);
    mock_resource_task.send(resource_task::Exit);
}

#[test]
fn should_not_request_image_from_resource_task_if_image_is_already_available() {

    let image_bin_sent = port();
    let image_bin_sent_chan = image_bin_sent.chan();

    let resource_task_exited = port();
    let resource_task_exited_chan = resource_task_exited.chan();

    let mock_resource_task = do spawn_listener |from_client| {

        // infer me
        let from_client: port<resource_task::ControlMsg> = from_client;

        loop {
            match from_client.recv() {
              resource_task::Load(_, response) => {
                response.send(resource_task::Payload(test_image_bin()));
                response.send(resource_task::Done(result::ok(())));
                image_bin_sent_chan.send(());
              }
              resource_task::Exit => {
                resource_task_exited_chan.send(());
                break
              }
            }
        }
    };

    let image_cache_task = image_cache_task(mock_resource_task);
    let url = make_url(~"file", none);

    image_cache_task.send(Prefetch(url));

    // Wait until our mock resource task has sent the image to the image cache
    image_bin_sent.recv();

    let response_port = port();
    image_cache_task.send(GetImage(url, response_port.chan()));
    match response_port.recv() {
      ImageReady(_) => (),
      _ => fail
    }

    image_cache_task.send(Prefetch(url));

    let response_port = port();
    image_cache_task.send(GetImage(url, response_port.chan()));
    response_port.recv();

    image_cache_task.send(Exit);
    mock_resource_task.send(resource_task::Exit);

    resource_task_exited.recv();

    // Our resource task should not have received another request for the image
    // because it's already cached
    assert !image_bin_sent.peek();
}

#[test]
fn should_return_not_ready_if_image_bin_cannot_be_fetched() {

    let image_bin_sent = port();
    let image_bin_sent_chan = image_bin_sent.chan();

    let mock_resource_task = do spawn_listener |from_client| {

        // infer me
        let from_client: port<resource_task::ControlMsg> = from_client;

        loop {
            match from_client.recv() {
              resource_task::Load(_, response) => {
                response.send(resource_task::Payload(test_image_bin()));
                // ERROR fetching image
                response.send(resource_task::Done(result::err(())));
                image_bin_sent_chan.send(());
              }
              resource_task::Exit => break
            }
        }
    };

    let image_cache_task = image_cache_task(mock_resource_task);
    let url = make_url(~"file", none);

    image_cache_task.send(Prefetch(url));

    // Wait until our mock resource task has sent the image to the image cache
    image_bin_sent.recv();

    let response_port = port();
    image_cache_task.send(GetImage(url, response_port.chan()));
    match response_port.recv() {
      ImageNotReady => (),
      _ => fail
    }

    image_cache_task.send(Exit);
    mock_resource_task.send(resource_task::Exit);
}
<|endoftext|>


<commit_before>use story_builder::article_provider::*;
use std::rc::Rc;
use std::borrow::Borrow;
use std::ops::Deref;

pub struct StoryBuilder<'a> {
    article_provider: &'a ArticleProvider,
    max_depth: u8,
}

impl <'a> StoryBuilder<'a>  {
    pub fn new(article_provider: &'a ArticleProvider) -> StoryBuilder<'a> {
        StoryBuilder {
            article_provider,
            max_depth: 5, // default value for now
        }
    }

    pub fn build_story(&self, start_topic: &str , end_topic: &str) -> Result<String, String> {
        let end_topic = &end_topic.to_lowercase();
                   // If one of the topics is an empty string, do not try to make a story out of it.
        if start_topic == "" {
            return Err("Missing start topic.".to_owned());
        }
        if end_topic == "" {
            return Err("Missing end topic.".to_owned());
        }

                   // If both topics are the same,
                   // there is no point in trying to figure out the story.
        if start_topic == end_topic {
            return Err("No story to build; same start and end topics.".to_owned());
        }

                   // Load the first article
        let start_article = match self.article_provider.get(start_topic) {
            Some(start_article) => start_article,
            None => {return Err(self.build_suggestions_msg(start_topic));},
        };

                   // Load the end article
        let end_article = match self.article_provider.get(end_topic) {
            Some(end_topic) => end_topic,
            None => {return Err(self.build_suggestions_msg(end_topic));},
        };

                   /* To build a story, we need to build a tree starting at the start_article
                      node and going down in a "breadth-first" way; that way, once we find
                      the end note, we know it is the shortest path to it. Also, going depth-first
                      will be impossibly long to complete sincethe depth of the wikipedia
                      article tree is almost infinite. */
                   /* We look for a paragraph that holds a reference to our end topic
                      somewhere in the last level we fetched: */
        let mut last_level: Vec<Rc<ArticleNode>> = vec![Rc::new(ArticleNode::new(start_article))]; // starts with start article
        for i in 0..self.max_depth { // To prevent overloading the system, stop after X level deep
                       // Start by loading the next level of articles:
           if i > 0 {
               // Any other iteration: go one level deeper:
               let mut current_level = vec![];
               for rc_article_node in last_level.iter() {
                       /* Iterate on each paragraph of this article, then map on it to
                          get the article for each related topic in it. */
               let rc_local = rc_article_node.clone();
               let article = rc_local.deref();
               for paragraph in article.get_paragraphs().iter() {
                   for topic in paragraph.topics.iter() {
                       let mut new_node = ArticleNode::new((&self).article_provider.get(topic).unwrap());
                       new_node.attach_to(rc_local.clone(), &paragraph.text);
                       current_level.push(Rc::new(new_node));
                       }
                   }
               }
               last_level = current_level; // Replace the previous level with this level.
           }

           for article_node in last_level.iter() {
               if let Some(text) = StoryBuilder::find_text_for_topic_in_article(article_node.deref().deref().borrow(), end_topic) {
                   // Found the topic. Format and return.
                   return Ok(StoryBuilder::build_final_text(article_node.clone(), text));
               }
           }
       }

        Err(format!("Reached depth of <{}> without finding <{}>. Stopping search.", self.max_depth, end_topic).to_owned())
    }

    fn build_suggestions_msg(&self, topic: &str) -> String {
        let mut msg = String::from(format!("Cannot find wikipedia article for <{}>, try one of the following suggestions:\r\n", topic));
        for sugg in self.article_provider.search(topic) {
        msg.push_str(&format!("- {}\r\n", &sugg));
        }

        msg
    }

    fn find_text_for_topic_in_article<'b>(article: &'b Article, topic: &str) -> Option<&'b str> {
        if let Some(paragraph) = article.get_paragraphs().iter().find(|par| {
                   // if any of the topics in the paragraph is <end>, return it.
            par.topics.iter().any(|t| {&t.to_lowercase() == topic})
            }) {
                   // We found the paragraph; return it directly.
            return Some(&paragraph.text);
        } else {
            None
        }
    }

    fn build_final_text(article_node: Rc<ArticleNode>, final_text: &str) -> String {
        let mut text = String::new();
        {
            let n: &ArticleNode = article_node.borrow();
            if let Some(n_text) = n.text() {
                text.push_str(&format!("{}\r\n", n_text));
            }
        }

        let mut node = article_node;
        loop {
            node = match node.parent() {
                Some(rc) => {
                        {
                        let n: &ArticleNode = rc.borrow();
                        if let Some(n_text) = n.text() {
                            text.push_str(&format!("{}\r\n", n_text));
                        }
                        }
                    rc
                    },
                _ => break,
            }
        }
        text.push_str(&format!("{}\r\n", final_text));
        text
    }
}

struct ArticleNode {
    data: Box<Article>,
    parent: Option<Rc<ArticleNode>>,
    text: Option<String>,
}

impl ArticleNode {
    fn new(data: Box<Article>) -> ArticleNode {
        ArticleNode {
            data,
            parent: None,
            text: None,
        }
    }
    fn attach_to(&mut self, parent: Rc<ArticleNode>, paragraph_text: &str) {
        self.parent = Some(parent);
        self.text = Some(String::from(paragraph_text));
    }
    fn parent(&self) -> Option<Rc<ArticleNode>> {
        self.parent.clone()
    }
    fn text(&self) -> Option<String> {
        self.text.clone()
    }
}

impl <'n> Deref for ArticleNode {
    type Target = Box<Article>;

    fn deref(&self) -> &Box<Article> {
        &self.data
    }
}

#[cfg(test)]
mod tests;
<commit_msg>Re-indent story_builder<commit_after>use story_builder::article_provider::*;
use std::rc::Rc;
use std::borrow::Borrow;
use std::ops::Deref;

pub struct StoryBuilder<'a> {
    article_provider: &'a ArticleProvider,
    max_depth: u8,
}

impl <'a> StoryBuilder<'a>  {
    pub fn new(article_provider: &'a ArticleProvider) -> StoryBuilder<'a> {
        StoryBuilder {
            article_provider,
            max_depth: 5, // default value for now
        }
    }

    pub fn build_story(&self, start_topic: &str , end_topic: &str) -> Result<String, String> {
        // If one of the topics is an empty string, do not try to make a story out of it.
        if start_topic == "" {
            return Err("Missing start topic.".to_owned());
        }
        if end_topic == "" {
            return Err("Missing end topic.".to_owned());
        }

        // If both topics are the same,
        // there is no point in trying to figure out the story.
        if start_topic == end_topic {
            return Err("No story to build; same start and end topics.".to_owned());
        }

        // Load the first article
        let start_article = match self.article_provider.get(start_topic) {
            Some(start_article) => start_article,
            None => {return Err(self.build_suggestions_msg(start_topic));},
        };

        // Load the end article
        let end_article = match self.article_provider.get(end_topic) {
            Some(end_topic) => end_topic,
            None => {return Err(self.build_suggestions_msg(end_topic));},
        };

        /* To build a story, we need to build a tree starting at the start_article
           node and going down in a "breadth-first" way; that way, once we find
           the end note, we know it is the shortest path to it. Also, going depth-first
           will be impossibly long to complete sincethe depth of the wikipedia
           article tree is almost infinite. */
        /* We look for a paragraph that holds a reference to our end topic
           somewhere in the last level we fetched: */
        let mut last_level: Vec<Rc<ArticleNode>> = vec![Rc::new(ArticleNode::new(start_article))]; // starts with start article
        for i in 0..self.max_depth { // To prevent overloading the system, stop after X level deep
            // Start by loading the next level of articles:
            if i > 0 {
                // Any other iteration: go one level deeper:
                let mut current_level = vec![];
                for rc_article_node in last_level.iter() {
                    /* Iterate on each paragraph of this article, then map on it to
                       get the article for each related topic in it. */
                    let rc_local = rc_article_node.clone();
                    let article = rc_local.deref();
                    for paragraph in article.get_paragraphs().iter() {
                        // First: spawn all threads first
                        let mut threads: Vec<JoinHandle<Article>> = Vec::new();
                        for topic in paragraph.topics.iter() {
                            threads.push(thread::spawn(|| {
                                (&self).article_provider.get(topic.to_owned()).unwrap()
                            }));
                        }
                        // Then, join them up one at a time.
                        for topic in paragraph.topics.iter() {

                            let mut new_node = ArticleNode::new();
                            new_node.attach_to(rc_local.clone(), &paragraph.text);
                            current_level.push(Rc::new(new_node));
                        }
                    }
                }
                last_level = current_level; // Replace the previous level with this level.
            }

            for article_node in last_level.iter() {
                if let Some(text) = StoryBuilder::find_text_for_topic_in_article(article_node.deref().deref().borrow(), end_topic) {
                    // Found the topic. Format and return.
                    return Ok(StoryBuilder::build_final_text(article_node.clone(), text));
                }
            }
        }

        Err(format!("Reached depth of <{}> without finding <{}>. Stopping search.", self.max_depth, end_topic).to_owned())
    }

    fn build_suggestions_msg(&self, topic: &str) -> String {
        let mut msg = String::from(format!("Cannot find wikipedia article for <{}>, try one of the following suggestions:\r\n", topic));
        for sugg in self.article_provider.search(topic) {
            msg.push_str(&format!("- {}\r\n", &sugg));
        }

        msg
    }

    fn find_text_for_topic_in_article<'b>(article: &'b Article, topic: &str) -> Option<&'b str> {
        if let Some(paragraph) = article.get_paragraphs().iter().find(|par| {
            // if any of the topics in the paragraph is <end>, return it.
            par.topics.iter().any(|t| {t == topic})
        }) {
            // We found the paragraph; return it directly.
            return Some(&paragraph.text);
        } else {
            None
        }
    }

    fn build_final_text(article_node: Rc<ArticleNode>, final_text: &str) -> String {
        let mut text = String::new();
        {
            let n: &ArticleNode = article_node.borrow();
            if let Some(n_text) = n.text() {
                text.push_str(&format!("{}\r\n", n_text));
            }
        }

        let mut node = article_node;
        loop {
            node = match node.parent() {
                Some(rc) => {
                    {
                        let n: &ArticleNode = rc.borrow();
                        if let Some(n_text) = n.text() {
                            text.push_str(&format!("{}\r\n", n_text));
                        }
                    }
                    rc
                },
                _ => break,
            }
        }
        text.push_str(&format!("{}\r\n", final_text));
        text
    }
}

struct ArticleNode {
    data: Box<Article>,
    parent: Option<Rc<ArticleNode>>,
    text: Option<String>,
}

impl ArticleNode {
    fn new(data: Box<Article>) -> ArticleNode {
        ArticleNode {
            data,
            parent: None,
            text: None,
        }
    }
    fn attach_to(&mut self, parent: Rc<ArticleNode>, paragraph_text: &str) {
        self.parent = Some(parent);
        self.text = Some(String::from(paragraph_text));
    }
    fn parent(&self) -> Option<Rc<ArticleNode>> {
        self.parent.clone()
    }
    fn text(&self) -> Option<String> {
        self.text.clone()
    }
}

impl <'n> Deref for ArticleNode {
    type Target = Box<Article>;

    fn deref(&self) -> &Box<Article> {
        &self.data
    }
}

#[cfg(test)]
mod tests;
<|endoftext|>


<commit_before>// Abstraction of the database.
// For the purposes of the backend, the only necessary
// actions are the following:
//
// 1. Put a pending build into the database.
// 2. Grab a pending build from the database, which
//    will immediately undergo being built.
// 3. Put test results into a database, given what
//    build was pending

extern crate postgres;
#[phase(plugin)]
extern crate pg_typeprovider;
extern crate github;

use self::github::notification::PushNotification;
use clone_url::CloneUrl;
use builder::BuildResult;

use self::EntryStatus::{Pending, InProgress, Done};

pub struct PendingBuild {
    pub clone_url: CloneUrl,
    pub branch: String
}

/// Type A is some key
pub trait Database : Sync + Send {
    fn add_pending(&self, entry: PushNotification);

    /// Optionally gets a pending build from the database.
    /// If `Some` is returned, it will not be returned again.
    /// If `None` is returned, it is expected that the caller will sleep.
    fn get_pending(&self) -> Option<PendingBuild>;

    fn add_test_results(&self, entry: &PendingBuild, results: BuildResult);
}

pub enum EntryStatus {
    Pending,
    InProgress,
    Done
}

impl EntryStatus {
    pub fn to_int(&self) -> i32 {
        match *self {
            Pending => 0,
            InProgress => 1,
            Done => 2
        }
    }
}

pub mod postgres_db {
    extern crate time;
    extern crate pg_typeprovider;
    extern crate github;

    use super::PendingBuild;

    use self::github::notification::PushNotification;

    use self::time::Timespec;
    use self::pg_typeprovider::util::Joinable;

    use std::sync::Mutex;

    use super::postgres::{Connection, GenericConnection, SslMode, ToSql};

    use builder::BuildResult;
    use clone_url::CloneUrl;
    use super::EntryStatus::{Pending, InProgress, Done};
    use super::Database;

    pg_table!(builds)
    pg_table!(commits)

    pub struct PostgresDatabase {
        db: Mutex<Connection>
    }

    impl PostgresDatabase {
        pub fn new(loc: &str) -> Option<PostgresDatabase> {
            Connection::connect(loc, &SslMode::None).ok().map(|db| {
                PostgresDatabase {
                    db: Mutex::new(db)
                }
            })
        }

        pub fn new_testing() -> Option<PostgresDatabase> {
            let retval = PostgresDatabase::new(
                "postgres://jroesch@localhost/gradr-test");
            match retval {
                Some(ref db) => {
                    db.with_connection(|conn| {
                        conn.execute(
                            "DELETE FROM users", &[]).unwrap();
                        conn.execute(
                            "DELETE FROM builds", &[]).unwrap();
                    })
                },
                None => ()
            };
            retval
        }

        pub fn with_connection<A>(&self, f: |&Connection| -> A) -> A {
            f(&*self.db.lock())
        }
    }

    fn get_one_build(conn: &GenericConnection) -> Option<Build> {
        BuildSearch::new()
            .where_status((&Pending).to_int())
            .search(conn, Some(1)).pop()
    }

    // returns true if it was able to lock it, else false
    fn try_lock_build(conn: &GenericConnection, b: &Build) -> bool {
        BuildUpdate::new()
            .status_to((&InProgress).to_int())
            .where_id(b.id)
            .where_status((&Pending).to_int())
            .update(conn) == 1
    }

    trait ToPendingBuild {
        fn to_pending_build(&self, conn: &GenericConnection) -> PendingBuild;
    }

    impl ToPendingBuild for Build {
        fn to_pending_build(&self, conn: &GenericConnection) -> PendingBuild {
            let commit = CommitSearch::new()
                .where_id(self.commit_id)
                .search(conn, Some(1))
                .pop()
                .unwrap(); // should be in there
            PendingBuild {
                clone_url: CloneUrl::new_from_str(
                    commit.clone_url.as_slice()).unwrap(),
                branch: commit.branch_name
            }
        }
    }

    impl Database for PostgresDatabase {
        fn add_pending(&self, entry: PushNotification) {
            // self.with_connection(|conn| {
            //     let trans = conn.transaction().unwrap();

            // self.with_connection(|conn| entry.insert(conn));
        }

        fn get_pending(&self) -> Option<PendingBuild> {
            self.with_connection(|conn| {
                loop {
                    let trans = conn.transaction().unwrap();
                    match get_one_build(&trans) {
                        Some(b) => {
                            let res = try_lock_build(&trans, &b);
                            assert!(res);
                            if trans.commit().is_ok() {
                                return Some(b.to_pending_build(conn))
                            }
                        },
                        None => { return None; }
                    }
                }
            })
        }

        fn add_test_results(&self, entry: &PendingBuild, results: BuildResult) {
            // TODO: we do a copy here because we cannot move into a closure,
            // even though this is what we want.  I spent around 2 hours trying
            // to get around this, but to no avail.

            // let res = results.consume_to_json().to_string();
            // let s = res.as_slice();
            // self.with_connection(|conn| {
            //     let num_updated =
            //         BuildUpdate::new()
            //         .status_to((&Done).to_int())
            //         .results_to(s.to_string())
            //         .where_id(entry.id)
            //         .update(conn);
            //     assert_eq!(num_updated, 1);
            // });
        }
    }
}
<commit_msg>add_test_results now compiles<commit_after>// Abstraction of the database.
// For the purposes of the backend, the only necessary
// actions are the following:
//
// 1. Put a pending build into the database.
// 2. Grab a pending build from the database, which
//    will immediately undergo being built.
// 3. Put test results into a database, given what
//    build was pending

extern crate postgres;
#[phase(plugin)]
extern crate pg_typeprovider;
extern crate github;

use self::github::notification::PushNotification;
use clone_url::CloneUrl;
use builder::BuildResult;

use self::EntryStatus::{Pending, InProgress, Done};

pub struct PendingBuild {
    pub clone_url: CloneUrl,
    pub branch: String,
    build_id: i32
}

/// Type A is some key
pub trait Database : Sync + Send {
    fn add_pending(&self, entry: PushNotification);

    /// Optionally gets a pending build from the database.
    /// If `Some` is returned, it will not be returned again.
    /// If `None` is returned, it is expected that the caller will sleep.
    fn get_pending(&self) -> Option<PendingBuild>;

    fn add_test_results(&self, entry: &PendingBuild, results: BuildResult);
}

pub enum EntryStatus {
    Pending,
    InProgress,
    Done
}

impl EntryStatus {
    pub fn to_int(&self) -> i32 {
        match *self {
            Pending => 0,
            InProgress => 1,
            Done => 2
        }
    }
}

pub mod postgres_db {
    extern crate time;
    extern crate pg_typeprovider;
    extern crate github;

    use super::PendingBuild;

    use self::github::notification::PushNotification;

    use self::time::Timespec;
    use self::pg_typeprovider::util::Joinable;

    use std::sync::Mutex;

    use super::postgres::{Connection, GenericConnection, SslMode, ToSql};

    use builder::BuildResult;
    use clone_url::CloneUrl;
    use super::EntryStatus::{Pending, InProgress, Done};
    use super::Database;

    pg_table!(builds)
    pg_table!(commits)

    pub struct PostgresDatabase {
        db: Mutex<Connection>
    }

    impl PostgresDatabase {
        pub fn new(loc: &str) -> Option<PostgresDatabase> {
            Connection::connect(loc, &SslMode::None).ok().map(|db| {
                PostgresDatabase {
                    db: Mutex::new(db)
                }
            })
        }

        pub fn new_testing() -> Option<PostgresDatabase> {
            let retval = PostgresDatabase::new(
                "postgres://jroesch@localhost/gradr-test");
            match retval {
                Some(ref db) => {
                    db.with_connection(|conn| {
                        conn.execute(
                            "DELETE FROM users", &[]).unwrap();
                        conn.execute(
                            "DELETE FROM builds", &[]).unwrap();
                    })
                },
                None => ()
            };
            retval
        }

        pub fn with_connection<A>(&self, f: |&Connection| -> A) -> A {
            f(&*self.db.lock())
        }
    }

    fn get_one_build(conn: &GenericConnection) -> Option<Build> {
        BuildSearch::new()
            .where_status((&Pending).to_int())
            .search(conn, Some(1)).pop()
    }

    // returns true if it was able to lock it, else false
    fn try_lock_build(conn: &GenericConnection, b: &Build) -> bool {
        BuildUpdate::new()
            .status_to((&InProgress).to_int())
            .where_id(b.id)
            .where_status((&Pending).to_int())
            .update(conn) == 1
    }

    trait ToPendingBuild {
        fn to_pending_build(&self, conn: &GenericConnection) -> PendingBuild;
    }

    impl ToPendingBuild for Build {
        fn to_pending_build(&self, conn: &GenericConnection) -> PendingBuild {
            let commit = CommitSearch::new()
                .where_id(self.commit_id)
                .search(conn, Some(1))
                .pop()
                .unwrap(); // should be in there
            PendingBuild {
                clone_url: CloneUrl::new_from_str(
                    commit.clone_url.as_slice()).unwrap(),
                branch: commit.branch_name,
                build_id: self.id
            }
        }
    }

    impl Database for PostgresDatabase {
        fn add_pending(&self, entry: PushNotification) {
            // self.with_connection(|conn| {
            //     let trans = conn.transaction().unwrap();

            // self.with_connection(|conn| entry.insert(conn));
        }

        fn get_pending(&self) -> Option<PendingBuild> {
            self.with_connection(|conn| {
                loop {
                    let trans = conn.transaction().unwrap();
                    match get_one_build(&trans) {
                        Some(b) => {
                            let res = try_lock_build(&trans, &b);
                            assert!(res);
                            if trans.commit().is_ok() {
                                return Some(b.to_pending_build(conn))
                            }
                        },
                        None => { return None; }
                    }
                }
            })
        }

        fn add_test_results(&self, entry: &PendingBuild, results: BuildResult) {
            // TODO: we do a copy here because we cannot move into a closure,
            // even though this is what we want.  I spent around 2 hours trying
            // to get around this, but to no avail.

            let res = results.consume_to_json().to_string();
            let s = res.as_slice();
            self.with_connection(|conn| {
                let num_updated =
                    BuildUpdate::new()
                    .status_to((&Done).to_int())
                    .results_to(s.to_string())
                    .where_id(entry.build_id)
                    .update(conn);
                assert_eq!(num_updated, 1);
            });
        }
    }
}
<|endoftext|>


<commit_before>use std::fmt::Debug;

use toml::Value;

use self::error::HookError;

pub type HookResult<T> = Result<T, HookError>;

pub trait Configureable {
    fn set_config(&mut self, cfg: Value);
}

pub trait Hook : Configureable + Debug {
}

pub mod read {
    use storeid::StoreId;
    use store::FileLockEntry;
    use super::HookResult;
    use super::Hook;

    pub trait PreReadHook : Hook {
        fn pre_read(&self, &StoreId) -> HookResult<()>;
    }

    pub trait PostReadHook : Hook {
        fn post_read<'a>(&self, FileLockEntry<'a>) -> HookResult<FileLockEntry<'a>>;
    }

}

pub mod create {
    use storeid::StoreId;
    use store::FileLockEntry;
    use super::HookResult;
    use super::Hook;

    pub trait PreCreateHook : Hook {
        fn pre_create(&self, &StoreId) -> HookResult<()>;
    }

    pub trait PostCreateHook : Hook {
        fn post_create<'a>(&self, FileLockEntry<'a>) -> HookResult<FileLockEntry<'a>>;
    }

}

pub mod retrieve {
    use storeid::StoreId;
    use store::FileLockEntry;
    use super::HookResult;
    use super::Hook;

    pub trait PreRetrieveHook : Hook {
        fn pre_retrieve(&self, &StoreId) -> HookResult<()>;
    }

    pub trait PostRetrieveHook : Hook {
        fn post_retrieve<'a>(&self, FileLockEntry<'a>) -> HookResult<FileLockEntry<'a>>;
    }
}

pub mod update {
    use store::FileLockEntry;
    use super::HookResult;
    use super::Hook;

    pub trait PreUpdateHook : Hook {
        fn pre_update(&self, &FileLockEntry) -> HookResult<()>;
    }

    pub trait PostUpdateHook : Hook {
        fn post_update(&self, &FileLockEntry) -> HookResult<()>;
    }
}

pub mod delete {
    use storeid::StoreId;
    use store::FileLockEntry;
    use super::HookResult;
    use super::Hook;

    pub trait PreDeleteHook : Hook {
        fn pre_delete(&self, &StoreId) -> HookResult<()>;
    }

    pub trait PostDeleteHook : Hook {
        fn post_delete(&self, &StoreId) -> HookResult<()>;
    }
}

pub mod error {
    use std::error::Error;
    use std::fmt::Error as FmtError;
    use std::clone::Clone;
    use std::fmt::{Display, Formatter};
    use std::convert::Into;

    /**
     * Kind of error
     */
    #[derive(Clone, Copy, Debug)]
    pub enum HookErrorKind {
        Pre(PreHookErrorKind),
        Post(PostHookErrorKind)
    }

    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    pub enum PreHookErrorKind {
        // ...
    }

    impl Into<HookErrorKind> for PreHookErrorKind {
        fn into(self) -> HookErrorKind {
            HookErrorKind::Pre(self)
        }
    }

    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    pub enum PostHookErrorKind {
        // ...
    }

    impl Into<HookErrorKind> for PostHookErrorKind {
        fn into(self) -> HookErrorKind {
            HookErrorKind::Post(self)
        }
    }

    pub trait IntoHookError {
        fn into_hookerror(self) -> HookError;
        fn into_hookerror_with_cause(self, cause: Box<Error>) -> HookError;
    }

    impl Into<HookError> for HookErrorKind {

        fn into(self) -> HookError {
            HookError::new(self, None)
        }

    }

    impl Into<HookError> for (HookErrorKind, Box<Error>) {

        fn into(self) -> HookError {
            HookError::new(self.0, Some(self.1))
        }

    }

    impl Into<HookError> for PreHookErrorKind {

        fn into(self) -> HookError {
            HookError::new(HookErrorKind::Pre(self), None)
        }

    }

    impl Into<HookError> for (PreHookErrorKind, Box<Error>) {

        fn into(self) -> HookError {
            HookError::new(HookErrorKind::Pre(self.0), Some(self.1))
        }

    }

    impl Into<HookError> for PostHookErrorKind {

        fn into(self) -> HookError {
            HookError::new(HookErrorKind::Post(self), None)
        }

    }

    impl Into<HookError> for (PostHookErrorKind, Box<Error>) {

        fn into(self) -> HookError {
            HookError::new(HookErrorKind::Post(self.0), Some(self.1))
        }

    }

    fn hook_error_type_as_str(e: &HookErrorKind) -> &'static str {
        match e {
            _ => "",
        }
    }

    impl Display for HookErrorKind {

        fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
            try!(write!(fmt, "{}", hook_error_type_as_str(self)));
            Ok(())
        }

    }

    /**
     * Error type
     */
    #[derive(Debug)]
    pub struct HookError {
        err_type: HookErrorKind,
        cause: Option<Box<Error>>,
    }

    impl HookError {

        /**
         * Build a new HookError from an HookErrorKind, optionally with cause
         */
        pub fn new(errtype: HookErrorKind, cause: Option<Box<Error>>)
            -> HookError
            {
                HookError {
                    err_type: errtype,
                    cause: cause,
                }
            }

        /**
         * Get the error type of this HookError
         */
        pub fn err_type(&self) -> HookErrorKind {
            self.err_type.clone()
        }

    }

    impl Display for HookError {

        fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
            try!(write!(fmt, "[{}]", hook_error_type_as_str(&self.err_type.clone())));
            Ok(())
        }

    }

    impl Error for HookError {

        fn description(&self) -> &str {
            hook_error_type_as_str(&self.err_type.clone())
        }

        fn cause(&self) -> Option<&Error> {
            self.cause.as_ref().map(|e| &**e)
        }

    }

}

<commit_msg>Add HookAccessor types for accessing mutable/nonmutable hook calling in a generic way<commit_after>use std::fmt::Debug;

use toml::Value;

use self::error::HookError;
use store::FileLockEntry;

pub type HookResult<T> = Result<T, HookError>;

pub trait Configureable {
    fn set_config(&mut self, cfg: Value);
}

pub trait MutableHookDataAccessor : Send + Sync {
    fn access_mut(&self, &mut FileLockEntry) -> HookResult<()>;
}

pub trait NonMutableHookDataAccessor : Send + Sync {
    fn access(&self, &FileLockEntry) -> HookResult<()>;
}

pub enum HookDataAccessor {
    MutableAccess(Box<MutableHookDataAccessor>),
    NonMutableAccess(Box<NonMutableHookDataAccessor>),
}

pub trait HookDataAccessorProvider {
    fn accessor(&self) -> Box<HookDataAccessor>;
}

pub trait Hook : Configureable + Debug + Send + Sync {
}

pub mod read {
    use storeid::StoreId;
    use store::FileLockEntry;
    use super::HookDataAccessorProvider;
    use super::HookResult;
    use super::Hook;

    pub trait PreReadHook : Hook {
        fn pre_read(&self, &StoreId) -> HookResult<()>;
    }

    pub trait PostReadHook : Hook + HookDataAccessorProvider {
    }

}

pub mod create {
    use storeid::StoreId;
    use store::FileLockEntry;
    use super::HookDataAccessorProvider;
    use super::HookResult;
    use super::Hook;

    pub trait PreCreateHook : Hook {
        fn pre_create(&self, &StoreId) -> HookResult<()>;
    }

    pub trait PostCreateHook : Hook + HookDataAccessorProvider {
    }

}

pub mod retrieve {
    use storeid::StoreId;
    use store::FileLockEntry;
    use super::HookDataAccessorProvider;
    use super::HookResult;
    use super::Hook;

    pub trait PreRetrieveHook : Hook {
        fn pre_retrieve(&self, &StoreId) -> HookResult<()>;
    }

    pub trait PostRetrieveHook : Hook + HookDataAccessorProvider {
    }
}

pub mod update {
    use store::FileLockEntry;
    use super::HookDataAccessorProvider;
    use super::HookResult;
    use super::Hook;

    pub trait PreUpdateHook : Hook {
        fn pre_update(&self, &FileLockEntry) -> HookResult<()>;
    }

    pub trait PostUpdateHook : Hook + HookDataAccessorProvider {
    }
}

pub mod delete {
    use storeid::StoreId;
    use store::FileLockEntry;
    use super::HookDataAccessorProvider;
    use super::HookResult;
    use super::Hook;

    pub trait PreDeleteHook : Hook {
        fn pre_delete(&self, &StoreId) -> HookResult<()>;
    }

    pub trait PostDeleteHook : Hook {
        fn post_delete(&self, &StoreId) -> HookResult<()>;
    }
}

pub mod error {
    use std::error::Error;
    use std::fmt::Error as FmtError;
    use std::clone::Clone;
    use std::fmt::{Display, Formatter};
    use std::convert::Into;

    /**
     * Kind of error
     */
    #[derive(Clone, Copy, Debug)]
    pub enum HookErrorKind {
        Pre(PreHookErrorKind),
        Post(PostHookErrorKind)
    }

    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    pub enum PreHookErrorKind {
        // ...
    }

    impl Into<HookErrorKind> for PreHookErrorKind {
        fn into(self) -> HookErrorKind {
            HookErrorKind::Pre(self)
        }
    }

    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    pub enum PostHookErrorKind {
        // ...
    }

    impl Into<HookErrorKind> for PostHookErrorKind {
        fn into(self) -> HookErrorKind {
            HookErrorKind::Post(self)
        }
    }

    pub trait IntoHookError {
        fn into_hookerror(self) -> HookError;
        fn into_hookerror_with_cause(self, cause: Box<Error>) -> HookError;
    }

    impl Into<HookError> for HookErrorKind {

        fn into(self) -> HookError {
            HookError::new(self, None)
        }

    }

    impl Into<HookError> for (HookErrorKind, Box<Error>) {

        fn into(self) -> HookError {
            HookError::new(self.0, Some(self.1))
        }

    }

    impl Into<HookError> for PreHookErrorKind {

        fn into(self) -> HookError {
            HookError::new(HookErrorKind::Pre(self), None)
        }

    }

    impl Into<HookError> for (PreHookErrorKind, Box<Error>) {

        fn into(self) -> HookError {
            HookError::new(HookErrorKind::Pre(self.0), Some(self.1))
        }

    }

    impl Into<HookError> for PostHookErrorKind {

        fn into(self) -> HookError {
            HookError::new(HookErrorKind::Post(self), None)
        }

    }

    impl Into<HookError> for (PostHookErrorKind, Box<Error>) {

        fn into(self) -> HookError {
            HookError::new(HookErrorKind::Post(self.0), Some(self.1))
        }

    }

    fn hook_error_type_as_str(e: &HookErrorKind) -> &'static str {
        match e {
            _ => "",
        }
    }

    impl Display for HookErrorKind {

        fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
            try!(write!(fmt, "{}", hook_error_type_as_str(self)));
            Ok(())
        }

    }

    /**
     * Error type
     */
    #[derive(Debug)]
    pub struct HookError {
        err_type: HookErrorKind,
        cause: Option<Box<Error>>,
    }

    impl HookError {

        /**
         * Build a new HookError from an HookErrorKind, optionally with cause
         */
        pub fn new(errtype: HookErrorKind, cause: Option<Box<Error>>)
            -> HookError
            {
                HookError {
                    err_type: errtype,
                    cause: cause,
                }
            }

        /**
         * Get the error type of this HookError
         */
        pub fn err_type(&self) -> HookErrorKind {
            self.err_type.clone()
        }

    }

    impl Display for HookError {

        fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
            try!(write!(fmt, "[{}]", hook_error_type_as_str(&self.err_type.clone())));
            Ok(())
        }

    }

    impl Error for HookError {

        fn description(&self) -> &str {
            hook_error_type_as_str(&self.err_type.clone())
        }

        fn cause(&self) -> Option<&Error> {
            self.cause.as_ref().map(|e| &**e)
        }

    }

}

<|endoftext|>