<commit_before>#![feature(phase, globs, slicing_syntax, if_let, unboxed_closures)]

extern crate getopts;
extern crate serialize;
extern crate iron;
extern crate persistent;
extern crate regex;
extern crate "conduit-mime-types" as conduit_mime;
extern crate mime;
extern crate mustache;

use std::{ str, os };
use std::str::from_str;
use std::path::{ Path, GenericPath };
use std::io::{ fs, Reader };
use std::io::fs::{ File, PathExtensions };
use std::default::Default;
use std::sync::{ Arc };

use regex::Regex;

use conduit_mime::Types;

use getopts::{ optopt, optflag, getopts, usage, OptGroup };

use serialize::json;
use serialize::json::Json;

use iron::prelude::*;
use iron::response::modifiers::*;
use iron::status;
use iron::mime::*;
use iron::middleware::ChainBuilder;
use iron::typemap::Assoc;

use persistent::Read;

use mustache::{ Template, VecBuilder, MapBuilder };

use constants::*;

pub mod constants;

#[deriving(Send, Clone, Default, Encodable, Decodable)]
struct Options {
    host: Option<String>,
    port: Option<u16>,
    root: Option<Path>,
    filter: Option<String>,
    max_size: Option<u64>,
    template: Option<String>
}

struct OptCarrier;
impl Assoc<Arc<Options>> for OptCarrier {}

#[deriving(Send, Clone)]
struct State {
    template: Template,
    mime_types: Arc<Types>
}

struct StateCarrier;
impl Assoc<Arc<State>> for StateCarrier {}

const HOST: &'static str = "0.0.0.0";
const PORT: u16 = 8080;

static UNITS: &'static [&'static str] = &["B", "kB", "MB", "GB", "TB"];
const BRIEF: &'static str = "A minimal directory server, written in Rust with Iron.";

const KEY_TITLE: &'static str = "title";
const KEY_CONTENT: &'static str = "content";
const KEY_URL: &'static str = "url";
const KEY_SIZE: &'static str = "size";
const KEY_NAME: &'static str = "name";

fn size_with_unit(mut size: u64) -> String {
    let mut frac = 0;
    let mut index = 0;

    while size > 1000 && index + 1 < UNITS.len() {
        frac = size % 1000;
        size /= 1000;
        index += 1;
    }

    format!("{}.{} {}", size, frac, UNITS[index])
}

fn render<'a>(template: Template, root: Path, dir: Path, files: Vec<Path>, filter: Option<Regex>) -> String {
    let data = MapBuilder::<'a>::new()
        .insert_str(KEY_TITLE, dir.display().as_cow().into_owned())
        .insert_vec(KEY_CONTENT, |mut vec: VecBuilder<'a>| {
            let item = |map: MapBuilder, url: &Path, size: u64, name: String| {
                map.insert(KEY_URL, &format!("{}", url.display())[]).unwrap()
                   .insert(KEY_SIZE, &size_with_unit(size)[]).unwrap()
                   .insert_str(KEY_NAME, name)
            };

            // add `..` entry if necessary
            let mut up = dir.clone();
            up.pop();
            if root.is_ancestor_of(&up) {
                vec = vec.push_map(|map: MapBuilder| item(map, &up.path_relative_from(&root).unwrap(), 0, "..".into_string()));
            }

            for file in files.iter() {
                let relative = file.path_relative_from(&root).unwrap();
                let stat = file.stat().unwrap();
                let filename = file.filename_display().as_cow().into_owned();
                if filter.as_ref().map_or(true, |f| f.is_match(filename[])) {
                    vec = vec.push_map(|map| item(map, &relative, stat.size, filename.clone()));
                }
            }
            vec
        }).build();

    let mut out = Vec::new(); // with_capacity(template.len())
    template.render_data(&mut out, &data);
    // The template should be valid utf8, the filenames might not be
    unsafe { String::from_utf8_unchecked(out) }
}

fn plain<B: Bodyable>(content: B) -> IronResult<Response> {
    Ok(Response::new()
       .set(Status(status::Ok))
       .set(Body(content)))
}

fn html<B: Bodyable>(content: B) -> IronResult<Response> {
    plain(content).map(|r| r.set(ContentType(Mime(Text, Html, Vec::new()))))
}

fn serve(req: &mut Request) -> IronResult<Response> {
    let (root, filter_str, max_size) = {
        let o = req.get::<Read<OptCarrier, Arc<Options>>>().unwrap();
        (o.root.clone().unwrap_or_else(|| os::getcwd().ok().unwrap()),
         o.filter.clone(),
         o.max_size)
    };

    let (template, mime_types) = {
        let s = req.get::<Read<StateCarrier, Arc<State>>>().unwrap();
        (s.template.clone(),
         s.mime_types.clone())
    };

    let mut path = root.clone();
    for part in req.url.path.iter() { path.push(part[]) }
    if !path.exists() { return html("Well, no... We don't have that today."); }

    let filter = filter_str.and_then(|s| Regex::new(s[]).ok());

    if path.is_file() && root.is_ancestor_of(&path) {
        let stat = path.stat();
        if stat.as_ref().ok().is_some() && max_size.is_some() && stat.ok().unwrap().size > max_size.unwrap() {
            return html("I'm afraid, I'm too lazy to serve the requested file. It's pretty big...")
        }
        let content = match File::open(&path).read_to_end() {
            Ok(s) => s,
            Err(e) => return html(e.desc)
        };

        if filter.as_ref().map_or(false, |f| !f.is_match(path.filename_str().unwrap())) {
            return html("I don't think you're allowed to do this.");
        }
        let mime: Option<iron::mime::Mime> = path.extension_str()
            .map_or(None, |e| mime_types.get_mime_type(e))
            .map_or(None, |m| from_str(m));
        if mime.as_ref().is_some() {
            plain(content[]).map(|r| r.set(ContentType((*mime.as_ref().unwrap()).clone())))
        } else {
            plain(content[])
        }
    } else {
        let mut content = match fs::readdir(&path) {
            Ok(s) => s,
            Err(e) => return html(e.desc)
        };
        content.sort_by(|a, b| a.filename_str().unwrap().cmp(b.filename_str().unwrap()));
        html(render(template, root, path, content, filter)[])
    }
}

fn print_usage(program: &str, opts: &[OptGroup]) {
    println!("Usage: {} [options]\n", program);
    println!("{}", usage(BRIEF, opts));
}

fn main() {
    let args: Vec<String> = os::args();
    let program = args[0].clone();
    let opts = &[
        optopt("c", "config", "set config file name", "NAME"),
        optopt("a", "address", "the address to bind to", "HOST"),
        optopt("p", "port", "the port to serve", "PORT"),
        optopt("r", "root", "the uppermost directory to serve", "ROOT"),
        optopt("f", "filter", "a regular expression to filter the filenames", "REGEX"),
        optopt("s", "size", "the maximum size of a file that will be served", "BYTES"),
        optopt("t", "template", "a mustache template to use for rendering", "TEMPLATE"),
        optflag("h", "help", "print this help menu")
    ];
    let matches = match getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => {
            println!("{}", f.to_string());
            return;
        }
    };

    if matches.opt_present("h") {
        print_usage(program[], opts);
        return;
    }

    let mut options: Options = Default::default();

    matches.opt_str("c").map(|conf_file| {
        let conf_file = File::open(&Path::new(conf_file));
        conf_file.as_ref().map_err::<()>(|e| panic!("{}", e.desc)).unwrap();

        // cannot if-let, because typesafe errors are helpful
        let json = match json::from_reader(&mut conf_file.ok().unwrap()) {
            Ok(Json::Object(o)) => o,
            _ => panic!("Invalid configuration file. Doesn't contain top-level object.")
        };
        options.host = match json.get("address") {
            Some(&Json::String(ref s)) => Some((*s).clone()),
            None => None,
            _ => panic!("Invalid configuration file. `address` field must be a string.")
        };
        options.port = match json.get("port") {
            Some(&Json::U64(u)) => Some(u as u16),
            None => None,
            _ => panic!("Invalid configuration file. `port` field must be an unsigned integer.")
        };
        options.root = match json.get("root") {
            Some(&Json::String(ref s)) => Some(Path::new((*s).clone())),
            None => None,
            _ => panic!("Invalid configuration file. `root` field must be a string.")
        };
        options.filter = match json.get("filter") {
            Some(&Json::String(ref s)) => Some((*s).clone()),
            None => None,
            _ => panic!("Invalid configuration file. `filter` field must be a string.")
        };
        options.max_size = match json.get("size") {
            Some(&Json::U64(u)) => Some(u),
            None => None,
            _ => panic!("Invalid configuration file. `size` field must be an unsigned integer.")
        };
        options.template = match json.get("template") {
            Some(&Json::String(ref s)) => Some((*s).clone()),
            None => None,
            _ => panic!("Invalid configuration file. `template` field must be a string.")
        };
    });

    let (host, port) = {
        options.host = matches.opt_str("a").or(options.host);
        options.port = matches.opt_str("p").and_then(|p| str::from_str(p[])).or(options.port);
        options.root = matches.opt_str("r").and_then(|p| Path::new_opt(p)).or(options.root);
        options.filter = matches.opt_str("f").or(options.filter);
        options.max_size = matches.opt_str("s").and_then(|s| str::from_str(s[])).or(options.max_size);
        options.template = matches.opt_str("t").or(options.template);
        (options.host.clone().unwrap_or(HOST.into_string()),
         options.port.clone().unwrap_or(PORT))
    };

    let template = mustache::compile_str(options.template.clone().unwrap_or(OPT_TEMPLATE.into_string())[]);
    let state = State {
        template: template,
        mime_types: Arc::new(Types::new().ok().unwrap())
    };

    let mut chain = ChainBuilder::new(serve);
    chain.link(Read::<OptCarrier, Arc<Options>>::both(Arc::new(options)));
    chain.link(Read::<StateCarrier, Arc<State>>::both(Arc::new(state)));
    match Iron::new(chain).listen((host[], port)) {
        Ok(_) => (),
        Err(e) => println!("I'm sorry, I failed you. {}", e)
    }
}
<commit_msg>Less allocation, possibly<commit_after>#![feature(phase, globs, slicing_syntax, if_let, unboxed_closures)]

extern crate getopts;
extern crate serialize;
extern crate iron;
extern crate persistent;
extern crate regex;
extern crate "conduit-mime-types" as conduit_mime;
extern crate mime;
extern crate mustache;
extern crate libc;

use std::{ str, os };
use std::str::from_str;
use std::path::{ Path, GenericPath };
use std::io::{ fs, Reader };
use std::io::fs::{ File, PathExtensions };
use std::default::Default;
use std::sync::Arc;
use std::cell::{ RefCell };

use regex::Regex;

use conduit_mime::Types;

use getopts::{ optopt, optflag, getopts, usage, OptGroup };

use serialize::json;
use serialize::json::Json;

use iron::prelude::*;
use iron::response::modifiers::*;
use iron::status;
use iron::mime::*;
use iron::middleware::ChainBuilder;
use iron::typemap::Assoc;

use persistent::Read;

use mustache::{ Template, VecBuilder, MapBuilder };

use constants::*;

pub mod constants;

#[deriving(Send, Clone, Default, Encodable, Decodable)]
struct Options {
    host: Option<String>,
    port: Option<u16>,
    root: Option<Path>,
    filter: Option<String>,
    max_size: Option<u64>,
    template: Option<String>,
    fork: Option<bool>,
    threads: Option<uint>
}

struct OptCarrier;
impl Assoc<Arc<Options>> for OptCarrier {}

#[deriving(Send, Clone)]
struct State {
    template: Template,
    mime_types: Arc<Types>
}

struct StateCarrier;
impl Assoc<Arc<State>> for StateCarrier {}

const HOST: &'static str = "0.0.0.0";
const PORT: u16 = 8080;

static UNITS: &'static [&'static str] = &["B", "kB", "MB", "GB", "TB"];
const BRIEF: &'static str = "A minimal directory server, written in Rust with Iron.";

const KEY_TITLE: &'static str = "title";
const KEY_CONTENT: &'static str = "content";
const KEY_URL: &'static str = "url";
const KEY_SIZE: &'static str = "size";
const KEY_NAME: &'static str = "name";

const DEF_LEN: uint = 10000;

thread_local! (static OUT: RefCell<Vec<u8>> = RefCell::new(Vec::with_capacity(DEF_LEN)))

fn fork() {
    unsafe {
        println!("Forking now!");
        let pid = libc::funcs::posix88::unistd::fork();
        if pid == 0 {
            // we are child, now get to work
            return;
        } else if pid > 0 {
            // fork succeeded, die
            libc::funcs::c95::stdlib::exit(0);
        } else if pid < 0 {
            // unsuccessful, don't die
            return;
        }
    }
}

fn size_with_unit(mut size: u64) -> String {
    let mut frac = 0;
    let mut index = 0;

    while size > 1000 && index + 1 < UNITS.len() {
        frac = size % 1000;
        size /= 1000;
        index += 1;
    }

    format!("{}.{} {}", size, frac, UNITS[index])
}

fn render<'a>(template: Template, root: Path, dir: Path, files: Vec<Path>, filter: Option<Regex>) -> String {
    let data = MapBuilder::<'a>::new()
        .insert_str(KEY_TITLE, dir.display().as_cow().into_owned())
        .insert_vec(KEY_CONTENT, |mut vec: VecBuilder<'a>| {
            let item = |map: MapBuilder, url: &Path, size: u64, name: String| {
                map.insert(KEY_URL, &format!("{}", url.display())[]).unwrap()
                   .insert(KEY_SIZE, &size_with_unit(size)[]).unwrap()
                   .insert_str(KEY_NAME, name)
            };

            // add `..` entry if necessary
            let mut up = dir.clone();
            up.pop();
            if root.is_ancestor_of(&up) {
                vec = vec.push_map(|map: MapBuilder| item(map, &up.path_relative_from(&root).unwrap(), 0, "..".into_string()));
            }

            for file in files.iter() {
                let relative = file.path_relative_from(&root).unwrap();
                let stat = file.stat().unwrap();
                let filename = file.filename_display().as_cow().into_owned();
                if filter.as_ref().map_or(true, |f| f.is_match(filename[])) {
                    vec = vec.push_map(|map| item(map, &relative, stat.size, filename.clone()));
                }
            }
            vec
        }).build();

    // Use thread-local storage to reduce allocation
    OUT.with(|ref_out| {
        {
            let mut o = ref_out.borrow_mut();
            o.clear();
            template.render_data(&mut *o, &data);
        }
        // The template should be valid utf8, the filenames might not be
        String::from_utf8(ref_out.borrow().clone()).unwrap_or_else(|v| {
            String::from_utf8_lossy(v[]).into_owned()
        })
    })
    }

fn plain<B: Bodyable>(content: B) -> IronResult<Response> {
    Ok(Response::new()
       .set(Status(status::Ok))
       .set(Body(content)))
}

fn html<B: Bodyable>(content: B) -> IronResult<Response> {
    plain(content).map(|r| r.set(ContentType(Mime(Text, Html, Vec::new()))))
}

fn serve(req: &mut Request) -> IronResult<Response> {
    let (root, filter_str, max_size) = {
        let o = req.get::<Read<OptCarrier, Arc<Options>>>().unwrap();
        (o.root.clone().unwrap_or_else(|| os::getcwd().ok().unwrap()),
         o.filter.clone(),
         o.max_size)
    };

    let (template, mime_types) = {
        let s = req.get::<Read<StateCarrier, Arc<State>>>().unwrap();
        (s.template.clone(),
         s.mime_types.clone())
    };

    let mut path = root.clone();
    for part in req.url.path.iter() { path.push(part[]) }
    if !path.exists() { return html("Well, no... We don't have that today."); }

    let filter = filter_str.and_then(|s| Regex::new(s[]).ok());

    if path.is_file() && root.is_ancestor_of(&path) {
        let stat = path.stat();
        if stat.as_ref().ok().is_some() && max_size.is_some() && stat.ok().unwrap().size > max_size.unwrap() {
            return html("I'm afraid, I'm too lazy to serve the requested file. It's pretty big...")
        }
        let content = match File::open(&path).read_to_end() {
            Ok(s) => s,
            Err(e) => return html(e.desc)
        };

        if filter.as_ref().map_or(false, |f| !f.is_match(path.filename_str().unwrap())) {
            return html("I don't think you're allowed to do this.");
        }
        let mime: Option<iron::mime::Mime> = path.extension_str()
            .map_or(None, |e| mime_types.get_mime_type(e))
            .map_or(None, |m| from_str(m));
        if mime.as_ref().is_some() {
            plain(content[]).map(|r| r.set(ContentType((*mime.as_ref().unwrap()).clone())))
        } else {
            plain(content[])
        }
    } else {
        let mut content = match fs::readdir(&path) {
            Ok(s) => s,
            Err(e) => return html(e.desc)
        };
        content.sort_by(|a, b| a.filename_str().unwrap().cmp(b.filename_str().unwrap()));
        html(render(template, root, path, content, filter))
    }
}

fn print_usage(program: &str, opts: &[OptGroup]) {
    println!("Usage: {} [options]\n", program);
    println!("{}", usage(BRIEF, opts));
}

fn main() {
    let args: Vec<String> = os::args();
    let program = args[0].clone();
    let opts = &[
        optopt("c", "config", "set config file name", "NAME"),
        optopt("a", "address", "the address to bind to", "HOST"),
        optopt("p", "port", "the port to serve", "PORT"),
        optopt("r", "root", "the uppermost directory to serve", "ROOT"),
        optopt("f", "filter", "a regular expression to filter the filenames", "REGEX"),
        optopt("s", "size", "the maximum size of a file that will be served", "BYTES"),
        optopt("t", "template", "a mustache template to use for rendering", "TEMPLATE"),
        optopt("", "threads", "amount of threads to use for serving", "THREADS"),
        optflag("", "fork", "fork sersve into a background process"),
        optflag("h", "help", "print this help menu")
    ];
    let matches = match getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => {
            println!("{}", f.to_string());
            return;
        }
    };

    if matches.opt_present("h") {
        print_usage(program[], opts);
        return;
    }

    let mut options: Options = Default::default();

    matches.opt_str("c").map(|conf_file| {
        let conf_file = File::open(&Path::new(conf_file));
        conf_file.as_ref().map_err::<()>(|e| panic!("{}", e.desc)).unwrap();

        // cannot if-let, because typesafe errors are helpful
        let json = match json::from_reader(&mut conf_file.ok().unwrap()) {
            Ok(Json::Object(o)) => o,
            _ => panic!("Invalid configuration file. Doesn't contain top-level object.")
        };
        options.host = match json.get("address") {
            Some(&Json::String(ref s)) => Some((*s).clone()),
            None => None,
            _ => panic!("Invalid configuration file. `address` field must be a string.")
        };
        options.port = match json.get("port") {
            Some(&Json::U64(u)) => Some(u as u16),
            None => None,
            _ => panic!("Invalid configuration file. `port` field must be an unsigned integer.")
        };
        options.root = match json.get("root") {
            Some(&Json::String(ref s)) => Some(Path::new((*s).clone())),
            None => None,
            _ => panic!("Invalid configuration file. `root` field must be a string.")
        };
        options.filter = match json.get("filter") {
            Some(&Json::String(ref s)) => Some((*s).clone()),
            None => None,
            _ => panic!("Invalid configuration file. `filter` field must be a string.")
        };
        options.max_size = match json.get("size") {
            Some(&Json::U64(u)) => Some(u),
            None => None,
            _ => panic!("Invalid configuration file. `size` field must be an unsigned integer.")
        };
        options.template = match json.get("template") {
            Some(&Json::String(ref s)) => Some((*s).clone()),
            None => None,
            _ => panic!("Invalid configuration file. `template` field must be a string.")
        };
        options.fork = match json.get("fork") {
            Some(&Json::Boolean(b)) => Some(b),
            None => None,
            _ => panic!("Invalid configuration file. `fork` field must be a boolean")
        };
        options.threads = match json.get("threads") {
            Some(&Json::U64(u)) => Some(u as uint),
            None => None,
            _ => panic!("Invalid configuration file. `threads` field must be a string.")
        }
    });

    let (host, port, threads) = {
        options.host = matches.opt_str("a").or(options.host);
        options.port = matches.opt_str("p").and_then(|p| str::from_str(p[])).or(options.port);
        options.root = matches.opt_str("r").and_then(|p| Path::new_opt(p)).or(options.root);
        options.filter = matches.opt_str("f").or(options.filter);
        options.max_size = matches.opt_str("s").and_then(|s| str::from_str(s[])).or(options.max_size);
        options.template = matches.opt_str("t").or(options.template);
        options.threads = matches.opt_str("threads").and_then(|s| str::from_str(s[])).or(options.threads);
        (options.host.clone().unwrap_or(HOST.into_string()),
         options.port.clone().unwrap_or(PORT),
         options.threads.unwrap_or(os::num_cpus()))
    };

    if options.fork.unwrap_or(false) || matches.opt_present("fork") {
        fork();
    }

    let template = mustache::compile_str(options.template.clone().unwrap_or(OPT_TEMPLATE.into_string())[]);
    let state = State {
        template: template,
        mime_types: Arc::new(Types::new().ok().unwrap())
    };

    let mut chain = ChainBuilder::new(serve);
    chain.link(Read::<OptCarrier, Arc<Options>>::both(Arc::new(options)));
    chain.link(Read::<StateCarrier, Arc<State>>::both(Arc::new(state)));
    match Iron::new(chain).listen_with((host[], port), threads) {
        Ok(_) => (),
        Err(e) => println!("I'm sorry, I failed you.\nError: {}", e)
    }
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::ffi::{CString, CStr};
use std::slice;

use ll::*;
use tcl::TclEnvironment;
use interpreter::Interpreter;

/// A Tcl value
pub struct Object<'env> {
    _env: &'env TclEnvironment,
    raw: *mut Tcl_Obj
}

pub trait TclObject {
    type FromObject;
    /// Converts self into a Tcl object.
    fn into_object(self, &TclEnvironment) -> Object;
    /// Converts from a tcl object to Self::FromObject
    fn from_object<'a>(obj: &Object, &'a mut Interpreter)
    -> Result<Self::FromObject, Cow<'a, str>>;
    /// Updates the value of a Tcl object.
    fn set_object(self, &mut Object);
}

impl TclObject for () {
    type FromObject = ();

    fn into_object(self, env: &TclEnvironment) -> Object {
        Object {
            _env: env,
            raw: unsafe {
                let raw = Tcl_NewObj();
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    fn from_object<'a>(_: &Object, _: &'a mut Interpreter)
    -> Result<Self::FromObject, Cow<'a, str>> {
        Ok(())
    }

    fn set_object(self, _: &mut Object) {}
}

impl TclObject for i32 {
    type FromObject = i32;

    fn into_object(self, env: &TclEnvironment) -> Object {
        Object {
            _env: env,
            raw: unsafe {
                let raw = Tcl_NewIntObj(self);
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    fn from_object<'a>(obj: &Object, interp: &'a mut Interpreter)
    -> Result<Self::FromObject, Cow<'a, str>> {
        let mut output = 0i32;
        unsafe {
            if Tcl_GetIntFromObj(interp.raw(), obj.raw(), &mut output) == TCL_OK {
                Ok(output)
            } else {
                Err(interp.string_result())
            }
        }
    }

    fn set_object(self, obj: &mut Object) {
        unsafe {
            Tcl_SetIntObj(obj.raw, self);
        }
    }
}

impl TclObject for bool {
    type FromObject = bool;

    fn into_object(self, env: &TclEnvironment) -> Object {
        Object {
            _env: env,
            raw: unsafe {
                let raw = Tcl_NewBooleanObj(if self { 1 } else { 0 });
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    fn from_object<'a>(obj: &Object, interp: &'a mut Interpreter)
    -> Result<Self::FromObject, Cow<'a, str>> {
        let mut output = 0i32;
        unsafe {
            if Tcl_GetBooleanFromObj(interp.raw(), obj.raw(), &mut output) == TCL_OK {
                Ok(output != 0)
            } else {
                Err(interp.string_result())
            }
        }
    }

    fn set_object(self, obj: &mut Object) {
        unsafe {
            Tcl_SetBooleanObj(obj.raw, if self { 1 } else { 0 });
        }
    }
}

impl TclObject for i64 {
    type FromObject = i64;

    fn into_object(self, env: &TclEnvironment) -> Object {
        Object {
            _env: env,
            raw: unsafe {
                let raw = Tcl_NewLongObj(self);
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    fn from_object<'a>(obj: &Object, interp: &'a mut Interpreter)
    -> Result<Self::FromObject, Cow<'a, str>> {
        let mut output = 0i64;
        unsafe {
            if Tcl_GetLongFromObj(interp.raw(), obj.raw(), &mut output) == TCL_OK {
                Ok(output)
            } else {
                Err(interp.string_result())
            }
        }
    }

    fn set_object(self, obj: &mut Object) {
        unsafe {
            Tcl_SetLongObj(obj.raw, self);
        }
    }
}

//TODO: WideInt
//TODO: BigNum

impl TclObject for f64 {
    type FromObject = f64;

    fn into_object(self, env: &TclEnvironment) -> Object {
        Object {
            _env: env,
            raw: unsafe {
                let raw = Tcl_NewDoubleObj(self);
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    fn from_object<'a>(obj: &Object, interp: &'a mut Interpreter)
    -> Result<Self::FromObject, Cow<'a, str>> {
        let mut output = 0f64;
        unsafe {
            if Tcl_GetDoubleFromObj(interp.raw(), obj.raw(), &mut output) == TCL_OK {
                Ok(output)
            } else {
                Err(interp.string_result())
            }
        }
    }

    fn set_object(self, obj: &mut Object) {
        unsafe {
            Tcl_SetDoubleObj(obj.raw, self);
        }
    }
}

impl<'b> TclObject for &'b str {
    type FromObject = String;

    fn into_object(self, env: &TclEnvironment) -> Object {
        let buf = CString::new(self.as_bytes()).unwrap().as_ptr();
        Object {
            _env: env,
            raw: unsafe {
                let raw = Tcl_NewStringObj(buf, self.len() as i32);
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    fn from_object<'a>(obj: &Object, _: &'a mut Interpreter)
    -> Result<Self::FromObject, Cow<'a, str>> {
        Ok(obj.get_string())
    }

    fn set_object(self, obj: &mut Object) {
        let buf = CString::new(self.as_bytes()).unwrap().as_ptr();
        unsafe {
            Tcl_SetStringObj(obj.raw, buf, self.len() as i32);
        }
    }
}

impl<'b> TclObject for &'b [u8] {
    type FromObject = Vec<u8>;

    fn into_object(self, env: &TclEnvironment) -> Object {
        Object {
            _env: env,
            raw: unsafe {
                let raw = Tcl_NewByteArrayObj(self.as_ptr(), self.len() as i32);
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    fn set_object(self, obj: &mut Object) {
        unsafe {
            Tcl_SetByteArrayObj(obj.raw, self.as_ptr(), self.len() as i32);
        }
    }

    fn from_object<'a>(obj: &Object, _: &'a mut Interpreter)
    -> Result<Self::FromObject, Cow<'a, str>> {
        Ok(obj.get_byte_array())
    }
}

impl<'env> Object<'env> {

    pub fn from_raw(env: &TclEnvironment, raw: *mut Tcl_Obj) -> Object {
        Object {
            _env: env,
            raw: unsafe {
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    /// Create a new Tcl value
    pub fn new<V: TclObject>(env: &TclEnvironment, val: V) -> Object {
        val.into_object(env)
    }

    // Set the contents of a Tcl object to val
    pub fn set<V: TclObject>(&mut self, val: V) {
        val.set_object(self)
    }

    // Get the contents of a Tcl object
    pub fn get<'a, V: TclObject>(&self, interp: &'a mut Interpreter) -> Result<V::FromObject, Cow<'a, str>> {
        V::from_object(self, interp)
    }

    /// Get the string representation of a Tcl value
    pub fn get_string(&self) -> String {
        unsafe {
            let mut raw_string_length = 0;
            let raw_string_ptr = Tcl_GetStringFromObj(self.raw, &mut raw_string_length);
            String::from_utf8_lossy(CStr::from_ptr(raw_string_ptr as *const i8).to_bytes()).to_string()
        }
    }

    /// Get the byte array representation of a Tcl value
    pub fn get_byte_array(&self) -> Vec<u8> {
        unsafe {
            let mut raw_length = 0;
            let raw_vec_ptr = Tcl_GetByteArrayFromObj(self.raw, &mut raw_length);
            slice::from_raw_parts(raw_vec_ptr, raw_length as usize).to_vec()
        }
    }

    pub unsafe fn raw(&self) -> *mut Tcl_Obj {
        self.raw
    }

    /// Is the value currently used to represent multiple variables in an interpreter
    pub fn is_shared(&self) -> bool {
        unsafe {
            Tcl_IsShared(self.raw) != 0
        }
    }
}

impl<'env> Drop for Object<'env> {
    fn drop(&mut self) {
        unsafe { Tcl_DecrRefCount(self.raw) };
    }
}

impl<'env> Clone for Object<'env> {

    fn clone(&self) -> Object<'env> {
        Object {
            _env: self._env,
            raw: unsafe {
                // FIXME change clone semantics. Object is like Rc:
                // Rc::clone does not clone the contents but only the pointer
                let raw = Tcl_DuplicateObj(self.raw);
                // TODO check if this incr ref count correct in this case
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }
}
<commit_msg>improve docs<commit_after>use std::borrow::Cow;
use std::ffi::{CString, CStr};
use std::slice;

use ll::*;
use tcl::TclEnvironment;
use interpreter::Interpreter;

/// A Tcl value
pub struct Object<'env> {
    _env: &'env TclEnvironment,
    raw: *mut Tcl_Obj
}

pub trait TclObject {
    type FromObject;
    /// Converts self into a Tcl object.
    fn into_object(self, &TclEnvironment) -> Object;
    /// Reads the contents of this Tcl object
    fn from_object<'a>(obj: &Object, &'a mut Interpreter)
    -> Result<Self::FromObject, Cow<'a, str>>;
    /// Updates the value of a Tcl object.
    fn set_object(self, &mut Object);
}

impl TclObject for () {
    type FromObject = ();

    fn into_object(self, env: &TclEnvironment) -> Object {
        Object {
            _env: env,
            raw: unsafe {
                let raw = Tcl_NewObj();
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    fn from_object<'a>(_: &Object, _: &'a mut Interpreter)
    -> Result<Self::FromObject, Cow<'a, str>> {
        Ok(())
    }

    fn set_object(self, _: &mut Object) {}
}

impl TclObject for i32 {
    type FromObject = i32;

    fn into_object(self, env: &TclEnvironment) -> Object {
        Object {
            _env: env,
            raw: unsafe {
                let raw = Tcl_NewIntObj(self);
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    fn from_object<'a>(obj: &Object, interp: &'a mut Interpreter)
    -> Result<Self::FromObject, Cow<'a, str>> {
        let mut output = 0i32;
        unsafe {
            if Tcl_GetIntFromObj(interp.raw(), obj.raw(), &mut output) == TCL_OK {
                Ok(output)
            } else {
                Err(interp.string_result())
            }
        }
    }

    fn set_object(self, obj: &mut Object) {
        unsafe {
            Tcl_SetIntObj(obj.raw, self);
        }
    }
}

impl TclObject for bool {
    type FromObject = bool;

    fn into_object(self, env: &TclEnvironment) -> Object {
        Object {
            _env: env,
            raw: unsafe {
                let raw = Tcl_NewBooleanObj(if self { 1 } else { 0 });
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    fn from_object<'a>(obj: &Object, interp: &'a mut Interpreter)
    -> Result<Self::FromObject, Cow<'a, str>> {
        let mut output = 0i32;
        unsafe {
            if Tcl_GetBooleanFromObj(interp.raw(), obj.raw(), &mut output) == TCL_OK {
                Ok(output != 0)
            } else {
                Err(interp.string_result())
            }
        }
    }

    fn set_object(self, obj: &mut Object) {
        unsafe {
            Tcl_SetBooleanObj(obj.raw, if self { 1 } else { 0 });
        }
    }
}

impl TclObject for i64 {
    type FromObject = i64;

    fn into_object(self, env: &TclEnvironment) -> Object {
        Object {
            _env: env,
            raw: unsafe {
                let raw = Tcl_NewLongObj(self);
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    fn from_object<'a>(obj: &Object, interp: &'a mut Interpreter)
    -> Result<Self::FromObject, Cow<'a, str>> {
        let mut output = 0i64;
        unsafe {
            if Tcl_GetLongFromObj(interp.raw(), obj.raw(), &mut output) == TCL_OK {
                Ok(output)
            } else {
                Err(interp.string_result())
            }
        }
    }

    fn set_object(self, obj: &mut Object) {
        unsafe {
            Tcl_SetLongObj(obj.raw, self);
        }
    }
}

//TODO: WideInt
//TODO: BigNum

impl TclObject for f64 {
    type FromObject = f64;

    fn into_object(self, env: &TclEnvironment) -> Object {
        Object {
            _env: env,
            raw: unsafe {
                let raw = Tcl_NewDoubleObj(self);
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    fn from_object<'a>(obj: &Object, interp: &'a mut Interpreter)
    -> Result<Self::FromObject, Cow<'a, str>> {
        let mut output = 0f64;
        unsafe {
            if Tcl_GetDoubleFromObj(interp.raw(), obj.raw(), &mut output) == TCL_OK {
                Ok(output)
            } else {
                Err(interp.string_result())
            }
        }
    }

    fn set_object(self, obj: &mut Object) {
        unsafe {
            Tcl_SetDoubleObj(obj.raw, self);
        }
    }
}

impl<'b> TclObject for &'b str {
    type FromObject = String;

    fn into_object(self, env: &TclEnvironment) -> Object {
        let buf = CString::new(self.as_bytes()).unwrap().as_ptr();
        Object {
            _env: env,
            raw: unsafe {
                let raw = Tcl_NewStringObj(buf, self.len() as i32);
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    fn from_object<'a>(obj: &Object, _: &'a mut Interpreter)
    -> Result<Self::FromObject, Cow<'a, str>> {
        Ok(obj.get_string())
    }

    fn set_object(self, obj: &mut Object) {
        let buf = CString::new(self.as_bytes()).unwrap().as_ptr();
        unsafe {
            Tcl_SetStringObj(obj.raw, buf, self.len() as i32);
        }
    }
}

impl<'b> TclObject for &'b [u8] {
    type FromObject = Vec<u8>;

    fn into_object(self, env: &TclEnvironment) -> Object {
        Object {
            _env: env,
            raw: unsafe {
                let raw = Tcl_NewByteArrayObj(self.as_ptr(), self.len() as i32);
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    fn set_object(self, obj: &mut Object) {
        unsafe {
            Tcl_SetByteArrayObj(obj.raw, self.as_ptr(), self.len() as i32);
        }
    }

    fn from_object<'a>(obj: &Object, _: &'a mut Interpreter)
    -> Result<Self::FromObject, Cow<'a, str>> {
        Ok(obj.get_byte_array())
    }
}

impl<'env> Object<'env> {

    pub fn from_raw(env: &TclEnvironment, raw: *mut Tcl_Obj) -> Object {
        Object {
            _env: env,
            raw: unsafe {
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }

    /// Create a new Tcl value
    pub fn new<V: TclObject>(env: &TclEnvironment, val: V) -> Object {
        val.into_object(env)
    }

    /// Set the contents of this Tcl object to val
    pub fn set<V: TclObject>(&mut self, val: V) {
        val.set_object(self)
    }

    /// Get the contents of this Tcl object
    pub fn get<'a, V: TclObject>(&self, interp: &'a mut Interpreter) -> Result<V::FromObject, Cow<'a, str>> {
        V::from_object(self, interp)
    }

    /// Get the string representation of a Tcl value
    pub fn get_string(&self) -> String {
        unsafe {
            let mut raw_string_length = 0;
            let raw_string_ptr = Tcl_GetStringFromObj(self.raw, &mut raw_string_length);
            String::from_utf8_lossy(CStr::from_ptr(raw_string_ptr as *const i8).to_bytes()).to_string()
        }
    }

    /// Get the byte array representation of a Tcl value
    pub fn get_byte_array(&self) -> Vec<u8> {
        unsafe {
            let mut raw_length = 0;
            let raw_vec_ptr = Tcl_GetByteArrayFromObj(self.raw, &mut raw_length);
            slice::from_raw_parts(raw_vec_ptr, raw_length as usize).to_vec()
        }
    }

    pub unsafe fn raw(&self) -> *mut Tcl_Obj {
        self.raw
    }

    /// Is the value currently used to represent multiple variables in an interpreter
    pub fn is_shared(&self) -> bool {
        unsafe {
            Tcl_IsShared(self.raw) != 0
        }
    }
}

impl<'env> Drop for Object<'env> {
    fn drop(&mut self) {
        unsafe { Tcl_DecrRefCount(self.raw) };
    }
}

impl<'env> Clone for Object<'env> {

    fn clone(&self) -> Object<'env> {
        Object {
            _env: self._env,
            raw: unsafe {
                // FIXME change clone semantics. Object is like Rc:
                // Rc::clone does not clone the contents but only the pointer
                let raw = Tcl_DuplicateObj(self.raw);
                // TODO check if this incr ref count correct in this case
                Tcl_IncrRefCount(raw);
                raw
            }
        }
    }
}
<|endoftext|>


<commit_before>pub use self::grammar::sequence_set;

peg_file! grammar("grammar.rustpeg")

#[deriving(Clone, PartialEq, Show)]
enum SequenceItem {
    Number(uint),
    Range(Box<SequenceItem>, Box<SequenceItem>),
    All
}

#[cfg(test)]
mod tests {
    use super::sequence_set;
    use super::{All, Number, Range};

    #[test]
    fn test_invalid_sequences() {
        assert!(sequence_set("").is_err());
        assert!(sequence_set("a").is_err());
        assert!(sequence_set("0").is_err());
        assert!(sequence_set("a:*").is_err());
        assert!(sequence_set(":*").is_err());
        assert!(sequence_set("1:").is_err());
        assert!(sequence_set("1:0").is_err());
        assert!(sequence_set("0:1").is_err());
        assert!(sequence_set("4,5,6,").is_err());
    }

    #[test]
    fn test_sequence_num() {
        let seq = sequence_set("4324");
        assert!(seq.is_ok());
        let seq = seq.unwrap();
        let expected = vec![Number(4324)];
        assert_eq!(seq, expected);
    }

    #[test]
    fn test_sequence_all() {
        let seq = sequence_set("*");
        assert!(seq.is_ok());
        let seq = seq.unwrap();
        let expected = vec![All];
        assert_eq!(seq, expected);
    }

    #[test]
    fn test_sequence_ranges() {
        let seq = sequence_set("98:100");
        assert!(seq.is_ok());
        let seq = seq.unwrap();
        let expected = vec![Range(box Number(98), box Number(100))];
        assert_eq!(seq, expected);

        assert!(sequence_set("1:5").is_ok());
        assert!(sequence_set("21:44").is_ok());
    }

    #[test]
    fn test_sequence_range_all() {
        let seq = sequence_set("31:*");
        assert!(seq.is_ok());
        let seq = seq.unwrap();
        let expected = vec![Range(box Number(31), box All)];
        assert_eq!(seq, expected);
    }

    #[test]
    fn test_sequence_set() {
        let seq = sequence_set("1231,1342,12,98:104,16");
        assert!(seq.is_ok());
        let seq = seq.unwrap();
        let expected = vec![Number(1231), Number(1342), Number(12), Range(box Number(98), box Number(104)), Number(16)];
        assert_eq!(seq, expected);
    }
}
<commit_msg>Begin writing basic FETCH tests<commit_after>pub use self::grammar::{fetch, sequence_set};

peg_file! grammar("grammar.rustpeg")

#[deriving(Clone, PartialEq, Show)]
enum SequenceItem {
    Number(uint),
    Range(Box<SequenceItem>, Box<SequenceItem>),
    All
}

#[cfg(test)]
mod tests {
    use super::{fetch, sequence_set};
    use super::{All, Number, Range};

    #[test]
    fn test_invalid_sequences() {
        assert!(sequence_set("").is_err());
        assert!(sequence_set("a").is_err());
        assert!(sequence_set("0").is_err());
        assert!(sequence_set("a:*").is_err());
        assert!(sequence_set(":*").is_err());
        assert!(sequence_set("1:").is_err());
        assert!(sequence_set("1:0").is_err());
        assert!(sequence_set("0:1").is_err());
        assert!(sequence_set("4,5,6,").is_err());
    }

    #[test]
    fn test_sequence_num() {
        let seq = sequence_set("4324");
        assert!(seq.is_ok());
        let seq = seq.unwrap();
        let expected = vec![Number(4324)];
        assert_eq!(seq, expected);
    }

    #[test]
    fn test_sequence_all() {
        let seq = sequence_set("*");
        assert!(seq.is_ok());
        let seq = seq.unwrap();
        let expected = vec![All];
        assert_eq!(seq, expected);
    }

    #[test]
    fn test_sequence_ranges() {
        let seq = sequence_set("98:100");
        assert!(seq.is_ok());
        let seq = seq.unwrap();
        let expected = vec![Range(box Number(98), box Number(100))];
        assert_eq!(seq, expected);

        assert!(sequence_set("1:5").is_ok());
        assert!(sequence_set("21:44").is_ok());
    }

    #[test]
    fn test_sequence_range_all() {
        let seq = sequence_set("31:*");
        assert!(seq.is_ok());
        let seq = seq.unwrap();
        let expected = vec![Range(box Number(31), box All)];
        assert_eq!(seq, expected);
    }

    #[test]
    fn test_sequence_set() {
        let seq = sequence_set("1231,1342,12,98:104,16");
        assert!(seq.is_ok());
        let seq = seq.unwrap();
        let expected = vec![Number(1231), Number(1342), Number(12), Range(box Number(98), box Number(104)), Number(16)];
        assert_eq!(seq, expected);
    }

    #[test]
    fn test_simple_fetch() {
        let fetch = fetch("FETCH 1:2 (FLAGS BODY[HEADER.FIELDS (DATE FROM)])");
        assert!(fetch.is_ok());
        let fetch = fetch.unwrap();
        //let expected = vec![Number(1231), Number(1342), Number(12), Range(box Number(98), box Number(104)), Number(16)];
        //assert_eq!(seq, expected);
    }
}
<|endoftext|>


<commit_before>
use pair::Pair;
use error::{NcclError, ErrorKind};
use token::{Token, TokenKind};

pub struct Parser {
    current: usize,
    path: Vec<String>,
    tokens: Vec<Token>,
    pair: Pair,
    line: u64,
}

// nccl = pair+
// pair = value schema? newline key+
// schema = ": " value
// key = indent (pair | value) newline
// value = [^:]+
// newline = "\n" | "\r\n"
// indent = " "+ | "\t"

impl Parser {
    pub fn new(tokens: Vec<Token>) -> Self {
        Parser {
            current: 0,
            path: vec![],
            tokens: tokens,
            pair: Pair::new("__top_level__"),
            line: 1,
        }
    }

    pub fn parse(&mut self) -> Result<Pair, NcclError> {
        while !self.is_at_end() {
            self.value()?;
            if self.peek().kind == TokenKind::Value {
                self.colon()?;
                self.value()?;
            }
            self.newline()?;
            self.indent()?;
            self.value()?;
            self.newline()?;
        }

        Ok(self.pair.clone())
    }

    fn add(&mut self, value: String) {
        //self.pair.add_vec(self.path, value);
    }

    fn value(&mut self) -> Result<(), NcclError> {
        if !self.is_at_end() {
            Ok(())
        } else {
            Err(NcclError::new(ErrorKind::ParseError, "Expected value, found EOF", self.line))
        }
    }

    fn indent(&mut self) -> Result<(), NcclError> {
        if !self.is_at_end() {
            Ok(())
        } else {
            Err(NcclError::new(ErrorKind::ParseError, "Expected indent, found EOF", self.line))
        }
    }

    fn newline(&mut self) -> Result<(), NcclError> {
        if !self.is_at_end() {
            self.line += 1;
            Ok(())
        } else {
            Err(NcclError::new(ErrorKind::ParseError, "Expected newline, found EOF", self.line))
        }
    }

    fn colon(&mut self) -> Result<(), NcclError> {
        if !self.is_at_end() {
            Ok(())
        } else {
            Err(NcclError::new(ErrorKind::ParseError, "Expected colon, found EOF", self.line))
        }
    }

    fn matches(&mut self, kind: TokenKind) -> bool {
        if self.check(kind) {
            self.advance();
            true
        } else {
            false
        }
    }

    fn check(&mut self, kind: TokenKind) -> bool {
        if self.is_at_end() {
            false
        } else {
            self.peek().kind == kind
        }
    }

    fn advance(&mut self) -> &Token {
        if !self.is_at_end() {
            self.current += 1;
        }

        self.previous()
    }

    fn is_at_end(&mut self) -> bool {
        self.peek().kind == TokenKind::EOF
    }

    fn peek(&mut self) -> &Token {
        &self.tokens[self.current]
    }

    fn previous(&mut self) -> &Token {
        &self.tokens[self.current - 1]
    }

    fn consume(&mut self, kind: TokenKind, message: &str) -> Result<&Token, NcclError> {
        if self.check(kind) {
            Ok(self.advance())
        } else {
            Err(NcclError::new(ErrorKind::ParseError, message, self.line))
        }
    }
}

<commit_msg>update ebnf<commit_after>
use pair::Pair;
use error::{NcclError, ErrorKind};
use token::{Token, TokenKind};

pub struct Parser {
    current: usize,
    path: Vec<String>,
    tokens: Vec<Token>,
    pair: Pair,
    line: u64,
}

// nccl = (value newline)+
// value = name schema? newline (key newline)*
// key = indent value
// name = [^:]+
// schema = ": " name
// newline = ("\n" | "\r\n")+
// indent = " "+ | "\t"

impl Parser {
    pub fn new(tokens: Vec<Token>) -> Self {
        Parser {
            current: 0,
            path: vec![],
            tokens: tokens,
            pair: Pair::new("__top_level__"),
            line: 1,
        }
    }

    pub fn parse(&mut self) -> Result<Pair, NcclError> {
        while !self.is_at_end() {
            self.value()?;
            if self.peek().kind == TokenKind::Value {
                self.colon()?;
                self.value()?;
            }
            self.newline()?;
            self.indent()?;
            self.value()?;
            self.newline()?;
        }

        Ok(self.pair.clone())
    }

    fn add(&mut self, value: String) {
        //self.pair.add_vec(self.path, value);
    }

    fn value(&mut self) -> Result<(), NcclError> {
        if !self.is_at_end() {
            Ok(())
        } else {
            Err(NcclError::new(ErrorKind::ParseError, "Expected value, found EOF", self.line))
        }
    }

    fn indent(&mut self) -> Result<(), NcclError> {
        if !self.is_at_end() {
            Ok(())
        } else {
            Err(NcclError::new(ErrorKind::ParseError, "Expected indent, found EOF", self.line))
        }
    }

    fn newline(&mut self) -> Result<(), NcclError> {
        if !self.is_at_end() {
            self.line += 1;
            Ok(())
        } else {
            Err(NcclError::new(ErrorKind::ParseError, "Expected newline, found EOF", self.line))
        }
    }

    fn colon(&mut self) -> Result<(), NcclError> {
        if !self.is_at_end() {
            Ok(())
        } else {
            Err(NcclError::new(ErrorKind::ParseError, "Expected colon, found EOF", self.line))
        }
    }

    fn matches(&mut self, kind: TokenKind) -> bool {
        if self.check(kind) {
            self.advance();
            true
        } else {
            false
        }
    }

    fn check(&mut self, kind: TokenKind) -> bool {
        if self.is_at_end() {
            false
        } else {
            self.peek().kind == kind
        }
    }

    fn advance(&mut self) -> &Token {
        if !self.is_at_end() {
            self.current += 1;
        }

        self.previous()
    }

    fn is_at_end(&mut self) -> bool {
        self.peek().kind == TokenKind::EOF
    }

    fn peek(&mut self) -> &Token {
        &self.tokens[self.current]
    }

    fn previous(&mut self) -> &Token {
        &self.tokens[self.current - 1]
    }

    fn consume(&mut self, kind: TokenKind, message: &str) -> Result<&Token, NcclError> {
        if self.check(kind) {
            Ok(self.advance())
        } else {
            Err(NcclError::new(ErrorKind::ParseError, message, self.line))
        }
    }
}

<|endoftext|>


<commit_before>use lexicon::Token;
use lexicon::Token::*;
use lexicon::KeywordKind::*;
use lexicon::ReservedKind::*;
use lexicon::CompareKind::*;
use lexicon::OperatorKind::*;
use tokenizer::Tokenizer;
use std::iter::Peekable;
use literals::*;

/// Expects next token to match `$p`, otherwise panics.
macro_rules! expect {
    ($parser:ident, $p:pat) => {
        match $parser.tokenizer.next() {
            Some($p) => {},
            None     => panic!("Unexpected end of program"),
            token    => panic!("Unexpected token {:?}", token),
        }
    }
}

/// If the next token matches `$p`, consume that token and return
/// true, else do nothing and return false
macro_rules! allow {
    ($parser:ident, $p:pat) => {
        match $parser.tokenizer.peek() {
            Some(&$p) => {
                $parser.tokenizer.next();
                true
            },
            _ => false
        }
    }
}

/// Allow multiple occurences of `$p` in a row, consuming them all,
/// returns true if at least one was found
macro_rules! allow_many {
    ($parser:ident, $p:pat) => {
        if allow!($parser, $p) {
            while allow!($parser, $p) {}
            true
        } else {
            false
        }
    }
}

/// More robust version of the regular `match`, will peek at the next
/// token, if the token matches `$p` then consume that token, any line
/// breaks after and call the `$then` expression.
macro_rules! on {
    ($parser:ident, { $( $p:pat => $then:expr ),* }) => ({
        allow_many!($parser, LineTermination);
        match $parser.tokenizer.peek() {
            $(
                Some(&$p) => {
                    $parser.tokenizer.next();
                    allow_many!($parser, LineTermination);
                    $then
                }
            )*
            _ => {}
        }
    })
}

/// Expects a semicolon to end the statement and return `true`. If no
/// semicolon is found, but `LimeTermination` occured, invoke
/// automatic semicolon injection. Additionally a `$cont` token can
/// be specified, if met the macro will stop and return `false`.
macro_rules! expect_statement_end {
    ($parser:ident, $cont:pat) => ({
        let asi = allow_many!($parser, LineTermination);

        match $parser.tokenizer.next() {
            Some(Semicolon) => true,
            Some($cont)     => false,
            token => asi || panic!("Unexpected token {:?}", token)
        }
    });

    ($parser:ident) => ({
        let asi = allow_many!(LineTermination);

        match self.tokenizer.next() {
            Some(Semicolon) => true,
            token => asi || panic!("Unexpected token {:?}", token)
        }
    })
}

/// Read a list of items with predefined `$start`, `$end` and
/// `$separator` tokens and an `$item` expression that is then
/// pushed onto a vector.
macro_rules! expect_list {
    ($parser:ident, $start:pat, $item:expr, $separator:pat, $end:pat) => ({
        let mut list = Vec::new();

        expect!($parser, $start);
        loop {
            allow_many!($parser, LineTermination);
            if allow!($parser, $end) {
                break;
            }
            list.push($item);
            allow_many!($parser, LineTermination);
            if expect_list_end!($parser, $separator, $end) {
                break;
            } else {
                continue;
            }
        }

        list
    })
}

/// Shorthand for reading a key expression, separator token and
/// value expression in that order.
macro_rules! expect_key_value_pair {
    ($parser:ident, $key:expr, $separator:pat, $value:expr) => ({
        allow_many!($parser, LineTermination);
        let key = $key;
        allow_many!($parser, LineTermination);
        expect!($parser, $separator);
        (key, $value)
    })
}

/// Returns true if met with a list closing token `$p`, allows
/// a tailing comma to appear before `$p`.
macro_rules! expect_list_end {
    ($parser:ident, $separator:pat, $end:pat) => {
        match $parser.tokenizer.next() {
            Some($separator) => {
                allow_many!($parser, LineTermination);
                allow!($parser, $end)
            }
            Some($end)       => true,
            _                => false,
        }
    }
}

type PT<'a> = Peekable<Tokenizer<'a>>;

#[derive(Debug)]
pub enum VariableDeclarationKind {
    Var,
    Let,
    Const,
}

#[derive(Debug)]
pub enum ObjectKey {
    Static(String),
    Computed(Expression),
}

#[derive(Debug)]
pub enum Expression {
    Variable(String),
    Literal(LiteralValue),
    Array(Vec<Expression>),
    Object(Vec<(ObjectKey, Expression)>),
    Member {
        object: Box<Expression>,
        property: Box<ObjectKey>,
    },
    MethodCall {
        object: Box<Expression>,
        method: Box<ObjectKey>,
        arguments: Vec<Expression>,
    },
    Addition {
        left: Box<Expression>,
        right: Box<Expression>,
    }
}

#[derive(Debug)]
pub struct VariableDeclarator {
    id: String,
    value: Expression,
}

#[derive(Debug)]
pub struct Program {
    body: Vec<Statement>,
}

#[derive(Debug)]
pub enum Statement {
    VariableDeclaration {
        kind: VariableDeclarationKind,
        declarations: Vec<VariableDeclarator>,
    },
    Expression(Expression),
    Comment(String),
    BlockComment(String),
}

pub struct Parser<'a> {
    tokenizer: Peekable<Tokenizer<'a>>,
}

impl<'a> Parser<'a> {
    pub fn new(source: &'a String) -> Self {
        Parser {
            tokenizer: Tokenizer::new(source).peekable(),
        }
    }

    pub fn array(&mut self) -> Expression {
        Expression::Array(expect_list!(self,
            BracketOn,
            self.expression(),
            Comma,
            BracketOff
        ))
    }

    pub fn object(&mut self) -> Expression {
        Expression::Object(expect_list!(self,
            BlockOn,
            expect_key_value_pair!(self,
                self.object_key(),
                Colon,
                self.expression()
            ),
            Comma,
            BlockOff
        ))
    }

    pub fn object_key(&mut self) -> ObjectKey {
        match self.tokenizer.next() {
            Some(Identifier(key)) | Some(Literal(LiteralString(key))) => {
                ObjectKey::Static(key)
            },
            Some(BracketOn) => {
                allow_many!(self, LineTermination);
                let key = self.expression();
                allow_many!(self, LineTermination);
                expect!(self, BracketOff);
                ObjectKey::Computed(key)
            },
            token => {
                panic!("Expected object key, got {:?}", token)
            }
        }
    }

    pub fn object_property(&mut self) -> ObjectKey {
        match self.tokenizer.next() {
            Some(Identifier(property)) => {
                ObjectKey::Static(property)
            },
            Some(BracketOn) => {
                allow_many!(self, LineTermination);
                let property = self.expression();
                allow_many!(self, LineTermination);
                expect!(self, BracketOff);
                ObjectKey::Computed(property)
            },
            token => {
                panic!("Expected object property, got {:?}", token)
            }
        }
    }

    pub fn expression(&mut self) -> Expression {
        let left = match self.tokenizer.peek() {
            Some(&Literal(_)) => {
                if let Some(Literal(literal)) = self.tokenizer.next() {
                    Expression::Literal(literal)
                } else {
                    panic!("Failed to read expression")
                }
            }
            Some(&Identifier(_)) => {
                if let Some(Identifier(literal)) = self.tokenizer.next() {
                    Expression::Variable(literal)
                } else {
                    panic!("Failed to read expression")
                }
            }
            Some(&BracketOn) => self.array(),
            Some(&BlockOn)   => self.object(),
            Some(_)          => panic!("Unexpected token"),
            _                => panic!("Unexpected end of program")
        };

        on!(self, {
            Accessor => {
                let object = Box::new(left);
                let property = Box::new(self.object_property());
                allow_many!(self, LineTermination);

                if let Some(&ParenOn) = self.tokenizer.peek() {
                    return Expression::MethodCall {
                        object: object,
                        method: property,
                        arguments: expect_list!(self,
                            ParenOn,
                            self.expression(),
                            Comma,
                            ParenOff
                        )
                    };
                }

                return Expression::Member {
                    object: object,
                    property: property,
                }
            },
            Operator(Add) => return Expression::Addition {
                left: Box::new(left),
                right: Box::new(self.expression()),
            }
        });

        left
    }

    fn identifier(&mut self) -> String {
        match self.tokenizer.next() {
            Some(Identifier(id)) => id,
            token => panic!("Expected identifier, got {:?}", token),
        }
    }

    fn variable_declaration(&mut self, kind: VariableDeclarationKind)
    -> Statement {
        let mut declarations: Vec<VariableDeclarator> = Vec::new();

        loop {
            let (id, value) = expect_key_value_pair!(self,
                self.identifier(),
                Assign,
                self.expression()
            );

            declarations.push(VariableDeclarator {
                id: id,
                value: value,
            });

            if expect_statement_end!(self, Comma) {
                break
            }
        }

        Statement::VariableDeclaration {
            kind: kind,
            declarations: declarations,
        }
    }

    fn statement(&mut self) -> Option<Statement> {
        on!(self, {
            Keyword(Var)   => return Some(self.variable_declaration(
                VariableDeclarationKind::Var
            )),
            Keyword(Let)   => return Some(self.variable_declaration(
                VariableDeclarationKind::Let
            )),
            Keyword(Const) => return Some(self.variable_declaration(
                VariableDeclarationKind::Const
            ))
        });

        if let Some(_) = self.tokenizer.peek() {
            Some(Statement::Expression(self.expression()))
        } else {
            None
        }
        // if let Some(token) = self.tokenizer.next() {
        //     return match token {
        //         LineTermination       => self.statement(),
        //         Comment(comment)      => Some(
        //             Statement::Comment(comment)
        //         ),
        //         BlockComment(comment) => Some (
        //             Statement::BlockComment(comment)
        //         ),
        //         Keyword(Var)          => Some(self.variable_declaration(
        //             VariableDeclarationKind::Var
        //         )),
        //         Keyword(Let)          => Some(self.variable_declaration(
        //             VariableDeclarationKind::Let
        //         )),
        //         Keyword(Const)        => Some(self.variable_declaration(
        //             VariableDeclarationKind::Const
        //         )),
        //         _ => None,
        //     }
        // }
    }
}

pub fn parse(source: String) -> Program {
    let mut parser = Parser::new(&source);
    let mut program = Program { body: Vec::new() };

    // for token in tokenizer {
    //     println!("{:?}", token);
    // }
    while let Some(statement) = parser.statement() {
        program.body.push(statement);
    }

    println!("{:#?}", program);

    return program;
}
<commit_msg>cleanup<commit_after>use lexicon::Token;
use lexicon::Token::*;
use lexicon::KeywordKind::*;
use lexicon::ReservedKind::*;
use lexicon::CompareKind::*;
use lexicon::OperatorKind::*;
use tokenizer::Tokenizer;
use std::iter::Peekable;
use literals::*;

/// Expects next token to match `$p`, otherwise panics.
macro_rules! expect {
    ($parser:ident, $p:pat) => {
        match $parser.tokenizer.next() {
            Some($p) => {},
            None     => panic!("Unexpected end of program"),
            token    => panic!("Unexpected token {:?}", token),
        }
    }
}

/// If the next token matches `$p`, consume that token and return
/// true, else do nothing and return false
macro_rules! allow {
    ($parser:ident, $p:pat) => {
        match $parser.tokenizer.peek() {
            Some(&$p) => {
                $parser.tokenizer.next();
                true
            },
            _ => false
        }
    }
}

/// Allow multiple occurences of `$p` in a row, consuming them all,
/// returns true if at least one was found
macro_rules! allow_many {
    ($parser:ident, $p:pat) => {
        if allow!($parser, $p) {
            while allow!($parser, $p) {}
            true
        } else {
            false
        }
    }
}

/// More robust version of the regular `match`, will peek at the next
/// token, if the token matches `$p` then consume that token, any line
/// breaks after and call the `$then` expression.
macro_rules! on {
    ($parser:ident, { $( $p:pat => $then:expr ),* }) => ({
        allow_many!($parser, LineTermination);
        match $parser.tokenizer.peek() {
            $(
                Some(&$p) => {
                    $parser.tokenizer.next();
                    allow_many!($parser, LineTermination);
                    $then
                }
            )*
            _ => {}
        }
    })
}

/// Expects a semicolon to end the statement and return `true`. If no
/// semicolon is found, but `LimeTermination` occured, invoke
/// automatic semicolon injection. Additionally a `$cont` token can
/// be specified, if met the macro will stop and return `false`.
macro_rules! expect_statement_end {
    ($parser:ident, $cont:pat) => ({
        let asi = allow_many!($parser, LineTermination);

        match $parser.tokenizer.next() {
            Some(Semicolon) => true,
            Some($cont)     => false,
            token => asi || panic!("Unexpected token {:?}", token)
        }
    });

    ($parser:ident) => ({
        let asi = allow_many!(LineTermination);

        match self.tokenizer.next() {
            Some(Semicolon) => true,
            token => asi || panic!("Unexpected token {:?}", token)
        }
    })
}

/// Read a list of items with predefined `$start`, `$end` and
/// `$separator` tokens and an `$item` expression that is then
/// pushed onto a vector.
macro_rules! expect_list {
    ($parser:ident, $start:pat, $item:expr, $separator:pat, $end:pat) => ({
        let mut list = Vec::new();

        expect!($parser, $start);
        loop {
            allow_many!($parser, LineTermination);
            if allow!($parser, $end) {
                break;
            }
            list.push($item);
            allow_many!($parser, LineTermination);
            if expect_list_end!($parser, $separator, $end) {
                break;
            } else {
                continue;
            }
        }

        list
    })
}

/// Shorthand for reading a key expression, separator token and
/// value expression in that order.
macro_rules! expect_key_value_pair {
    ($parser:ident, $key:expr, $separator:pat, $value:expr) => ({
        allow_many!($parser, LineTermination);
        let key = $key;
        allow_many!($parser, LineTermination);
        expect!($parser, $separator);
        (key, $value)
    })
}

/// Returns true if met with a list closing token `$p`, allows
/// a tailing comma to appear before `$p`.
macro_rules! expect_list_end {
    ($parser:ident, $separator:pat, $end:pat) => {
        match $parser.tokenizer.next() {
            Some($separator) => {
                allow_many!($parser, LineTermination);
                allow!($parser, $end)
            }
            Some($end)       => true,
            _                => false,
        }
    }
}

type PT<'a> = Peekable<Tokenizer<'a>>;

#[derive(Debug)]
pub enum VariableDeclarationKind {
    Var,
    Let,
    Const,
}

#[derive(Debug)]
pub enum ObjectKey {
    Static(String),
    Computed(Expression),
}

#[derive(Debug)]
pub enum Expression {
    Variable(String),
    Literal(LiteralValue),
    Array(Vec<Expression>),
    Object(Vec<(ObjectKey, Expression)>),
    Member {
        object: Box<Expression>,
        property: Box<ObjectKey>,
    },
    MethodCall {
        object: Box<Expression>,
        method: Box<ObjectKey>,
        arguments: Vec<Expression>,
    },
    Addition {
        left: Box<Expression>,
        right: Box<Expression>,
    }
}

#[derive(Debug)]
pub struct Program {
    body: Vec<Statement>,
}

#[derive(Debug)]
pub enum Statement {
    VariableDeclaration {
        kind: VariableDeclarationKind,
        declarations: Vec<(String, Expression)>,
    },
    Expression(Expression),
    Comment(String),
    BlockComment(String),
}

pub struct Parser<'a> {
    tokenizer: Peekable<Tokenizer<'a>>,
}

impl<'a> Parser<'a> {
    pub fn new(source: &'a String) -> Self {
        Parser {
            tokenizer: Tokenizer::new(source).peekable(),
        }
    }

    pub fn array(&mut self) -> Expression {
        Expression::Array(expect_list!(self,
            BracketOn,
            self.expression(),
            Comma,
            BracketOff
        ))
    }

    pub fn object(&mut self) -> Expression {
        Expression::Object(expect_list!(self,
            BlockOn,
            expect_key_value_pair!(self,
                self.object_key(),
                Colon,
                self.expression()
            ),
            Comma,
            BlockOff
        ))
    }

    pub fn object_key(&mut self) -> ObjectKey {
        match self.tokenizer.next() {
            Some(Identifier(key)) | Some(Literal(LiteralString(key))) => {
                ObjectKey::Static(key)
            },
            Some(BracketOn) => {
                allow_many!(self, LineTermination);
                let key = self.expression();
                allow_many!(self, LineTermination);
                expect!(self, BracketOff);
                ObjectKey::Computed(key)
            },
            token => {
                panic!("Expected object key, got {:?}", token)
            }
        }
    }

    pub fn object_property(&mut self) -> ObjectKey {
        match self.tokenizer.next() {
            Some(Identifier(property)) => {
                ObjectKey::Static(property)
            },
            Some(BracketOn) => {
                allow_many!(self, LineTermination);
                let property = self.expression();
                allow_many!(self, LineTermination);
                expect!(self, BracketOff);
                ObjectKey::Computed(property)
            },
            token => {
                panic!("Expected object property, got {:?}", token)
            }
        }
    }

    pub fn expression(&mut self) -> Expression {
        let left = match self.tokenizer.peek() {
            Some(&Literal(_)) => {
                if let Some(Literal(literal)) = self.tokenizer.next() {
                    Expression::Literal(literal)
                } else {
                    panic!("Failed to read expression")
                }
            }
            Some(&Identifier(_)) => {
                if let Some(Identifier(literal)) = self.tokenizer.next() {
                    Expression::Variable(literal)
                } else {
                    panic!("Failed to read expression")
                }
            }
            Some(&BracketOn) => self.array(),
            Some(&BlockOn)   => self.object(),
            Some(_)          => panic!("Unexpected token"),
            _                => panic!("Unexpected end of program")
        };

        on!(self, {
            Accessor => {
                let object = Box::new(left);
                let property = Box::new(self.object_property());
                allow_many!(self, LineTermination);

                if let Some(&ParenOn) = self.tokenizer.peek() {
                    return Expression::MethodCall {
                        object: object,
                        method: property,
                        arguments: expect_list!(self,
                            ParenOn,
                            self.expression(),
                            Comma,
                            ParenOff
                        )
                    };
                }

                return Expression::Member {
                    object: object,
                    property: property,
                }
            },
            Operator(Add) => return Expression::Addition {
                left: Box::new(left),
                right: Box::new(self.expression()),
            }
        });

        left
    }

    fn identifier(&mut self) -> String {
        match self.tokenizer.next() {
            Some(Identifier(id)) => id,
            token => panic!("Expected identifier, got {:?}", token),
        }
    }

    fn variable_declaration(&mut self, kind: VariableDeclarationKind)
    -> Statement {
        let mut declarations = Vec::new();

        loop {
            declarations.push(expect_key_value_pair!(self,
                self.identifier(),
                Assign,
                self.expression()
            ));

            if expect_statement_end!(self, Comma) {
                break
            }
        }

        Statement::VariableDeclaration {
            kind: kind,
            declarations: declarations,
        }
    }

    fn statement(&mut self) -> Option<Statement> {
        on!(self, {
            Keyword(Var)   => return Some(self.variable_declaration(
                VariableDeclarationKind::Var
            )),
            Keyword(Let)   => return Some(self.variable_declaration(
                VariableDeclarationKind::Let
            )),
            Keyword(Const) => return Some(self.variable_declaration(
                VariableDeclarationKind::Const
            ))
        });

        if let Some(_) = self.tokenizer.peek() {
            Some(Statement::Expression(self.expression()))
        } else {
            None
        }
        // if let Some(token) = self.tokenizer.next() {
        //     return match token {
        //         LineTermination       => self.statement(),
        //         Comment(comment)      => Some(
        //             Statement::Comment(comment)
        //         ),
        //         BlockComment(comment) => Some (
        //             Statement::BlockComment(comment)
        //         ),
        //         Keyword(Var)          => Some(self.variable_declaration(
        //             VariableDeclarationKind::Var
        //         )),
        //         Keyword(Let)          => Some(self.variable_declaration(
        //             VariableDeclarationKind::Let
        //         )),
        //         Keyword(Const)        => Some(self.variable_declaration(
        //             VariableDeclarationKind::Const
        //         )),
        //         _ => None,
        //     }
        // }
    }
}

pub fn parse(source: String) -> Program {
    let mut parser = Parser::new(&source);
    let mut program = Program { body: Vec::new() };

    // for token in tokenizer {
    //     println!("{:?}", token);
    // }
    while let Some(statement) = parser.statement() {
        program.body.push(statement);
    }

    println!("{:#?}", program);

    return program;
}
<|endoftext|>


<commit_before>use lexer::{Token, TokenType, Lexer, COMMAND, SEMICOLON, EOF};
use std::libc::exit;

pub struct Parser {
    input: Lexer,
    k: uint,
    index: uint,
    lookahead: ~[Token],
}

impl Parser {
    pub fn consume(&mut self) {
        self.lookahead[self.index] = self.input.next_token();
        self.index = (self.index + 1) % self.k;
    }

    pub fn LT(&mut self, i: uint) -> Token {
        let token = copy self.lookahead[(self.index + i - 1) % self.k];
        token
    }

    pub fn LA(&mut self, i: uint) -> TokenType {
        self.LT(i).ttype
    }

    pub fn tmatch(&mut self, ttype: TokenType) {
        let current_ttype = self.LA(0) as int;
        let ttype = ttype as int;
        if (current_ttype == ttype) {
            self.consume();
        } else {
            error!("Expecting: " + ttype.to_str() + " Found: " + current_ttype.to_str());
            unsafe {
                exit(1);
            }
        }
    }

    pub fn input(&mut self) {
        self.commands();
        self.tmatch(EOF);
    }

    pub fn commands(&mut self) {
        self.command();
        while (self.LA(0) as int == SEMICOLON as int) {
            self.tmatch(SEMICOLON);
            self.command();
        }
    }

    pub fn command(&mut self) {
        self.tmatch(COMMAND);
    }
}

#[main]
pub fn main() {
    let mut lex = Lexer { input: ~"foo; bar; baz", index: 0, currentChar: 't', eof: false };
    let mut parser = Parser { input: lex, k: 1, index: 0, lookahead: ~[] };
    parser.lookahead.grow(parser.k, &Token {text: ~" ", ttype: COMMAND});
    let mut k_count = 1;
    while (k_count <= parser.k) {
        k_count += 1;
        parser.consume();
    }
    parser.input();
}
<commit_msg>pipes<commit_after>use lexer::{Token, TokenType, Lexer, COMMAND, SEMICOLON, PIPE, EOF};
use std::libc::exit;

pub struct Parser {
    input: Lexer,
    k: uint,
    index: uint,
    lookahead: ~[Token],
}

impl Parser {
    pub fn consume(&mut self) {
        self.lookahead[self.index] = self.input.next_token();
        self.index = (self.index + 1) % self.k;
    }

    pub fn LT(&mut self, i: uint) -> Token {
        let token = copy self.lookahead[(self.index + i) % self.k];
        token
    }

    pub fn LA(&mut self, i: uint) -> TokenType {
        self.LT(i).ttype
    }

    pub fn tmatch(&mut self, ttype: TokenType) {
        let current_ttype = self.LA(0) as int;
        let ttype = ttype as int;
        if (current_ttype == ttype) {
            self.consume();
        } else {
            error!("Expecting: " + ttype.to_str() + " Found: " + current_ttype.to_str());
            unsafe {
                exit(1);
            }
        }
    }

    pub fn input(&mut self) {
        self.commands();
        self.tmatch(EOF);
    }

    pub fn commands(&mut self) {
        loop {
            self.command();
            if (self.LA(0) as int == PIPE as int) {
                self.tmatch(PIPE);
                self.command();
            }
            if (self.LA(0) as int == SEMICOLON as int) {
                self.tmatch(SEMICOLON);
            }
            if (self.LA(0) as int == EOF as int) {
                break;
            }
        }
    }

    pub fn command(&mut self) {
        self.tmatch(COMMAND);
    }
}

#[main]
pub fn main() {
    let mut lex = Lexer { input: ~"foo; bar; baz a b c | faz;", index: 0, currentChar: 't', eof: false };
    let mut parser = Parser { input: lex, k: 3, index: 0, lookahead: ~[] };
    parser.lookahead.grow(parser.k, &Token {text: ~" ", ttype: COMMAND});
    let mut k_count = 1;
    while (k_count <= parser.k) {
        k_count += 1;
        parser.consume();
    }
    parser.input();
}
<|endoftext|>


<commit_before>//! Signals are values that discretely change over time.

use crate::stream::Stream;
use crate::types::{MaybeOwned, SharedImpl, SharedSignal, Storage};
use parking_lot::Mutex;
use std::fmt;
use std::sync::{mpsc, Arc};

use self::SigValue::*;

/// Represents a discrete value that changes over time.
///
/// Signals are usually constructed by stream operations and can be read using the `sample` or
/// `take` methods. They update lazily when someone reads them.
#[derive(Clone, Debug)]
pub struct Signal<T>(SigValue<T>);

/// The content source of a signal.
#[derive(Clone)]
enum SigValue<T> {
    /// A signal with constant value.
    Constant(T),
    /// A signal that generates it's values from a function.
    ///
    /// This is produced by `Signal::from_fn`
    Dynamic(Arc<dyn Fn() -> T + Send + Sync>),
    /// A signal that contains shared data.
    ///
    /// This is mainly produced by stream methods or mapping other signals.
    Shared(Arc<dyn SharedSignal<T> + Send + Sync>),
    /// A signal that contains a signal, and allows sampling the inner signal directly.
    ///
    /// This is produced by `Signal::switch`
    Nested(Arc<dyn Fn() -> Signal<T> + Send + Sync>),
}

impl<T> Signal<T> {
    /// Creates a signal with constant value.
    ///
    /// The value is assumed to be constant, so changing it while it's stored on the
    /// signal is a logic error and will cause unexpected results.
    pub fn constant(val: T) -> Self {
        Signal(Constant(val))
    }

    /// Creates a signal that samples it's values from an external source.
    ///
    /// The closure is meant to sample a continuous value from the real world,
    /// so the signal value is assumed to be always changing.
    pub fn from_fn<F>(f: F) -> Self
    where
        F: Fn() -> T + Send + Sync + 'static,
    {
        Signal(Dynamic(Arc::new(f)))
    }

    /// Creates a new shared signal.
    pub(crate) fn shared<S>(storage: Arc<S>) -> Self
    where
        S: SharedSignal<T> + Send + Sync + 'static,
    {
        Signal(Shared(storage))
    }

    /// Checks if the signal has changed since the last time it was sampled.
    pub fn has_changed(&self) -> bool {
        match self.0 {
            Constant(_) => false,
            Dynamic(_) | Nested(_) => true,
            Shared(ref s) => {
                s.update();
                s.get_storage().must_update()
            }
        }
    }

    /// Moves the value out of the signal.
    ///
    /// To avoid leaving the internal storage empty (and causing panics), it must be filled with
    /// a default value. This marks the signal chain as changed, so most of time no one will
    /// notice anything.
    pub fn take(self) -> T
    where
        T: Default,
    {
        match self.0 {
            Constant(val) => val,
            Dynamic(f) => f(),
            Shared(s) => {
                s.update();
                s.sample().replace(T::default())
            }
            Nested(f) => f().take(),
        }
    }

    /// Samples the value of the signal.
    ///
    /// This will clone the value stored in the signal.
    pub fn sample(&self) -> T
    where
        T: Clone,
    {
        match self.0 {
            Constant(ref val) => T::clone(val),
            Dynamic(ref f) => f(),
            Shared(ref s) => {
                s.update();
                s.sample().get()
            }
            Nested(ref f) => f().sample(),
        }
    }

    /// Maps a signal with the provided function.
    ///
    /// The closure is called only when the parent signal changes.
    pub fn map<F, R>(&self, f: F) -> Signal<R>
    where
        F: Fn(T) -> R + Send + Sync + 'static,
        T: Clone + 'static,
        R: Send + 'static,
    {
        match self.0 {
            // constant signal: apply f once to produce another constant signal
            Constant(ref val) => Signal::constant(f(val.clone())),
            // dynamic signal: sample and apply f
            Dynamic(ref sf) => {
                let sf_ = sf.clone();
                Signal::from_fn(move || f(sf_()))
            }
            // shared signal: apply f only when the parent signal has changed
            Shared(ref sig) => Signal::shared(
                SharedImpl {
                    storage: Storage::inherit(sig.get_storage()),
                    source: sig.clone(),
                    f,
                }
                .wrap(),
            ),
            // nested signal: extract signal, sample and apply f
            Nested(ref sf) => {
                let sf_ = sf.clone();
                Signal::from_fn(move || f(sf_().sample()))
            }
        }
    }
}

impl<T: Send + 'static> Signal<T> {
    /// Samples the value of this signal every time the trigger stream fires.
    pub fn snapshot<S, F, R>(&self, trigger: &Stream<S>, f: F) -> Stream<R>
    where
        F: Fn(T, MaybeOwned<'_, S>) -> R + Send + Sync + 'static,
        T: Clone,
        S: 'static,
        R: 'static,
    {
        let this = Mutex::new(self.clone()); // need Sync for T
        trigger.map(move |b| f(this.lock().sample(), b))
    }

    /// Stores the last value sent to a channel.
    ///
    /// When sampled, the resulting signal consumes all the current values on the channel
    /// (using non-blocking operations) and returns the last value seen.
    #[inline]
    pub fn from_channel(initial: T, rx: mpsc::Receiver<T>) -> Self {
        Self::fold_channel(initial, rx, |_, v| v)
    }

    /// Creates a signal that folds the values from a channel.
    ///
    /// When sampled, the resulting signal consumes all the current values on the channel
    /// (using non-blocking operations) and folds them using the current signal value as the
    /// initial accumulator state.
    pub fn fold_channel<V, F>(initial: T, rx: mpsc::Receiver<V>, f: F) -> Self
    where
        F: Fn(T, V) -> T + Send + Sync + 'static,
        V: Send + 'static,
    {
        Signal::shared(
            SharedImpl {
                storage: Storage::new(initial),
                source: Mutex::new(rx),
                f,
            }
            .wrap(),
        )
    }
}

impl<T: Clone + 'static> Signal<Signal<T>> {
    /// Creates a new signal that samples the inner value of a nested signal.
    pub fn switch(&self) -> Signal<T> {
        match self.0 {
            // constant signal: just extract the inner signal
            Constant(ref sig) => Signal::clone(sig),
            // dynamic signal: re-label as nested
            Dynamic(ref f) => Signal(Nested(f.clone())),
            // shared signal: sample to extract the inner signal
            Shared(ref sig_) => {
                let sig = sig_.clone();
                Signal(Nested(Arc::new(move || {
                    sig.update();
                    sig.sample().get()
                })))
            }
            // nested signal: remove one layer
            Nested(ref f_) => {
                let f = f_.clone();
                Signal(Nested(Arc::new(move || f().sample())))
            }
        }
    }
}

impl<T: Default> Default for Signal<T> {
    /// Creates a constant signal with T's default value.
    #[inline]
    fn default() -> Self {
        Signal::constant(T::default())
    }
}

impl<T> From<T> for Signal<T> {
    /// Creates a constant signal from T.
    #[inline]
    fn from(val: T) -> Self {
        Signal::constant(val)
    }
}

impl<T: fmt::Debug> fmt::Debug for SigValue<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Constant(ref val) => write!(f, "Constant({:?})", val),
            Dynamic(ref rf) => write!(f, "Dynamic(Fn@{:p})", rf),
            Shared(ref rs) => write!(f, "Shared(SharedSignal@{:p})", rs),
            Nested(ref rf) => write!(f, "Nested(Fn@{:p})", rf),
        }
    }
}

impl<T: fmt::Display + Clone> fmt::Display for Signal<T> {
    /// Samples the signal and formats the value.
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.sample(), f)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::sync::RwLock;
    use std::time::Instant;

    #[test]
    fn signal_basic() {
        let signal = Signal::constant(42);
        let double = signal.map(|a| a * 2);
        let plusone = double.map(|a| a + 1);
        assert_eq!(signal.sample(), 42);
        assert_eq!(double.sample(), 84);
        assert_eq!(plusone.sample(), 85);
    }

    #[test]
    fn signal_shared() {
        let st = SharedImpl::new(1, ()).wrap();
        let signal = Signal::shared(st.clone());
        let double = signal.map(|a| a * 2);

        assert_eq!(signal.sample(), 1);
        assert_eq!(double.sample(), 2);
        st.set(42);
        assert_eq!(signal.sample(), 42);
        assert_eq!(double.sample(), 84);
    }

    #[test]
    fn signal_dynamic() {
        let t = Instant::now();
        let signal = Signal::from_fn(move || t);
        assert_eq!(signal.sample(), t);

        let n = Arc::new(RwLock::new(1));
        let cloned = n.clone();
        let signal = Signal::from_fn(move || *cloned.read().unwrap());
        let double = signal.map(|a| a * 2);
        let plusone = double.map(|a| a + 1);
        assert_eq!(signal.sample(), 1);
        assert_eq!(double.sample(), 2);
        assert_eq!(plusone.sample(), 3);
        *n.write().unwrap() = 13;
        assert_eq!(signal.sample(), 13);
        assert_eq!(double.sample(), 26);
        assert_eq!(plusone.sample(), 27);
    }

    #[test]
    fn signal_take() {
        let signal = Signal::constant(42);
        assert_eq!(signal.take(), 42);

        let signal = Signal::from_fn(move || 33);
        assert_eq!(signal.clone().take(), 33);
        assert_eq!(signal.take(), 33);

        let st = SharedImpl::new(123, ()).wrap();
        let signal = Signal::shared(st.clone());
        assert_eq!(signal.clone().take(), 123);
        assert_eq!(signal.clone().take(), 0);
        st.set(13);
        assert_eq!(signal.take(), 13);
    }
}
<commit_msg>rewrite Signal::snapshot to require less locking<commit_after>//! Signals are values that discretely change over time.

use crate::stream::Stream;
use crate::types::{MaybeOwned, SharedImpl, SharedSignal, Storage};
use parking_lot::Mutex;
use std::fmt;
use std::sync::{mpsc, Arc};

use self::SigValue::*;

/// Represents a discrete value that changes over time.
///
/// Signals are usually constructed by stream operations and can be read using the `sample` or
/// `take` methods. They update lazily when someone reads them.
#[derive(Clone, Debug)]
pub struct Signal<T>(SigValue<T>);

/// The content source of a signal.
#[derive(Clone)]
enum SigValue<T> {
    /// A signal with constant value.
    Constant(T),
    /// A signal that generates it's values from a function.
    ///
    /// This is produced by `Signal::from_fn`
    Dynamic(Arc<dyn Fn() -> T + Send + Sync>),
    /// A signal that contains shared data.
    ///
    /// This is mainly produced by stream methods or mapping other signals.
    Shared(Arc<dyn SharedSignal<T> + Send + Sync>),
    /// A signal that contains a signal, and allows sampling the inner signal directly.
    ///
    /// This is produced by `Signal::switch`
    Nested(Arc<dyn Fn() -> Signal<T> + Send + Sync>),
}

impl<T> Signal<T> {
    /// Creates a signal with constant value.
    ///
    /// The value is assumed to be constant, so changing it while it's stored on the
    /// signal is a logic error and will cause unexpected results.
    pub fn constant(val: T) -> Self {
        Signal(Constant(val))
    }

    /// Creates a signal that samples it's values from an external source.
    ///
    /// The closure is meant to sample a continuous value from the real world,
    /// so the signal value is assumed to be always changing.
    pub fn from_fn<F>(f: F) -> Self
    where
        F: Fn() -> T + Send + Sync + 'static,
    {
        Signal(Dynamic(Arc::new(f)))
    }

    /// Creates a new shared signal.
    pub(crate) fn shared<S>(storage: Arc<S>) -> Self
    where
        S: SharedSignal<T> + Send + Sync + 'static,
    {
        Signal(Shared(storage))
    }

    /// Checks if the signal has changed since the last time it was sampled.
    pub fn has_changed(&self) -> bool {
        match self.0 {
            Constant(_) => false,
            Dynamic(_) | Nested(_) => true,
            Shared(ref s) => {
                s.update();
                s.get_storage().must_update()
            }
        }
    }

    /// Moves the value out of the signal.
    ///
    /// To avoid leaving the internal storage empty (and causing panics), it must be filled with
    /// a default value. This marks the signal chain as changed, so most of time no one will
    /// notice anything.
    pub fn take(self) -> T
    where
        T: Default,
    {
        match self.0 {
            Constant(val) => val,
            Dynamic(f) => f(),
            Shared(s) => {
                s.update();
                s.sample().replace(T::default())
            }
            Nested(f) => f().take(),
        }
    }

    /// Samples the value of the signal.
    ///
    /// This will clone the value stored in the signal.
    pub fn sample(&self) -> T
    where
        T: Clone,
    {
        match self.0 {
            Constant(ref val) => T::clone(val),
            Dynamic(ref f) => f(),
            Shared(ref s) => {
                s.update();
                s.sample().get()
            }
            Nested(ref f) => f().sample(),
        }
    }

    /// Maps a signal with the provided function.
    ///
    /// The closure is called only when the parent signal changes.
    pub fn map<F, R>(&self, f: F) -> Signal<R>
    where
        F: Fn(T) -> R + Send + Sync + 'static,
        T: Clone + 'static,
        R: Send + 'static,
    {
        match self.0 {
            // constant signal: apply f once to produce another constant signal
            Constant(ref val) => Signal::constant(f(val.clone())),
            // dynamic signal: sample and apply f
            Dynamic(ref sf) => {
                let sf_ = sf.clone();
                Signal::from_fn(move || f(sf_()))
            }
            // shared signal: apply f only when the parent signal has changed
            Shared(ref sig) => Signal::shared(
                SharedImpl {
                    storage: Storage::inherit(sig.get_storage()),
                    source: sig.clone(),
                    f,
                }
                .wrap(),
            ),
            // nested signal: extract signal, sample and apply f
            Nested(ref sf) => {
                let sf_ = sf.clone();
                Signal::from_fn(move || f(sf_().sample()))
            }
        }
    }
}

impl<T: Send + 'static> Signal<T> {
    /// Samples the value of this signal every time the trigger stream fires.
    pub fn snapshot<S, F, R>(&self, trigger: &Stream<S>, f: F) -> Stream<R>
    where
        F: Fn(T, MaybeOwned<'_, S>) -> R + Send + Sync + 'static,
        T: Clone,
        S: 'static,
        R: 'static,
    {
        match self.0 {
            Constant(ref val) => {
                let val = Mutex::new(val.clone()); // need Sync for T
                trigger.map(move |t| f(val.lock().clone(), t))
            }
            Dynamic(ref sf) => {
                let sf = sf.clone();
                trigger.map(move |t| f(sf(), t))
            }
            Shared(ref sig) => {
                let sig = sig.clone();
                trigger.map(move |t| {
                    sig.update();
                    f(sig.sample().get(), t)
                })
            }
            Nested(ref sf) => {
                let sf = sf.clone();
                trigger.map(move |t| f(sf().sample(), t))
            }
        }
    }

    /// Stores the last value sent to a channel.
    ///
    /// When sampled, the resulting signal consumes all the current values on the channel
    /// (using non-blocking operations) and returns the last value seen.
    #[inline]
    pub fn from_channel(initial: T, rx: mpsc::Receiver<T>) -> Self {
        Self::fold_channel(initial, rx, |_, v| v)
    }

    /// Creates a signal that folds the values from a channel.
    ///
    /// When sampled, the resulting signal consumes all the current values on the channel
    /// (using non-blocking operations) and folds them using the current signal value as the
    /// initial accumulator state.
    pub fn fold_channel<V, F>(initial: T, rx: mpsc::Receiver<V>, f: F) -> Self
    where
        F: Fn(T, V) -> T + Send + Sync + 'static,
        V: Send + 'static,
    {
        Signal::shared(
            SharedImpl {
                storage: Storage::new(initial),
                source: Mutex::new(rx),
                f,
            }
            .wrap(),
        )
    }
}

impl<T: Clone + 'static> Signal<Signal<T>> {
    /// Creates a new signal that samples the inner value of a nested signal.
    pub fn switch(&self) -> Signal<T> {
        match self.0 {
            // constant signal: just extract the inner signal
            Constant(ref sig) => Signal::clone(sig),
            // dynamic signal: re-label as nested
            Dynamic(ref f) => Signal(Nested(f.clone())),
            // shared signal: sample to extract the inner signal
            Shared(ref sig_) => {
                let sig = sig_.clone();
                Signal(Nested(Arc::new(move || {
                    sig.update();
                    sig.sample().get()
                })))
            }
            // nested signal: remove one layer
            Nested(ref f_) => {
                let f = f_.clone();
                Signal(Nested(Arc::new(move || f().sample())))
            }
        }
    }
}

impl<T: Default> Default for Signal<T> {
    /// Creates a constant signal with T's default value.
    #[inline]
    fn default() -> Self {
        Signal::constant(T::default())
    }
}

impl<T> From<T> for Signal<T> {
    /// Creates a constant signal from T.
    #[inline]
    fn from(val: T) -> Self {
        Signal::constant(val)
    }
}

impl<T: fmt::Debug> fmt::Debug for SigValue<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Constant(ref val) => write!(f, "Constant({:?})", val),
            Dynamic(ref rf) => write!(f, "Dynamic(Fn@{:p})", rf),
            Shared(ref rs) => write!(f, "Shared(SharedSignal@{:p})", rs),
            Nested(ref rf) => write!(f, "Nested(Fn@{:p})", rf),
        }
    }
}

impl<T: fmt::Display + Clone> fmt::Display for Signal<T> {
    /// Samples the signal and formats the value.
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.sample(), f)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::sync::RwLock;
    use std::time::Instant;

    #[test]
    fn signal_basic() {
        let signal = Signal::constant(42);
        let double = signal.map(|a| a * 2);
        let plusone = double.map(|a| a + 1);
        assert_eq!(signal.sample(), 42);
        assert_eq!(double.sample(), 84);
        assert_eq!(plusone.sample(), 85);
    }

    #[test]
    fn signal_shared() {
        let st = SharedImpl::new(1, ()).wrap();
        let signal = Signal::shared(st.clone());
        let double = signal.map(|a| a * 2);

        assert_eq!(signal.sample(), 1);
        assert_eq!(double.sample(), 2);
        st.set(42);
        assert_eq!(signal.sample(), 42);
        assert_eq!(double.sample(), 84);
    }

    #[test]
    fn signal_dynamic() {
        let t = Instant::now();
        let signal = Signal::from_fn(move || t);
        assert_eq!(signal.sample(), t);

        let n = Arc::new(RwLock::new(1));
        let cloned = n.clone();
        let signal = Signal::from_fn(move || *cloned.read().unwrap());
        let double = signal.map(|a| a * 2);
        let plusone = double.map(|a| a + 1);
        assert_eq!(signal.sample(), 1);
        assert_eq!(double.sample(), 2);
        assert_eq!(plusone.sample(), 3);
        *n.write().unwrap() = 13;
        assert_eq!(signal.sample(), 13);
        assert_eq!(double.sample(), 26);
        assert_eq!(plusone.sample(), 27);
    }

    #[test]
    fn signal_take() {
        let signal = Signal::constant(42);
        assert_eq!(signal.take(), 42);

        let signal = Signal::from_fn(move || 33);
        assert_eq!(signal.clone().take(), 33);
        assert_eq!(signal.take(), 33);

        let st = SharedImpl::new(123, ()).wrap();
        let signal = Signal::shared(st.clone());
        assert_eq!(signal.clone().take(), 123);
        assert_eq!(signal.clone().take(), 0);
        st.set(13);
        assert_eq!(signal.take(), 13);
    }
}
<|endoftext|>