<commit_before>#![deny(bare_trait_objects)]

#[macro_use]
extern crate clap;

use clap::Arg;
use failure::{format_err, Error};
use lucet_runtime::{self, DlModule, Limits, MmapRegion, Module, PublicKey, Region, RunResult};
use lucet_wasi::{self, WasiCtxBuilder, __wasi_exitcode_t};
use std::fs::File;
use std::path::PathBuf;
use std::sync::Arc;
use std::thread;
use std::time::Duration;

struct Config<'a> {
    lucet_module: &'a str,
    guest_args: Vec<&'a str>,
    entrypoint: &'a str,
    preopen_dirs: Vec<(File, &'a str)>,
    limits: Limits,
    timeout: Option<Duration>,
    verify: bool,
    pk_path: Option<PathBuf>,
}

fn parse_humansized(desc: &str) -> Result<u64, Error> {
    use human_size::{Byte, ParsingError, Size, SpecificSize};
    match desc.parse::<Size>() {
        Ok(s) => {
            let bytes: SpecificSize<Byte> = s.into();
            Ok(bytes.value() as u64)
        }
        Err(ParsingError::MissingMultiple) => Ok(desc.parse::<u64>()?),
        Err(e) => Err(e)?,
    }
}

fn main() {
    // No-ops, but makes sure the linker doesn't throw away parts
    // of the runtime:
    lucet_wasi::export_wasi_funcs();

    let matches = app_from_crate!()
        .arg(
            Arg::with_name("entrypoint")
                .long("entrypoint")
                .takes_value(true)
                .default_value("_start")
                .help("Entrypoint to run within the WASI module"),
        )
        .arg(
            Arg::with_name("preopen_dirs")
                .required(false)
                .long("dir")
                .takes_value(true)
                .multiple(true)
                .number_of_values(1)
                .help("A directory to provide to the WASI guest")
                .long_help(
                    "Directories on the host can be provided to the WASI guest as part of a \
                     virtual filesystem. Each directory is specified as \
                     --dir `host_path:guest_path`, where `guest_path` specifies the path that will \
                     correspond to `host_path` for calls like `fopen` in the guest.\
                     \n\n\
                     For example, `--dir /home/host_user/wasi_sandbox:/sandbox` will make \
                     `/home/host_user/wasi_sandbox` available within the guest as `/sandbox`.\
                     \n\n\
                     Guests will be able to access any files and directories under the \
                     `host_path`, but will be unable to access other parts of the host \
                     filesystem through relative paths (e.g., `/sandbox/../some_other_file`) \
                     or through symlinks.",
                ),
        )
        .arg(
            Arg::with_name("lucet_module")
                .required(true)
                .help("Path to the `lucetc`-compiled WASI module"),
        )
        .arg(
            Arg::with_name("heap_memory_size")
                .long("max-heap-size")
                .takes_value(true)
                .default_value("4 GiB")
                .help("Maximum heap size (must be a multiple of 4 KiB)"),
        )
        .arg(
            Arg::with_name("heap_address_space_size")
                .long("heap-address-space")
                .takes_value(true)
                .default_value("8 GiB")
                .help("Maximum heap address space size (must be a multiple of 4 KiB, and >= `max-heap-size`)"),
        )
        .arg(
            Arg::with_name("stack_size")
                .long("stack-size")
                .takes_value(true)
                .default_value("8 MiB")
                .help("Maximum stack size (must be a multiple of 4 KiB)"),
        )
        .arg(
            Arg::with_name("timeout").long("timeout").takes_value(true).help("Number of milliseconds the instance will be allowed to run")
            )
        .arg(
            Arg::with_name("guest_args")
                .required(false)
                .multiple(true)
                .help("Arguments to the WASI `main` function"),
        )
        .arg(
            Arg::with_name("verify")
                .long("--signature-verify")
                .takes_value(false)
                .help("Verify the signature of the source file")
        )
        .arg(
            Arg::with_name("pk_path")
                .long("--signature-pk")
                .takes_value(true)
                .help("Path to the public key to verify the source code signature")
        )
        .get_matches();

    let entrypoint = matches.value_of("entrypoint").unwrap();

    let lucet_module = matches.value_of("lucet_module").unwrap();

    let preopen_dirs = matches
        .values_of("preopen_dirs")
        .map(|vals| {
            vals.map(|preopen_dir| {
                if let [host_path, guest_path] =
                    preopen_dir.split(':').collect::<Vec<&str>>().as_slice()
                {
                    let host_dir = File::open(host_path).unwrap();
                    (host_dir, *guest_path)
                } else {
                    println!("Invalid directory specification: {}", preopen_dir);
                    println!("{}", matches.usage());
                    std::process::exit(1);
                }
            })
            .collect()
        })
        .unwrap_or(vec![]);

    let heap_memory_size = matches
        .value_of("heap_memory_size")
        .ok_or_else(|| format_err!("missing heap memory size"))
        .and_then(|v| parse_humansized(v))
        .unwrap() as usize;

    let heap_address_space_size = matches
        .value_of("heap_address_space_size")
        .ok_or_else(|| format_err!("missing heap address space size"))
        .and_then(|v| parse_humansized(v))
        .unwrap() as usize;

    if heap_memory_size > heap_address_space_size {
        println!("`heap-address-space` must be at least as large as `max-heap-size`");
        println!("{}", matches.usage());
        std::process::exit(1);
    }

    let stack_size = matches
        .value_of("stack_size")
        .ok_or_else(|| format_err!("missing stack size"))
        .and_then(|v| parse_humansized(v))
        .unwrap() as usize;

    let timeout = matches
        .value_of("timeout")
        .map(|t| Duration::from_millis(t.parse::<u64>().unwrap()));

    let limits = Limits {
        heap_memory_size,
        heap_address_space_size,
        stack_size,
        globals_size: 0, // calculated from module
    };

    let guest_args = matches
        .values_of("guest_args")
        .map(|vals| vals.collect())
        .unwrap_or(vec![]);

    let verify = matches.is_present("verify");
    let pk_path = matches.value_of("pk_path").map(PathBuf::from);

    let config = Config {
        lucet_module,
        guest_args,
        entrypoint,
        preopen_dirs,
        limits,
        timeout,
        verify,
        pk_path,
    };

    run(config)
}

fn run(config: Config<'_>) {
    let exitcode = {
        // doing all of this in a block makes sure everything gets dropped before exiting
        let pk = match (config.verify, config.pk_path) {
            (false, _) => None,
            (true, Some(pk_path)) => {
                Some(PublicKey::from_file(pk_path).expect("public key can be loaded"))
            }
            (true, None) => panic!("signature verification requires a public key"),
        };
        let module = if let Some(pk) = pk {
            DlModule::load_and_verify(&config.lucet_module, pk)
                .expect("signed module can be loaded")
        } else {
            DlModule::load(&config.lucet_module).expect("module can be loaded")
        };
        let min_globals_size = module.initial_globals_size();
        let globals_size = ((min_globals_size + 4096 - 1) / 4096) * 4096;

        let region = MmapRegion::create(
            1,
            &Limits {
                globals_size,
                ..config.limits
            },
        )
        .expect("region can be created");

        // put the path to the module on the front for argv[0]
        let args = std::iter::once(config.lucet_module)
            .chain(config.guest_args.into_iter())
            .collect::<Vec<&str>>();
        let mut ctx = WasiCtxBuilder::new()
            .expect("wasi context can be built")
            .args(args.iter())
            .expect("arguments can be stored")
            .inherit_stdio()
            .expect("stdio can be inherited")
            .inherit_env()
            .expect("environment can be inherited");
        for (dir, guest_path) in config.preopen_dirs {
            ctx = ctx.preopened_dir(dir, guest_path);
        }
        let mut inst = region
            .new_instance_builder(module as Arc<dyn Module>)
            .with_embed_ctx(ctx.build().expect("WASI ctx can be created"))
            .build()
            .expect("instance can be created");

        if let Some(timeout) = config.timeout {
            let kill_switch = inst.kill_switch();
            thread::spawn(move || {
                thread::sleep(timeout);
                // We may hit this line exactly when the guest exits, so sometimes `terminate` can
                // fail. That's still acceptable, so just ignore the result.
                kill_switch.terminate().ok();
            });
        }

        match inst.run(config.entrypoint, &[]) {
            // normal termination implies 0 exit code
            Ok(RunResult::Returned(_)) => 0,
            // none of the WASI hostcalls use yield yet, so this shouldn't happen
            Ok(RunResult::Yielded(_)) => panic!("lucet-wasi unexpectedly yielded"),
            Err(lucet_runtime::Error::RuntimeTerminated(
                lucet_runtime::TerminationDetails::Provided(any),
            )) => *any
                .downcast_ref::<__wasi_exitcode_t>()
                .expect("termination yields an exitcode"),
            Err(lucet_runtime::Error::RuntimeTerminated(
                lucet_runtime::TerminationDetails::Remote,
            )) => {
                println!("Terminated via remote kill switch (likely a timeout)");
                std::u32::MAX
            }
            Err(e) => panic!("lucet-wasi runtime error: {}", e),
        }
    };
    std::process::exit(exitcode as i32);
}
<commit_msg>replace lucet_runtime ensure_linked in lucet-wasi<commit_after>#![deny(bare_trait_objects)]

#[macro_use]
extern crate clap;

use clap::Arg;
use failure::{format_err, Error};
use lucet_runtime::{self, DlModule, Limits, MmapRegion, Module, PublicKey, Region, RunResult};
use lucet_wasi::{self, WasiCtxBuilder, __wasi_exitcode_t};
use std::fs::File;
use std::path::PathBuf;
use std::sync::Arc;
use std::thread;
use std::time::Duration;

struct Config<'a> {
    lucet_module: &'a str,
    guest_args: Vec<&'a str>,
    entrypoint: &'a str,
    preopen_dirs: Vec<(File, &'a str)>,
    limits: Limits,
    timeout: Option<Duration>,
    verify: bool,
    pk_path: Option<PathBuf>,
}

fn parse_humansized(desc: &str) -> Result<u64, Error> {
    use human_size::{Byte, ParsingError, Size, SpecificSize};
    match desc.parse::<Size>() {
        Ok(s) => {
            let bytes: SpecificSize<Byte> = s.into();
            Ok(bytes.value() as u64)
        }
        Err(ParsingError::MissingMultiple) => Ok(desc.parse::<u64>()?),
        Err(e) => Err(e)?,
    }
}

fn main() {
    // No-ops, but makes sure the linker doesn't throw away parts
    // of the runtime:
    lucet_runtime::lucet_internal_ensure_linked();
    lucet_wasi::export_wasi_funcs();

    let matches = app_from_crate!()
        .arg(
            Arg::with_name("entrypoint")
                .long("entrypoint")
                .takes_value(true)
                .default_value("_start")
                .help("Entrypoint to run within the WASI module"),
        )
        .arg(
            Arg::with_name("preopen_dirs")
                .required(false)
                .long("dir")
                .takes_value(true)
                .multiple(true)
                .number_of_values(1)
                .help("A directory to provide to the WASI guest")
                .long_help(
                    "Directories on the host can be provided to the WASI guest as part of a \
                     virtual filesystem. Each directory is specified as \
                     --dir `host_path:guest_path`, where `guest_path` specifies the path that will \
                     correspond to `host_path` for calls like `fopen` in the guest.\
                     \n\n\
                     For example, `--dir /home/host_user/wasi_sandbox:/sandbox` will make \
                     `/home/host_user/wasi_sandbox` available within the guest as `/sandbox`.\
                     \n\n\
                     Guests will be able to access any files and directories under the \
                     `host_path`, but will be unable to access other parts of the host \
                     filesystem through relative paths (e.g., `/sandbox/../some_other_file`) \
                     or through symlinks.",
                ),
        )
        .arg(
            Arg::with_name("lucet_module")
                .required(true)
                .help("Path to the `lucetc`-compiled WASI module"),
        )
        .arg(
            Arg::with_name("heap_memory_size")
                .long("max-heap-size")
                .takes_value(true)
                .default_value("4 GiB")
                .help("Maximum heap size (must be a multiple of 4 KiB)"),
        )
        .arg(
            Arg::with_name("heap_address_space_size")
                .long("heap-address-space")
                .takes_value(true)
                .default_value("8 GiB")
                .help("Maximum heap address space size (must be a multiple of 4 KiB, and >= `max-heap-size`)"),
        )
        .arg(
            Arg::with_name("stack_size")
                .long("stack-size")
                .takes_value(true)
                .default_value("8 MiB")
                .help("Maximum stack size (must be a multiple of 4 KiB)"),
        )
        .arg(
            Arg::with_name("timeout").long("timeout").takes_value(true).help("Number of milliseconds the instance will be allowed to run")
            )
        .arg(
            Arg::with_name("guest_args")
                .required(false)
                .multiple(true)
                .help("Arguments to the WASI `main` function"),
        )
        .arg(
            Arg::with_name("verify")
                .long("--signature-verify")
                .takes_value(false)
                .help("Verify the signature of the source file")
        )
        .arg(
            Arg::with_name("pk_path")
                .long("--signature-pk")
                .takes_value(true)
                .help("Path to the public key to verify the source code signature")
        )
        .get_matches();

    let entrypoint = matches.value_of("entrypoint").unwrap();

    let lucet_module = matches.value_of("lucet_module").unwrap();

    let preopen_dirs = matches
        .values_of("preopen_dirs")
        .map(|vals| {
            vals.map(|preopen_dir| {
                if let [host_path, guest_path] =
                    preopen_dir.split(':').collect::<Vec<&str>>().as_slice()
                {
                    let host_dir = File::open(host_path).unwrap();
                    (host_dir, *guest_path)
                } else {
                    println!("Invalid directory specification: {}", preopen_dir);
                    println!("{}", matches.usage());
                    std::process::exit(1);
                }
            })
            .collect()
        })
        .unwrap_or(vec![]);

    let heap_memory_size = matches
        .value_of("heap_memory_size")
        .ok_or_else(|| format_err!("missing heap memory size"))
        .and_then(|v| parse_humansized(v))
        .unwrap() as usize;

    let heap_address_space_size = matches
        .value_of("heap_address_space_size")
        .ok_or_else(|| format_err!("missing heap address space size"))
        .and_then(|v| parse_humansized(v))
        .unwrap() as usize;

    if heap_memory_size > heap_address_space_size {
        println!("`heap-address-space` must be at least as large as `max-heap-size`");
        println!("{}", matches.usage());
        std::process::exit(1);
    }

    let stack_size = matches
        .value_of("stack_size")
        .ok_or_else(|| format_err!("missing stack size"))
        .and_then(|v| parse_humansized(v))
        .unwrap() as usize;

    let timeout = matches
        .value_of("timeout")
        .map(|t| Duration::from_millis(t.parse::<u64>().unwrap()));

    let limits = Limits {
        heap_memory_size,
        heap_address_space_size,
        stack_size,
        globals_size: 0, // calculated from module
    };

    let guest_args = matches
        .values_of("guest_args")
        .map(|vals| vals.collect())
        .unwrap_or(vec![]);

    let verify = matches.is_present("verify");
    let pk_path = matches.value_of("pk_path").map(PathBuf::from);

    let config = Config {
        lucet_module,
        guest_args,
        entrypoint,
        preopen_dirs,
        limits,
        timeout,
        verify,
        pk_path,
    };

    run(config)
}

fn run(config: Config<'_>) {
    let exitcode = {
        // doing all of this in a block makes sure everything gets dropped before exiting
        let pk = match (config.verify, config.pk_path) {
            (false, _) => None,
            (true, Some(pk_path)) => {
                Some(PublicKey::from_file(pk_path).expect("public key can be loaded"))
            }
            (true, None) => panic!("signature verification requires a public key"),
        };
        let module = if let Some(pk) = pk {
            DlModule::load_and_verify(&config.lucet_module, pk)
                .expect("signed module can be loaded")
        } else {
            DlModule::load(&config.lucet_module).expect("module can be loaded")
        };
        let min_globals_size = module.initial_globals_size();
        let globals_size = ((min_globals_size + 4096 - 1) / 4096) * 4096;

        let region = MmapRegion::create(
            1,
            &Limits {
                globals_size,
                ..config.limits
            },
        )
        .expect("region can be created");

        // put the path to the module on the front for argv[0]
        let args = std::iter::once(config.lucet_module)
            .chain(config.guest_args.into_iter())
            .collect::<Vec<&str>>();
        let mut ctx = WasiCtxBuilder::new()
            .expect("wasi context can be built")
            .args(args.iter())
            .expect("arguments can be stored")
            .inherit_stdio()
            .expect("stdio can be inherited")
            .inherit_env()
            .expect("environment can be inherited");
        for (dir, guest_path) in config.preopen_dirs {
            ctx = ctx.preopened_dir(dir, guest_path);
        }
        let mut inst = region
            .new_instance_builder(module as Arc<dyn Module>)
            .with_embed_ctx(ctx.build().expect("WASI ctx can be created"))
            .build()
            .expect("instance can be created");

        if let Some(timeout) = config.timeout {
            let kill_switch = inst.kill_switch();
            thread::spawn(move || {
                thread::sleep(timeout);
                // We may hit this line exactly when the guest exits, so sometimes `terminate` can
                // fail. That's still acceptable, so just ignore the result.
                kill_switch.terminate().ok();
            });
        }

        match inst.run(config.entrypoint, &[]) {
            // normal termination implies 0 exit code
            Ok(RunResult::Returned(_)) => 0,
            // none of the WASI hostcalls use yield yet, so this shouldn't happen
            Ok(RunResult::Yielded(_)) => panic!("lucet-wasi unexpectedly yielded"),
            Err(lucet_runtime::Error::RuntimeTerminated(
                lucet_runtime::TerminationDetails::Provided(any),
            )) => *any
                .downcast_ref::<__wasi_exitcode_t>()
                .expect("termination yields an exitcode"),
            Err(lucet_runtime::Error::RuntimeTerminated(
                lucet_runtime::TerminationDetails::Remote,
            )) => {
                println!("Terminated via remote kill switch (likely a timeout)");
                std::u32::MAX
            }
            Err(e) => panic!("lucet-wasi runtime error: {}", e),
        }
    };
    std::process::exit(exitcode as i32);
}
<|endoftext|>


<commit_before>use rayon::prelude::*;

use page::Page;
use front_matter::SortBy;

/// Sort pages by the given criteria
///
/// Any pages that doesn't have a the required field when the sorting method is other than none
/// will be ignored.
pub fn sort_pages(pages: Vec<Page>, sort_by: SortBy) -> (Vec<Page>, Vec<Page>) {
    if sort_by == SortBy::None {
        return (pages,  vec![]);
    }

    let (mut can_be_sorted, cannot_be_sorted): (Vec<_>, Vec<_>) = pages
        .into_par_iter()
        .partition(|ref page| {
            match sort_by {
                SortBy::Date => page.meta.date.is_some(),
                SortBy::Order => page.meta.order.is_some(),
                SortBy::Weight => page.meta.weight.is_some(),
                _ => unreachable!()
            }
        });

    match sort_by {
        SortBy::Date => can_be_sorted.par_sort_unstable_by(|a, b| b.meta.date().unwrap().cmp(&a.meta.date().unwrap())),
        SortBy::Order => can_be_sorted.par_sort_unstable_by(|a, b| b.meta.order().cmp(&a.meta.order())),
        SortBy::Weight => can_be_sorted.par_sort_unstable_by(|a, b| a.meta.weight().cmp(&b.meta.weight())),
        _ => unreachable!()
    };

    (can_be_sorted, cannot_be_sorted)
}

/// Horribly inefficient way to set previous and next on each pages
/// So many clones
pub fn populate_previous_and_next_pages(input: Vec<Page>) -> Vec<Page> {
    let mut res = Vec::with_capacity(input.len());

    // The input is already sorted
    for (i, _) in input.iter().enumerate() {
        let mut new_page = input[i].clone();

        if i > 0 {
            let mut previous_page = input[i - 1].clone();
            // Remove prev/next otherwise we serialise the whole thing...
            previous_page.previous = None;
            previous_page.next = None;
            new_page.previous = Some(Box::new(previous_page));
        }

        if i < input.len() - 1 {
            let mut next_page = input[i + 1].clone();
            // Remove prev/next otherwise we serialise the whole thing...
            next_page.previous = None;
            next_page.next = None;
            new_page.next = Some(Box::new(next_page));
        }
        res.push(new_page);
    }

    res
}

#[cfg(test)]
mod tests {
    use front_matter::{PageFrontMatter, SortBy};
    use page::Page;
    use super::{sort_pages, populate_previous_and_next_pages};

    fn create_page_with_date(date: &str) -> Page {
        let mut front_matter = PageFrontMatter::default();
        front_matter.date = Some(date.to_string());
        Page::new("content/hello.md", front_matter)
    }

    fn create_page_with_order(order: usize) -> Page {
        let mut front_matter = PageFrontMatter::default();
        front_matter.order = Some(order);
        Page::new("content/hello.md", front_matter)
    }

    fn create_page_with_weight(weight: usize) -> Page {
        let mut front_matter = PageFrontMatter::default();
        front_matter.weight = Some(weight);
        Page::new("content/hello.md", front_matter)
    }

    #[test]
    fn can_sort_by_dates() {
        let input = vec![
            create_page_with_date("2018-01-01"),
            create_page_with_date("2017-01-01"),
            create_page_with_date("2019-01-01"),
        ];
        let (pages, _) = sort_pages(input, SortBy::Date);
        // Should be sorted by date
        assert_eq!(pages[0].clone().meta.date.unwrap(), "2019-01-01");
        assert_eq!(pages[1].clone().meta.date.unwrap(), "2018-01-01");
        assert_eq!(pages[2].clone().meta.date.unwrap(), "2017-01-01");
    }

    #[test]
    fn can_sort_by_order() {
        let input = vec![
            create_page_with_order(2),
            create_page_with_order(3),
            create_page_with_order(1),
        ];
        let (pages, _) = sort_pages(input, SortBy::Order);
        // Should be sorted by date
        assert_eq!(pages[0].clone().meta.order.unwrap(), 3);
        assert_eq!(pages[1].clone().meta.order.unwrap(), 2);
        assert_eq!(pages[2].clone().meta.order.unwrap(), 1);
    }

    #[test]
    fn can_sort_by_weight() {
        let input = vec![
            create_page_with_weight(2),
            create_page_with_weight(3),
            create_page_with_weight(1),
        ];
        let (pages, _) = sort_pages(input, SortBy::Weight);
        // Should be sorted by date
        assert_eq!(pages[0].clone().meta.weight.unwrap(), 1);
        assert_eq!(pages[1].clone().meta.weight.unwrap(), 2);
        assert_eq!(pages[2].clone().meta.weight.unwrap(), 3);
    }

    #[test]
    fn can_sort_by_none() {
        let input = vec![
            create_page_with_order(2),
            create_page_with_order(3),
            create_page_with_order(1),
        ];
        let (pages, _) = sort_pages(input, SortBy::None);
        // Should be sorted by date
        assert_eq!(pages[0].clone().meta.order.unwrap(), 2);
        assert_eq!(pages[1].clone().meta.order.unwrap(), 3);
        assert_eq!(pages[2].clone().meta.order.unwrap(), 1);
    }

    #[test]
    fn ignore_page_with_missing_field() {
        let input = vec![
            create_page_with_order(2),
            create_page_with_order(3),
            create_page_with_date("2019-01-01"),
        ];
        let (pages, unsorted) = sort_pages(input, SortBy::Order);
        assert_eq!(pages.len(), 2);
        assert_eq!(unsorted.len(), 1);
    }

    #[test]
    fn can_populate_previous_and_next_pages() {
        let input = vec![
            create_page_with_order(1),
            create_page_with_order(2),
            create_page_with_order(3),
        ];
        let pages = populate_previous_and_next_pages(input);

        assert!(pages[0].clone().previous.is_none());
        assert!(pages[0].clone().next.is_some());
        assert_eq!(pages[0].clone().next.unwrap().meta.order.unwrap(), 2);

        assert!(pages[1].clone().next.is_some());
        assert!(pages[1].clone().previous.is_some());
        assert_eq!(pages[1].clone().previous.unwrap().meta.order.unwrap(), 1);
        assert_eq!(pages[1].clone().next.unwrap().meta.order.unwrap(), 3);

        assert!(pages[2].clone().next.is_none());
        assert!(pages[2].clone().previous.is_some());
        assert_eq!(pages[2].clone().previous.unwrap().meta.order.unwrap(), 2);
    }
}
<commit_msg>Previous behaviour of populate was correct so reverting back to it<commit_after>use rayon::prelude::*;

use page::Page;
use front_matter::SortBy;

/// Sort pages by the given criteria
///
/// Any pages that doesn't have a the required field when the sorting method is other than none
/// will be ignored.
pub fn sort_pages(pages: Vec<Page>, sort_by: SortBy) -> (Vec<Page>, Vec<Page>) {
    if sort_by == SortBy::None {
        return (pages,  vec![]);
    }

    let (mut can_be_sorted, cannot_be_sorted): (Vec<_>, Vec<_>) = pages
        .into_par_iter()
        .partition(|ref page| {
            match sort_by {
                SortBy::Date => page.meta.date.is_some(),
                SortBy::Order => page.meta.order.is_some(),
                SortBy::Weight => page.meta.weight.is_some(),
                _ => unreachable!()
            }
        });

    match sort_by {
        SortBy::Date => can_be_sorted.par_sort_unstable_by(|a, b| b.meta.date().unwrap().cmp(&a.meta.date().unwrap())),
        SortBy::Order => can_be_sorted.par_sort_unstable_by(|a, b| b.meta.order().cmp(&a.meta.order())),
        SortBy::Weight => can_be_sorted.par_sort_unstable_by(|a, b| a.meta.weight().cmp(&b.meta.weight())),
        _ => unreachable!()
    };

    (can_be_sorted, cannot_be_sorted)
}

/// Horribly inefficient way to set previous and next on each pages
/// So many clones
pub fn populate_previous_and_next_pages(input: Vec<Page>) -> Vec<Page> {
    let mut res = Vec::with_capacity(input.len());

    // The input is already sorted
    for (i, _) in input.iter().enumerate() {
        let mut new_page = input[i].clone();

        if i > 0 {
            let mut next_page = input[i - 1].clone();
            // Remove prev/next otherwise we serialise the whole thing...
            next_page.previous = None;
            next_page.next = None;
            new_page.next = Some(Box::new(next_page));
        }

        if i < input.len() - 1 {
            let mut previous_page = input[i + 1].clone();
            // Remove prev/next otherwise we serialise the whole thing...
            previous_page.previous = None;
            previous_page.next = None;
            new_page.previous = Some(Box::new(previous_page));
        }
        res.push(new_page);
    }

    res
}

#[cfg(test)]
mod tests {
    use front_matter::{PageFrontMatter, SortBy};
    use page::Page;
    use super::{sort_pages, populate_previous_and_next_pages};

    fn create_page_with_date(date: &str) -> Page {
        let mut front_matter = PageFrontMatter::default();
        front_matter.date = Some(date.to_string());
        Page::new("content/hello.md", front_matter)
    }

    fn create_page_with_order(order: usize) -> Page {
        let mut front_matter = PageFrontMatter::default();
        front_matter.order = Some(order);
        Page::new("content/hello.md", front_matter)
    }

    fn create_page_with_weight(weight: usize) -> Page {
        let mut front_matter = PageFrontMatter::default();
        front_matter.weight = Some(weight);
        Page::new("content/hello.md", front_matter)
    }

    #[test]
    fn can_sort_by_dates() {
        let input = vec![
            create_page_with_date("2018-01-01"),
            create_page_with_date("2017-01-01"),
            create_page_with_date("2019-01-01"),
        ];
        let (pages, _) = sort_pages(input, SortBy::Date);
        // Should be sorted by date
        assert_eq!(pages[0].clone().meta.date.unwrap(), "2019-01-01");
        assert_eq!(pages[1].clone().meta.date.unwrap(), "2018-01-01");
        assert_eq!(pages[2].clone().meta.date.unwrap(), "2017-01-01");
    }

    #[test]
    fn can_sort_by_order() {
        let input = vec![
            create_page_with_order(2),
            create_page_with_order(3),
            create_page_with_order(1),
        ];
        let (pages, _) = sort_pages(input, SortBy::Order);
        // Should be sorted by date
        assert_eq!(pages[0].clone().meta.order.unwrap(), 3);
        assert_eq!(pages[1].clone().meta.order.unwrap(), 2);
        assert_eq!(pages[2].clone().meta.order.unwrap(), 1);
    }

    #[test]
    fn can_sort_by_weight() {
        let input = vec![
            create_page_with_weight(2),
            create_page_with_weight(3),
            create_page_with_weight(1),
        ];
        let (pages, _) = sort_pages(input, SortBy::Weight);
        // Should be sorted by date
        assert_eq!(pages[0].clone().meta.weight.unwrap(), 1);
        assert_eq!(pages[1].clone().meta.weight.unwrap(), 2);
        assert_eq!(pages[2].clone().meta.weight.unwrap(), 3);
    }

    #[test]
    fn can_sort_by_none() {
        let input = vec![
            create_page_with_order(2),
            create_page_with_order(3),
            create_page_with_order(1),
        ];
        let (pages, _) = sort_pages(input, SortBy::None);
        // Should be sorted by date
        assert_eq!(pages[0].clone().meta.order.unwrap(), 2);
        assert_eq!(pages[1].clone().meta.order.unwrap(), 3);
        assert_eq!(pages[2].clone().meta.order.unwrap(), 1);
    }

    #[test]
    fn ignore_page_with_missing_field() {
        let input = vec![
            create_page_with_order(2),
            create_page_with_order(3),
            create_page_with_date("2019-01-01"),
        ];
        let (pages, unsorted) = sort_pages(input, SortBy::Order);
        assert_eq!(pages.len(), 2);
        assert_eq!(unsorted.len(), 1);
    }

    #[test]
    fn can_populate_previous_and_next_pages() {
        let input = vec![
            create_page_with_order(1),
            create_page_with_order(2),
            create_page_with_order(3),
        ];
        let pages = populate_previous_and_next_pages(input);

        assert!(pages[0].clone().next.is_none());
        assert!(pages[0].clone().previous.is_some());
        assert_eq!(pages[0].clone().previous.unwrap().meta.order.unwrap(), 2);

        assert!(pages[1].clone().next.is_some());
        assert!(pages[1].clone().previous.is_some());
        assert_eq!(pages[1].clone().previous.unwrap().meta.order.unwrap(), 3);
        assert_eq!(pages[1].clone().next.unwrap().meta.order.unwrap(), 1);

        assert!(pages[2].clone().next.is_some());
        assert!(pages[2].clone().previous.is_none());
        assert_eq!(pages[2].clone().next.unwrap().meta.order.unwrap(), 2);
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::io::{Read, Write};

use httparse;
use hyper;
use hyper::method::Method;
use hyper::version::HttpVersion;
use hyper::net::{HttpConnector, HttpsStream, NetworkConnector, SslClient};

use proxy::ProxyInfo;

/// A connector that uses an HTTP proxy server (pass-through for plaintext and tunneled for SSL
/// sessions).
pub struct ProxyHttpsConnector<S: SslClient> {
    proxy: ProxyInfo,
    proxy_connector: HttpConnector,
    ssl_client: S,
}

impl<S: SslClient> ProxyHttpsConnector<S> {
    /// Creates a new connection using the provided proxy server configuration and SSL
    /// implementation.
    pub fn new(proxy: ProxyInfo, ssl: S) -> hyper::Result<Self> {
        Ok(ProxyHttpsConnector {
            proxy: proxy,
            proxy_connector: HttpConnector,
            ssl_client: ssl,
        })
    }
}

impl<S: SslClient> NetworkConnector for ProxyHttpsConnector<S> {
    type Stream = HttpsStream<S::Stream>;

    fn connect(&self, host: &str, port: u16, scheme: &str) -> hyper::Result<Self::Stream> {
        // Initial connection to the proxy server, using an `HttpConnector`
        let mut stream = try!(self.proxy_connector
            .connect(self.proxy.host(), self.proxy.port(), "http"));
        match scheme {
            "https" => {
                // If the target URL is an `"https"` scheme, then we use proxy/TCP tunneling as
                // per the RFC draft:
                // http://www.web-cache.com/Writings/Internet-Drafts/draft-luotonen-web-proxy-tunneling-01.txt
                //
                // We can't yet use hyper directly and therefore use the underlying http parsing
                // library to establish the connection and parse the response. This implementation
                // is largely based on hyper's internal proxy tunneling code.
                let mut connect_msg = format!("{method} {host}:{port} {version}\r\nHost \
                                               {host}:{port}\r\n",
                                              method = Method::Connect,
                                              version = HttpVersion::Http11,
                                              host = host,
                                              port = port);
                if let Some(header_value) = self.proxy.authorization_header_value() {
                    connect_msg.push_str(&format!("Proxy-Authorization: {}\r\n", header_value));
                };
                connect_msg.push_str("\r\n");
                debug!("Proxy {}:{} {:?}",
                       self.proxy.host(),
                       self.proxy.port(),
                       connect_msg.trim().replace("\r\n", ", "));
                try!(stream.write_all(connect_msg.as_bytes()));
                try!(stream.flush());
                let mut buf = [0; 1024];
                let mut n = 0;
                while n < buf.len() {
                    n += try!(stream.read(&mut buf[n..]));
                    let mut headers = [httparse::EMPTY_HEADER; 10];
                    let mut res = httparse::Response::new(&mut headers);
                    if try!(res.parse(&buf[..n])).is_complete() {
                        let code = res.code.expect("complete parsing lost code");
                        if code >= 200 && code < 300 {
                            debug!("Proxy {}:{} CONNECT success = {}",
                                   self.proxy.host(),
                                   self.proxy.port(),
                                   code);
                            return self.ssl_client
                                .wrap_client(stream, host)
                                .map(HttpsStream::Https);
                        } else {
                            debug!("Proxy {}:{} CONNECT failed response = {}",
                                   self.proxy.host(),
                                   self.proxy.port(),
                                   code);
                            return Err(hyper::Error::Status);
                        }
                    }
                }
                Err(hyper::Error::TooLarge)
            }
            _ => Ok(HttpsStream::Http(stream)),
        }
    }
}
<commit_msg>[http-client] Add missing colon on Host header when connecting to proxy.<commit_after>// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::io::{Read, Write};

use httparse;
use hyper;
use hyper::method::Method;
use hyper::version::HttpVersion;
use hyper::net::{HttpConnector, HttpsStream, NetworkConnector, SslClient};

use proxy::ProxyInfo;

/// A connector that uses an HTTP proxy server (pass-through for plaintext and tunneled for SSL
/// sessions).
pub struct ProxyHttpsConnector<S: SslClient> {
    proxy: ProxyInfo,
    proxy_connector: HttpConnector,
    ssl_client: S,
}

impl<S: SslClient> ProxyHttpsConnector<S> {
    /// Creates a new connection using the provided proxy server configuration and SSL
    /// implementation.
    pub fn new(proxy: ProxyInfo, ssl: S) -> hyper::Result<Self> {
        Ok(ProxyHttpsConnector {
            proxy: proxy,
            proxy_connector: HttpConnector,
            ssl_client: ssl,
        })
    }
}

impl<S: SslClient> NetworkConnector for ProxyHttpsConnector<S> {
    type Stream = HttpsStream<S::Stream>;

    fn connect(&self, host: &str, port: u16, scheme: &str) -> hyper::Result<Self::Stream> {
        // Initial connection to the proxy server, using an `HttpConnector`
        let mut stream = try!(self.proxy_connector
            .connect(self.proxy.host(), self.proxy.port(), "http"));
        match scheme {
            "https" => {
                // If the target URL is an `"https"` scheme, then we use proxy/TCP tunneling as
                // per the RFC draft:
                // http://www.web-cache.com/Writings/Internet-Drafts/draft-luotonen-web-proxy-tunneling-01.txt
                //
                // We can't yet use hyper directly and therefore use the underlying http parsing
                // library to establish the connection and parse the response. This implementation
                // is largely based on hyper's internal proxy tunneling code.
                let mut connect_msg = format!("{method} {host}:{port} {version}\r\nHost: \
                                               {host}:{port}\r\n",
                                              method = Method::Connect,
                                              version = HttpVersion::Http11,
                                              host = host,
                                              port = port);
                if let Some(header_value) = self.proxy.authorization_header_value() {
                    connect_msg.push_str(&format!("Proxy-Authorization: {}\r\n", header_value));
                };
                connect_msg.push_str("\r\n");
                debug!("Proxy {}:{} {:?}",
                       self.proxy.host(),
                       self.proxy.port(),
                       connect_msg.trim().replace("\r\n", ", "));
                try!(stream.write_all(connect_msg.as_bytes()));
                try!(stream.flush());
                let mut buf = [0; 1024];
                let mut n = 0;
                while n < buf.len() {
                    n += try!(stream.read(&mut buf[n..]));
                    let mut headers = [httparse::EMPTY_HEADER; 10];
                    let mut res = httparse::Response::new(&mut headers);
                    if try!(res.parse(&buf[..n])).is_complete() {
                        let code = res.code.expect("complete parsing lost code");
                        if code >= 200 && code < 300 {
                            debug!("Proxy {}:{} CONNECT success = {}",
                                   self.proxy.host(),
                                   self.proxy.port(),
                                   code);
                            return self.ssl_client
                                .wrap_client(stream, host)
                                .map(HttpsStream::Https);
                        } else {
                            debug!("Proxy {}:{} CONNECT failed response = {}",
                                   self.proxy.host(),
                                   self.proxy.port(),
                                   code);
                            return Err(hyper::Error::Status);
                        }
                    }
                }
                Err(hyper::Error::TooLarge)
            }
            _ => Ok(HttpsStream::Http(stream)),
        }
    }
}
<|endoftext|>


<commit_before>use dao::Dao;
use std::slice;
use value::Value;

/// use this to store data retrieved from the database
/// This is also slimmer than Vec<Dao> when serialized
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Rows {
    pub columns: Vec<String>,
    pub data: Vec<Vec<Value>>,
    /// can be optionally set, indicates how many total rows are there in the table
    pub count: Option<u64>,
}

impl Rows {
    pub fn empty() -> Self {
        Rows::new(vec![])
    }
    pub fn new(columns: Vec<String>) -> Self {
        Rows {
            columns,
            data: vec![],
            count: None,
        }
    }

    pub fn push(&mut self, row: Vec<Value>) {
        self.data.push(row)
    }

    /// Returns an iterator over the `Row`s.
    pub fn iter<'a>(&'a self) -> Iter<'a> {
        Iter {
            columns: &self.columns,
            iter: self.data.iter(),
        }
    }
}

impl<'a> IntoIterator for &'a Rows {
    type Item = Dao<'a>;
    type IntoIter = Iter<'a>;

    fn into_iter(self) -> Iter<'a> {
        self.iter()
    }
}

/// An iterator over `Row`s.
pub struct Iter<'a> {
    columns: &'a Vec<String>,
    iter: slice::Iter<'a, Vec<Value>>,
}

impl<'a> Iterator for Iter<'a> {
    type Item = Dao<'a>;

    fn next(&mut self) -> Option<Dao<'a>> {
        let next_row = self.iter.next();
        if let Some(row) = next_row {
            if row.len() > 0 {
                let mut dao = Dao::new();
                for (i, column) in self.columns.iter().enumerate() {
                    if let Some(value) = row.get(i) {
                        dao.insert(column, value.clone());
                    }
                }
                Some(dao)
            } else {
                None
            }
        } else {
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.iter.size_hint()
    }
}

impl<'a> ExactSizeIterator for Iter<'a> {}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn iteration_count() {
        let columns = vec!["id".to_string(), "username".to_string()];
        let data: Vec<Vec<Value>> = vec![vec![1.into(), "ivanceras".into()]];
        let rows = Rows {
            columns: columns,
            data: data,
            count: None,
        };
        assert_eq!(1, rows.iter().count());
    }

    #[test]
    fn iteration_count2() {
        let columns = vec!["id".to_string(), "username".to_string()];
        let data: Vec<Vec<Value>> = vec![
            vec![1.into(), "ivanceras".into()],
            vec![2.into(), "lee".into()],
        ];
        let rows = Rows {
            columns: columns,
            data: data,
            count: None,
        };
        assert_eq!(2, rows.iter().count());
    }

    #[test]
    fn dao() {
        let columns = vec!["id".to_string(), "username".to_string()];
        let data: Vec<Vec<Value>> = vec![vec![1.into(), "ivanceras".into()]];
        let rows = Rows {
            columns: columns,
            data: data,
            count: None,
        };
        let mut dao = Dao::new();
        dao.insert("id", 1);
        dao.insert("username", "ivanceras");
        assert_eq!(dao, rows.iter().next().unwrap());
    }

    #[test]
    fn dao2() {
        let columns = vec!["id".to_string(), "username".to_string()];
        let data: Vec<Vec<Value>> = vec![
            vec![1.into(), "ivanceras".into()],
            vec![2.into(), "lee".into()],
        ];
        let rows = Rows {
            columns: columns,
            data: data,
            count: None,
        };
        let mut iter = rows.iter();
        let mut dao = Dao::new();
        dao.insert("id", 1);
        dao.insert("username", "ivanceras");
        assert_eq!(dao, iter.next().unwrap());

        let mut dao2 = Dao::new();
        dao2.insert("id", 2);
        dao2.insert("username", "lee");
        assert_eq!(dao2, iter.next().unwrap());
    }

    #[test]
    fn from_back() {
        let columns = vec!["id".to_string(), "username".to_string()];
        let data: Vec<Vec<Value>> = vec![
            vec![1.into(), "ivanceras".into()],
            vec![2.into(), "lee".into()],
        ];
        let rows = Rows {
            columns: columns,
            data: data,
            count: None,
        };
        let mut iter = rows.iter();
        let mut dao2 = Dao::new();
        dao2.insert("id", 2);
        dao2.insert("username", "lee");
        assert_eq!(dao2, iter.next_back().unwrap());

        let mut dao = Dao::new();
        dao.insert("id", 1);
        dao.insert("username", "ivanceras");
        assert_eq!(dao, iter.next_back().unwrap());
    }

    #[test]
    fn into_iter() {
        let columns = vec!["id".to_string(), "username".to_string()];
        let data: Vec<Vec<Value>> = vec![
            vec![1.into(), "ivanceras".into()],
            vec![2.into(), "lee".into()],
        ];
        let rows = Rows {
            columns: columns,
            data: data,
            count: None,
        };
        let mut iter = rows.into_iter();
        let mut dao = Dao::new();
        dao.insert("id", 1);
        dao.insert("username", "ivanceras");
        assert_eq!(dao, iter.next().unwrap());

        let mut dao2 = Dao::new();
        dao2.insert("id", 2);
        dao2.insert("username", "lee");
        assert_eq!(dao2, iter.next().unwrap());

        assert_eq!(2, rows.iter().count());
    }
}
<commit_msg>Fix test, remove next_back<commit_after>use dao::Dao;
use std::slice;
use value::Value;

/// use this to store data retrieved from the database
/// This is also slimmer than Vec<Dao> when serialized
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Rows {
    pub columns: Vec<String>,
    pub data: Vec<Vec<Value>>,
    /// can be optionally set, indicates how many total rows are there in the table
    pub count: Option<u64>,
}

impl Rows {
    pub fn empty() -> Self {
        Rows::new(vec![])
    }
    pub fn new(columns: Vec<String>) -> Self {
        Rows {
            columns,
            data: vec![],
            count: None,
        }
    }

    pub fn push(&mut self, row: Vec<Value>) {
        self.data.push(row)
    }

    /// Returns an iterator over the `Row`s.
    pub fn iter<'a>(&'a self) -> Iter<'a> {
        Iter {
            columns: &self.columns,
            iter: self.data.iter(),
        }
    }
}

impl<'a> IntoIterator for &'a Rows {
    type Item = Dao<'a>;
    type IntoIter = Iter<'a>;

    fn into_iter(self) -> Iter<'a> {
        self.iter()
    }
}

/// An iterator over `Row`s.
pub struct Iter<'a> {
    columns: &'a Vec<String>,
    iter: slice::Iter<'a, Vec<Value>>,
}

impl<'a> Iterator for Iter<'a> {
    type Item = Dao<'a>;

    fn next(&mut self) -> Option<Dao<'a>> {
        let next_row = self.iter.next();
        if let Some(row) = next_row {
            if row.len() > 0 {
                let mut dao = Dao::new();
                for (i, column) in self.columns.iter().enumerate() {
                    if let Some(value) = row.get(i) {
                        dao.insert(column, value.clone());
                    }
                }
                Some(dao)
            } else {
                None
            }
        } else {
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.iter.size_hint()
    }
}

impl<'a> ExactSizeIterator for Iter<'a> {}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn iteration_count() {
        let columns = vec!["id".to_string(), "username".to_string()];
        let data: Vec<Vec<Value>> = vec![vec![1.into(), "ivanceras".into()]];
        let rows = Rows {
            columns: columns,
            data: data,
            count: None,
        };
        assert_eq!(1, rows.iter().count());
    }

    #[test]
    fn iteration_count2() {
        let columns = vec!["id".to_string(), "username".to_string()];
        let data: Vec<Vec<Value>> = vec![
            vec![1.into(), "ivanceras".into()],
            vec![2.into(), "lee".into()],
        ];
        let rows = Rows {
            columns: columns,
            data: data,
            count: None,
        };
        assert_eq!(2, rows.iter().count());
    }

    #[test]
    fn dao() {
        let columns = vec!["id".to_string(), "username".to_string()];
        let data: Vec<Vec<Value>> = vec![vec![1.into(), "ivanceras".into()]];
        let rows = Rows {
            columns: columns,
            data: data,
            count: None,
        };
        let mut dao = Dao::new();
        dao.insert("id", 1);
        dao.insert("username", "ivanceras");
        assert_eq!(dao, rows.iter().next().unwrap());
    }

    #[test]
    fn dao2() {
        let columns = vec!["id".to_string(), "username".to_string()];
        let data: Vec<Vec<Value>> = vec![
            vec![1.into(), "ivanceras".into()],
            vec![2.into(), "lee".into()],
        ];
        let rows = Rows {
            columns: columns,
            data: data,
            count: None,
        };
        let mut iter = rows.iter();
        let mut dao = Dao::new();
        dao.insert("id", 1);
        dao.insert("username", "ivanceras");
        assert_eq!(dao, iter.next().unwrap());

        let mut dao2 = Dao::new();
        dao2.insert("id", 2);
        dao2.insert("username", "lee");
        assert_eq!(dao2, iter.next().unwrap());
    }

    #[test]
    fn into_iter() {
        let columns = vec!["id".to_string(), "username".to_string()];
        let data: Vec<Vec<Value>> = vec![
            vec![1.into(), "ivanceras".into()],
            vec![2.into(), "lee".into()],
        ];
        let rows = Rows {
            columns: columns,
            data: data,
            count: None,
        };
        let mut iter = rows.into_iter();
        let mut dao = Dao::new();
        dao.insert("id", 1);
        dao.insert("username", "ivanceras");
        assert_eq!(dao, iter.next().unwrap());

        let mut dao2 = Dao::new();
        dao2.insert("id", 2);
        dao2.insert("username", "lee");
        assert_eq!(dao2, iter.next().unwrap());

        assert_eq!(2, rows.iter().count());
    }
}
<|endoftext|>


<commit_before>//! Module for built-in filter functions
//!
//! Contains all the built-in filter functions for use in templates.
//! Currently, there is no way to define filters outside this module.
use std::fmt;

fn escapable(b: &u8) -> bool {
    *b == b'<' || *b == b'>' || *b == b'&'
}

/// Escapes `&`, `<` and `>` in strings
pub fn escape(s: &fmt::Display) -> String {
    let s = format!("{}", s);
    let mut found = Vec::new();
    for (i, b) in s.as_bytes().iter().enumerate() {
        if escapable(b) {
            found.push(i);
        }
    }
    if found.is_empty() {
        return s;
    }

    let bytes = s.as_bytes();
    let max_len = bytes.len() + found.len() * 3;
    let mut res = Vec::<u8>::with_capacity(max_len);
    let mut start = 0;
    for idx in &found {
        if start < *idx {
            res.extend(&bytes[start..*idx]);
        }
        start = *idx + 1;
        match bytes[*idx] {
            b'<' => { res.extend(b"<"); },
            b'>' => { res.extend(b">"); },
            b'&' => { res.extend(b"&"); },
            _ => panic!("incorrect indexing"),
        }
    }
    if start < bytes.len() - 1 {
        res.extend(&bytes[start..]);
    }

    String::from_utf8(res).unwrap()
}

/// Alias for the `escape()` filter
pub fn e(s: &fmt::Display) -> String {
    escape(s)
}

/// Formats arguments according to the specified format
///
/// The first argument to this filter must be a string literal (as in normal
/// Rust). All arguments are passed through to the `format!()`
/// [macro](https://doc.rust-lang.org/stable/std/macro.format.html) by
/// the Askama code generator.
pub fn format() { }

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_escape() {
        assert_eq!(escape(&""), "");
        assert_eq!(escape(&"<&>"), "<&>");
        assert_eq!(escape(&"bla&"), "bla&");
        assert_eq!(escape(&"<foo"), "<foo");
    }
}
<commit_msg>Add `lower` and `upper` filters<commit_after>//! Module for built-in filter functions
//!
//! Contains all the built-in filter functions for use in templates.
//! Currently, there is no way to define filters outside this module.
use std::fmt;

fn escapable(b: &u8) -> bool {
    *b == b'<' || *b == b'>' || *b == b'&'
}

/// Escapes `&`, `<` and `>` in strings
pub fn escape(s: &fmt::Display) -> String {
    let s = format!("{}", s);
    let mut found = Vec::new();
    for (i, b) in s.as_bytes().iter().enumerate() {
        if escapable(b) {
            found.push(i);
        }
    }
    if found.is_empty() {
        return s;
    }

    let bytes = s.as_bytes();
    let max_len = bytes.len() + found.len() * 3;
    let mut res = Vec::<u8>::with_capacity(max_len);
    let mut start = 0;
    for idx in &found {
        if start < *idx {
            res.extend(&bytes[start..*idx]);
        }
        start = *idx + 1;
        match bytes[*idx] {
            b'<' => { res.extend(b"<"); },
            b'>' => { res.extend(b">"); },
            b'&' => { res.extend(b"&"); },
            _ => panic!("incorrect indexing"),
        }
    }
    if start < bytes.len() - 1 {
        res.extend(&bytes[start..]);
    }

    String::from_utf8(res).unwrap()
}

/// Alias for the `escape()` filter
pub fn e(s: &fmt::Display) -> String {
    escape(s)
}

/// Formats arguments according to the specified format
///
/// The first argument to this filter must be a string literal (as in normal
/// Rust). All arguments are passed through to the `format!()`
/// [macro](https://doc.rust-lang.org/stable/std/macro.format.html) by
/// the Askama code generator.
pub fn format() { }

/// Converts to lowercase.
pub fn lower(s: &fmt::Display) -> String {
    let s = format!("{}", s);
    s.to_lowercase()
}

/// Alias for the `lower()` filter.
pub fn lowercase(s: &fmt::Display) -> String {
    lower(s)
}

/// Converts to uppercase.
pub fn upper(s: &fmt::Display) -> String {
    let s = format!("{}", s);
    s.to_uppercase()
}

/// Alias for the `upper()` filter.
pub fn uppercase(s: &fmt::Display) -> String {
    upper(s)
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_escape() {
        assert_eq!(escape(&""), "");
        assert_eq!(escape(&"<&>"), "<&>");
        assert_eq!(escape(&"bla&"), "bla&");
        assert_eq!(escape(&"<foo"), "<foo");

        assert_eq!(lower(&"Foo"), "foo");
        assert_eq!(lower(&"FOO"), "foo");
        assert_eq!(lower(&"FooBar"), "foobar");
        assert_eq!(lower(&"foo"), "foo");

        assert_eq!(upper(&"Foo"), "FOO");
        assert_eq!(upper(&"FOO"), "FOO");
        assert_eq!(upper(&"FooBar"), "FOOBAR");
        assert_eq!(upper(&"foo"), "FOO");
    }
}
<|endoftext|>


<commit_before>use modular_flow::graph::*;
use modular_flow::context::*;
use std::thread;
use rustfft::FFTplanner;
use rustfft::num_complex::Complex;
use rustfft::num_traits::Zero;
use std::collections::VecDeque;
use apodize;
use std::iter;

type T = f32; // fix this because generic numbers are so annoying

pub fn run_stft(ctx: NodeContext, size: usize, hop: usize) -> usize {
    let window: Vec<T> = apodize::hanning_iter(size).map(|x| x as f32).collect();
    thread::spawn(move || {
        let mut queues = vec![
            {
                let mut q = VecDeque::<f32>::new();
                q.extend(vec![0.0; size - hop]);
                q
            };
            ctx.node().in_ports().len()
        ];
        let mut input = vec![Complex::zero(); size];
        let mut output = vec![Complex::zero(); size];

        let mut planner = FFTplanner::new(false);
        let fft = planner.plan_fft(size);

        loop {
            for ((in_port, out_port), queue) in
                ctx.node().in_ports().iter().zip(ctx.node().out_ports()).zip(queues.iter_mut())
            {
                let lock = ctx.lock();
                lock.wait(|lock| lock.available::<f32>(in_port.id()) >= hop);
                queue.extend(lock.read_n::<f32>(in_port.id(), hop).unwrap());
                drop(lock);

                for ((dst, src), mul) in input.iter_mut().zip(queue.iter()).zip(&window) {
                    dst.re = *src * mul;
                    dst.im = 0.0;
                }
                queue.drain(..hop);
                fft.process(&mut input, &mut output);

                ctx.lock().write(out_port.id(), &output[..output.len() / 2]).unwrap();
            }
        }
    });
    size / 2
}

pub fn run_istft(ctx: NodeContext, size: usize, hop: usize) {
    let window: Vec<f32> = apodize::hanning_iter(size).map(|x| x as f32).collect();
    thread::spawn(move || {
        let mut queues = vec![
            {
                let mut q = VecDeque::<f32>::new();
                q.extend(vec![0.0; size - hop]);
                q
            };
            ctx.node().in_ports().len()
        ];
        let mut output = vec![Complex::zero(); size];

        let mut planner = FFTplanner::new(true);
        let fft = planner.plan_fft(size);

        loop {
            for ((in_port, out_port), queue) in
                ctx.node().in_ports().iter().zip(ctx.node().out_ports()).zip(queues.iter_mut())
            {
                let lock = ctx.lock();
                lock.wait(|lock| lock.available::<Complex<f32>>(in_port.id()) >= size / 2);
                let frame = lock.read_n::<Complex<f32>>(in_port.id(), size / 2).unwrap();
                drop(lock);
                queue.extend(vec![0.0; hop]);
                let mut input: Vec<_> =
                    frame.iter().cloned().chain(iter::repeat(Complex::zero())).take(size).collect();
                fft.process(&mut input, &mut output);
                for ((src, dst), window) in output.iter().zip(queue.iter_mut()).zip(&window) {
                    *dst += src.re * *window / size as f32 / 4.0;
                }
                let samples = queue.drain(..hop).collect::<Vec<_>>();
                ctx.lock().write(out_port.id(), &samples).unwrap();
            }
        }
    });
}

pub fn run_stft_render(ctx: NodeContext, size: usize) {
    use palette::*;
    use palette::pixel::*;

    let mut max = 1.0;
    let mut prev_frame = vec![Complex::<f32>::zero(); size];
    thread::spawn(move || loop {
        // TODO rewrite this so we can drop the lock during processing
        let lock = ctx.lock();
        let mut frame = lock.node().in_ports().iter().map(|port| {
            lock.wait(|lock| lock.available::<Complex<f32>>(port.id()) >= size);
            lock.read_n::<Complex<f32>>(port.id(), size).unwrap()
        });
        let ch1 = frame.next().unwrap();
        let frame = frame.skip(1).fold(ch1, |a, x| a.iter().zip(x.iter()).map(|(l, r)| l + r).collect());
        let out: Vec<_> = frame
            .iter()
            .zip(prev_frame)
            .map(|(x, prev)| {
                // compute hue
                let hue = x.arg() - prev.arg();

                // compute intensity
                let norm = x.norm();
                max = f32::max(norm, max);
                let value = norm / max;

                // output colour
                let (r, g, b, a): (f32, f32, f32, f32) =
                    Srgb::linear_to_pixel(Hsv::new(RgbHue::from_radians(hue), 1.0, value));
                let (r, g, b, a) = (r * 255.0, g * 255.0, b * 255.0, a * 255.0);
                (r as u32) << 24 | (g as u32) << 16 | (b as u32) << 8 | (a as u32)
            })
            .collect();
        prev_frame = frame;
        lock.write(OutPortID(0), &out).unwrap();
    });
}
<commit_msg>better stft windowing, heuristic normalization, fixed spectrogram mono mix<commit_after>use modular_flow::graph::*;
use modular_flow::context::*;
use std::thread;
use rustfft::FFTplanner;
use rustfft::num_complex::Complex;
use rustfft::num_traits::Zero;
use std::collections::VecDeque;
use apodize;
use std::iter;

type T = f32; // fix this because generic numbers are so annoying

pub fn run_stft(ctx: NodeContext, size: usize, hop: usize) -> usize {
    let window: Vec<T> = apodize::hanning_iter(size).map(|x| x.sqrt() as f32).collect();
    thread::spawn(move || {
        let mut queues = vec![
            {
                let mut q = VecDeque::<f32>::new();
                q.extend(vec![0.0; size - hop]);
                q
            };
            ctx.node().in_ports().len()
        ];
        let mut input = vec![Complex::zero(); size];
        let mut output = vec![Complex::zero(); size];

        let mut planner = FFTplanner::new(false);
        let fft = planner.plan_fft(size);

        loop {
            for ((in_port, out_port), queue) in
                ctx.node().in_ports().iter().zip(ctx.node().out_ports()).zip(queues.iter_mut())
            {
                let lock = ctx.lock();
                lock.wait(|lock| lock.available::<f32>(in_port.id()) >= hop);
                queue.extend(lock.read_n::<f32>(in_port.id(), hop).unwrap());
                drop(lock);

                for ((dst, src), mul) in input.iter_mut().zip(queue.iter()).zip(&window) {
                    dst.re = *src * mul;
                    dst.im = 0.0;
                }
                queue.drain(..hop);
                fft.process(&mut input, &mut output);

                ctx.lock().write(out_port.id(), &output[..output.len() / 2]).unwrap();
            }
        }
    });
    size / 2
}

pub fn run_istft(ctx: NodeContext, size: usize, hop: usize) {
    let window: Vec<f32> = apodize::hanning_iter(size).map(|x| x.sqrt() as f32).collect();
    thread::spawn(move || {
        let mut queues = vec![
            {
                let mut q = VecDeque::<f32>::new();
                q.extend(vec![0.0; size - hop]);
                q
            };
            ctx.node().in_ports().len()
        ];
        let mut output = vec![Complex::zero(); size];

        let mut planner = FFTplanner::new(true);
        let fft = planner.plan_fft(size);

        loop {
            for ((in_port, out_port), queue) in
                ctx.node().in_ports().iter().zip(ctx.node().out_ports()).zip(queues.iter_mut())
            {
                let lock = ctx.lock();
                lock.wait(|lock| lock.available::<Complex<f32>>(in_port.id()) >= size / 2);
                let frame = lock.read_n::<Complex<f32>>(in_port.id(), size / 2).unwrap();
                drop(lock);
                queue.extend(vec![0.0; hop]);
                let mut input: Vec<_> =
                    frame.iter().cloned().chain(iter::repeat(Complex::zero())).take(size).collect();
                fft.process(&mut input, &mut output);
                for ((src, dst), window) in output.iter().zip(queue.iter_mut()).zip(&window) {
                    *dst += src.re * *window / size as f32 / (size / hop) as f32 * 4.0;
                }
                let samples = queue.drain(..hop).collect::<Vec<_>>();
                ctx.lock().write(out_port.id(), &samples).unwrap();
            }
        }
    });
}

pub fn run_stft_render(ctx: NodeContext, size: usize) {
    use palette::*;
    use palette::pixel::*;

    let mut max = 1.0;
    let mut prev_frame = vec![Complex::<f32>::zero(); size];
    thread::spawn(move || loop {
        // TODO rewrite this so we can drop the lock during processing
        let lock = ctx.lock();
        let mut frame = lock.node().in_ports().iter().map(|port| {
            lock.wait(|lock| lock.available::<Complex<f32>>(port.id()) >= size);
            lock.read_n::<Complex<f32>>(port.id(), size).unwrap()
        });
        let ch1 = frame.next().unwrap();
        let frame = frame.fold(ch1, |a, x| a.iter().zip(x.iter()).map(|(l, r)| l + r).collect());
        let out: Vec<_> = frame
            .iter()
            .zip(prev_frame)
            .map(|(x, prev)| {
                // compute hue
                let hue = x.arg() - prev.arg();

                // compute intensity
                let norm = x.norm();
                max = f32::max(norm, max);
                let value = norm / max;

                // output colour
                let (r, g, b, a): (f32, f32, f32, f32) =
                    Srgb::linear_to_pixel(Hsv::new(RgbHue::from_radians(hue), 1.0, value));
                let (r, g, b, a) = (r * 255.0, g * 255.0, b * 255.0, a * 255.0);
                (r as u32) << 24 | (g as u32) << 16 | (b as u32) << 8 | (a as u32)
            })
            .collect();
        prev_frame = frame;
        lock.write(OutPortID(0), &out).unwrap();
    });
}
<|endoftext|>


<commit_before>//! [Lambda terms](https://en.wikipedia.org/wiki/Lambda_calculus#Lambda_terms)

use self::Term::*;
use self::Error::*;
use std::fmt;
use std::borrow::Cow;
use std::char::from_u32;

/// Set to `true` for λ or `false` for \ when displaying lambda terms. The default is `true`.
pub const DISPLAY_PRETTY: bool = true;

/// Set to `true` for classic lambda mode or `false` for the De Bruijn index display mode when
/// displaying lambda terms. The default is `false`.
pub const DISPLAY_CLASSIC: bool = false;

/// A lambda term that is either a variable with a De Bruijn index, an abstraction over a term or
/// an applicaction of one term to another.
#[derive(Debug, PartialEq, Clone)]
pub enum Term {
    /// a variable
    Var(usize),
    /// an abstraction
    Abs(Box<Term>),
    /// an application
    App(Box<Term>, Box<Term>)
}

/// An error that can be returned when an inapplicable function is applied to a term.
#[derive(Debug, PartialEq)]
pub enum Error {
    NotAVar,
    NotAnAbs,
    NotAnApp,
    NotANum,
    NotAPair,
    NotAList
}

impl Term {
    /// Applies `self` to another term without substitution or reduction.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::arithmetic::{zero, succ};
    ///
    /// assert_eq!(succ().app(zero()), App(Box::new(succ()), Box::new(zero())));
    /// ```
    pub fn app(self, argument: Term) -> Term { App(Box::new(self), Box::new(argument)) }

    /// Consumes a lambda variable and returns its De Bruijn index.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).unvar(), Ok(1));
    /// ```
    pub fn unvar(self) -> Result<usize, Error> {
        match self {
            Var(n) => Ok(n),
            _ => Err(NotAVar)
        }
    }

    /// Returns a reference to a variable's index.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).unvar_ref(), Ok(&1));
    /// ```
    pub fn unvar_ref(&self) -> Result<&usize, Error> {
        match *self {
            Var(ref n) => Ok(&n),
            _ => Err(NotAVar)
        }
    }

    /// Consumes an abstraction and returns its underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs(), Ok(Var(1)));
    /// ```
    pub fn unabs(self) -> Result<Term, Error> {
        match self {
            Abs(x) => Ok(*x),
            _ => Err(NotAnAbs)
        }
    }

    /// Returns a reference to an abstraction's underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs_ref(), Ok(&Var(1)));
    /// ```
    pub fn unabs_ref(&self) -> Result<&Term, Error> {
        match *self {
            Abs(ref x) => Ok(&*x),
            _ => Err(NotAnAbs)
        }
    }

    /// Returns a mutable reference to an abstraction's underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs_ref_mut(), Ok(&mut Var(1)));
    /// ```
    pub fn unabs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        match *self {
            Abs(ref mut x) => Ok(&mut *x),
            _ => Err(NotAnAbs)
        }
    }

    /// Consumes an application and returns a pair containing its underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).app(Var(2)).unapp(), Ok((Var(1), Var(2))));
    /// ```
    pub fn unapp(self) -> Result<(Term, Term), Error> {
        match self {
            App(lhs, rhs) => Ok((*lhs, *rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns a pair containing references to an application's underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).app(Var(2)).unapp_ref(), Ok((&Var(1), &Var(2))));
    /// ```
    pub fn unapp_ref(&self) -> Result<(&Term, &Term), Error> {
        match *self {
            App(ref lhs, ref rhs) => Ok((&*lhs, &*rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns a pair containing mutable references to an application's underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).app(Var(2)).unapp_ref_mut(), Ok((&mut Var(1), &mut Var(2))));
    /// ```
    pub fn unapp_ref_mut(&mut self) -> Result<(&mut Term, &mut Term), Error> {
        match *self {
            App(ref mut lhs, ref mut rhs) => Ok((&mut *lhs, &mut *rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns the left-hand side term of an application. Consumes `self`.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).app(Var(2)).lhs(), Ok(Var(1)));
    /// ```
    pub fn lhs(self) -> Result<Term, Error> {
        if let Ok((lhs, _)) = self.unapp() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns a reference to the left-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).app(Var(2)).lhs_ref(), Ok(&Var(1)));
    /// ```
    pub fn lhs_ref(&self) -> Result<&Term, Error> {
        if let Ok((lhs, _)) = self.unapp_ref() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns a mutable reference to the left-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).app(Var(2)).lhs_ref_mut(), Ok(&mut Var(1)));
    /// ```
    pub fn lhs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        if let Ok((lhs, _)) = self.unapp_ref_mut() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns the right-hand side term of an application. Consumes `self`.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).app(Var(2)).rhs(), Ok(Var(2)));
    /// ```
    pub fn rhs(self) -> Result<Term, Error> {
        if let Ok((_, rhs)) = self.unapp() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Returns a reference to the right-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).app(Var(2)).rhs_ref(), Ok(&Var(2)));
    /// ```
    pub fn rhs_ref(&self) -> Result<&Term, Error> {
        if let Ok((_, rhs)) = self.unapp_ref() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Returns a mutable reference to the right-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).app(Var(2)).rhs_ref_mut(), Ok(&mut Var(2)));
    /// ```
    pub fn rhs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        if let Ok((_, rhs)) = self.unapp_ref_mut() { Ok(rhs) } else { Err(NotAnApp) }
    }
}

/// Wraps a `Term` in an `Abs`traction. Consumes its argument.
///
/// # Example
/// ```
/// use lambda_calculus::term::Term::*;
/// use lambda_calculus::term::abs;
///
/// assert_eq!(abs(Var(1)), Abs(Box::new(Var(1))));
/// ```
pub fn abs(term: Term) -> Term { Abs(Box::new(term)) }

/// Produces an `App`lication of its `Term` arguments without any reduction, consuming them in the
/// process.
///
/// # Example
/// ```
/// use lambda_calculus::term::Term::*;
/// use lambda_calculus::term::app;
///
/// assert_eq!(app(Var(1), Var(2)), App(Box::new(Var(1)), Box::new(Var(2))));
/// ```
pub fn app(lhs: Term, rhs: Term) -> Term { App(Box::new(lhs), Box::new(rhs)) }

impl fmt::Display for Term {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", show_precedence(self, 0, 0))
    }
}

// TODO: make it safer (unwraps)
#[doc(hidden)]
pub fn show_precedence(term: &Term, context_precedence: usize, depth: u32) -> String {
    match *term {
        Var(i) => if DISPLAY_CLASSIC {
            if depth >= i as u32 {
                format!("{}", from_u32(depth + 97 - i as u32).unwrap())
            } else {
                format!("{}", from_u32(depth + 96 + i as u32).unwrap())
            }
        } else {
            format!("{:X}", i)
        },
        Abs(ref t) => {
            let ret = if DISPLAY_CLASSIC {
                format!("{}{}. {}", if DISPLAY_PRETTY { 'λ' } else { '\\' }, from_u32(depth + 97).unwrap(), show_precedence(t, 0, depth + 1))
            } else {
                format!("{}{}", if DISPLAY_PRETTY { 'λ' } else { '\\' }, t)
            };
            parenthesize_if(&ret, context_precedence > 1).into()
        },
        App(ref t1, ref t2) => {
            let ret = if DISPLAY_CLASSIC {
                format!("{} {}", show_precedence(t1, 2, depth), show_precedence(t2, 3, depth))
            } else {
                format!("{}{}", show_precedence(t1, 2, depth), show_precedence(t2, 3, depth))
            };
            parenthesize_if(&ret, context_precedence == 3).into()
        }
    }
}

fn parenthesize_if(input: &str, condition: bool) -> Cow<str> {
    if condition {
        format!("({})", input).into()
    } else {
        input.into()
    }
}

macro_rules! app {
    ($x:expr, $($y:expr),+) => {
        {
            let mut term = $x;
            $(term = term.app($y);)*
            term
        }
    };
}

#[cfg(test)]
mod test {
    use super::{Var, DISPLAY_CLASSIC};
    use arithmetic::{zero, succ, pred};

    #[test]
    fn app_macro() {
        assert_eq!(&format!("{}", app!(succ(), app!(Var(1), Var(2), Var(3)))), "(λλλ2(321))(123)");
    }

    #[test]
    fn display_modes() {
        if DISPLAY_CLASSIC {
            assert_eq!(&format!("{}", zero()), "λa. λb. b");
            assert_eq!(&format!("{}", succ()), "λa. λb. λc. b (a b c)");
            assert_eq!(&format!("{}", pred()), "λa. λb. λc. a (λd. λe. e (d b)) (λd. c) (λd. d)");
        } else {
            assert_eq!(&format!("{}", zero()), "λλ1");
            assert_eq!(&format!("{}", succ()), "λλλ2(321)");
            assert_eq!(&format!("{}", pred()), "λλλ3(λλ1(24))(λ2)(λ1)");
        }
    }
}
<commit_msg>add app!() doc<commit_after>//! [Lambda terms](https://en.wikipedia.org/wiki/Lambda_calculus#Lambda_terms)

use self::Term::*;
use self::Error::*;
use std::fmt;
use std::borrow::Cow;
use std::char::from_u32;

/// Set to `true` for λ or `false` for \ when displaying lambda terms. The default is `true`.
pub const DISPLAY_PRETTY: bool = true;

/// Set to `true` for classic lambda mode or `false` for the De Bruijn index display mode when
/// displaying lambda terms. The default is `false`.
pub const DISPLAY_CLASSIC: bool = false;

/// A lambda term that is either a variable with a De Bruijn index, an abstraction over a term or
/// an applicaction of one term to another.
#[derive(Debug, PartialEq, Clone)]
pub enum Term {
    /// a variable
    Var(usize),
    /// an abstraction
    Abs(Box<Term>),
    /// an application
    App(Box<Term>, Box<Term>)
}

/// An error that can be returned when an inapplicable function is applied to a term.
#[derive(Debug, PartialEq)]
pub enum Error {
    NotAVar,
    NotAnAbs,
    NotAnApp,
    NotANum,
    NotAPair,
    NotAList
}

impl Term {
    /// Applies `self` to another term without substitution or reduction.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::arithmetic::{zero, succ};
    ///
    /// assert_eq!(succ().app(zero()), App(Box::new(succ()), Box::new(zero())));
    /// ```
    pub fn app(self, argument: Term) -> Term { App(Box::new(self), Box::new(argument)) }

    /// Consumes a lambda variable and returns its De Bruijn index.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).unvar(), Ok(1));
    /// ```
    pub fn unvar(self) -> Result<usize, Error> {
        match self {
            Var(n) => Ok(n),
            _ => Err(NotAVar)
        }
    }

    /// Returns a reference to a variable's index.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).unvar_ref(), Ok(&1));
    /// ```
    pub fn unvar_ref(&self) -> Result<&usize, Error> {
        match *self {
            Var(ref n) => Ok(&n),
            _ => Err(NotAVar)
        }
    }

    /// Consumes an abstraction and returns its underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs(), Ok(Var(1)));
    /// ```
    pub fn unabs(self) -> Result<Term, Error> {
        match self {
            Abs(x) => Ok(*x),
            _ => Err(NotAnAbs)
        }
    }

    /// Returns a reference to an abstraction's underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs_ref(), Ok(&Var(1)));
    /// ```
    pub fn unabs_ref(&self) -> Result<&Term, Error> {
        match *self {
            Abs(ref x) => Ok(&*x),
            _ => Err(NotAnAbs)
        }
    }

    /// Returns a mutable reference to an abstraction's underlying term.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    /// use lambda_calculus::term::abs;
    ///
    /// assert_eq!(abs(Var(1)).unabs_ref_mut(), Ok(&mut Var(1)));
    /// ```
    pub fn unabs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        match *self {
            Abs(ref mut x) => Ok(&mut *x),
            _ => Err(NotAnAbs)
        }
    }

    /// Consumes an application and returns a pair containing its underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).app(Var(2)).unapp(), Ok((Var(1), Var(2))));
    /// ```
    pub fn unapp(self) -> Result<(Term, Term), Error> {
        match self {
            App(lhs, rhs) => Ok((*lhs, *rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns a pair containing references to an application's underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).app(Var(2)).unapp_ref(), Ok((&Var(1), &Var(2))));
    /// ```
    pub fn unapp_ref(&self) -> Result<(&Term, &Term), Error> {
        match *self {
            App(ref lhs, ref rhs) => Ok((&*lhs, &*rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns a pair containing mutable references to an application's underlying terms.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).app(Var(2)).unapp_ref_mut(), Ok((&mut Var(1), &mut Var(2))));
    /// ```
    pub fn unapp_ref_mut(&mut self) -> Result<(&mut Term, &mut Term), Error> {
        match *self {
            App(ref mut lhs, ref mut rhs) => Ok((&mut *lhs, &mut *rhs)),
            _ => Err(NotAnApp)
        }
    }

    /// Returns the left-hand side term of an application. Consumes `self`.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).app(Var(2)).lhs(), Ok(Var(1)));
    /// ```
    pub fn lhs(self) -> Result<Term, Error> {
        if let Ok((lhs, _)) = self.unapp() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns a reference to the left-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).app(Var(2)).lhs_ref(), Ok(&Var(1)));
    /// ```
    pub fn lhs_ref(&self) -> Result<&Term, Error> {
        if let Ok((lhs, _)) = self.unapp_ref() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns a mutable reference to the left-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).app(Var(2)).lhs_ref_mut(), Ok(&mut Var(1)));
    /// ```
    pub fn lhs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        if let Ok((lhs, _)) = self.unapp_ref_mut() { Ok(lhs) } else { Err(NotAnApp) }
    }

    /// Returns the right-hand side term of an application. Consumes `self`.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).app(Var(2)).rhs(), Ok(Var(2)));
    /// ```
    pub fn rhs(self) -> Result<Term, Error> {
        if let Ok((_, rhs)) = self.unapp() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Returns a reference to the right-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).app(Var(2)).rhs_ref(), Ok(&Var(2)));
    /// ```
    pub fn rhs_ref(&self) -> Result<&Term, Error> {
        if let Ok((_, rhs)) = self.unapp_ref() { Ok(rhs) } else { Err(NotAnApp) }
    }

    /// Returns a mutable reference to the right-hand side term of an application.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::Term::*;
    ///
    /// assert_eq!(Var(1).app(Var(2)).rhs_ref_mut(), Ok(&mut Var(2)));
    /// ```
    pub fn rhs_ref_mut(&mut self) -> Result<&mut Term, Error> {
        if let Ok((_, rhs)) = self.unapp_ref_mut() { Ok(rhs) } else { Err(NotAnApp) }
    }
}

/// Wraps a `Term` in an `Abs`traction. Consumes its argument.
///
/// # Example
/// ```
/// use lambda_calculus::term::Term::*;
/// use lambda_calculus::term::abs;
///
/// assert_eq!(abs(Var(1)), Abs(Box::new(Var(1))));
/// ```
pub fn abs(term: Term) -> Term { Abs(Box::new(term)) }

/// Produces an `App`lication of its `Term` arguments without any reduction, consuming them in the
/// process.
///
/// # Example
/// ```
/// use lambda_calculus::term::Term::*;
/// use lambda_calculus::term::app;
///
/// assert_eq!(app(Var(1), Var(2)), App(Box::new(Var(1)), Box::new(Var(2))));
/// ```
pub fn app(lhs: Term, rhs: Term) -> Term { App(Box::new(lhs), Box::new(rhs)) }

impl fmt::Display for Term {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", show_precedence(self, 0, 0))
    }
}

// TODO: make it safer (unwraps)
#[doc(hidden)]
pub fn show_precedence(term: &Term, context_precedence: usize, depth: u32) -> String {
    match *term {
        Var(i) => if DISPLAY_CLASSIC {
            if depth >= i as u32 {
                format!("{}", from_u32(depth + 97 - i as u32).unwrap())
            } else {
                format!("{}", from_u32(depth + 96 + i as u32).unwrap())
            }
        } else {
            format!("{:X}", i)
        },
        Abs(ref t) => {
            let ret = if DISPLAY_CLASSIC {
                format!("{}{}. {}", if DISPLAY_PRETTY { 'λ' } else { '\\' }, from_u32(depth + 97).unwrap(), show_precedence(t, 0, depth + 1))
            } else {
                format!("{}{}", if DISPLAY_PRETTY { 'λ' } else { '\\' }, t)
            };
            parenthesize_if(&ret, context_precedence > 1).into()
        },
        App(ref t1, ref t2) => {
            let ret = if DISPLAY_CLASSIC {
                format!("{} {}", show_precedence(t1, 2, depth), show_precedence(t2, 3, depth))
            } else {
                format!("{}{}", show_precedence(t1, 2, depth), show_precedence(t2, 3, depth))
            };
            parenthesize_if(&ret, context_precedence == 3).into()
        }
    }
}

fn parenthesize_if(input: &str, condition: bool) -> Cow<str> {
    if condition {
        format!("({})", input).into()
    } else {
        input.into()
    }
}

/// A macro for chain application of `Term`s.
///
/// # Example
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::term::Term::*;
/// use lambda_calculus::term::app;
///
/// assert_eq!(app!(Var(1), Var(2), Var(3)), Var(1).app(Var(2)).app(Var(3)));
/// # }
/// ```
#[macro_export]
macro_rules! app {
    ($term1:expr, $($term2:expr),+) => {
        {
            let mut term = $term1;
            $(term = term.app($term2);)*
            term
        }
    };
}

#[cfg(test)]
mod test {
    use super::{Var, DISPLAY_CLASSIC};
    use arithmetic::{zero, succ, pred};

    #[test]
    fn app_macro() {
        assert_eq!(&format!("{}", app!(succ(), app!(Var(1), Var(2), Var(3)))), "(λλλ2(321))(123)");
    }

    #[test]
    fn display_modes() {
        if DISPLAY_CLASSIC {
            assert_eq!(&format!("{}", zero()), "λa. λb. b");
            assert_eq!(&format!("{}", succ()), "λa. λb. λc. b (a b c)");
            assert_eq!(&format!("{}", pred()), "λa. λb. λc. a (λd. λe. e (d b)) (λd. c) (λd. d)");
        } else {
            assert_eq!(&format!("{}", zero()), "λλ1");
            assert_eq!(&format!("{}", succ()), "λλλ2(321)");
            assert_eq!(&format!("{}", pred()), "λλλ3(λλ1(24))(λ2)(λ1)");
        }
    }
}
<|endoftext|>