<commit_before>extern crate getopts;

use getopts::{Options, ParsingStyle};
use std::io::{stdout, Write};
use std::fmt;
use std::iter::repeat;

/// Version of this crate. It is parsed from the environment variable that
/// cargo sets when building the crate.
pub const VERSION: &'static str         = env!("CARGO_PKG_VERSION");

/// Name of this tool.
pub const NAME: &'static str            = "yes";

/// A description of this tool.
pub const DESCRIPTION: &'static str     = "Repeats a given string and a newline \
character infinitely on stdout.";

/// The default string to repeat.
pub const DEFAULT_STRING: &'static str  = "y";

/// The buffer size to use when printing the repeated string.
pub const BUFSIZE: usize                = 16384;

/// Represents a request, this tells the yes utility what it has to do.
/// Depending on the options passed on the command line, we might take
/// different actions, which are represented here.
#[derive(Debug, PartialEq)]
pub enum Request {
    /// Main purpose of this utility: send a given string followed by a newline
    /// repeatedly to `stdout`.
    RepeatString(String),

    /// Print help information to `stdout`.
    ShowHelp,

    /// Print the utility’s version number to `stdout`.
    ShowVersion
}

/// These are all the errors that can occur in this utility.
#[derive(Debug, PartialEq)]
pub enum Error {

    /// An invalid option was encountered while parsing the options.
    InvalidOption(getopts::Fail)
}

use Request::*;
use Error::*;

impl Error {
    /// Returns the error message associated with the error.
    pub fn message(&self) -> String {
        match *self {
            InvalidOption(ref fail) => fail.to_string()
        }
    }

    /// Prints the error to stderr.
    pub fn show(&self) {
        eprintln!("{}", self.message());
    }
}

impl fmt::Display for Request {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ShowHelp            => write!(f, "{}", help_text()),
            ShowVersion         => write!(f, "{}", version_text()),
            RepeatString(ref s) => write!(f, "{}", s)
        }
    }
}

impl Request {
    /// Do the request. This performs the action suggested by the request.
    pub fn do_it(&self) {
        match *self {
            ShowHelp => println!("{}", self),
            ShowVersion => println!("{}", self),
            RepeatString(ref s) => repeat_string(s)
        }
    }
}

/// Create an options object.
fn options() -> Options {
    let mut opts = Options::new();
    opts.parsing_style(ParsingStyle::StopAtFirstFree);

    // command-line options that this utility supports
    opts.optflag("h", "help", "display help text and exit");
    opts.optflag("v", "version", "output version information and exit");

    opts
}

/// Parse the command line arguments as options, returning either the parsed
/// `Request` or an `Error`, depending on the arguments.
pub fn parse_args(args: Vec<String>) -> Result<Request, Error> {
    // parse the command-line arguments, and return on error.
    let matches = match options().parse(args) {
        Ok(matches)  => matches,
        Err(failure) => return Err(InvalidOption(failure))
    };

    // if the help flag is set, we want to show the help text.
    if matches.opt_present("help") {
        return Ok(ShowHelp);
    }

    // if the version flag is set, we want to show the version.
    if matches.opt_present("version") {
        return Ok(ShowVersion);
    }

    // if there is no string set, we use the default one, otherwise we take the
    // free strings and join them together.
    let string = if matches.free.is_empty() {
        DEFAULT_STRING.to_owned()
    } else {
        matches.free.join(" ")
    };

    Ok(RepeatString(string))
}

#[test]
fn test_parse_args() {
    // test help options
    assert_eq!(parse_args(vec!["-h".to_string()]), Ok(ShowHelp));
    assert_eq!(parse_args(vec!["--help".to_string()]), Ok(ShowHelp));

    // test version options
    assert_eq!(parse_args(vec!["-v".to_string()]), Ok(ShowVersion));
    assert_eq!(parse_args(vec!["--version".to_string()]), Ok(ShowVersion));

    // use the DEAFULT_STRING if nothing is specified
    assert_eq!(parse_args(vec![]), Ok(RepeatString(DEFAULT_STRING.to_string())));

    // use whatever is given on command line
    assert_eq!(parse_args(vec!["str".to_string()]),
               Ok(RepeatString("str".to_string())));

    // join multiple arguments together
    assert_eq!(parse_args(vec!["str".to_string(), "dup".to_string()]),
               Ok(RepeatString("str dup".to_string())));

    // allow command line flags as string
    assert_eq!(parse_args(vec!["--".to_string(), "-h".to_string()]),
               Ok(RepeatString("-h".to_string())));
}

/// Generates a help text using the `getopts` crate. It is derived from the
/// `NAME`, the `DESCRIPTION`, as well as from the options.
pub fn help_text() -> String {
    let opts = options();
    let short_usage = format!("{} [STRING]", opts.short_usage(NAME));
    opts.usage(&short_usage)
}

/// Generates a version text, uses the `VERSION` as well as the `NAME` consts.
pub fn version_text() -> String {
    format!("{} version {}", NAME, VERSION)
}

/// Print a string repeatedly to `stdout`.
pub fn repeat_string(string: &str) {
    // creates a buffer, containing a lot of repetitions of our string
    let buf = repeat_buffer(string);

    // create a handle to stdout
    let stdout = stdout();
    let mut stdout = stdout.lock();

    // continuously print to stdout
    loop {
        match stdout.write_all(buf.as_bytes()) {
            Ok(_) => continue,
            Err(_) => break
        }
    }
}

/// Generates a buffer, which is maximally `BUFSIZE` big, consisting of the
/// string repeated as many times as will fit into the buffer, joined with
/// newline characters.
pub fn repeat_buffer(string: &str) -> String {
    // append newline to string
    let mut s = string.to_owned();
    s.push('\n');

    // calculate how many times our string fits into a bufsize
    let times = BUFSIZE / s.len();

    // repeat the string this many times and join it together
    repeat(s).take(times).collect::<Vec<String>>().join("")
}
<commit_msg>fixes docstring<commit_after>extern crate getopts;

use getopts::{Options, ParsingStyle};
use std::io::{stdout, Write};
use std::fmt;
use std::iter::repeat;

/// Version of this crate. It is parsed from the environment variable that
/// cargo sets when building the crate.
pub const VERSION: &'static str         = env!("CARGO_PKG_VERSION");

/// Name of this tool.
pub const NAME: &'static str            = "yes";

/// A description of this tool.
pub const DESCRIPTION: &'static str     = "Repeats a given string and a newline \
character infinitely on stdout.";

/// The default string to repeat.
pub const DEFAULT_STRING: &'static str  = "y";

/// The buffer size to use when printing the repeated string.
pub const BUFSIZE: usize                = 16384;

/// Represents a request, this tells the `yes` utility what it has to do.
/// Depending on the options passed on the command line, we might take
/// different actions, which are represented here.
#[derive(Debug, PartialEq)]
pub enum Request {
    /// Main purpose of this utility: send a given string followed by a newline
    /// repeatedly to `stdout`.
    RepeatString(String),

    /// Print help information to `stdout`.
    ShowHelp,

    /// Print the utility’s version number to `stdout`.
    ShowVersion
}

/// These are all the errors that can occur in this utility.
#[derive(Debug, PartialEq)]
pub enum Error {

    /// An invalid option was encountered while parsing the options.
    InvalidOption(getopts::Fail)
}

use Request::*;
use Error::*;

impl Error {
    /// Returns the error message associated with the error.
    pub fn message(&self) -> String {
        match *self {
            InvalidOption(ref fail) => fail.to_string()
        }
    }

    /// Prints the error to stderr.
    pub fn show(&self) {
        eprintln!("{}", self.message());
    }
}

impl fmt::Display for Request {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ShowHelp            => write!(f, "{}", help_text()),
            ShowVersion         => write!(f, "{}", version_text()),
            RepeatString(ref s) => write!(f, "{}", s)
        }
    }
}

impl Request {
    /// Do the request. This performs the action suggested by the request.
    pub fn do_it(&self) {
        match *self {
            ShowHelp => println!("{}", self),
            ShowVersion => println!("{}", self),
            RepeatString(ref s) => repeat_string(s)
        }
    }
}

/// Create an options object.
fn options() -> Options {
    let mut opts = Options::new();
    opts.parsing_style(ParsingStyle::StopAtFirstFree);

    // command-line options that this utility supports
    opts.optflag("h", "help", "display help text and exit");
    opts.optflag("v", "version", "output version information and exit");

    opts
}

/// Parse the command line arguments as options, returning either the parsed
/// `Request` or an `Error`, depending on the arguments.
pub fn parse_args(args: Vec<String>) -> Result<Request, Error> {
    // parse the command-line arguments, and return on error.
    let matches = match options().parse(args) {
        Ok(matches)  => matches,
        Err(failure) => return Err(InvalidOption(failure))
    };

    // if the help flag is set, we want to show the help text.
    if matches.opt_present("help") {
        return Ok(ShowHelp);
    }

    // if the version flag is set, we want to show the version.
    if matches.opt_present("version") {
        return Ok(ShowVersion);
    }

    // if there is no string set, we use the default one, otherwise we take the
    // free strings and join them together.
    let string = if matches.free.is_empty() {
        DEFAULT_STRING.to_owned()
    } else {
        matches.free.join(" ")
    };

    Ok(RepeatString(string))
}

#[test]
fn test_parse_args() {
    // test help options
    assert_eq!(parse_args(vec!["-h".to_string()]), Ok(ShowHelp));
    assert_eq!(parse_args(vec!["--help".to_string()]), Ok(ShowHelp));

    // test version options
    assert_eq!(parse_args(vec!["-v".to_string()]), Ok(ShowVersion));
    assert_eq!(parse_args(vec!["--version".to_string()]), Ok(ShowVersion));

    // use the DEAFULT_STRING if nothing is specified
    assert_eq!(parse_args(vec![]), Ok(RepeatString(DEFAULT_STRING.to_string())));

    // use whatever is given on command line
    assert_eq!(parse_args(vec!["str".to_string()]),
               Ok(RepeatString("str".to_string())));

    // join multiple arguments together
    assert_eq!(parse_args(vec!["str".to_string(), "dup".to_string()]),
               Ok(RepeatString("str dup".to_string())));

    // allow command line flags as string
    assert_eq!(parse_args(vec!["--".to_string(), "-h".to_string()]),
               Ok(RepeatString("-h".to_string())));
}

/// Generates a help text using the `getopts` crate. It is derived from the
/// `NAME`, the `DESCRIPTION`, as well as from the options.
pub fn help_text() -> String {
    let opts = options();
    let short_usage = format!("{} [STRING]", opts.short_usage(NAME));
    opts.usage(&short_usage)
}

/// Generates a version text, uses the `VERSION` as well as the `NAME` consts.
pub fn version_text() -> String {
    format!("{} version {}", NAME, VERSION)
}

/// Print a string repeatedly to `stdout`.
pub fn repeat_string(string: &str) {
    // creates a buffer, containing a lot of repetitions of our string
    let buf = repeat_buffer(string);

    // create a handle to stdout
    let stdout = stdout();
    let mut stdout = stdout.lock();

    // continuously print to stdout
    loop {
        match stdout.write_all(buf.as_bytes()) {
            Ok(_) => continue,
            Err(_) => break
        }
    }
}

/// Generates a buffer, which is maximally `BUFSIZE` big, consisting of the
/// string repeated as many times as will fit into the buffer, joined with
/// newline characters.
pub fn repeat_buffer(string: &str) -> String {
    // append newline to string
    let mut s = string.to_owned();
    s.push('\n');

    // calculate how many times our string fits into a bufsize
    let times = BUFSIZE / s.len();

    // repeat the string this many times and join it together
    repeat(s).take(times).collect::<Vec<String>>().join("")
}
<|endoftext|>


<commit_before>//! Use this library to open a path or URL using the program configured on the system.
//!
//! # Usage
//!
//! The following should open the given URL in a web browser
//!
//! ```test_harness,no_run
//! extern crate open;
//!
//! # #[test]
//! # fn doit() {
//! open::that("http://rust-lang.org").unwrap();
//! # }
//! ```
//!
//! # Notes
//!
//! As an operating system program is used, chances are that the open operation fails.
//! Therefore, you are advised to at least check the result with `.is_err()` and
//! behave accordingly, e.g. by letting the user know what you tried to open, and failed.
//!
//! ```
//! # fn doit() {
//! match open::that("http://rust-lang.org") {
//!     Ok(exit_status) => {
//!         if exit_status.success() {
//!             println!("Look at your browser!");
//!         } else {
//!             if let Some(code) = exit_status.code() {
//!                 println!("Command returned non-zero exit status {}!", code);
//!             } else {
//!                 println!("Command returned with unknown exit status!");
//!             }
//!         }
//!     }
//!     Err(why) => println!("Failure to execute command: {}", why),
//! }
//! # }
//! ```
#[cfg(windows)]
extern crate winapi;

#[cfg(not(target_os = "windows"))]
use std::process::{Command, Stdio};

use std::ffi::OsStr;
use std::io;
use std::process::ExitStatus;

#[cfg(not(any(target_os = "windows", target_os = "macos")))]
pub fn that<T: AsRef<OsStr> + Sized>(path: T) -> io::Result<ExitStatus> {
    let path_ref = path.as_ref();
    let mut last_err: io::Error = io::Error::from_raw_os_error(0);
    for program in &["xdg-open", "gnome-open", "kde-open", "wslview"] {
        match Command::new(program)
            .stdout(Stdio::null())
            .stderr(Stdio::null())
            .arg(path_ref)
            .spawn()
        {
            Ok(mut child) => return child.wait(),
            Err(err) => {
                last_err = err;
                continue;
            }
        }
    }
    Err(last_err)
}

#[cfg(target_os = "windows")]
pub fn that<T: AsRef<OsStr> + Sized>(path: T) -> io::Result<ExitStatus> {
    use std::os::windows::ffi::OsStrExt;
    use std::os::windows::process::ExitStatusExt;
    use std::ptr;
    use winapi::ctypes::c_int;
    use winapi::um::shellapi::ShellExecuteW;

    const SW_SHOW: c_int = 5;

    let path = windows::convert_path(path.as_ref())?;
    let operation: Vec<u16> = OsStr::new("open\0").encode_wide().collect();
    let result = unsafe {
        ShellExecuteW(
            ptr::null_mut(),
            operation.as_ptr(),
            path.as_ptr(),
            ptr::null(),
            ptr::null(),
            SW_SHOW,
        )
    };
    if result as c_int > 32 {
        Ok(ExitStatus::from_raw(0))
    } else {
        Err(io::Error::last_os_error())
    }
}

#[cfg(target_os = "macos")]
pub fn that<T: AsRef<OsStr> + Sized>(path: T) -> io::Result<ExitStatus> {
    Command::new("open")
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .arg(path.as_ref())
        .spawn()?
        .wait()
}

#[cfg(windows)]
mod windows {
    use std::ffi::OsStr;
    use std::io;
    use std::os::windows::ffi::OsStrExt;

    pub fn convert_path(path: &OsStr) -> io::Result<Vec<u16>> {
        let mut maybe_result: Vec<_> = path.encode_wide().collect();
        if maybe_result.iter().any(|&u| u == 0) {
            return Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                "path contains NUL byte(s)",
            ));
        }
        maybe_result.push(0);
        Ok(maybe_result)
    }
}
<commit_msg>Add that_in_background<commit_after>//! Use this library to open a path or URL using the program configured on the system.
//!
//! # Usage
//!
//! The following should open the given URL in a web browser
//!
//! ```test_harness,no_run
//! extern crate open;
//!
//! # #[test]
//! # fn doit() {
//! open::that("http://rust-lang.org").unwrap();
//! # }
//! ```
//!
//! # Notes
//!
//! As an operating system program is used, chances are that the open operation fails.
//! Therefore, you are advised to at least check the result with `.is_err()` and
//! behave accordingly, e.g. by letting the user know what you tried to open, and failed.
//!
//! ```
//! # fn doit() {
//! match open::that("http://rust-lang.org") {
//!     Ok(exit_status) => {
//!         if exit_status.success() {
//!             println!("Look at your browser!");
//!         } else {
//!             if let Some(code) = exit_status.code() {
//!                 println!("Command returned non-zero exit status {}!", code);
//!             } else {
//!                 println!("Command returned with unknown exit status!");
//!             }
//!         }
//!     }
//!     Err(why) => println!("Failure to execute command: {}", why),
//! }
//! # }
//! ```
#[cfg(windows)]
extern crate winapi;

#[cfg(not(target_os = "windows"))]
use std::process::{Command, Stdio};

use std::ffi::OsStr;
use std::io;
use std::process::ExitStatus;
use std::thread;

#[cfg(not(any(target_os = "windows", target_os = "macos")))]
pub fn that<T: AsRef<OsStr> + Sized>(path: T) -> io::Result<ExitStatus> {
    let path_ref = path.as_ref();
    let mut last_err: io::Error = io::Error::from_raw_os_error(0);
    for program in &["xdg-open", "gnome-open", "kde-open", "wslview"] {
        match Command::new(program)
            .stdout(Stdio::null())
            .stderr(Stdio::null())
            .arg(path_ref)
            .spawn()
        {
            Ok(mut child) => return child.wait(),
            Err(err) => {
                last_err = err;
                continue;
            }
        }
    }
    Err(last_err)
}

#[cfg(target_os = "windows")]
pub fn that<T: AsRef<OsStr> + Sized>(path: T) -> io::Result<ExitStatus> {
    use std::os::windows::ffi::OsStrExt;
    use std::os::windows::process::ExitStatusExt;
    use std::ptr;
    use winapi::ctypes::c_int;
    use winapi::um::shellapi::ShellExecuteW;

    const SW_SHOW: c_int = 5;

    let path = windows::convert_path(path.as_ref())?;
    let operation: Vec<u16> = OsStr::new("open\0").encode_wide().collect();
    let result = unsafe {
        ShellExecuteW(
            ptr::null_mut(),
            operation.as_ptr(),
            path.as_ptr(),
            ptr::null(),
            ptr::null(),
            SW_SHOW,
        )
    };
    if result as c_int > 32 {
        Ok(ExitStatus::from_raw(0))
    } else {
        Err(io::Error::last_os_error())
    }
}

#[cfg(target_os = "macos")]
pub fn that<T: AsRef<OsStr> + Sized>(path: T) -> io::Result<ExitStatus> {
    Command::new("open")
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .arg(path.as_ref())
        .spawn()?
        .wait()
}

/// Convenience function for opening the passed path in a new thread.
pub fn that_in_background<T: AsRef<OsStr> + Sized>(
    path: T,
) -> thread::JoinHandle<io::Result<ExitStatus>> {
    let path = path.as_ref().to_os_string();
    thread::spawn(|| that(path))
}

#[cfg(windows)]
mod windows {
    use std::ffi::OsStr;
    use std::io;
    use std::os::windows::ffi::OsStrExt;

    pub fn convert_path(path: &OsStr) -> io::Result<Vec<u16>> {
        let mut maybe_result: Vec<_> = path.encode_wide().collect();
        if maybe_result.iter().any(|&u| u == 0) {
            return Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                "path contains NUL byte(s)",
            ));
        }
        maybe_result.push(0);
        Ok(maybe_result)
    }
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! SOS: the Stupid Operating System
//!
//! SOS is a simple, tiny toy OS implemented in Rust.
//!
//! I'm writing this mostly for fun, to learn more about OS design and kernel //! hacking, so don't expect anything new or exciting out of this project.

#![crate_name = "sos_kernel"]
#![crate_type = "staticlib"]
#![feature( no_std
          , lang_items
          , asm )]
#![feature( const_fn
          , slice_patterns
          )]
#![feature(collections)]
#![no_std]

extern crate collections;
extern crate rlibc;
extern crate spin;

extern crate sos_multiboot2 as multiboot;
extern crate sos_alloc as alloc;

#[macro_use] extern crate sos_vga as vga;
#[macro_use] extern crate bitflags;

pub mod arch;
pub mod util;
pub mod panic;
pub mod memory;

#[macro_use] pub mod io;

use arch::cpu;

/// Kernel main loop
pub fn kernel_main() {
    loop { }
}

/// Kernel initialization function called from ASM
///
/// The kernel main loop expects to be passed the address of a valid
/// Multiboot 2 info struct. It's the bootloader's responsibility to ensure
/// that this is passed in the correct register as expected by the calling
/// convention (`edi` on x86). If this isn't there, you can expect to have a
/// bad problem and not go to space today.
#[no_mangle]
pub extern fn kernel_start(multiboot_addr: usize) {
    io::term::CONSOLE.lock().clear();

    println!("Hello from the kernel!");

    // -- Unpack multiboot tag ------------------------------------------------
    let boot_info = unsafe { multiboot::Info::from(multiboot_addr) };

    let mmap_tag // Extract the memory map tag from the multiboot info
        = boot_info.mem_map()
                   .expect("Memory map tag required!");

    println!("Detected memory areas:");
    for a in mmap_tag.areas() {
        println!("     start: {:#08x}, end: {:#08x}"
                , a.base, a.length );
    }

    let elf_sections_tag // Extract ELF sections tag from the multiboot info
        = boot_info.elf64_sections()
                   .expect("ELF sections tag required!");

    println!("Detecting kernel ELF sections:");

    let kernel_begin    // Extract kernel ELF sections from  multiboot info
        = elf_sections_tag.sections()
            .map(|s| {
                println!("     address: {:#08x}, size: {:#08x}, flags: {:#08x}"
                        , s.address
                        , s.length
                        , s.flags );
                s.address })
            .min()
            .expect("Could not find kernel start section!\
                    \nSomething is deeply wrong.");

    let mut n_elf_sections = 0;
    let kernel_end
        = elf_sections_tag.sections()
            .map(|s| { n_elf_sections += 1;
                     s.address })
            .max()
            .expect("Could not find kernel end section!\
                    \nSomething is deeply wrong.");

    println!( "Detected {} kernel ELF sections.", n_elf_sections);
    println!( "Kernel begins at {:#x} and ends at {:#x}."
             , kernel_begin, kernel_end );

    let multiboot_end = multiboot_addr + boot_info.length as usize;

    println!( "Multiboot info begins at {:#x} and ends at {:#x}."
             , multiboot_addr, multiboot_end);

    // -- initialize interrupts ----------------------------------------------
    unsafe {
        cpu::interrupts::initialize();
        println!("Intializing interrupts...    [DONE]" );
    };

    // -- initialize the heap ------------------------------------------------
    unsafe {
        print!("Intializing heap...    ");
        memory::init_heap();
        println!( "[DONE]\nHeap begins at {:#x} and ends at {:#x}."
                , memory::heap_base_addr(), memory::heap_top_addr() );
    };

    // let mut a_vec = collections::vec::Vec::<usize>::new();
    // println!( "TEST: Created a vector in kernel space! {:?}", a_vec);
    // a_vec.push(1);
    // println!( "TEST: pushed to vec: {:?}", a_vec);
    // a_vec.push(2);
    // println!( "TEST: pushed to vec: {:?}", a_vec);

    // -- call into kernel main loop ------------------------------------------
    // (currently, this does nothing)
    kernel_main()
}
<commit_msg>[kernel] Enable Unique feature gate in lib.rs<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! SOS: the Stupid Operating System
//!
//! SOS is a simple, tiny toy OS implemented in Rust.
//!
//! I'm writing this mostly for fun, to learn more about OS design and kernel //! hacking, so don't expect anything new or exciting out of this project.

#![crate_name = "sos_kernel"]
#![crate_type = "staticlib"]
#![feature( no_std
          , lang_items
          , asm )]
#![feature( const_fn
          , slice_patterns
          , associated_consts
          , unique
          )]
#![feature(collections)]
#![no_std]

extern crate collections;
extern crate rlibc;
extern crate spin;

extern crate sos_multiboot2 as multiboot;
extern crate sos_alloc as alloc;

#[macro_use] extern crate sos_vga as vga;
#[macro_use] extern crate bitflags;

pub mod arch;
pub mod util;
pub mod panic;
pub mod memory;

#[macro_use] pub mod io;

use arch::cpu;

/// Kernel main loop
pub fn kernel_main() {
    let mut a_vec = collections::vec::Vec::<usize>::new();
    println!( "TEST: Created a vector in kernel space! {:?}", a_vec);
    // a_vec.push(1);
    // println!( "TEST: pushed to vec: {:?}", a_vec);
    // a_vec.push(2);
    // println!( "TEST: pushed to vec: {:?}", a_vec);
    loop { }
}

/// Kernel initialization function called from ASM
///
/// The kernel main loop expects to be passed the address of a valid
/// Multiboot 2 info struct. It's the bootloader's responsibility to ensure
/// that this is passed in the correct register as expected by the calling
/// convention (`edi` on x86). If this isn't there, you can expect to have a
/// bad problem and not go to space today.
#[no_mangle]
pub extern fn kernel_start(multiboot_addr: usize) {
    io::term::CONSOLE.lock().clear();

    println!("Hello from the kernel!");

    // -- Unpack multiboot tag ------------------------------------------------
    let boot_info = unsafe { multiboot::Info::from(multiboot_addr) };

    let mmap_tag // Extract the memory map tag from the multiboot info
        = boot_info.mem_map()
                   .expect("Memory map tag required!");

    println!("Detected memory areas:");
    for a in mmap_tag.areas() {
        println!("     start: {:#08x}, end: {:#08x}"
                , a.base, a.length );
    }

    let elf_sections_tag // Extract ELF sections tag from the multiboot info
        = boot_info.elf64_sections()
                   .expect("ELF sections tag required!");

    println!("Detecting kernel ELF sections:");

    let kernel_begin    // Extract kernel ELF sections from  multiboot info
        = elf_sections_tag.sections()
            .map(|s| {
                println!("     address: {:#08x}, size: {:#08x}, flags: {:#08x}"
                        , s.address
                        , s.length
                        , s.flags );
                s.address })
            .min()
            .expect("Could not find kernel start section!\
                    \nSomething is deeply wrong.");

    let mut n_elf_sections = 0;
    let kernel_end
        = elf_sections_tag.sections()
            .map(|s| { n_elf_sections += 1;
                     s.address })
            .max()
            .expect("Could not find kernel end section!\
                    \nSomething is deeply wrong.");

    println!( "Detected {} kernel ELF sections.", n_elf_sections);
    println!( "Kernel begins at {:#x} and ends at {:#x}."
             , kernel_begin, kernel_end );

    let multiboot_end = multiboot_addr + boot_info.length as usize;

    println!( "Multiboot info begins at {:#x} and ends at {:#x}."
             , multiboot_addr, multiboot_end);

    // -- initialize interrupts ----------------------------------------------
    unsafe {
        cpu::interrupts::initialize();
        println!("Intializing interrupts...    [DONE]" );
    };

    // -- initialize the heap ------------------------------------------------
    unsafe {
        print!("Intializing heap...    ");
        memory::init_heap();
        println!( "[DONE]\nHeap begins at {:#x} and ends at {:#x}."
                , memory::heap_base_addr(), memory::heap_top_addr() );
    };

    // -- call into kernel main loop ------------------------------------------
    // (currently, this does nothing)
    kernel_main()
}
<|endoftext|>


<commit_before>//! A library providing a generic connection pool.
#![feature(unsafe_destructor, phase)]
#![warn(missing_doc)]
#![doc(html_root_url="http://www.rust-ci.org/sfackler/r2d2/doc")]

#[phase(plugin, link)]
extern crate log;

use std::comm;
use std::collections::{Deque, RingBuf};
use std::sync::{Arc, Mutex};
use std::fmt;

pub use config::Config;

mod config;

/// A trait which provides database-specific functionality.
pub trait PoolManager<C, E>: Send+Sync {
    /// Attempts to create a new connection.
    fn connect(&self) -> Result<C, E>;

    /// Determines if the connection is still connected to the database.
    ///
    /// A standard implementation would check if a simple query like `SELECT 1`
    /// succeeds.
    fn is_valid(&self, conn: &C) -> bool;
}

/// A trait which handles errors reported by the `PoolManager`.
pub trait ErrorHandler<E>: Send+Sync {
    /// Handles a connection error.
    fn handle_error(&self, error: E);
}

/// An `ErrorHandler` which does nothing.
pub struct NoopErrorHandler;

impl<E> ErrorHandler<E> for NoopErrorHandler {
    fn handle_error(&self, _: E) {}
}

/// An `ErrorHandler` which logs at the error level.
pub struct LoggingErrorHandler<E>;

impl<E> ErrorHandler<E> for LoggingErrorHandler<E> where E: fmt::Show {
    fn handle_error(&self, error: E) {
        error!("Error opening connection: {}", error);
    }
}

/// An error type returned if pool creation fails.
#[deriving(PartialEq, Eq)]
pub enum NewPoolError<E> {
    /// The provided pool configuration was invalid.
    InvalidConfig(&'static str),
    /// The manager returned an error when creating a connection.
    ConnectionError(E),
}

impl<E> fmt::Show for NewPoolError<E> where E: fmt::Show {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            InvalidConfig(ref error) => write!(f, "Invalid config: {}", error),
            ConnectionError(ref error) => write!(f, "Unable to create connections: {}", error),
        }
    }
}

enum Command<C> {
    AddConnection,
    TestConnection(C),
}

struct PoolInternals<C, E> {
    conns: RingBuf<C>,
    num_conns: uint,
}

struct InnerPool<C, E, M, H>
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    config: Config,
    manager: M,
    error_handler: H,
    internals: Mutex<PoolInternals<C, E>>,
}

/// A generic connection pool.
pub struct Pool<C, E, M, H>
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    helper_chan: Mutex<Sender<Command<C>>>,
    inner: Arc<InnerPool<C, E, M, H>>
}

impl<C, E, M, H> Pool<C, E, M, H>
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    /// Creates a new connection pool.
    pub fn new(config: Config, manager: M, error_handler: H)
               -> Result<Pool<C, E, M, H>, NewPoolError<E>> {
        match config.validate() {
            Ok(()) => {}
            Err(err) => return Err(InvalidConfig(err))
        }

        let mut internals = PoolInternals {
            conns: RingBuf::new(),
            num_conns: config.pool_size,
        };

        for _ in range(0, config.pool_size) {
            match manager.connect() {
                Ok(conn) => internals.conns.push(conn),
                Err(err) => return Err(ConnectionError(err)),
            }
        }

        let inner = Arc::new(InnerPool {
            config: config,
            manager: manager,
            error_handler: error_handler,
            internals: Mutex::new(internals),
        });

        let (sender, receiver) = comm::channel();
        // FIXME :(
        let receiver = Arc::new(Mutex::new(receiver));

        for _ in range(0, config.helper_tasks) {
            let inner = inner.clone();
            let receiver = receiver.clone();
            spawn(proc() helper_task(receiver, inner));
        }

        Ok(Pool {
            helper_chan: Mutex::new(sender),
            inner: inner,
        })
    }

    /// Retrieves a connection from the pool.
    pub fn get<'a>(&'a self) -> Result<PooledConnection<'a, C, E, M, H>, E> {
        let mut internals = self.inner.internals.lock();

        loop {
            match internals.conns.pop_front() {
                Some(conn) => {
                    if self.inner.config.test_on_check_out {
                        drop(internals);
                        let valid = self.inner.manager.is_valid(&conn);

                        if !valid {
                            internals = self.inner.internals.lock();
                            internals.num_conns -= 1;
                            continue;
                        }
                    }

                    return Ok(PooledConnection {
                        pool: self,
                        conn: Some(conn),
                    })
                }
                None => internals.cond.wait(),
            }
        }
    }

    fn put_back(&self, conn: C) {
        let mut internals = self.inner.internals.lock();
        internals.conns.push(conn);
        internals.cond.signal();
    }
}

fn helper_task<C, E, M, H>(receiver: Arc<Mutex<Receiver<Command<C>>>>,
                           inner: Arc<InnerPool<C, E, M, H>>)
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    loop {
        let mut receiver = receiver.lock();
        let res = receiver.recv_opt();
        drop(receiver);

        match res {
            Ok(AddConnection) => add_connection(&*inner),
            Ok(TestConnection(conn)) => test_connection(&*inner, conn),
            Err(()) => break,
        }
    }
}

fn add_connection<C, E, M, H>(inner: &InnerPool<C, E, M, H>)
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    match inner.manager.connect() {
        Ok(conn) => {
            let mut internals = inner.internals.lock();
            internals.conns.push(conn);
            internals.num_conns += 1;
            internals.cond.signal();
        }
        Err(err) => inner.error_handler.handle_error(err),
    }
}

fn test_connection<C, E, M, H>(inner: &InnerPool<C, E, M, H>, conn: C)
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    let is_valid = inner.manager.is_valid(&conn);
    let mut internals = inner.internals.lock();
    if is_valid {
        internals.conns.push(conn);
    } else {
        internals.num_conns -= 1;
    }
}

/// A smart pointer wrapping an underlying connection.
pub struct PooledConnection<'a, C, E, M, H>
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    pool: &'a Pool<C, E, M, H>,
    conn: Option<C>,
}

#[unsafe_destructor]
impl<'a, C, E, M, H> Drop for PooledConnection<'a, C, E, M, H>
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    fn drop(&mut self) {
        self.pool.put_back(self.conn.take().unwrap());
    }
}

impl<'a, C, E, M, H> Deref<C> for PooledConnection<'a, C, E, M, H>
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    fn deref(&self) -> &C {
        self.conn.as_ref().unwrap()
    }
}
<commit_msg>Remove parameter from LoggingErrorHandler<commit_after>//! A library providing a generic connection pool.
#![feature(unsafe_destructor, phase)]
#![warn(missing_doc)]
#![doc(html_root_url="http://www.rust-ci.org/sfackler/r2d2/doc")]

#[phase(plugin, link)]
extern crate log;

use std::comm;
use std::collections::{Deque, RingBuf};
use std::sync::{Arc, Mutex};
use std::fmt;

pub use config::Config;

mod config;

/// A trait which provides database-specific functionality.
pub trait PoolManager<C, E>: Send+Sync {
    /// Attempts to create a new connection.
    fn connect(&self) -> Result<C, E>;

    /// Determines if the connection is still connected to the database.
    ///
    /// A standard implementation would check if a simple query like `SELECT 1`
    /// succeeds.
    fn is_valid(&self, conn: &C) -> bool;
}

/// A trait which handles errors reported by the `PoolManager`.
pub trait ErrorHandler<E>: Send+Sync {
    /// Handles a connection error.
    fn handle_error(&self, error: E);
}

/// An `ErrorHandler` which does nothing.
pub struct NoopErrorHandler;

impl<E> ErrorHandler<E> for NoopErrorHandler {
    fn handle_error(&self, _: E) {}
}

/// An `ErrorHandler` which logs at the error level.
pub struct LoggingErrorHandler;

impl<E> ErrorHandler<E> for LoggingErrorHandler where E: fmt::Show {
    fn handle_error(&self, error: E) {
        error!("Error opening connection: {}", error);
    }
}

/// An error type returned if pool creation fails.
#[deriving(PartialEq, Eq)]
pub enum NewPoolError<E> {
    /// The provided pool configuration was invalid.
    InvalidConfig(&'static str),
    /// The manager returned an error when creating a connection.
    ConnectionError(E),
}

impl<E> fmt::Show for NewPoolError<E> where E: fmt::Show {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            InvalidConfig(ref error) => write!(f, "Invalid config: {}", error),
            ConnectionError(ref error) => write!(f, "Unable to create connections: {}", error),
        }
    }
}

enum Command<C> {
    AddConnection,
    TestConnection(C),
}

struct PoolInternals<C, E> {
    conns: RingBuf<C>,
    num_conns: uint,
}

struct InnerPool<C, E, M, H>
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    config: Config,
    manager: M,
    error_handler: H,
    internals: Mutex<PoolInternals<C, E>>,
}

/// A generic connection pool.
pub struct Pool<C, E, M, H>
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    helper_chan: Mutex<Sender<Command<C>>>,
    inner: Arc<InnerPool<C, E, M, H>>
}

impl<C, E, M, H> Pool<C, E, M, H>
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    /// Creates a new connection pool.
    pub fn new(config: Config, manager: M, error_handler: H)
               -> Result<Pool<C, E, M, H>, NewPoolError<E>> {
        match config.validate() {
            Ok(()) => {}
            Err(err) => return Err(InvalidConfig(err))
        }

        let mut internals = PoolInternals {
            conns: RingBuf::new(),
            num_conns: config.pool_size,
        };

        for _ in range(0, config.pool_size) {
            match manager.connect() {
                Ok(conn) => internals.conns.push(conn),
                Err(err) => return Err(ConnectionError(err)),
            }
        }

        let inner = Arc::new(InnerPool {
            config: config,
            manager: manager,
            error_handler: error_handler,
            internals: Mutex::new(internals),
        });

        let (sender, receiver) = comm::channel();
        // FIXME :(
        let receiver = Arc::new(Mutex::new(receiver));

        for _ in range(0, config.helper_tasks) {
            let inner = inner.clone();
            let receiver = receiver.clone();
            spawn(proc() helper_task(receiver, inner));
        }

        Ok(Pool {
            helper_chan: Mutex::new(sender),
            inner: inner,
        })
    }

    /// Retrieves a connection from the pool.
    pub fn get<'a>(&'a self) -> Result<PooledConnection<'a, C, E, M, H>, E> {
        let mut internals = self.inner.internals.lock();

        loop {
            match internals.conns.pop_front() {
                Some(conn) => {
                    if self.inner.config.test_on_check_out {
                        drop(internals);
                        let valid = self.inner.manager.is_valid(&conn);

                        if !valid {
                            internals = self.inner.internals.lock();
                            internals.num_conns -= 1;
                            continue;
                        }
                    }

                    return Ok(PooledConnection {
                        pool: self,
                        conn: Some(conn),
                    })
                }
                None => internals.cond.wait(),
            }
        }
    }

    fn put_back(&self, conn: C) {
        let mut internals = self.inner.internals.lock();
        internals.conns.push(conn);
        internals.cond.signal();
    }
}

fn helper_task<C, E, M, H>(receiver: Arc<Mutex<Receiver<Command<C>>>>,
                           inner: Arc<InnerPool<C, E, M, H>>)
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    loop {
        let mut receiver = receiver.lock();
        let res = receiver.recv_opt();
        drop(receiver);

        match res {
            Ok(AddConnection) => add_connection(&*inner),
            Ok(TestConnection(conn)) => test_connection(&*inner, conn),
            Err(()) => break,
        }
    }
}

fn add_connection<C, E, M, H>(inner: &InnerPool<C, E, M, H>)
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    match inner.manager.connect() {
        Ok(conn) => {
            let mut internals = inner.internals.lock();
            internals.conns.push(conn);
            internals.num_conns += 1;
            internals.cond.signal();
        }
        Err(err) => inner.error_handler.handle_error(err),
    }
}

fn test_connection<C, E, M, H>(inner: &InnerPool<C, E, M, H>, conn: C)
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    let is_valid = inner.manager.is_valid(&conn);
    let mut internals = inner.internals.lock();
    if is_valid {
        internals.conns.push(conn);
    } else {
        internals.num_conns -= 1;
    }
}

/// A smart pointer wrapping an underlying connection.
pub struct PooledConnection<'a, C, E, M, H>
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    pool: &'a Pool<C, E, M, H>,
    conn: Option<C>,
}

#[unsafe_destructor]
impl<'a, C, E, M, H> Drop for PooledConnection<'a, C, E, M, H>
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    fn drop(&mut self) {
        self.pool.put_back(self.conn.take().unwrap());
    }
}

impl<'a, C, E, M, H> Deref<C> for PooledConnection<'a, C, E, M, H>
        where C: Send, E: Send, M: PoolManager<C, E>, H: ErrorHandler<E> {
    fn deref(&self) -> &C {
        self.conn.as_ref().unwrap()
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate lazy_static;

#[macro_use]
extern crate log;

extern crate uuid;
use self::uuid::Uuid;

#[cfg(unix)]
mod imp {
    mod unix;
    pub use self::unix::get_machine_id;
}

#[cfg(not(any(unix)))]
mod imp {
    fn get_machine_id() -> Option<Uuid> {
        None
    }
}

use std::fmt;

/// A machine-specific ID.
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub struct MachineId {
    uuid: Uuid,
}

lazy_static! {
    static ref GENERATED_ID: Uuid = Uuid::new_v4();
    static ref GLOBAL_ID: Option<Uuid> = imp::get_machine_id();
}

impl MachineId {
    /// Retrieves or generates the machine-specific ID.
    pub fn new() -> MachineId {
        MachineId {
            uuid: GLOBAL_ID.unwrap_or(*GENERATED_ID).clone(),
        }
    }
}

impl fmt::Display for MachineId {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.uuid)
    }
}
<commit_msg>lib: rename creation method<commit_after>#[macro_use]
extern crate lazy_static;

#[macro_use]
extern crate log;

extern crate uuid;
use self::uuid::Uuid;

#[cfg(unix)]
mod imp {
    mod unix;
    pub use self::unix::get_machine_id;
}

#[cfg(not(any(unix)))]
mod imp {
    fn get_machine_id() -> Option<Uuid> {
        None
    }
}

use std::fmt;

/// A machine-specific ID.
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub struct MachineId {
    uuid: Uuid,
}

lazy_static! {
    static ref GENERATED_ID: Uuid = Uuid::new_v4();
    static ref GLOBAL_ID: Option<Uuid> = imp::get_machine_id();
}

impl MachineId {
    /// Retrieves or generates the machine-specific ID.
    pub fn get() -> MachineId {
        MachineId {
            uuid: GLOBAL_ID.unwrap_or(*GENERATED_ID).clone(),
        }
    }
}

impl fmt::Display for MachineId {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.uuid)
    }
}
<|endoftext|>


<commit_before>#![feature(libc, alloc, core, collections)]
#![warn(missing_docs)]

//! rust-vst2 is a rust implementation of the VST2.4 API
//!
//! # Plugins
//! All Plugins must implement the `Vst` trait and `std::default::Default`. The `vst_main!` macro
//! must also be called in order to export the necessary functions for the VST to function.
//!
//! ## `Vst` Trait
//! All methods in this trait have a default implementation except for the `get_info` method which
//! must be implemented by the Vst object. Any of the default implementations may be overriden for
//! custom functionality; the defaults do nothing on their own.
//!
//! ## `vst_main!` macro
//! `vst_main!` will export the necessary functions to create a proper VST. This must be called
//! with your VST struct name in order for the vst to work.
//!
//! ## Example plugin
//! A barebones VST plugin:
//!
//! ```no_run
//! #[macro_use]
//! extern crate vst2;
//! use std::default::Default;
//!
//! use vst2::{Vst, Info};
//!
//! struct BasicVst {
//!     info: Info
//! }
//!
//! impl Vst for BasicVst {
//!     fn get_info(&mut self) -> &mut Info {
//!         &mut self.info
//!     }
//! }
//!
//! impl Default for BasicVst {
//!     fn default() -> BasicVst {
//!         BasicVst {
//!             info: Info {
//!                 name: "BasicVst".to_string(),
//!
//!                 ..Default::default()
//!             }
//!         }
//!     }
//! }
//!
//! vst_main!(BasicVst); //Important!
//! # fn main() {} //no_run
//! ```
//!
//! # Hosts
//! Hosts are currently not supported. TODO

extern crate collections;
extern crate libc;
extern crate num;
#[macro_use] extern crate log;
#[macro_use] extern crate bitflags;

use std::default::Default;
use std::{ptr, mem};
use std::iter::IntoIterator;

use libc::c_void;

pub mod buffer;
pub mod enums;
pub mod api;
pub mod editor;
mod interfaces;

use enums::flags::plugin::*;
use enums::{PluginCategory, CanDo, Supported};
use api::{HostCallback, AEffect};
pub use buffer::AudioBuffer;
use editor::Editor;

/// VST plugins are identified by a magic number. This corresponds to 0x56737450.
const VST_MAGIC: i32 = ('V' as i32) << 24 |
                       ('s' as i32) << 16 |
                       ('t' as i32) << 8  |
                       ('P' as i32) << 0  ;

/// Exports the necessary symbols for the plugin to be used by a vst host.
///
/// This macro takes a type which must implement the traits `Vst` and `std::default::Default`.
#[macro_export]
macro_rules! vst_main {
    ($t:ty) => {
        #[cfg(target_os = "mac")]
        pub extern "system" fn main_macho (callback: $crate::callbacks::HostCallback) -> *mut $crate::api::AEffect {
            VSTPluginMain(callback)
        }

        #[cfg(target_os = "windows")]
        pub extern "system" fn MAIN (callback: $crate::callbacks::HostCallback) -> *mut $crate::api::AEffect {
            VSTPluginMain(callback)
        }

        #[allow(non_snake_case, unused_variables)]
        #[no_mangle]
        pub extern "system" fn VSTPluginMain(callback: $crate::api::HostCallback) -> *mut $crate::api::AEffect {
            $crate::main::<$t>(callback)
        }
    }
}

/// Initializes a VST plugin and returns a raw pointer to an AEffect struct.
#[doc(hidden)]
pub fn main<T: Vst + Default>(callback: HostCallback) -> *mut AEffect {
    // 1 is audioMasterVersion TODO: abstract this
    if callback(ptr::null_mut(), 1, 0, 0, ptr::null_mut(), 0.0) == 0 {
        return ptr::null_mut();
    }

    let mut vst: T = Default::default();
    let info = vst.get_info().clone();
    trace!("Creating VST Instance...");

    unsafe { mem::transmute(Box::new(AEffect {
        magic: VST_MAGIC,
        dispatcher: interfaces::dispatch, // fn pointer

        _process: interfaces::process_deprecated, // fn pointer

        setParameter: interfaces::set_parameter, // fn pointer
        getParameter: interfaces::get_parameter, // fn pointer

        numPrograms: info.presets,
        numParams: info.parameters,
        numInputs: info.inputs,
        numOutputs: info.outputs,

        flags: {
            let mut flag = CAN_REPLACING;

            if info.f64_precision {
                flag = flag | CAN_DOUBLE_REPLACING;
            }

            if vst.get_editor().is_some() {
                flag = flag | HAS_EDITOR;
            }

            if info.preset_chunks {
                flag = flag | PROGRAM_CHUNKS;
            }

            if let PluginCategory::Synth = info.category {
                flag = flag | IS_SYNTH;
            }

            flag.bits()
        },

        reserved1: 0,
        reserved2: 0,

        initialDelay: 0,

        _realQualities: 0,
        _offQualities: 0,
        _ioRatio: 0.0,

        object: mem::transmute(Box::new(Box::new(vst) as Box<Vst>)),
        user: ptr::null_mut(),

        uniqueId: 0,
        version: 0001,

        processReplacing: interfaces::process_replacing, // fn pointer
        processReplacingF64: interfaces::process_replacing_f64, //fn pointer

        future: [0u8; 56]
    })) }
}

/// A structure representing static plugin information.
#[derive(Clone, Debug)]
pub struct Info {
    /// Plugin Name.
    pub name: String,

    /// Plugin Vendor.
    pub vendor: String,

    /// Number of different presets.
    pub presets: i32,

    /// Number of parameters.
    pub parameters: i32,

    /// Number of inputs.
    pub inputs: i32,

    /// Number of outputs.
    pub outputs: i32,

    /// Unique plugin ID. Can be registered with Steinberg to prevent conflicts with other plugins.
    ///
    /// This ID is used to identify a plugin during save and load of a preset and project.
    pub unique_id: i32,

    /// Plugin version (e.g. 0001 = `v0.0.0.1`, 1283 = `v1.2.8.3`).
    pub version: i32,

    /// Plugin category. Possible values are found in `enums::PluginCategory`.
    pub category: PluginCategory,

    //TODO: Doc
    pub initial_delay: i32,

    /// Indicates whether preset data is handled in formatless chunks. If false,
    /// host saves and restores plugins by reading/writing parameter data. If true, it is up to
    /// the plugin to manage saving preset data by implementing  the
    /// `{get, load}_{preset, bank}_chunks()` methods. Default is `false`.
    pub preset_chunks: bool,

    /// Indicates whether this plugin can process f64 based `AudioBuffer` buffers. Default is
    /// `false`.
    pub f64_precision: bool,

    //no_sound_in_stop: bool, //TODO: Implement this somehow
}

impl Default for Info {
    fn default() -> Info {
        Info {
            name: String::from_str("VST"),
            vendor: String::new(),

            presets: 1, // default preset
            parameters: 0,
            inputs: 2, // Stereo in,out
            outputs: 2,

            unique_id: 0,
            version: 0001, // v0.0.0.1

            category: PluginCategory::Effect,

            initial_delay: 0,

            preset_chunks: false,
            f64_precision: false,
        }
    }
}

/// Must be implemented by all VST plugins.
///
/// All methods except `get_info` provide a default implementation which does nothing and can be
/// safely overridden.
#[allow(unused_variables)]
pub trait Vst {
    /// This method must return a mutable reference to an `Info` struct.
    fn get_info(&mut self) -> &mut Info;

    /// Called when VST is initialized.
    fn init(&mut self) { trace!("Initialized vst."); }

    /// Set the current preset to the index specified by `preset`.
    fn change_preset(&mut self, preset: i32) { }

    /// Get the current preset index.
    fn get_preset_num(&mut self) -> i32 { 0 }

    /// Set the current preset name.
    fn set_preset_name(&mut self, name: String) { }

    /// Get the name of the preset at the index specified by `preset`.
    fn get_preset_name(&mut self, preset: i32) -> String { "".to_string() }

    /// Get parameter label for parameter at `index` (e.g. "db", "sec", "ms", "%").
    fn get_parameter_label(&mut self, index: i32) -> String { "".to_string() }

    /// Get the parameter value for parameter at `index` (e.g. "1.0", "150", "Plate", "Off").
    fn get_parameter_text(&mut self, index: i32) -> String {
        format!("{:.3}", self.get_parameter(index))
    }

    /// Get the name of parameter at `index`.
    fn get_parameter_name(&mut self, index: i32) -> String { format!("Param {}", index) }

    /// Get the value of paramater at `index`. Should be value between 0.0 and 1.0.
    fn get_parameter(&mut self, index: i32) -> f32 { 0.0 }

    /// Set the value of parameter at `index`. `value` is between 0.0 and 1.0.
    fn set_parameter(&mut self, index: i32, value: f32) { }

    /// Return whether parameter at `index` can be automated.
    fn can_be_automated(&mut self, index: i32) -> bool { false }

    /// Called when sample rate is changed by host.
    fn sample_rate_changed(&mut self, rate: f32) { }

    /// Called when block size is changed by host.
    fn block_size_changed(&mut self, size: i64) { }

    /// Called when plugin is turned on.
    fn on_resume(&mut self) { }

    /// Called when plugin is turned off.
    fn on_suspend(&mut self) { }

    /// Vendor specific handling.
    fn vendor_specific(&mut self, index: i32, value: isize, ptr: *mut c_void, opt: f32) { }

    /// Return whether plugin supports specified action.
    fn can_do(&mut self, can_do: CanDo) -> Supported {
        info!("Host is asking if plugin can: {:?}.", can_do);
        Supported::Maybe
    }

    /// Get the tail size of plugin when it is stopped. Used in offline processing as well.
    fn get_tail_size(&mut self) -> isize { 0 }

    /// Process an audio buffer containing `f32` values. TODO: Examples
    fn process(&mut self, buffer: AudioBuffer<f32>) {
        // For each input and output
        for (input, output) in buffer.zip() {
            // For each input sample and output sample in buffer
            for (in_frame, out_frame) in input.into_iter().zip(output.into_iter()) {
                *out_frame = *in_frame;
            }
        }
    }

    /// Process an audio buffer containing `f64` values. TODO: Examples
    fn process_f64(&mut self, buffer: AudioBuffer<f64>) {
        // For each input and output
        for (input, output) in buffer.zip() {
            // For each input sample and output sample in buffer
            for (in_frame, out_frame) in input.into_iter().zip(output.into_iter()) {
                *out_frame = *in_frame;
            }
        }
    }

    /// Return handle to plugin editor if supported.
    fn get_editor(&mut self) -> Option<&mut Editor> { None }

    /// If `preset_chunks` is set to true in plugin info, this should return the raw chunk data for
    /// the current preset.
    fn get_preset_chunks(&mut self) -> Vec<u8> { Vec::new() }

    /// If `preset_chunks` is set to true in plugin info, this should return the raw chunk data for
    /// the current plugin bank.
    fn get_bank_chunks(&mut self) -> Vec<u8> { Vec::new() }

    /// If `preset_chunks` is set to true in plugin info, this should load a preset from the given
    /// chunk data.
    fn load_preset_chunks(&mut self, data: Vec<u8>) {}

    /// If `preset_chunks` is set to true in plugin info, this should load a preset bank from the
    /// given chunk data.
    fn load_bank_chunks(&mut self, data: Vec<u8>) {}
}

#[cfg(test)]
mod tests {
    use std::default::Default;
    use std::ptr;

    use libc::c_void;

    use Vst;
    use Info;
    use main;
    use api::AEffect;

    struct TestVst { info: Info }

    impl Vst for TestVst {
        fn get_info(&mut self) -> &mut Info {
            &mut self.info
        }
    }

    impl Default for TestVst {
        fn default() -> TestVst {
            TestVst {
                info: Info {
                    name: "TestVST".to_string(),
                    ..Default::default()
                }
            }
        }
    }

    fn pass_callback(_effect: *mut AEffect, _opcode: i32, _index: i32, _value: isize, _ptr: *mut c_void, _opt: f32) -> isize {
        1
    }

    fn fail_callback(_effect: *mut AEffect, _opcode: i32, _index: i32, _value: isize, _ptr: *mut c_void, _opt: f32) -> isize {
        0
    }

    #[test]
    fn host_callback() {
        assert_eq!(main::<TestVst>(fail_callback), ptr::null_mut());
    }

    #[test]
    fn aeffect_created() {
        let aeffect = main::<TestVst>(pass_callback);
        assert!(!aeffect.is_null());
    }

    #[test]
    fn vst_drop() {
        use std::mem;

        static mut drop_test: bool = false;

        impl Drop for TestVst {
            fn drop(&mut self) {
                unsafe { drop_test = true; }
            }
        }

        let aeffect = main::<TestVst>(pass_callback);
        assert!(!aeffect.is_null());

        unsafe { drop(mem::transmute::<*mut AEffect, Box<AEffect>>(aeffect)) };

        // Assert that the VST is shut down and dropped.
        assert!(unsafe { drop_test });
    }

    #[test]
    fn vst_deref() {
        let aeffect = main::<TestVst>(pass_callback);
        assert!(!aeffect.is_null());

        let vst = unsafe { (*aeffect).get_vst() };
        // Assert that deref works correctly.
        assert!(vst.get_info().name == "TestVST");
    }
}
<commit_msg>Make get_info return a struct, not a reference.<commit_after>#![feature(libc, alloc, core, collections)]
#![warn(missing_docs)]

//! rust-vst2 is a rust implementation of the VST2.4 API
//!
//! # Plugins
//! All Plugins must implement the `Vst` trait and `std::default::Default`. The `vst_main!` macro
//! must also be called in order to export the necessary functions for the VST to function.
//!
//! ## `Vst` Trait
//! All methods in this trait have a default implementation except for the `get_info` method which
//! must be implemented by the Vst object. Any of the default implementations may be overriden for
//! custom functionality; the defaults do nothing on their own.
//!
//! ## `vst_main!` macro
//! `vst_main!` will export the necessary functions to create a proper VST. This must be called
//! with your VST struct name in order for the vst to work.
//!
//! ## Example plugin
//! A barebones VST plugin:
//!
//! ```no_run
//! #[macro_use]
//! extern crate vst2;
//! use std::default::Default;
//!
//! use vst2::{Vst, Info};
//!
//! struct BasicVst {
//!     info: Info
//! }
//!
//! impl Vst for BasicVst {
//!     fn get_info(&mut self) -> &mut Info {
//!         &mut self.info
//!     }
//! }
//!
//! impl Default for BasicVst {
//!     fn default() -> BasicVst {
//!         BasicVst {
//!             info: Info {
//!                 name: "BasicVst".to_string(),
//!
//!                 ..Default::default()
//!             }
//!         }
//!     }
//! }
//!
//! vst_main!(BasicVst); //Important!
//! # fn main() {} //no_run
//! ```
//!
//! # Hosts
//! Hosts are currently not supported. TODO

extern crate collections;
extern crate libc;
extern crate num;
#[macro_use] extern crate log;
#[macro_use] extern crate bitflags;

use std::default::Default;
use std::{ptr, mem};
use std::iter::IntoIterator;

use libc::c_void;

pub mod buffer;
pub mod enums;
pub mod api;
pub mod editor;
mod interfaces;

use enums::flags::plugin::*;
use enums::{PluginCategory, CanDo, Supported};
use api::{HostCallback, AEffect};
pub use buffer::AudioBuffer;
use editor::Editor;

/// VST plugins are identified by a magic number. This corresponds to 0x56737450.
const VST_MAGIC: i32 = ('V' as i32) << 24 |
                       ('s' as i32) << 16 |
                       ('t' as i32) << 8  |
                       ('P' as i32) << 0  ;

/// Exports the necessary symbols for the plugin to be used by a vst host.
///
/// This macro takes a type which must implement the traits `Vst` and `std::default::Default`.
#[macro_export]
macro_rules! vst_main {
    ($t:ty) => {
        #[cfg(target_os = "mac")]
        pub extern "system" fn main_macho (callback: $crate::callbacks::HostCallback) -> *mut $crate::api::AEffect {
            VSTPluginMain(callback)
        }

        #[cfg(target_os = "windows")]
        pub extern "system" fn MAIN (callback: $crate::callbacks::HostCallback) -> *mut $crate::api::AEffect {
            VSTPluginMain(callback)
        }

        #[allow(non_snake_case, unused_variables)]
        #[no_mangle]
        pub extern "system" fn VSTPluginMain(callback: $crate::api::HostCallback) -> *mut $crate::api::AEffect {
            $crate::main::<$t>(callback)
        }
    }
}

/// Initializes a VST plugin and returns a raw pointer to an AEffect struct.
#[doc(hidden)]
pub fn main<T: Vst + Default>(callback: HostCallback) -> *mut AEffect {
    // 1 is audioMasterVersion TODO: abstract this
    if callback(ptr::null_mut(), 1, 0, 0, ptr::null_mut(), 0.0) == 0 {
        return ptr::null_mut();
    }

    let mut vst: T = Default::default();
    let info = vst.get_info().clone();
    trace!("Creating VST Instance...");

    unsafe { mem::transmute(Box::new(AEffect {
        magic: VST_MAGIC,
        dispatcher: interfaces::dispatch, // fn pointer

        _process: interfaces::process_deprecated, // fn pointer

        setParameter: interfaces::set_parameter, // fn pointer
        getParameter: interfaces::get_parameter, // fn pointer

        numPrograms: info.presets,
        numParams: info.parameters,
        numInputs: info.inputs,
        numOutputs: info.outputs,

        flags: {
            let mut flag = CAN_REPLACING;

            if info.f64_precision {
                flag = flag | CAN_DOUBLE_REPLACING;
            }

            if vst.get_editor().is_some() {
                flag = flag | HAS_EDITOR;
            }

            if info.preset_chunks {
                flag = flag | PROGRAM_CHUNKS;
            }

            if let PluginCategory::Synth = info.category {
                flag = flag | IS_SYNTH;
            }

            flag.bits()
        },

        reserved1: 0,
        reserved2: 0,

        initialDelay: 0,

        _realQualities: 0,
        _offQualities: 0,
        _ioRatio: 0.0,

        object: mem::transmute(Box::new(Box::new(vst) as Box<Vst>)),
        user: ptr::null_mut(),

        uniqueId: 0,
        version: 0001,

        processReplacing: interfaces::process_replacing, // fn pointer
        processReplacingF64: interfaces::process_replacing_f64, //fn pointer

        future: [0u8; 56]
    })) }
}

/// A structure representing static plugin information.
#[derive(Clone, Debug)]
pub struct Info {
    /// Plugin Name.
    pub name: String,

    /// Plugin Vendor.
    pub vendor: String,

    /// Number of different presets.
    pub presets: i32,

    /// Number of parameters.
    pub parameters: i32,

    /// Number of inputs.
    pub inputs: i32,

    /// Number of outputs.
    pub outputs: i32,

    /// Unique plugin ID. Can be registered with Steinberg to prevent conflicts with other plugins.
    ///
    /// This ID is used to identify a plugin during save and load of a preset and project.
    pub unique_id: i32,

    /// Plugin version (e.g. 0001 = `v0.0.0.1`, 1283 = `v1.2.8.3`).
    pub version: i32,

    /// Plugin category. Possible values are found in `enums::PluginCategory`.
    pub category: PluginCategory,

    //TODO: Doc
    pub initial_delay: i32,

    /// Indicates whether preset data is handled in formatless chunks. If false,
    /// host saves and restores plugins by reading/writing parameter data. If true, it is up to
    /// the plugin to manage saving preset data by implementing  the
    /// `{get, load}_{preset, bank}_chunks()` methods. Default is `false`.
    pub preset_chunks: bool,

    /// Indicates whether this plugin can process f64 based `AudioBuffer` buffers. Default is
    /// `false`.
    pub f64_precision: bool,

    //no_sound_in_stop: bool, //TODO: Implement this somehow
}

impl Default for Info {
    fn default() -> Info {
        Info {
            name: String::from_str("VST"),
            vendor: String::new(),

            presets: 1, // default preset
            parameters: 0,
            inputs: 2, // Stereo in,out
            outputs: 2,

            unique_id: 0,
            version: 0001, // v0.0.0.1

            category: PluginCategory::Effect,

            initial_delay: 0,

            preset_chunks: false,
            f64_precision: false,
        }
    }
}

/// Must be implemented by all VST plugins.
///
/// All methods except `get_info` provide a default implementation which does nothing and can be
/// safely overridden.
#[allow(unused_variables)]
pub trait Vst {
    /// This method must return an `Info` struct.
    fn get_info(&self) -> Info;

    /// Called when VST is initialized.
    fn init(&mut self) { trace!("Initialized vst."); }

    /// Set the current preset to the index specified by `preset`.
    fn change_preset(&mut self, preset: i32) { }

    /// Get the current preset index.
    fn get_preset_num(&mut self) -> i32 { 0 }

    /// Set the current preset name.
    fn set_preset_name(&mut self, name: String) { }

    /// Get the name of the preset at the index specified by `preset`.
    fn get_preset_name(&mut self, preset: i32) -> String { "".to_string() }

    /// Get parameter label for parameter at `index` (e.g. "db", "sec", "ms", "%").
    fn get_parameter_label(&mut self, index: i32) -> String { "".to_string() }

    /// Get the parameter value for parameter at `index` (e.g. "1.0", "150", "Plate", "Off").
    fn get_parameter_text(&mut self, index: i32) -> String {
        format!("{:.3}", self.get_parameter(index))
    }

    /// Get the name of parameter at `index`.
    fn get_parameter_name(&mut self, index: i32) -> String { format!("Param {}", index) }

    /// Get the value of paramater at `index`. Should be value between 0.0 and 1.0.
    fn get_parameter(&mut self, index: i32) -> f32 { 0.0 }

    /// Set the value of parameter at `index`. `value` is between 0.0 and 1.0.
    fn set_parameter(&mut self, index: i32, value: f32) { }

    /// Return whether parameter at `index` can be automated.
    fn can_be_automated(&mut self, index: i32) -> bool { false }

    /// Called when sample rate is changed by host.
    fn sample_rate_changed(&mut self, rate: f32) { }

    /// Called when block size is changed by host.
    fn block_size_changed(&mut self, size: i64) { }

    /// Called when plugin is turned on.
    fn on_resume(&mut self) { }

    /// Called when plugin is turned off.
    fn on_suspend(&mut self) { }

    /// Vendor specific handling.
    fn vendor_specific(&mut self, index: i32, value: isize, ptr: *mut c_void, opt: f32) { }

    /// Return whether plugin supports specified action.
    fn can_do(&mut self, can_do: CanDo) -> Supported {
        info!("Host is asking if plugin can: {:?}.", can_do);
        Supported::Maybe
    }

    /// Get the tail size of plugin when it is stopped. Used in offline processing as well.
    fn get_tail_size(&mut self) -> isize { 0 }

    /// Process an audio buffer containing `f32` values. TODO: Examples
    fn process(&mut self, buffer: AudioBuffer<f32>) {
        // For each input and output
        for (input, output) in buffer.zip() {
            // For each input sample and output sample in buffer
            for (in_frame, out_frame) in input.into_iter().zip(output.into_iter()) {
                *out_frame = *in_frame;
            }
        }
    }

    /// Process an audio buffer containing `f64` values. TODO: Examples
    fn process_f64(&mut self, buffer: AudioBuffer<f64>) {
        // For each input and output
        for (input, output) in buffer.zip() {
            // For each input sample and output sample in buffer
            for (in_frame, out_frame) in input.into_iter().zip(output.into_iter()) {
                *out_frame = *in_frame;
            }
        }
    }

    /// Return handle to plugin editor if supported.
    fn get_editor(&mut self) -> Option<&mut Editor> { None }

    /// If `preset_chunks` is set to true in plugin info, this should return the raw chunk data for
    /// the current preset.
    fn get_preset_chunks(&mut self) -> Vec<u8> { Vec::new() }

    /// If `preset_chunks` is set to true in plugin info, this should return the raw chunk data for
    /// the current plugin bank.
    fn get_bank_chunks(&mut self) -> Vec<u8> { Vec::new() }

    /// If `preset_chunks` is set to true in plugin info, this should load a preset from the given
    /// chunk data.
    fn load_preset_chunks(&mut self, data: Vec<u8>) {}

    /// If `preset_chunks` is set to true in plugin info, this should load a preset bank from the
    /// given chunk data.
    fn load_bank_chunks(&mut self, data: Vec<u8>) {}
}

#[cfg(test)]
mod tests {
    use std::default::Default;
    use std::ptr;

    use libc::c_void;

    use Vst;
    use Info;
    use main;
    use api::AEffect;

    struct TestVst { info: Info }

    impl Vst for TestVst {
        fn get_info(&mut self) -> &mut Info {
            &mut self.info
        }
    }

    impl Default for TestVst {
        fn default() -> TestVst {
            TestVst {
                info: Info {
                    name: "TestVST".to_string(),
                    ..Default::default()
                }
            }
        }
    }

    fn pass_callback(_effect: *mut AEffect, _opcode: i32, _index: i32, _value: isize, _ptr: *mut c_void, _opt: f32) -> isize {
        1
    }

    fn fail_callback(_effect: *mut AEffect, _opcode: i32, _index: i32, _value: isize, _ptr: *mut c_void, _opt: f32) -> isize {
        0
    }

    #[test]
    fn host_callback() {
        assert_eq!(main::<TestVst>(fail_callback), ptr::null_mut());
    }

    #[test]
    fn aeffect_created() {
        let aeffect = main::<TestVst>(pass_callback);
        assert!(!aeffect.is_null());
    }

    #[test]
    fn vst_drop() {
        use std::mem;

        static mut drop_test: bool = false;

        impl Drop for TestVst {
            fn drop(&mut self) {
                unsafe { drop_test = true; }
            }
        }

        let aeffect = main::<TestVst>(pass_callback);
        assert!(!aeffect.is_null());

        unsafe { drop(mem::transmute::<*mut AEffect, Box<AEffect>>(aeffect)) };

        // Assert that the VST is shut down and dropped.
        assert!(unsafe { drop_test });
    }

    #[test]
    fn vst_deref() {
        let aeffect = main::<TestVst>(pass_callback);
        assert!(!aeffect.is_null());

        let vst = unsafe { (*aeffect).get_vst() };
        // Assert that deref works correctly.
        assert!(vst.get_info().name == "TestVST");
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::ops::{Sub, Div};
use itertools::Itertools;
use error::*;
use eval::Env;
use types::{Expr, List, Vector, Function, Lambda};
use util::*;

pub fn env<'a>() -> Env<'a> {
    let table: Vec<(&str, Lambda)> = vec![
        ("not", not),
        ("or", or),
        ("and", and),
        ("print", print),
        ("+", add),
        ("-", sub),
        ("*", mul),
        ("/", div),
        ("=", equal),
        ("<", less),
        ("<=", less_eq),
        (">", greater),
        (">=", greater_eq),
        ("first", first),
        ("rest", rest),
        ("cons", cons),
        ("list", list),
        ("exit", exit),
    ];

    let builtins = table
        .into_iter()
        .map(|(symbol, f)| {
            (
                String::from(symbol),
                Expr::from(Function::builtin(symbol, f)),
            )
        })
        .collect::<HashMap<_, _>>();

    Env::new(builtins, None)
}

fn numeric_op<F, G>(name: &str, args: &[Expr], fn_int: F, fn_flt: G) -> Result<Expr>
where
    F: Fn(&[i64]) -> Result<i64>,
    G: Fn(&[f64]) -> Result<f64>,
{
    // Check all arguments are numeric
    if args.iter().all(Expr::is_num) {
        if args.iter().any(Expr::is_flt) {
            // If any are float, promote to float
            let floats = args.iter()
                .map(|x| match x {
                    &Expr::Int(y) => y as f64,
                    &Expr::Flt(y) => y,
                    _ => unreachable!(),
                })
                .collect::<Vec<_>>();
            fn_flt(&floats).map(Expr::from)
        } else {
            // Otherwise perform integer operation
            let ints = args.iter()
                .map(|x| match x {
                    &Expr::Int(y) => y,
                    _ => unreachable!(),
                })
                .collect::<Vec<_>>();
            fn_int(&ints).map(Expr::from)
        }
    } else {
        Err(format!("#[{}] expected numeric", name).into())
    }
}

pub fn add(args: &[Expr]) -> Result<Expr> {
    numeric_op("+", args,
        |ints| Ok(ints.iter().sum::<i64>()),
        |floats| Ok(floats.iter().sum::<f64>())
    )
}

pub fn sub(args: &[Expr]) -> Result<Expr> {
    ensure_min_args("-", args, 1)?;

    // Check all arguments are numeric
    if !args.iter().all(Expr::is_num) {
        return Err("#[-] expected numeric".into());
    }

    // If one argument, negate and return
    if args.len() == 1 {
        return match args[0] {
            Expr::Int(x) => Ok(Expr::from(-x)),
            Expr::Flt(x) => Ok(Expr::from(-x)),
            _ => Err("invalid type".into())
        }
    }

    numeric_op("-", args,
        |ints| Ok(ints[1..].iter().fold(ints[0], Sub::sub)),
        |floats| Ok(floats[1..].iter().fold(floats[0], Sub::sub))
    )
}

pub fn mul(args: &[Expr]) -> Result<Expr> {
    numeric_op("*", args,
        |ints| Ok(ints.iter().product::<i64>()),
        |floats| Ok(floats.iter().product::<f64>())
    )
}

pub fn div(args: &[Expr]) -> Result<Expr> {
    ensure_min_args("[/]", args, 1)?;

    // Check all arguments are numeric
    if !args.iter().all(Expr::is_num) {
        return Err("#[-] expected numeric".into());
    }

    // If one argument, invert and return
    if args.len() == 1 {
        return match args[0] {
            Expr::Int(x) => Ok(Expr::from((x as f64).recip())),
            Expr::Flt(x) => Ok(Expr::from(x.recip())),
            _ => Err("invalid type".into())
        }
    }

    let int_div = |ints: &[i64]| {
        ints[1..].iter()
            .map(|&x| if x == 0i64 { Err("division by zero".into()) } else { Ok(x) })
            .fold_results(ints[0], Div::div)
    };

    numeric_op("/", args,
        int_div,
        |floats| Ok(floats[1..].iter().fold(floats[0], Div::div))
    )
}

pub fn equal(args: &[Expr]) -> Result<Expr> {
    ensure_args("[=]", args, 2)?;
    Ok(Expr::from(args[0] == args[1]))
}

pub fn less(args: &[Expr]) -> Result<Expr> {
    ensure_args("[<]", args, 2)?;
    match (&args[0], &args[1]) {
        (&Expr::Int(ref a), &Expr::Int(ref b)) => Ok(Expr::from(a < b)),
        (&Expr::Flt(ref a), &Expr::Flt(ref b)) => Ok(Expr::from(a < b)),
        (&Expr::Str(ref a), &Expr::Str(ref b)) => Ok(Expr::from(a < b)),
        _ => Err(
            format!("comparison undefined for: {}, {}", args[0], args[1]).into(),
        ),
    }
}

pub fn less_eq(args: &[Expr]) -> Result<Expr> {
    ensure_args("[<=]", args, 2)?;
    match (&args[0], &args[1]) {
        (&Expr::Int(ref a), &Expr::Int(ref b)) => Ok(Expr::from(a <= b)),
        (&Expr::Flt(ref a), &Expr::Flt(ref b)) => Ok(Expr::from(a <= b)),
        (&Expr::Str(ref a), &Expr::Str(ref b)) => Ok(Expr::from(a <= b)),
        _ => Err(
            format!("comparison undefined for: {}, {}", args[0], args[1]).into(),
        ),
    }
}

pub fn greater(args: &[Expr]) -> Result<Expr> {
    ensure_args("[>]", args, 2)?;
    match (&args[0], &args[1]) {
        (&Expr::Int(ref a), &Expr::Int(ref b)) => Ok(Expr::from(a > b)),
        (&Expr::Flt(ref a), &Expr::Flt(ref b)) => Ok(Expr::from(a > b)),
        (&Expr::Str(ref a), &Expr::Str(ref b)) => Ok(Expr::from(a > b)),
        _ => Err(
            format!("comparison undefined for: {}, {}", args[0], args[1]).into(),
        ),
    }
}

pub fn greater_eq(args: &[Expr]) -> Result<Expr> {
    ensure_args("[>=]", args, 2)?;
    match (&args[0], &args[1]) {
        (&Expr::Int(ref a), &Expr::Int(ref b)) => Ok(Expr::from(a >= b)),
        (&Expr::Flt(ref a), &Expr::Flt(ref b)) => Ok(Expr::from(a >= b)),
        (&Expr::Str(ref a), &Expr::Str(ref b)) => Ok(Expr::from(a >= b)),
        _ => Err(
            format!("comparison undefined for: {}, {}", args[0], args[1]).into(),
        ),
    }
}

pub fn not(args: &[Expr]) -> Result<Expr> {
    ensure_args("[not]", args, 1)?;
    Ok(Expr::from(!args[0].truthiness()))
}

// TODO: convert to special form
pub fn and(args: &[Expr]) -> Result<Expr> {
    args
        .into_iter()
        .map(|a| a.boolean())
        .collect::<Option<Vec<_>>>()
        .ok_or("#[and] expected boolean argument".into())
        .map(|bools| bools.iter().all(|b| *b))
        .map(Expr::from)
}

// TODO: convert to special form
pub fn or(args: &[Expr]) -> Result<Expr> {
    args
        .into_iter()
        .map(|a| a.boolean())
        .collect::<Option<Vec<_>>>()
        .ok_or("#[or] expected boolean argument".into())
        .map(|bools| bools.iter().any(|b| *b))
        .map(Expr::from)
}

pub fn print(args: &[Expr]) -> Result<Expr> {
    ensure_args("#[print]", args, 1)?;
    println!("{}", args[0]);
    Ok(Expr::Nil)
}

pub fn first(args: &[Expr]) -> Result<Expr> {
    ensure_args("#[first]", args, 1)?;

    match &args[0] {
        &Expr::List(ref l) => Ok(l.0.first().cloned().unwrap_or(Expr::Nil)),
        &Expr::Vector(ref q) => Ok(q.0.first().cloned().unwrap_or(Expr::Nil)),
        _ => Err("#[first] expected list".into()),
    }
}

pub fn rest(args: &[Expr]) -> Result<Expr> {
    match &args[0] {
        &Expr::List(ref l) => {
            Ok(
                l.0
                    .split_first()
                    .map(|(_, rest)| Expr::List(List(rest.to_vec())))
                    .unwrap_or(Expr::Nil),
            )
        }
        &Expr::Vector(ref v) => {
            Ok(
                v.0
                    .split_first()
                    .map(|(_, rest)| Expr::Vector(Vector(rest.to_vec())))
                    .unwrap_or(Expr::Nil),
            )
        }
        _ => Err("#[rest] expected list".into()),
    }
}

// (cons item seq)
pub fn cons(args: &[Expr]) -> Result<Expr> {
    ensure_args("cons]", args, 2)?;

    match &args[2] {
        &Expr::List(ref l) => {
            let mut new = l.clone();
            new.0.insert(0, args[1].clone());
            Ok(Expr::List(new))
        }
        &Expr::Vector(ref v) => {
            let mut new = v.clone();
            new.0.push(args[1].clone());
            Ok(Expr::Vector(new))
        }
        _ => Err("#[cons] expected list".into()),
    }
}

pub fn list(args: &[Expr]) -> Result<Expr> {
    Ok(Expr::List(List(args.to_vec())))
}

// (exit)
pub fn exit(_args: &[Expr]) -> Result<Expr> {
    Err(ErrorKind::Exit(0).into())
}
<commit_msg>Return nil for (list)<commit_after>use std::collections::HashMap;
use std::ops::{Sub, Div};
use itertools::Itertools;
use error::*;
use eval::Env;
use types::{Expr, List, Vector, Function, Lambda};
use util::*;

pub fn env<'a>() -> Env<'a> {
    let table: Vec<(&str, Lambda)> = vec![
        ("not", not),
        ("or", or),
        ("and", and),
        ("print", print),
        ("+", add),
        ("-", sub),
        ("*", mul),
        ("/", div),
        ("=", equal),
        ("<", less),
        ("<=", less_eq),
        (">", greater),
        (">=", greater_eq),
        ("first", first),
        ("rest", rest),
        ("cons", cons),
        ("list", list),
        ("exit", exit),
    ];

    let builtins = table
        .into_iter()
        .map(|(symbol, f)| {
            (
                String::from(symbol),
                Expr::from(Function::builtin(symbol, f)),
            )
        })
        .collect::<HashMap<_, _>>();

    Env::new(builtins, None)
}

fn numeric_op<F, G>(name: &str, args: &[Expr], fn_int: F, fn_flt: G) -> Result<Expr>
where
    F: Fn(&[i64]) -> Result<i64>,
    G: Fn(&[f64]) -> Result<f64>,
{
    // Check all arguments are numeric
    if args.iter().all(Expr::is_num) {
        if args.iter().any(Expr::is_flt) {
            // If any are float, promote to float
            let floats = args.iter()
                .map(|x| match x {
                    &Expr::Int(y) => y as f64,
                    &Expr::Flt(y) => y,
                    _ => unreachable!(),
                })
                .collect::<Vec<_>>();
            fn_flt(&floats).map(Expr::from)
        } else {
            // Otherwise perform integer operation
            let ints = args.iter()
                .map(|x| match x {
                    &Expr::Int(y) => y,
                    _ => unreachable!(),
                })
                .collect::<Vec<_>>();
            fn_int(&ints).map(Expr::from)
        }
    } else {
        Err(format!("#[{}] expected numeric", name).into())
    }
}

pub fn add(args: &[Expr]) -> Result<Expr> {
    numeric_op("+", args,
        |ints| Ok(ints.iter().sum::<i64>()),
        |floats| Ok(floats.iter().sum::<f64>())
    )
}

pub fn sub(args: &[Expr]) -> Result<Expr> {
    ensure_min_args("-", args, 1)?;

    // Check all arguments are numeric
    if !args.iter().all(Expr::is_num) {
        return Err("#[-] expected numeric".into());
    }

    // If one argument, negate and return
    if args.len() == 1 {
        return match args[0] {
            Expr::Int(x) => Ok(Expr::from(-x)),
            Expr::Flt(x) => Ok(Expr::from(-x)),
            _ => Err("invalid type".into())
        }
    }

    numeric_op("-", args,
        |ints| Ok(ints[1..].iter().fold(ints[0], Sub::sub)),
        |floats| Ok(floats[1..].iter().fold(floats[0], Sub::sub))
    )
}

pub fn mul(args: &[Expr]) -> Result<Expr> {
    numeric_op("*", args,
        |ints| Ok(ints.iter().product::<i64>()),
        |floats| Ok(floats.iter().product::<f64>())
    )
}

pub fn div(args: &[Expr]) -> Result<Expr> {
    ensure_min_args("[/]", args, 1)?;

    // Check all arguments are numeric
    if !args.iter().all(Expr::is_num) {
        return Err("#[-] expected numeric".into());
    }

    // If one argument, invert and return
    if args.len() == 1 {
        return match args[0] {
            Expr::Int(x) => Ok(Expr::from((x as f64).recip())),
            Expr::Flt(x) => Ok(Expr::from(x.recip())),
            _ => Err("invalid type".into())
        }
    }

    let int_div = |ints: &[i64]| {
        ints[1..].iter()
            .map(|&x| if x == 0i64 { Err("division by zero".into()) } else { Ok(x) })
            .fold_results(ints[0], Div::div)
    };

    numeric_op("/", args,
        int_div,
        |floats| Ok(floats[1..].iter().fold(floats[0], Div::div))
    )
}

pub fn equal(args: &[Expr]) -> Result<Expr> {
    ensure_args("[=]", args, 2)?;
    Ok(Expr::from(args[0] == args[1]))
}

pub fn less(args: &[Expr]) -> Result<Expr> {
    ensure_args("[<]", args, 2)?;
    match (&args[0], &args[1]) {
        (&Expr::Int(ref a), &Expr::Int(ref b)) => Ok(Expr::from(a < b)),
        (&Expr::Flt(ref a), &Expr::Flt(ref b)) => Ok(Expr::from(a < b)),
        (&Expr::Str(ref a), &Expr::Str(ref b)) => Ok(Expr::from(a < b)),
        _ => Err(
            format!("comparison undefined for: {}, {}", args[0], args[1]).into(),
        ),
    }
}

pub fn less_eq(args: &[Expr]) -> Result<Expr> {
    ensure_args("[<=]", args, 2)?;
    match (&args[0], &args[1]) {
        (&Expr::Int(ref a), &Expr::Int(ref b)) => Ok(Expr::from(a <= b)),
        (&Expr::Flt(ref a), &Expr::Flt(ref b)) => Ok(Expr::from(a <= b)),
        (&Expr::Str(ref a), &Expr::Str(ref b)) => Ok(Expr::from(a <= b)),
        _ => Err(
            format!("comparison undefined for: {}, {}", args[0], args[1]).into(),
        ),
    }
}

pub fn greater(args: &[Expr]) -> Result<Expr> {
    ensure_args("[>]", args, 2)?;
    match (&args[0], &args[1]) {
        (&Expr::Int(ref a), &Expr::Int(ref b)) => Ok(Expr::from(a > b)),
        (&Expr::Flt(ref a), &Expr::Flt(ref b)) => Ok(Expr::from(a > b)),
        (&Expr::Str(ref a), &Expr::Str(ref b)) => Ok(Expr::from(a > b)),
        _ => Err(
            format!("comparison undefined for: {}, {}", args[0], args[1]).into(),
        ),
    }
}

pub fn greater_eq(args: &[Expr]) -> Result<Expr> {
    ensure_args("[>=]", args, 2)?;
    match (&args[0], &args[1]) {
        (&Expr::Int(ref a), &Expr::Int(ref b)) => Ok(Expr::from(a >= b)),
        (&Expr::Flt(ref a), &Expr::Flt(ref b)) => Ok(Expr::from(a >= b)),
        (&Expr::Str(ref a), &Expr::Str(ref b)) => Ok(Expr::from(a >= b)),
        _ => Err(
            format!("comparison undefined for: {}, {}", args[0], args[1]).into(),
        ),
    }
}

pub fn not(args: &[Expr]) -> Result<Expr> {
    ensure_args("[not]", args, 1)?;
    Ok(Expr::from(!args[0].truthiness()))
}

// TODO: convert to special form
pub fn and(args: &[Expr]) -> Result<Expr> {
    args
        .into_iter()
        .map(|a| a.boolean())
        .collect::<Option<Vec<_>>>()
        .ok_or("#[and] expected boolean argument".into())
        .map(|bools| bools.iter().all(|b| *b))
        .map(Expr::from)
}

// TODO: convert to special form
pub fn or(args: &[Expr]) -> Result<Expr> {
    args
        .into_iter()
        .map(|a| a.boolean())
        .collect::<Option<Vec<_>>>()
        .ok_or("#[or] expected boolean argument".into())
        .map(|bools| bools.iter().any(|b| *b))
        .map(Expr::from)
}

pub fn print(args: &[Expr]) -> Result<Expr> {
    ensure_args("#[print]", args, 1)?;
    println!("{}", args[0]);
    Ok(Expr::Nil)
}

pub fn first(args: &[Expr]) -> Result<Expr> {
    ensure_args("#[first]", args, 1)?;

    match &args[0] {
        &Expr::List(ref l) => Ok(l.0.first().cloned().unwrap_or(Expr::Nil)),
        &Expr::Vector(ref q) => Ok(q.0.first().cloned().unwrap_or(Expr::Nil)),
        _ => Err("#[first] expected list".into()),
    }
}

pub fn rest(args: &[Expr]) -> Result<Expr> {
    match &args[0] {
        &Expr::List(ref l) => {
            Ok(
                l.0
                    .split_first()
                    .map(|(_, rest)| Expr::List(List(rest.to_vec())))
                    .unwrap_or(Expr::Nil),
            )
        }
        &Expr::Vector(ref v) => {
            Ok(
                v.0
                    .split_first()
                    .map(|(_, rest)| Expr::Vector(Vector(rest.to_vec())))
                    .unwrap_or(Expr::Nil),
            )
        }
        _ => Err("#[rest] expected list".into()),
    }
}

// (cons item seq)
pub fn cons(args: &[Expr]) -> Result<Expr> {
    ensure_args("cons]", args, 2)?;

    match &args[2] {
        &Expr::List(ref l) => {
            let mut new = l.clone();
            new.0.insert(0, args[1].clone());
            Ok(Expr::List(new))
        }
        &Expr::Vector(ref v) => {
            let mut new = v.clone();
            new.0.push(args[1].clone());
            Ok(Expr::Vector(new))
        }
        _ => Err("#[cons] expected list".into()),
    }
}

// (list items*)
pub fn list(args: &[Expr]) -> Result<Expr> {
    if args.is_empty() {
        Ok(Expr::Nil)
    } else {
        Ok(Expr::List(List(args.to_vec())))
    }
}

// (exit)
pub fn exit(_args: &[Expr]) -> Result<Expr> {
    Err(ErrorKind::Exit(0).into())
}
<|endoftext|>