<commit_before>use runtime::Runtime;
use module::Module;
use module::ModuleResult;
use module::ModuleError;
use std::path::Path;
use std::result::Result;
use clap::ArgMatches;
use regex::Regex;

pub struct BMModule {
    path: Option<String>,
}

const CALLNAMES : &'static [&'static str] = &[ "bm", "bookmark" ];

impl Module for BMModule {

    fn new(rt : &Runtime) -> BMModule {
        BMModule {
            path: None
        }
    }

    fn callnames() -> &'static [&'static str] {
        CALLNAMES
    }

    fn name(&self) -> &'static str{
        "Bookmark"
    }

    fn execute(&self, rt : &Runtime) -> ModuleResult {
        let cmd = rt.config.cli_matches.subcommand_matches("bm").unwrap();
        match cmd.subcommand_name() {
            Some("add")     => { add(rt, cmd.subcommand_matches("add").unwrap()) }
            Some("list")    => { list(rt, cmd.subcommand_matches("list").unwrap()) }
            Some("remove")  => { list(rt, cmd.subcommand_matches("remove").unwrap()) }
            _ => {
                info!("Not calling any of add, list, remove");
                Ok(())
            }
        }
    }

    fn shutdown(&self, rt : &Runtime) -> ModuleResult {
        Ok(())
    }
}

fn add<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> ModuleResult {
    let url = sub.value_of("url").unwrap();
    let tags = get_tags(rt, sub);
    info!("Adding url '{}' with tags '{:?}'", url, tags.unwrap_or(vec!()));

    Ok(())
}

fn list<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> ModuleResult {
    let tags    = get_tags(rt, sub);
    let matcher = get_matcher(rt, sub);

    match matcher {
        Some(reg) => {
            info!("Listing urls with matcher '{}' and with tags {:?}",
                     reg.as_str(),
                     tags.unwrap_or(vec!()));
        }
        None => {
            info!("Listing urls with tags {:?}", tags.unwrap_or(vec!()));
        }
    }

    Ok(())
}

fn remove<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> ModuleResult {
    let tags    = get_tags(rt, sub);
    let matcher = get_matcher(rt, sub);
    let id      = get_id(rt, sub);

    match id {
        Some(idstr) => {
            info!("Removing urls with id '{}'", idstr);
        }
        None => {
            match matcher {
                Some(reg) => {
                    info!("Removing urls with matcher '{}' and with tags {:?}",
                             reg.as_str(),
                             tags.unwrap_or(vec!()));
                }
                None => {
                    info!("Listing urls with tags {:?}", tags.unwrap_or(vec!()));
                }
            }
        }
    }

    Ok(())
}

fn get_tags<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Option<Vec<String>> {
    sub.value_of("tags").and_then(|tags|
                                  Some(tags.split(",")
                                       .collect::<Vec<_>>()
                                       .iter()
                                       .map(|s| s.to_string())
                                       .collect()
                                      )
                                 )

}

fn get_matcher<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Option<Regex> {
    if let Some(s) = sub.value_of("match") {
        if let Ok(r) = Regex::new(s) {
            return Some(r)
        } else {
            error!("Regex error, continuing without regex");
        }
    }
    None

}

fn get_id<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Option<String> {
    sub.value_of("id").and_then(|s| Some(String::from(s)))
}

<commit_msg>Add some debugging output in bm module<commit_after>use runtime::Runtime;
use module::Module;
use module::ModuleResult;
use module::ModuleError;
use std::path::Path;
use std::result::Result;
use clap::ArgMatches;
use regex::Regex;

pub struct BMModule {
    path: Option<String>,
}

const CALLNAMES : &'static [&'static str] = &[ "bm", "bookmark" ];

impl Module for BMModule {

    fn new(rt : &Runtime) -> BMModule {
        BMModule {
            path: None
        }
    }

    fn callnames() -> &'static [&'static str] {
        CALLNAMES
    }

    fn name(&self) -> &'static str{
        "Bookmark"
    }

    fn execute(&self, rt : &Runtime) -> ModuleResult {
        let cmd = rt.config.cli_matches.subcommand_matches("bm").unwrap();
        match cmd.subcommand_name() {
            Some("add")     => {
                debug!("Calling 'add'...");
                add(rt, cmd.subcommand_matches("add").unwrap())
            }
            Some("list")    => {
                debug!("Calling 'list'...");
                list(rt, cmd.subcommand_matches("list").unwrap())
            }
            Some("remove")  => {
                debug!("Calling 'remove'...");
                list(rt, cmd.subcommand_matches("remove").unwrap())
            }
            _ => {
                info!("Not calling any of add, list, remove");
                Ok(())
            }
        }
    }

    fn shutdown(&self, rt : &Runtime) -> ModuleResult {
        Ok(())
    }
}

fn add<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> ModuleResult {
    let url = sub.value_of("url").unwrap();
    let tags = get_tags(rt, sub);
    info!("Adding url '{}' with tags '{:?}'", url, tags.unwrap_or(vec!()));

    Ok(())
}

fn list<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> ModuleResult {
    let tags    = get_tags(rt, sub);
    let matcher = get_matcher(rt, sub);

    match matcher {
        Some(reg) => {
            info!("Listing urls with matcher '{}' and with tags {:?}",
                     reg.as_str(),
                     tags.unwrap_or(vec!()));
        }
        None => {
            info!("Listing urls with tags {:?}", tags.unwrap_or(vec!()));
        }
    }

    Ok(())
}

fn remove<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> ModuleResult {
    let tags    = get_tags(rt, sub);
    let matcher = get_matcher(rt, sub);
    let id      = get_id(rt, sub);

    match id {
        Some(idstr) => {
            info!("Removing urls with id '{}'", idstr);
        }
        None => {
            match matcher {
                Some(reg) => {
                    info!("Removing urls with matcher '{}' and with tags {:?}",
                             reg.as_str(),
                             tags.unwrap_or(vec!()));
                }
                None => {
                    info!("Listing urls with tags {:?}", tags.unwrap_or(vec!()));
                }
            }
        }
    }

    Ok(())
}

fn get_tags<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Option<Vec<String>> {
    debug!("Fetching tags from commandline");
    sub.value_of("tags").and_then(|tags|
                                  Some(tags.split(",")
                                       .collect::<Vec<_>>()
                                       .iter()
                                       .map(|s| s.to_string())
                                       .collect()
                                      )
                                 )

}

fn get_matcher<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Option<Regex> {
    debug!("Fetching matcher from commandline");
    if let Some(s) = sub.value_of("match") {
        if let Ok(r) = Regex::new(s) {
            return Some(r)
        } else {
            error!("Regex error, continuing without regex");
        }
    }
    None

}

fn get_id<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Option<String> {
    debug!("Fetching id from commandline");
    sub.value_of("id").and_then(|s| Some(String::from(s)))
}

<|endoftext|>


<commit_before>use command_mapper::{
    RustBotPlugin,
    CommandMapperDispatch,
    IrcBotConfigurator
};
use message::{
    IrcMessage
};
use std::io::TcpStream;
use std::str::replace;
use serialize::json;
use serialize::json::{DecodeResult, ApplicationError};

static DEER: &'static str = concat!(
    "\u000301,01@@@@@@@@\u000300,00@\u000301,01@@\u000300,00@\u000301,01@\n",
    "\u000301,01@@@@@@@@\u000300,00@\u000301,01@@\u000300,00@\u000301,01@\n",
    "\u000301,01@@@@@@@@@\u000300,00@@\u000301,01@@\n",
    "\u000301,01@@@@@@@@\u000300,00@@@\u000301,01@@\n",
    "\u000301,01@@@@@@@@@\u000300,00@@\u000301,01@@\n",
    "\u000301,01@@\u000300,00@@@@@@@@@\u000301,01@@\n",
    "\u000301,01@\u000300,00@@@@@@@@@@\u000301,01@@\n",
    "\u000301,01@\u000300,00@@@@@@@@@@\u000301,01@@\n",
    "\u000301,01@\u000300,00@\u000301,01@\u000300,00@",
    "\u000301,01@@@@\u000300,00@\u000301,01@\u000300,00@\u000301,01@@\n",
    "\u000301,01@\u000300,00@\u000301,01@\u000300,00@",
    "\u000301,01@@@@\u000300,00@\u000301,01@\u000300,00@\u000301,01@@\n",
    "\u000301,01@\u000300,00@\u000301,01@\u000300,00@",
    "\u000301,01@@@@\u000300,00@\u000301,01@\u000300,00@\u000301,01@@");

static HTTP_SERVER: &'static str = "deer.satf.se";

#[deriving(Decodable, Encodable)]
struct DeerApiResponse {
    irccode: String
}

fn get_deer(deer_name: &str) -> DecodeResult<DeerApiResponse> {
    let mut stream = match TcpStream::connect(HTTP_SERVER, 80) {
        Ok(stream) => stream,
        Err(_) => return Err(ApplicationError(format!("Host Connection Error")))
    };
    let deer_name = replace(deer_name, " ", "%20");

    assert!(stream.write(format!(
        "GET /deerlist.php?deer={} HTTP/1.0\r\nHost: deer.satf.se\r\n\r\n",
        deer_name.as_slice()
    ).as_bytes()).is_ok());

    let http_doc = match stream.read_to_string() {
        Ok(string) => string,
        Err(_) => return Err(ApplicationError(format!("Read Error")))
    };
    drop(stream);

    let index = match http_doc.as_slice().find_str("\r\n\r\n") {
        Some(index) => index,
        None => return Err(ApplicationError(format!("HTTP Parse Error")))
    };
    let json_doc = http_doc.as_slice().slice_from(index);
    println!("json_doc = {}", json_doc);

    json::decode::<DeerApiResponse>(json_doc)
}

pub struct DeerPlugin {
    sender: Option<SyncSender<(CommandMapperDispatch, IrcMessage)>>
}

impl DeerPlugin {
    pub fn new() -> DeerPlugin {
        DeerPlugin { sender: None }
    }
}

enum DeerCommandType<'a> {
    Deer,
    Deerkins(&'a str)
}

fn parse_deerkins<'a>(message: &'a IrcMessage) -> Option<DeerCommandType<'a>> {
    let message_body = message.get_arg(1).as_slice();
    match message_body.find(' ') {
        Some(idx) => Some(Deerkins(message_body.slice_from(idx + 1))),
        None => None
    }
}

fn parse_command<'a>(m: &CommandMapperDispatch, message: &'a IrcMessage) -> Option<DeerCommandType<'a>> {
    match m.command() {
        Some("deer") => Some(Deer),
        Some("deerkins") => parse_deerkins(message),
        Some(_) => None,
        None => None
    }
}

impl RustBotPlugin for DeerPlugin {
    fn configure(&mut self, conf: &mut IrcBotConfigurator) {
        conf.map("deer");
        conf.map("deerkins");
    }

    fn start(&mut self) {
        let (tx, rx) = sync_channel(10);
        self.sender = Some(tx);

        spawn(proc() {
            for (m, message) in rx.iter() {
                if message.get_args().len() < 2 {
                    continue
                }
                match parse_command(&m, &message) {
                    Some(Deer) => {
                        for deer_line in DEER.split('\n') {
                            m.reply(String::from_str(deer_line));
                        }
                    },
                    Some(Deerkins(deer_name)) => {
                        match get_deer(deer_name) {
                            Ok(deer_data) => {
                                for deer_line in deer_data.irccode.as_slice().split('\n') {
                                    m.reply(String::from_str(deer_line));
                                }
                            },
                            Err(err) => {
                                m.reply(format!("error: {}", err));
                            }
                        }
                    },
                    None => continue
                };
            }
        });
    }

    fn dispatch_cmd(&mut self, m: &CommandMapperDispatch, message: &IrcMessage) {
        match self.sender {
            Some(ref sender) => sender.send((m.clone(), message.clone())),
            None => ()
        };
    }
}
<commit_msg>Add throttle to deer<commit_after>use command_mapper::{
    RustBotPlugin,
    CommandMapperDispatch,
    IrcBotConfigurator
};
use message::{
    IrcMessage
};
use std::io::TcpStream;
use std::str::replace;
use serialize::json;
use serialize::json::{DecodeResult, ApplicationError};
use time::{get_time, Timespec};

static DEER: &'static str = concat!(
    "\u000301,01@@@@@@@@\u000300,00@\u000301,01@@\u000300,00@\u000301,01@\n",
    "\u000301,01@@@@@@@@\u000300,00@\u000301,01@@\u000300,00@\u000301,01@\n",
    "\u000301,01@@@@@@@@@\u000300,00@@\u000301,01@@\n",
    "\u000301,01@@@@@@@@\u000300,00@@@\u000301,01@@\n",
    "\u000301,01@@@@@@@@@\u000300,00@@\u000301,01@@\n",
    "\u000301,01@@\u000300,00@@@@@@@@@\u000301,01@@\n",
    "\u000301,01@\u000300,00@@@@@@@@@@\u000301,01@@\n",
    "\u000301,01@\u000300,00@@@@@@@@@@\u000301,01@@\n",
    "\u000301,01@\u000300,00@\u000301,01@\u000300,00@",
    "\u000301,01@@@@\u000300,00@\u000301,01@\u000300,00@\u000301,01@@\n",
    "\u000301,01@\u000300,00@\u000301,01@\u000300,00@",
    "\u000301,01@@@@\u000300,00@\u000301,01@\u000300,00@\u000301,01@@\n",
    "\u000301,01@\u000300,00@\u000301,01@\u000300,00@",
    "\u000301,01@@@@\u000300,00@\u000301,01@\u000300,00@\u000301,01@@");

static HTTP_SERVER: &'static str = "deer.satf.se";

#[deriving(Decodable, Encodable)]
struct DeerApiResponse {
    irccode: String
}

fn get_deer(deer_name: &str) -> DecodeResult<DeerApiResponse> {
    let mut stream = match TcpStream::connect(HTTP_SERVER, 80) {
        Ok(stream) => stream,
        Err(_) => return Err(ApplicationError(format!("Host Connection Error")))
    };
    let deer_name = replace(deer_name, " ", "%20");

    assert!(stream.write(format!(
        "GET /deerlist.php?deer={} HTTP/1.0\r\nHost: {}\r\n\r\n",
        deer_name.as_slice(),
        HTTP_SERVER
    ).as_bytes()).is_ok());

    let http_doc = match stream.read_to_string() {
        Ok(string) => string,
        Err(_) => return Err(ApplicationError(format!("Read Error")))
    };
    drop(stream);

    let index = match http_doc.as_slice().find_str("\r\n\r\n") {
        Some(index) => index,
        None => return Err(ApplicationError(format!("HTTP Parse Error")))
    };
    let json_doc = http_doc.as_slice().slice_from(index);
    println!("json_doc = {}", json_doc);

    json::decode::<DeerApiResponse>(json_doc)
}

pub struct DeerPlugin {
    sender: Option<SyncSender<(CommandMapperDispatch, IrcMessage)>>
}

impl DeerPlugin {
    pub fn new() -> DeerPlugin {
        DeerPlugin { sender: None }
    }
}

struct DeerInternalState {
    lines_sent: u64,
    last_request: Option<Timespec>
}

impl DeerInternalState {
    fn new() -> DeerInternalState {
        DeerInternalState {
            lines_sent: 0,
            last_request: None
        }
    }

    fn throttle(&mut self) -> bool {
        let now = get_time();

        let (new_last_request, throttle_ok) = match self.last_request {
            Some(last_request) => {
                if (now - last_request).sec < 60 {
                    (Some(last_request), true)
                } else {
                    (Some(now), false)
                }
            },
            None => (Some(now), false)
        };
        self.last_request = new_last_request;
        throttle_ok
    }

    fn start(&mut self, rx: Receiver<(CommandMapperDispatch, IrcMessage)>) {
        for (m, message) in rx.iter() {
            if message.get_args().len() < 2 {
                continue
            }
            let command_data = match parse_command(&m, &message) {
                Some(DeerStats) => Some(DeerStats),
                Some(command_data) => {
                    if self.throttle() {
                        m.reply(String::from_str("2deer4plus"));
                        None
                    } else {
                        Some(command_data)
                    }
                },
                None => None
            };
            match command_data {
                Some(Deer) => {
                    for deer_line in DEER.split('\n') {
                        m.reply(String::from_str(deer_line));
                        self.lines_sent += 1;
                    }
                },
                Some(Deerkins(deer_name)) => {
                    match get_deer(deer_name) {
                        Ok(deer_data) => {
                            for deer_line in deer_data.irccode.as_slice().split('\n') {
                                m.reply(String::from_str(deer_line));
                                self.lines_sent += 1;
                            }
                        },
                        Err(err) => {
                            m.reply(format!("error: {}", err));
                        }
                    }
                },
                Some(DeerStats) => {
                    m.reply(format!("lines sent: {}", self.lines_sent));
                }
                None => continue
            };
        }
    }
}

enum DeerCommandType<'a> {
    Deer,
    Deerkins(&'a str),
    DeerStats
}

fn parse_deerkins<'a>(message: &'a IrcMessage) -> Option<DeerCommandType<'a>> {
    let message_body = message.get_arg(1).as_slice();
    match message_body.find(' ') {
        Some(idx) => Some(Deerkins(message_body.slice_from(idx + 1))),
        None => None
    }
}

fn parse_command<'a>(m: &CommandMapperDispatch, message: &'a IrcMessage) -> Option<DeerCommandType<'a>> {
    match m.command() {
        Some("deer") => Some(Deer),
        Some("deerkins") => parse_deerkins(message),
        Some("deerstats") => Some(DeerStats),
        Some(_) => None,
        None => None
    }
}

impl RustBotPlugin for DeerPlugin {
    fn configure(&mut self, conf: &mut IrcBotConfigurator) {
        conf.map("deer");
        conf.map("deerstats");
        conf.map("deerkins");
    }

    fn start(&mut self) {
        let (tx, rx) = sync_channel(10);
        self.sender = Some(tx);

        spawn(proc() {
            let mut deer_internal_state = DeerInternalState::new();
            deer_internal_state.start(rx);
        });
    }

    fn dispatch_cmd(&mut self, m: &CommandMapperDispatch, message: &IrcMessage) {
        match self.sender {
            Some(ref sender) => sender.send((m.clone(), message.clone())),
            None => ()
        };
    }
}
<|endoftext|>


<commit_before>extern crate image;
extern crate time;

use std::iter;
use std::path::Path;
use std::thread;

use vector::*;
use objects::*;
mod lighting;

pub type Image = image::RgbImage;

pub fn write_image(image: Image, filename: &str) {
    let _ = image.save(&Path::new(filename));
}

pub fn raytrace(scene: &Scene, width: u32, height: u32, h_fov: f64) {
    let f_width = width as f64;
    let f_height = height as f64;
    let v_fov = h_fov * (f_height / f_width);

    let vw_per_pixel = 2.0 * (h_fov / 2.0).tan() / f_width;
    let vh_per_pixel = 2.0 * (v_fov / 2.0).tan() / f_height;

    let norm_up = scene.camera.up.normalized();
    let norm_right = scene.camera.direction.cross(scene.camera.up).normalized();

    let mut image = image::RgbImage::new(width, height);
    let start_time = time::precise_time_ns();

    let mut rays = 0;

    let tracer = |rays: &mut u64, row_start, num_rows| {
        let coords = (0..width).flat_map(|y| {
            iter::repeat(y).zip(row_start..num_rows)
        });
        let trace_coord = |coord| {
            let (x, y) = coord;
            let cast_ray = |dx: f64, dy: f64, rays: &mut u64| {
                *rays += 1;
                let fi = (x as f64) + dx;
                let fj = (y as f64) + dy;

                let u_rel = vw_per_pixel * (fi - (f_width / 2.0));
                let v_rel = vh_per_pixel * (fj - (f_height / 2.0));

                let pixel_loc = scene.camera.position + scene.camera.direction +
                    (norm_right * u_rel) + (norm_up * v_rel);

                let ray = Ray {
                    origin: scene.camera.position,
                    direction: pixel_loc - scene.camera.position
                };

                let (color, new_rays) = lighting::get_color(&scene, &ray, 3);
                *rays += new_rays;
                color
            };

            let c1 = cast_ray(0.25, 0.25, rays);
            let c2 = cast_ray(0.75, 0.25, rays);
            let c3 = cast_ray(0.50, 0.50, rays);
            let c4 = cast_ray(0.75, 0.75, rays);
            let c5 = cast_ray(0.25, 0.75, rays);

            (coord, (c1 + c2 + c3 + c4 + c5) / 5.0)
        };

        let mut result = Vec::with_capacity((num_rows * width) as usize);
        for pixel in coords.map(trace_coord) {
            result.push(pixel)
        }

        result
    };

    let pixels = tracer(&mut rays, 0, height);

    for ((x, y), pixel) in pixels {
        image.put_pixel(x as u32, y as u32, pixel.to_rgb());
    }

    write_image(image, "test.png");

    let elapsed_time = ((time::precise_time_ns() - start_time) as f64) /
        1000000.0;
    let rays_per_s = 1000.0 * (rays as f64) / elapsed_time;
    println!("Traced {} rays in {} ms ({} rays per second).",
             rays, elapsed_time, rays_per_s);
}
<commit_msg>Localize mutable state in anticipation of threading<commit_after>extern crate image;
extern crate time;

use std::iter;
use std::path::Path;
use std::thread;

use vector::*;
use objects::*;
mod lighting;

pub type Image = image::RgbImage;

pub fn write_image(image: Image, filename: &str) {
    let _ = image.save(&Path::new(filename));
}

pub fn raytrace(scene: &Scene, width: u32, height: u32, h_fov: f64) {
    let f_width = width as f64;
    let f_height = height as f64;
    let v_fov = h_fov * (f_height / f_width);

    let vw_per_pixel = 2.0 * (h_fov / 2.0).tan() / f_width;
    let vh_per_pixel = 2.0 * (v_fov / 2.0).tan() / f_height;

    let norm_up = scene.camera.up.normalized();
    let norm_right = scene.camera.direction.cross(scene.camera.up).normalized();

    let mut image = image::RgbImage::new(width, height);
    let start_time = time::precise_time_ns();

    let tracer = |row_start, num_rows| {
        let coords = (0..width).flat_map(|y| {
            iter::repeat(y).zip(row_start..num_rows)
        });
        let trace_coord = |coord| {
            let (x, y) = coord;
            let mut rays = 0;
            let cast_ray = |dx: f64, dy: f64, rays: &mut u64| {
                *rays += 1;
                let fi = (x as f64) + dx;
                let fj = (y as f64) + dy;

                let u_rel = vw_per_pixel * (fi - (f_width / 2.0));
                let v_rel = vh_per_pixel * (fj - (f_height / 2.0));

                let pixel_loc = scene.camera.position + scene.camera.direction +
                    (norm_right * u_rel) + (norm_up * v_rel);

                let ray = Ray {
                    origin: scene.camera.position,
                    direction: pixel_loc - scene.camera.position
                };

                let (color, new_rays) = lighting::get_color(&scene, &ray, 3);
                *rays += new_rays;
                color
            };

            let c1 = cast_ray(0.25, 0.25, &mut rays);
            let c2 = cast_ray(0.75, 0.25, &mut rays);
            let c3 = cast_ray(0.50, 0.50, &mut rays);
            let c4 = cast_ray(0.75, 0.75, &mut rays);
            let c5 = cast_ray(0.25, 0.75, &mut rays);

            (coord, rays, (c1 + c2 + c3 + c4 + c5) / 5.0)
        };

        let mut result = Vec::with_capacity((num_rows * width) as usize);
        let mut rays = 0;
        for (coord, new_rays, color) in coords.map(trace_coord) {
            rays += new_rays;
            result.push((coord, color))
        }

        (rays, result)
    };

    let (rays, pixels) = tracer(0, height);

    for ((x, y), pixel) in pixels {
        image.put_pixel(x as u32, y as u32, pixel.to_rgb());
    }

    write_image(image, "test.png");

    let elapsed_time = ((time::precise_time_ns() - start_time) as f64) /
        1000000.0;
    let rays_per_s = 1000.0 * (rays as f64) / elapsed_time;
    println!("Traced {} rays in {} ms ({} rays per second).",
             rays, elapsed_time, rays_per_s);
}
<|endoftext|>


<commit_before>use std::sync::{Arc, Mutex};
use std::thread::{self, JoinHandle};
use std::time::Duration;
use crossbeam::sync::MsQueue;
use env_logger;
use rustc_serialize::Decodable;

pub struct EventBus<M: 'static + Sync + Send + Decodable, E: 'static + Sized + Sync + Send> {
  actors: Arc<Mutex<Vec<Box<Actor<M, E>>>>>,
  queue: Arc<MsQueue<M>>,
  stopped: Arc<Mutex<bool>>,
  thread: JoinHandle<Result<(), E>>,
}

unsafe impl<M: 'static + Sync + Send + Decodable, E: 'static + Sized + Sync + Send> Sync for EventBus<M, E> {}
unsafe impl<M: 'static + Sync + Send + Decodable, E: 'static + Sized + Sync + Send> Send for EventBus<M, E> {}

impl<M: Sync + Send + Decodable, E: Sized + Sync + Send> EventBus<M, E> {

  pub fn new(actors: Vec<Box<Actor<M,E>>>) -> EventBus<M, E> {
    let actors = Arc::new(Mutex::new(actors));
    let queue = Arc::new(MsQueue::new());
    let stopped = Arc::new(Mutex::new(false));
    let sleep_time = Duration::from_millis(50);

    EventBus {
      actors: actors.clone(),
      queue: queue.clone(),
      stopped: stopped.clone(),
      thread: run(stopped, queue, actors)
    }
  }

  pub fn stop(&mut self) {
    debug!("stop enter");
    let mut stopped = self.stopped.lock().unwrap();
    *stopped = true;
    debug!("stop leave");
  }

  pub fn join(self) -> Result<(), E> {
    self.thread.join().unwrap()
  }

  pub fn send(&self, m: M) {
    self.queue.push(m);
  }
}

pub fn run<M, E>(stop: Arc<Mutex<bool>>, queue: Arc<MsQueue<M>>,
    actors: Arc<Mutex<Vec<Box<Actor<M, E>>>>>) -> JoinHandle<Result<(), E>>
    where M: 'static + Send + Sync + Decodable, E: 'static + Sync + Send {

  let sleep_time = Duration::from_millis(50);

  thread::spawn(move || -> Result<(), E> {

     loop {
        if let Some(m) = queue.try_pop() {
          for actor in actors.lock().unwrap().iter_mut().filter(|x| x.accept(&m)) {
            match actor.on_receive(&m) {
              Err(e) => return Err(e),
              _ => {}
            }
          }
       } else {
         thread::sleep(sleep_time);
        }

        if *stop.lock().unwrap() == true {
          break;
        }
      }

      Ok(())
  })
}

pub trait Actor<M: Sync + Send + Decodable, E: Sized + Send + Sync>: Send + Sync {
  fn accept(&self, m: &M) -> bool { true }
  fn on_receive(&mut self, m: &M) -> Result<(), E>;
}

#[cfg(test)]
mod tests {
  use std::thread;
  use std::time::Duration;

  use env_logger;
  use super::*;

  #[derive(RustcDecodable, RustcEncodable)]
  pub enum Message {
    Ask(String),
    Others
  }

  impl Message {
    pub fn ask(message: &str) -> Message {
      Message::Ask(message.to_owned())
    }
  }

  unsafe impl Send for Message {}
  unsafe impl Sync for Message {}

  pub struct TestActor;

  pub enum ActorErr {
    Err(String)
  }

  unsafe impl Send for ActorErr {}
  unsafe impl Sync for ActorErr {}

  impl Actor<Message, ActorErr> for TestActor {
    fn on_receive(&mut self, m: &Message) -> Result<(), ActorErr> {
      match *m {
        Message::Ask(ref s) => {
          debug!(">>> received: {}", s);
          Ok(())
        }
        _ => panic!("unknown message")
      }
    }
  }

  #[test]
  fn test() {
    env_logger::init().unwrap();

    let actors: Vec<Box<Actor<Message, ActorErr>>> = vec![Box::new(TestActor)];
    let mut dispatcher = EventBus::new(actors);

    let time = Duration::from_secs(5);
    dispatcher.send(Message::Ask("abc".to_owned()));
    thread::sleep(time);

    debug!("after sleep");
    dispatcher.stop();
    debug!("after stop");
    dispatcher.join();
  }
}<commit_msg>Add initial transporter trait.<commit_after>use std::sync::{Arc, Mutex};
use std::thread::{self, JoinHandle};
use std::time::Duration;
use crossbeam::sync::MsQueue;
use env_logger;
use rustc_serialize::Decodable;

//pub trait MessageTrait: 'static + Sync + Send + Decodable {}

pub trait Transporter<M: 'static + Sync + Send + Decodable, E: 'static + Sized + Sync + Send> {
  fn tx(&self, m: &M) -> Result<(), E>;
  fn rx(&self) -> Result<M, E>;
}

pub struct EventBus<M: 'static + Sync + Send + Decodable, E: 'static + Sized + Sync + Send> {
  actors: Arc<Mutex<Vec<Box<Actor<M, E>>>>>,
  queue: Arc<MsQueue<M>>,
  stopped: Arc<Mutex<bool>>,
  thread: JoinHandle<Result<(), E>>,
  txpt: Option<Arc<Box<Transporter<M, E>>>>
}

unsafe impl<M: 'static + Sync + Send + Decodable, E: 'static + Sized + Sync + Send> Sync for EventBus<M, E> {}
unsafe impl<M: 'static + Sync + Send + Decodable, E: 'static + Sized + Sync + Send> Send for EventBus<M, E> {}

pub struct NullTransporter;

impl<M: 'static + Sync + Send + Decodable, E: 'static + Sized + Sync + Send> Transporter<M, E> for NullTransporter {
  fn tx(&self, m: &M) -> Result<(), E> {
    Ok(())
  }

  fn rx(&self) -> Result<M, E> {
    panic!("NullTransporter::rx shouldn't be called!");
  }
}

impl<M: Sync + Send + Decodable, E: Sized + Sync + Send> EventBus<M, E> {

  pub fn new(actors: Vec<Box<Actor<M,E>>>) -> EventBus<M, E> {
    let actors = Arc::new(Mutex::new(actors));
    let queue = Arc::new(MsQueue::new());
    let stopped = Arc::new(Mutex::new(false));
    let sleep_time = Duration::from_millis(50);

    EventBus {
      actors: actors.clone(),
      queue: queue.clone(),
      stopped: stopped.clone(),
      thread: run(stopped, queue, actors),
      txpt: None
    }
  }

  pub fn stop(&mut self) {
    debug!("stop enter");
    let mut stopped = self.stopped.lock().unwrap();
    *stopped = true;
    debug!("stop leave");
  }

  pub fn join(self) -> Result<(), E> {
    self.thread.join().unwrap()
  }

  pub fn send(&self, m: M) {
    self.queue.push(m);
  }
}

pub fn run<M, E>(stop: Arc<Mutex<bool>>, queue: Arc<MsQueue<M>>,
    actors: Arc<Mutex<Vec<Box<Actor<M, E>>>>>) -> JoinHandle<Result<(), E>>
    where M: 'static + Send + Sync + Decodable, E: 'static + Sync + Send {

  let sleep_time = Duration::from_millis(50);

  thread::spawn(move || -> Result<(), E> {

     loop {
        if let Some(m) = queue.try_pop() {
          for actor in actors.lock().unwrap().iter_mut().filter(|x| x.accept(&m)) {
            match actor.on_receive(&m) {
              Err(e) => return Err(e),
              _ => {}
            }
          }
       } else {
         thread::sleep(sleep_time);
        }

        if *stop.lock().unwrap() == true {
          break;
        }
      }

      Ok(())
  })
}

pub trait Actor<M: Sync + Send + Decodable, E: Sized + Send + Sync>: Send + Sync {
  fn accept(&self, m: &M) -> bool { true }
  fn on_receive(&mut self, m: &M) -> Result<(), E>;
}

#[cfg(test)]
mod tests {
  use std::thread;
  use std::time::Duration;

  use env_logger;
  use super::*;

  #[derive(RustcDecodable, RustcEncodable)]
  pub enum Message {
    Ask(String),
    Others
  }

  impl Message {
    pub fn ask(message: &str) -> Message {
      Message::Ask(message.to_owned())
    }
  }

  unsafe impl Send for Message {}
  unsafe impl Sync for Message {}

  pub struct TestActor;

  pub enum ActorErr {
    Err(String)
  }

  unsafe impl Send for ActorErr {}
  unsafe impl Sync for ActorErr {}

  impl Actor<Message, ActorErr> for TestActor {
    fn on_receive(&mut self, m: &Message) -> Result<(), ActorErr> {
      match *m {
        Message::Ask(ref s) => {
          debug!(">>> received: {}", s);
          Ok(())
        }
        _ => panic!("unknown message")
      }
    }
  }

  #[test]
  fn test() {
    env_logger::init().unwrap();

    let actors: Vec<Box<Actor<Message, ActorErr>>> = vec![Box::new(TestActor)];
    let mut dispatcher = EventBus::new(actors);

    let time = Duration::from_secs(5);
    dispatcher.send(Message::Ask("abc".to_owned()));
    thread::sleep(time);

    debug!("after sleep");
    dispatcher.stop();
    debug!("after stop");
    dispatcher.join().ok().unwrap();
  }
}<|endoftext|>


<commit_before>use gl::types::*;

pub struct Renderbuffer {
    gl_id: GLuint
}

impl Renderbuffer {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}
<commit_msg>Add `Drop` impl for renderbuffer<commit_after>use gl;
use gl::types::*;

pub struct Renderbuffer {
    gl_id: GLuint
}

impl Renderbuffer {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl Drop for Renderbuffer {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteRenderbuffers(1, &self.gl_id as *const GLuint);
        }
    }
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![allow(dead_code)]

extern crate quickcheck;
#[plugin] #[no_link] extern crate quickcheck_macros;

fn reverse<T: Clone>(xs: &[T]) -> Vec<T> {
    let mut rev = vec!();
    for x in xs.iter() {
        rev.insert(0, x.clone())
    }
    rev
}

#[quickcheck]
fn double_reversal_is_identity(xs: Vec<isize>) -> bool {
    xs == reverse(reverse(xs.as_slice()).as_slice())
}

fn main() {}
<commit_msg>lingering rustup<commit_after>#![feature(core, plugin)]
#![allow(dead_code)]

extern crate quickcheck;
#[plugin] #[no_link] extern crate quickcheck_macros;

fn reverse<T: Clone>(xs: &[T]) -> Vec<T> {
    let mut rev = vec!();
    for x in xs {
        rev.insert(0, x.clone())
    }
    rev
}

#[quickcheck]
fn double_reversal_is_identity(xs: Vec<isize>) -> bool {
    xs == reverse(reverse(xs.as_slice()).as_slice())
}

fn main() {}
<|endoftext|>


<commit_before>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use diff;
use std::collections::VecDeque;
use std::io;
use term;
use utils::isatty;

#[derive(Debug, PartialEq)]
pub enum DiffLine {
    Context(String),
    Expected(String),
    Resulting(String),
}

#[derive(Debug, PartialEq)]
pub struct Mismatch {
    pub line_number: u32,
    pub lines: Vec<DiffLine>,
}

impl Mismatch {
    fn new(line_number: u32) -> Mismatch {
        Mismatch {
            line_number: line_number,
            lines: Vec::new(),
        }
    }
}

// Produces a diff between the expected output and actual output of rustfmt.
pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Mismatch> {
    let mut line_number = 1;
    let mut context_queue: VecDeque<&str> = VecDeque::with_capacity(context_size);
    let mut lines_since_mismatch = context_size + 1;
    let mut results = Vec::new();
    let mut mismatch = Mismatch::new(0);

    for result in diff::lines(expected, actual) {
        match result {
            diff::Result::Left(str) => {
                if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
                    results.push(mismatch);
                    mismatch = Mismatch::new(line_number - context_queue.len() as u32);
                }

                while let Some(line) = context_queue.pop_front() {
                    mismatch.lines.push(DiffLine::Context(line.to_owned()));
                }

                mismatch.lines.push(DiffLine::Resulting(str.to_owned()));
                lines_since_mismatch = 0;
            }
            diff::Result::Right(str) => {
                if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
                    results.push(mismatch);
                    mismatch = Mismatch::new(line_number - context_queue.len() as u32);
                }

                while let Some(line) = context_queue.pop_front() {
                    mismatch.lines.push(DiffLine::Context(line.to_owned()));
                }

                mismatch.lines.push(DiffLine::Expected(str.to_owned()));
                line_number += 1;
                lines_since_mismatch = 0;
            }
            diff::Result::Both(str, _) => {
                if context_queue.len() >= context_size {
                    let _ = context_queue.pop_front();
                }

                if lines_since_mismatch < context_size {
                    mismatch.lines.push(DiffLine::Context(str.to_owned()));
                } else if context_size > 0 {
                    context_queue.push_back(str);
                }

                line_number += 1;
                lines_since_mismatch += 1;
            }
        }
    }

    results.push(mismatch);
    results.remove(0);

    results
}

pub fn print_diff<F>(diff: Vec<Mismatch>, get_section_title: F)
where
    F: Fn(u32) -> String,
{
    match term::stdout() {
        Some(ref t) if isatty() && t.supports_color() => {
            print_diff_fancy(diff, get_section_title, term::stdout().unwrap())
        }
        _ => print_diff_basic(diff, get_section_title),
    }
}

fn print_diff_fancy<F>(
    diff: Vec<Mismatch>,
    get_section_title: F,
    mut t: Box<term::Terminal<Output = io::Stdout>>,
) where
    F: Fn(u32) -> String,
{
    for mismatch in diff {
        let title = get_section_title(mismatch.line_number);
        writeln!(t, "{}", title).unwrap();

        for line in mismatch.lines {
            match line {
                DiffLine::Context(ref str) => {
                    t.reset().unwrap();
                    writeln!(t, " {}⏎", str).unwrap();
                }
                DiffLine::Expected(ref str) => {
                    t.fg(term::color::GREEN).unwrap();
                    writeln!(t, "+{}⏎", str).unwrap();
                }
                DiffLine::Resulting(ref str) => {
                    t.fg(term::color::RED).unwrap();
                    writeln!(t, "-{}⏎", str).unwrap();
                }
            }
        }
        t.reset().unwrap();
    }
}

pub fn print_diff_basic<F>(diff: Vec<Mismatch>, get_section_title: F)
where
    F: Fn(u32) -> String,
{
    for mismatch in diff {
        let title = get_section_title(mismatch.line_number);
        println!("{}", title);

        for line in mismatch.lines {
            match line {
                DiffLine::Context(ref str) => {
                    println!(" {}⏎", str);
                }
                DiffLine::Expected(ref str) => {
                    println!("+{}⏎", str);
                }
                DiffLine::Resulting(ref str) => {
                    println!("-{}⏎", str);
                }
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::{make_diff,Mismatch};
    use super::DiffLine::*;

    #[test]
    fn diff_simple() {
        let src = "one\ntwo\nthree\nfour\nfive\n";
        let dest= "one\ntwo\ntrois\nfour\nfive\n";
        let diff = make_diff(src, dest, 1);
        assert_eq!(diff, vec![Mismatch { line_number: 2,
                                         lines: vec![
                                             Context("two".into()),
                                             Resulting("three".into()),
                                             Expected("trois".into()),
                                             Context("four".into()),
                                         ] }]);
    }

    #[test]
    fn diff_simple2() {
        let src = "one\ntwo\nthree\nfour\nfive\nsix\nseven\n";
        let dest= "one\ntwo\ntrois\nfour\ncinq\nsix\nseven\n";
        let diff = make_diff(src, dest, 1);
        assert_eq!(diff, vec![Mismatch { line_number: 2,
                                         lines: vec![
                                             Context("two".into()),
                                             Resulting("three".into()),
                                             Expected("trois".into()),
                                             Context("four".into()),
                                         ] },
                              Mismatch { line_number: 5,
                                         lines: vec![
                                             Resulting("five".into()),
                                             Expected("cinq".into()),
                                             Context("six".into()),
                                         ] }]);
    }

    #[test]
    fn diff_zerocontext() {
        let src = "one\ntwo\nthree\nfour\nfive\n";
        let dest= "one\ntwo\ntrois\nfour\nfive\n";
        let diff = make_diff(src, dest, 0);
        assert_eq!(diff, vec![Mismatch { line_number: 3,
                                         lines: vec![
                                             Resulting("three".into()),
                                             Expected("trois".into()),
                                         ] }]);
    }
}<commit_msg>Run rustfmt on the new changes.<commit_after>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use diff;
use std::collections::VecDeque;
use std::io;
use term;
use utils::isatty;

#[derive(Debug, PartialEq)]
pub enum DiffLine {
    Context(String),
    Expected(String),
    Resulting(String),
}

#[derive(Debug, PartialEq)]
pub struct Mismatch {
    pub line_number: u32,
    pub lines: Vec<DiffLine>,
}

impl Mismatch {
    fn new(line_number: u32) -> Mismatch {
        Mismatch {
            line_number: line_number,
            lines: Vec::new(),
        }
    }
}

// Produces a diff between the expected output and actual output of rustfmt.
pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Mismatch> {
    let mut line_number = 1;
    let mut context_queue: VecDeque<&str> = VecDeque::with_capacity(context_size);
    let mut lines_since_mismatch = context_size + 1;
    let mut results = Vec::new();
    let mut mismatch = Mismatch::new(0);

    for result in diff::lines(expected, actual) {
        match result {
            diff::Result::Left(str) => {
                if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
                    results.push(mismatch);
                    mismatch = Mismatch::new(line_number - context_queue.len() as u32);
                }

                while let Some(line) = context_queue.pop_front() {
                    mismatch.lines.push(DiffLine::Context(line.to_owned()));
                }

                mismatch.lines.push(DiffLine::Resulting(str.to_owned()));
                lines_since_mismatch = 0;
            }
            diff::Result::Right(str) => {
                if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
                    results.push(mismatch);
                    mismatch = Mismatch::new(line_number - context_queue.len() as u32);
                }

                while let Some(line) = context_queue.pop_front() {
                    mismatch.lines.push(DiffLine::Context(line.to_owned()));
                }

                mismatch.lines.push(DiffLine::Expected(str.to_owned()));
                line_number += 1;
                lines_since_mismatch = 0;
            }
            diff::Result::Both(str, _) => {
                if context_queue.len() >= context_size {
                    let _ = context_queue.pop_front();
                }

                if lines_since_mismatch < context_size {
                    mismatch.lines.push(DiffLine::Context(str.to_owned()));
                } else if context_size > 0 {
                    context_queue.push_back(str);
                }

                line_number += 1;
                lines_since_mismatch += 1;
            }
        }
    }

    results.push(mismatch);
    results.remove(0);

    results
}

pub fn print_diff<F>(diff: Vec<Mismatch>, get_section_title: F)
where
    F: Fn(u32) -> String,
{
    match term::stdout() {
        Some(ref t) if isatty() && t.supports_color() => {
            print_diff_fancy(diff, get_section_title, term::stdout().unwrap())
        }
        _ => print_diff_basic(diff, get_section_title),
    }
}

fn print_diff_fancy<F>(
    diff: Vec<Mismatch>,
    get_section_title: F,
    mut t: Box<term::Terminal<Output = io::Stdout>>,
) where
    F: Fn(u32) -> String,
{
    for mismatch in diff {
        let title = get_section_title(mismatch.line_number);
        writeln!(t, "{}", title).unwrap();

        for line in mismatch.lines {
            match line {
                DiffLine::Context(ref str) => {
                    t.reset().unwrap();
                    writeln!(t, " {}⏎", str).unwrap();
                }
                DiffLine::Expected(ref str) => {
                    t.fg(term::color::GREEN).unwrap();
                    writeln!(t, "+{}⏎", str).unwrap();
                }
                DiffLine::Resulting(ref str) => {
                    t.fg(term::color::RED).unwrap();
                    writeln!(t, "-{}⏎", str).unwrap();
                }
            }
        }
        t.reset().unwrap();
    }
}

pub fn print_diff_basic<F>(diff: Vec<Mismatch>, get_section_title: F)
where
    F: Fn(u32) -> String,
{
    for mismatch in diff {
        let title = get_section_title(mismatch.line_number);
        println!("{}", title);

        for line in mismatch.lines {
            match line {
                DiffLine::Context(ref str) => {
                    println!(" {}⏎", str);
                }
                DiffLine::Expected(ref str) => {
                    println!("+{}⏎", str);
                }
                DiffLine::Resulting(ref str) => {
                    println!("-{}⏎", str);
                }
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::{make_diff, Mismatch};
    use super::DiffLine::*;

    #[test]
    fn diff_simple() {
        let src = "one\ntwo\nthree\nfour\nfive\n";
        let dest = "one\ntwo\ntrois\nfour\nfive\n";
        let diff = make_diff(src, dest, 1);
        assert_eq!(
            diff,
            vec![
                Mismatch {
                    line_number: 2,
                    lines: vec![
                        Context("two".into()),
                        Resulting("three".into()),
                        Expected("trois".into()),
                        Context("four".into()),
                    ],
                },
            ]
        );
    }

    #[test]
    fn diff_simple2() {
        let src = "one\ntwo\nthree\nfour\nfive\nsix\nseven\n";
        let dest = "one\ntwo\ntrois\nfour\ncinq\nsix\nseven\n";
        let diff = make_diff(src, dest, 1);
        assert_eq!(
            diff,
            vec![
                Mismatch {
                    line_number: 2,
                    lines: vec![
                        Context("two".into()),
                        Resulting("three".into()),
                        Expected("trois".into()),
                        Context("four".into()),
                    ],
                },
                Mismatch {
                    line_number: 5,
                    lines: vec![
                        Resulting("five".into()),
                        Expected("cinq".into()),
                        Context("six".into()),
                    ],
                },
            ]
        );
    }

    #[test]
    fn diff_zerocontext() {
        let src = "one\ntwo\nthree\nfour\nfive\n";
        let dest = "one\ntwo\ntrois\nfour\nfive\n";
        let diff = make_diff(src, dest, 0);
        assert_eq!(
            diff,
            vec![
                Mismatch {
                    line_number: 3,
                    lines: vec![Resulting("three".into()), Expected("trois".into())],
                },
            ]
        );
    }
}
<|endoftext|>


<commit_before>//! Contains the binary logic of Ion.

use super::{DirectoryStack, FlowLogic, JobControl, Shell, ShellHistory, Variables};
use super::completer::*;
use super::flow_control::Statement;
use super::library::IonLibrary;
use super::status::*;
use liner::{BasicCompleter, Buffer, Context, CursorPosition, Event, EventKind};
use parser::*;
use parser::QuoteTerminator;
use smallstring::SmallString;
use smallvec::SmallVec;
use std::env;
use std::fs::File;
use std::io::{self, ErrorKind, Read, Write};
use std::iter::{self, FromIterator};
use std::mem;
use std::os::unix::io::FromRawFd;
use std::path::{Path, PathBuf};
use std::process;
use std::process::exit;
use sys;
use types::*;

pub(crate) trait Binary {
    /// Launches the shell, parses arguments, and then diverges into one of the `execution`
    /// paths.
    fn main(self);
    /// Parses and executes the arguments that were supplied to the shell.
    fn execute_arguments<A: Iterator<Item = String>>(&mut self, args: A);
    /// Creates an interactive session that reads from a prompt provided by Liner.
    fn execute_interactive(self);
    /// Ensures that read statements from a script are terminated.
    fn terminate_script_quotes<I: Iterator<Item = String>>(&mut self, lines: I);
    /// Ensures that read statements from the interactive prompt is terminated.
    fn terminate_quotes(&mut self, command: String) -> Result<String, ()>;
    /// Ion's interface to Liner's `read_line` method, which handles everything related to
    /// rendering, controlling, and getting input from the prompt.
    fn readln(&mut self) -> Option<String>;
    /// Generates the prompt that will be used by Liner.
    fn prompt(&mut self) -> String;
    /// Display version information and exit
    fn display_version(&self);
    // Executes the PROMPT function, if it exists, and returns the output.
    fn prompt_fn(&mut self) -> Option<String>;
}

impl<'a> Binary for Shell<'a> {
    fn prompt(&mut self) -> String {
        if self.flow_control.level == 0 {
            let rprompt = match self.prompt_fn() {
                Some(prompt) => prompt,
                None => self.variables.get_var_or_empty("PROMPT"),
            };
            expand_string(&rprompt, self, false).join(" ")
        } else {
            "    ".repeat(self.flow_control.level as usize)
        }
    }

    fn prompt_fn(&mut self) -> Option<String> {
        let function = match self.functions.get("PROMPT") {
            Some(func) => func.clone(),
            None => return None,
        };

        let (read_fd, write_fd) = match sys::pipe2(0) {
            Ok(fds) => fds,
            Err(why) => {
                eprintln!("ion: unable to create pipe: {}", why);
                return None;
            }
        };

        match unsafe { sys::fork() } {
            Ok(0) => {
                let _ = sys::dup2(write_fd, sys::STDOUT_FILENO);
                let _ = sys::close(read_fd);
                let _ = sys::close(write_fd);
                let _ = function.execute(self, &["ion"]);
                exit(0);
            }
            Ok(_) => {
                let _ = sys::close(write_fd);
                let mut child_stdout = unsafe { File::from_raw_fd(read_fd) };
                let mut output = String::new();
                let _ = child_stdout.read_to_string(&mut output);
                Some(output)
            }
            Err(why) => {
                let _ = sys::close(read_fd);
                let _ = sys::close(write_fd);
                eprintln!("ion: fork error: {}", why);
                None
            }
        }
    }

    fn readln(&mut self) -> Option<String> {
        {
            let vars_ptr = &self.variables as *const Variables;
            let dirs_ptr = &self.directory_stack as *const DirectoryStack;

            // Collects the current list of values from history for completion.
            let history = &self.context.as_ref().unwrap().history.buffers.iter()
                // Map each underlying `liner::Buffer` into a `String`.
                .map(|x| x.chars().cloned().collect())
                // Collect each result into a vector to avoid borrowing issues.
                .collect::<Vec<SmallString>>();

            loop {
                let prompt = self.prompt();
                let funcs = &self.functions;
                let vars = &self.variables;
                let builtins = self.builtins;

                let line = self.context.as_mut().unwrap().read_line(
                    prompt,
                    &mut move |Event {
                                   editor,
                                   kind,
                               }| {
                        if let EventKind::BeforeComplete = kind {
                            let (words, pos) = editor.get_words_and_cursor_position();

                            let filename = match pos {
                                CursorPosition::InWord(index) => index > 0,
                                CursorPosition::InSpace(Some(_), _) => true,
                                CursorPosition::InSpace(None, _) => false,
                                CursorPosition::OnWordLeftEdge(index) => index >= 1,
                                CursorPosition::OnWordRightEdge(index) => {
                                    match (words.into_iter().nth(index), env::current_dir()) {
                                        (Some((start, end)), Ok(file)) => {
                                            let filename = editor.current_buffer().range(start, end);
                                            complete_as_file(file, filename, index)
                                        }
                                        _ => false,
                                    }
                                }
                            };

                            if filename {
                                if let Ok(current_dir) = env::current_dir() {
                                    if let Some(url) = current_dir.to_str() {
                                        let completer = IonFileCompleter::new(Some(url), dirs_ptr, vars_ptr);
                                        mem::replace(&mut editor.context().completer, Some(Box::new(completer)));
                                    }
                                }
                            } else {
                                // Creates a list of definitions from the shell environment that will be used
                                // in the creation of a custom completer.
                                let words = builtins.iter()
                                // Add built-in commands to the completer's definitions.
                                .map(|(&s, _)| Identifier::from(s))
                                // Add the history list to the completer's definitions.
                                .chain(history.iter().cloned())
                                // Add the aliases to the completer's definitions.
                                .chain(vars.aliases.keys().cloned())
                                // Add the list of available functions to the completer's definitions.
                                .chain(funcs.keys().cloned())
                                // Add the list of available variables to the completer's definitions.
                                // TODO: We should make it free to do String->SmallString
                                //       and mostly free to go back (free if allocated)
                                .chain(vars.get_vars().into_iter().map(|s| ["$", &s].concat().into()))
                                .collect();

                                // Initialize a new completer from the definitions collected.
                                let custom_completer = BasicCompleter::new(words);

                                // Creates completers containing definitions from all directories listed
                                // in the environment's **$PATH** variable.
                                let mut file_completers = if let Ok(val) = env::var("PATH") {
                                    val.split(sys::PATH_SEPARATOR)
                                        .map(|s| IonFileCompleter::new(Some(s), dirs_ptr, vars_ptr))
                                        .collect()
                                } else {
                                    vec![IonFileCompleter::new(Some("/bin/"), dirs_ptr, vars_ptr)]
                                };

                                // Also add files/directories in the current directory to the completion list.
                                if let Ok(current_dir) = env::current_dir() {
                                    if let Some(url) = current_dir.to_str() {
                                        file_completers.push(IonFileCompleter::new(Some(url), dirs_ptr, vars_ptr));
                                    }
                                }

                                // Merge the collected definitions with the file path definitions.
                                let completer = MultiCompleter::new(file_completers, custom_completer);

                                // Replace the shell's current completer with the newly-created completer.
                                mem::replace(&mut editor.context().completer, Some(Box::new(completer)));
                            }
                        }
                    },
                );

                match line {
                    Ok(line) => return Some(line),
                    // Handles Ctrl + C
                    Err(ref err) if err.kind() == ErrorKind::Interrupted => return None,
                    // Handles Ctrl + D
                    Err(ref err) if err.kind() == ErrorKind::UnexpectedEof => break,
                    Err(err) => {
                        let stderr = io::stderr();
                        let mut stderr = stderr.lock();
                        let _ = writeln!(stderr, "ion: liner: {}", err);
                        return None;
                    }
                }
            }
        }

        let previous_status = self.previous_status;
        self.exit(previous_status);
    }

    fn terminate_script_quotes<I: Iterator<Item = String>>(&mut self, mut lines: I) {
        while let Some(command) = lines.next() {
            let mut buffer = QuoteTerminator::new(command);
            while !buffer.check_termination() {
                loop {
                    if let Some(command) = lines.next() {
                        buffer.append(command);
                        break;
                    } else {
                        let stderr = io::stderr();
                        let _ = writeln!(stderr.lock(), "ion: unterminated quote in script");
                        self.exit(FAILURE);
                    }
                }
            }
            self.on_command(&buffer.consume());
        }
        // The flow control level being non zero means that we have a statement that has
        // only been partially parsed.
        if self.flow_control.level != 0 {
            eprintln!(
                "ion: unexpected end of script: expected end block for `{}`",
                self.flow_control.current_statement.short()
            );
        }
    }

    fn terminate_quotes(&mut self, command: String) -> Result<String, ()> {
        let mut buffer = QuoteTerminator::new(command);
        self.flow_control.level += 1;
        while !buffer.check_termination() {
            loop {
                if let Some(command) = self.readln() {
                    buffer.append(command);
                    break;
                } else {
                    return Err(());
                }
            }
        }
        self.flow_control.level -= 1;
        Ok(buffer.consume())
    }

    fn execute_arguments<A: Iterator<Item = String>>(&mut self, mut args: A) {
        if let Some(mut arg) = args.next() {
            for argument in args {
                arg.push(' ');
                if argument == "" {
                    arg.push_str("''");
                } else {
                    arg.push_str(&argument);
                }
            }
            self.on_command(&arg);
        } else {
            let stderr = io::stderr();
            let mut stderr = stderr.lock();
            let _ = writeln!(stderr, "ion: -c requires an argument");
            self.exit(FAILURE);
        }
    }

    fn execute_interactive(mut self) {
        self.context = Some({
            let mut context = Context::new();
            context.word_divider_fn = Box::new(word_divide);
            if "1" == self.variables.get_var_or_empty("HISTFILE_ENABLED") {
                let path = self.variables.get_var("HISTFILE").expect("shell didn't set HISTFILE");
                context.history.set_file_name(Some(path.clone()));
                if !Path::new(path.as_str()).exists() {
                    eprintln!("ion: creating history file at \"{}\"", path);
                    if let Err(why) = File::create(path) {
                        eprintln!("ion: could not create history file: {}", why);
                    }
                }
                match context.history.load_history() {
                    Ok(()) => {
                        // pass
                    }
                    Err(ref err) if err.kind() == ErrorKind::NotFound => {
                        let history_filename = self.variables.get_var_or_empty("HISTFILE");
                        eprintln!("ion: failed to find history file {}: {}", history_filename, err);
                    }
                    Err(err) => {
                        eprintln!("ion: failed to load history: {}", err);
                    }
                }
            }
            context
        });

        self.evaluate_init_file();

        self.variables.set_array("args", iter::once(env::args().next().unwrap()).collect());

        loop {
            if let Some(command) = self.readln() {
                if !command.is_empty() {
                    if let Ok(command) = self.terminate_quotes(command) {
                        // Parse and potentially execute the command.
                        self.on_command(command.trim());
                        // save command in history (or maybe not, depending on HISTORY_IGNORE)
                        self.save_command_in_history(&command);
                    } else {
                        self.flow_control.level = 0;
                        self.flow_control.current_if_mode = 0;
                        self.flow_control.current_statement = Statement::Default;
                    }
                }
                self.update_variables();
            } else {
                self.flow_control.level = 0;
                self.flow_control.current_if_mode = 0;
                self.flow_control.current_statement = Statement::Default;
            }
        }
    }

    fn main(mut self) {
        let mut args = env::args().skip(1);
        if let Some(path) = args.next() {
            match path.as_str() {
                "-c" => self.execute_arguments(args),
                "--version" => self.display_version(),
                _ => {
                    let mut array = SmallVec::from_iter(Some(path.clone().into()));
                    for arg in args {
                        array.push(arg.into());
                    }
                    self.variables.set_array("args", array);
                    if let Err(err) = self.execute_script(&path) {
                        eprintln!("ion: {}", err);
                    }
                }
            }

            self.wait_for_background();
            let previous_status = self.previous_status;
            self.exit(previous_status);
        } else {
            self.execute_interactive();
        }
    }

    fn display_version(&self) {
        println!("{}", include!(concat!(env!("OUT_DIR"), "/version_string")));
        process::exit(0);
    }
}

fn word_divide(buf: &Buffer) -> Vec<(usize, usize)> {
    let mut res = Vec::new();
    let mut word_start = None;

    macro_rules! check_boundary {
        ($c:expr, $index:expr, $escaped:expr) => {{
            if let Some(start) = word_start {
                if $c == ' ' && !$escaped {
                    res.push((start, $index));
                    word_start = None;
                }
            } else {
                if $c != ' ' {
                    word_start = Some($index);
                }
            }
        }}
    }

    let mut iter = buf.chars().enumerate();
    while let Some((i, &c)) = iter.next() {
        match c {
            '\\' => {
                if let Some((_, &cnext)) = iter.next() {
                    // We use `i` in order to include the backslash as part of the word
                    check_boundary!(cnext, i, true);
                }
            }
            c => check_boundary!(c, i, false),
        }
    }
    if let Some(start) = word_start {
        // When start has been set, that means we have encountered a full word.
        res.push((start, buf.num_chars()));
    }
    res
}

/// Infer if the given filename is actually a partial filename
fn complete_as_file(current_dir: PathBuf, filename: String, index: usize) -> bool {
    let filename = filename.trim();
    let mut file = current_dir.clone();
    file.push(&filename);
    // If the user explicitly requests a file through this syntax then complete as a file
    if filename.trim().starts_with(".") {
        return true;
    }
    // If the file starts with a dollar sign, it's a variable, not a file
    if filename.trim().starts_with("$") {
        return false;
    }
    // Once we are beyond the first string, assume its a file
    if index > 0 {
        return true;
    }
    // If we are referencing a file that exists then just complete to that file
    if file.exists() {
        return true;
    }
    // If we have a partial file inside an existing directory, e.g. /foo/b when /foo/bar
    // exists, then treat it as file as long as `foo` isn't the current directory, otherwise
    // this would apply to any string `foo`
    if let Some(parent) = file.parent() {
        return parent.exists() && parent != current_dir;
    }
    // By default assume its not a file
    false
}
<commit_msg>Fix #544 Closes #544<commit_after>//! Contains the binary logic of Ion.

use super::{DirectoryStack, FlowLogic, JobControl, Shell, ShellHistory, Variables};
use super::completer::*;
use super::flow_control::Statement;
use super::library::IonLibrary;
use super::status::*;
use liner::{BasicCompleter, Buffer, Context, CursorPosition, Event, EventKind};
use parser::*;
use parser::QuoteTerminator;
use smallstring::SmallString;
use smallvec::SmallVec;
use std::env;
use std::fs::File;
use std::io::{self, ErrorKind, Read, Write};
use std::iter::{self, FromIterator};
use std::mem;
use std::os::unix::io::FromRawFd;
use std::path::{Path, PathBuf};
use std::process;
use std::process::exit;
use sys;
use types::*;

pub(crate) trait Binary {
    /// Launches the shell, parses arguments, and then diverges into one of the `execution`
    /// paths.
    fn main(self);
    /// Parses and executes the arguments that were supplied to the shell.
    fn execute_arguments<A: Iterator<Item = String>>(&mut self, args: A);
    /// Creates an interactive session that reads from a prompt provided by Liner.
    fn execute_interactive(self);
    /// Ensures that read statements from a script are terminated.
    fn terminate_script_quotes<I: Iterator<Item = String>>(&mut self, lines: I);
    /// Ensures that read statements from the interactive prompt is terminated.
    fn terminate_quotes(&mut self, command: String) -> Result<String, ()>;
    /// Ion's interface to Liner's `read_line` method, which handles everything related to
    /// rendering, controlling, and getting input from the prompt.
    fn readln(&mut self) -> Option<String>;
    /// Generates the prompt that will be used by Liner.
    fn prompt(&mut self) -> String;
    /// Display version information and exit
    fn display_version(&self);
    // Executes the PROMPT function, if it exists, and returns the output.
    fn prompt_fn(&mut self) -> Option<String>;
}

impl<'a> Binary for Shell<'a> {
    fn prompt(&mut self) -> String {
        if self.flow_control.level == 0 {
            let rprompt = match self.prompt_fn() {
                Some(prompt) => prompt,
                None => self.variables.get_var_or_empty("PROMPT"),
            };
            expand_string(&rprompt, self, false).join(" ")
        } else {
            "    ".repeat(self.flow_control.level as usize)
        }
    }

    fn prompt_fn(&mut self) -> Option<String> {
        let function = match self.functions.get("PROMPT") {
            Some(func) => func.clone(),
            None => return None,
        };

        let (read_fd, write_fd) = match sys::pipe2(0) {
            Ok(fds) => fds,
            Err(why) => {
                eprintln!("ion: unable to create pipe: {}", why);
                return None;
            }
        };

        match unsafe { sys::fork() } {
            Ok(0) => {
                let _ = sys::dup2(write_fd, sys::STDOUT_FILENO);
                let _ = sys::close(read_fd);
                let _ = sys::close(write_fd);
                let _ = function.execute(self, &["ion"]);
                exit(0);
            }
            Ok(_) => {
                let _ = sys::close(write_fd);
                let mut child_stdout = unsafe { File::from_raw_fd(read_fd) };
                let mut output = String::new();
                let _ = child_stdout.read_to_string(&mut output);
                Some(output)
            }
            Err(why) => {
                let _ = sys::close(read_fd);
                let _ = sys::close(write_fd);
                eprintln!("ion: fork error: {}", why);
                None
            }
        }
    }

    fn readln(&mut self) -> Option<String> {
        {
            let vars_ptr = &self.variables as *const Variables;
            let dirs_ptr = &self.directory_stack as *const DirectoryStack;

            // Collects the current list of values from history for completion.
            let history = &self.context.as_ref().unwrap().history.buffers.iter()
                // Map each underlying `liner::Buffer` into a `String`.
                .map(|x| x.chars().cloned().collect())
                // Collect each result into a vector to avoid borrowing issues.
                .collect::<Vec<SmallString>>();

            loop {
                let prompt = self.prompt();
                let funcs = &self.functions;
                let vars = &self.variables;
                let builtins = self.builtins;

                let line = self.context.as_mut().unwrap().read_line(
                    prompt,
                    &mut move |Event {
                                   editor,
                                   kind,
                               }| {
                        if let EventKind::BeforeComplete = kind {
                            let (words, pos) = editor.get_words_and_cursor_position();

                            let filename = match pos {
                                CursorPosition::InWord(index) => index > 0,
                                CursorPosition::InSpace(Some(_), _) => true,
                                CursorPosition::InSpace(None, _) => false,
                                CursorPosition::OnWordLeftEdge(index) => index >= 1,
                                CursorPosition::OnWordRightEdge(index) => {
                                    match (words.into_iter().nth(index), env::current_dir()) {
                                        (Some((start, end)), Ok(file)) => {
                                            let filename = editor.current_buffer().range(start, end);
                                            complete_as_file(file, filename, index)
                                        }
                                        _ => false,
                                    }
                                }
                            };

                            if filename {
                                if let Ok(current_dir) = env::current_dir() {
                                    if let Some(url) = current_dir.to_str() {
                                        let completer = IonFileCompleter::new(Some(url), dirs_ptr, vars_ptr);
                                        mem::replace(&mut editor.context().completer, Some(Box::new(completer)));
                                    }
                                }
                            } else {
                                // Creates a list of definitions from the shell environment that will be used
                                // in the creation of a custom completer.
                                let words = builtins.iter()
                                // Add built-in commands to the completer's definitions.
                                .map(|(&s, _)| Identifier::from(s))
                                // Add the history list to the completer's definitions.
                                .chain(history.iter().cloned())
                                // Add the aliases to the completer's definitions.
                                .chain(vars.aliases.keys().cloned())
                                // Add the list of available functions to the completer's definitions.
                                .chain(funcs.keys().cloned())
                                // Add the list of available variables to the completer's definitions.
                                // TODO: We should make it free to do String->SmallString
                                //       and mostly free to go back (free if allocated)
                                .chain(vars.get_vars().into_iter().map(|s| ["$", &s].concat().into()))
                                .collect();

                                // Initialize a new completer from the definitions collected.
                                let custom_completer = BasicCompleter::new(words);

                                // Creates completers containing definitions from all directories listed
                                // in the environment's **$PATH** variable.
                                let mut file_completers = if let Ok(val) = env::var("PATH") {
                                    val.split(sys::PATH_SEPARATOR)
                                        .map(|s| IonFileCompleter::new(Some(s), dirs_ptr, vars_ptr))
                                        .collect()
                                } else {
                                    vec![IonFileCompleter::new(Some("/bin/"), dirs_ptr, vars_ptr)]
                                };

                                // Also add files/directories in the current directory to the completion list.
                                if let Ok(current_dir) = env::current_dir() {
                                    if let Some(url) = current_dir.to_str() {
                                        file_completers.push(IonFileCompleter::new(Some(url), dirs_ptr, vars_ptr));
                                    }
                                }

                                // Merge the collected definitions with the file path definitions.
                                let completer = MultiCompleter::new(file_completers, custom_completer);

                                // Replace the shell's current completer with the newly-created completer.
                                mem::replace(&mut editor.context().completer, Some(Box::new(completer)));
                            }
                        }
                    },
                );

                match line {
                    Ok(line) => return Some(line),
                    // Handles Ctrl + C
                    Err(ref err) if err.kind() == ErrorKind::Interrupted => return None,
                    // Handles Ctrl + D
                    Err(ref err) if err.kind() == ErrorKind::UnexpectedEof => break,
                    Err(err) => {
                        let stderr = io::stderr();
                        let mut stderr = stderr.lock();
                        let _ = writeln!(stderr, "ion: liner: {}", err);
                        return None;
                    }
                }
            }
        }

        let previous_status = self.previous_status;
        self.exit(previous_status);
    }

    fn terminate_script_quotes<I: Iterator<Item = String>>(&mut self, mut lines: I) {
        while let Some(command) = lines.next() {
            let mut buffer = QuoteTerminator::new(command);
            while !buffer.check_termination() {
                loop {
                    if let Some(command) = lines.next() {
                        buffer.append(command);
                        break;
                    } else {
                        let stderr = io::stderr();
                        let _ = writeln!(stderr.lock(), "ion: unterminated quote in script");
                        self.exit(FAILURE);
                    }
                }
            }
            self.on_command(&buffer.consume());
        }
        // The flow control level being non zero means that we have a statement that has
        // only been partially parsed.
        if self.flow_control.level != 0 {
            eprintln!(
                "ion: unexpected end of script: expected end block for `{}`",
                self.flow_control.current_statement.short()
            );
        }
    }

    fn terminate_quotes(&mut self, command: String) -> Result<String, ()> {
        let mut buffer = QuoteTerminator::new(command);
        self.flow_control.level += 1;
        while !buffer.check_termination() {
            loop {
                if let Some(command) = self.readln() {
                    buffer.append(command);
                    break;
                } else {
                    return Err(());
                }
            }
        }
        self.flow_control.level -= 1;
        Ok(buffer.consume())
    }

    fn execute_arguments<A: Iterator<Item = String>>(&mut self, mut args: A) {
        if let Some(mut arg) = args.next() {
            for argument in args {
                arg.push(' ');
                if argument == "" {
                    arg.push_str("''");
                } else {
                    arg.push_str(&argument);
                }
            }
            self.on_command(&arg);
        } else {
            let stderr = io::stderr();
            let mut stderr = stderr.lock();
            let _ = writeln!(stderr, "ion: -c requires an argument");
            self.exit(FAILURE);
        }
    }

    fn execute_interactive(mut self) {
        self.context = Some({
            let mut context = Context::new();
            context.word_divider_fn = Box::new(word_divide);
            if "1" == self.variables.get_var_or_empty("HISTFILE_ENABLED") {
                let path = self.variables.get_var("HISTFILE").expect("shell didn't set HISTFILE");
                context.history.set_file_name(Some(path.clone()));
                if !Path::new(path.as_str()).exists() {
                    eprintln!("ion: creating history file at \"{}\"", path);
                    if let Err(why) = File::create(path) {
                        eprintln!("ion: could not create history file: {}", why);
                    }
                }
                match context.history.load_history() {
                    Ok(()) => {
                        // pass
                    }
                    Err(ref err) if err.kind() == ErrorKind::NotFound => {
                        let history_filename = self.variables.get_var_or_empty("HISTFILE");
                        eprintln!("ion: failed to find history file {}: {}", history_filename, err);
                    }
                    Err(err) => {
                        eprintln!("ion: failed to load history: {}", err);
                    }
                }
            }
            context
        });

        self.evaluate_init_file();

        self.variables.set_array("args", iter::once(env::args().next().unwrap()).collect());

        loop {
            if let Some(command) = self.readln() {
                if !command.is_empty() {
                    if let Ok(command) = self.terminate_quotes(command) {
                        let cmd = command.trim();
                        self.on_command(cmd);
                        match self.variables.tilde_expansion(cmd, &self.directory_stack) {
                            Some(ref cmd) if Path::new(cmd).is_dir() & !cmd.ends_with('/') => {
                                self.save_command_in_history(&[cmd, "/"].concat());
                            },
                            None if Path::new(cmd).is_dir() & !cmd.ends_with('/') => {
                                self.save_command_in_history(&[cmd, "/"].concat());
                            }
                            _ => self.save_command_in_history(cmd)
                        }
                    } else {
                        self.flow_control.level = 0;
                        self.flow_control.current_if_mode = 0;
                        self.flow_control.current_statement = Statement::Default;
                    }
                }
                self.update_variables();
            } else {
                self.flow_control.level = 0;
                self.flow_control.current_if_mode = 0;
                self.flow_control.current_statement = Statement::Default;
            }
        }
    }

    fn main(mut self) {
        let mut args = env::args().skip(1);
        if let Some(path) = args.next() {
            match path.as_str() {
                "-c" => self.execute_arguments(args),
                "--version" => self.display_version(),
                _ => {
                    let mut array = SmallVec::from_iter(Some(path.clone().into()));
                    for arg in args {
                        array.push(arg.into());
                    }
                    self.variables.set_array("args", array);
                    if let Err(err) = self.execute_script(&path) {
                        eprintln!("ion: {}", err);
                    }
                }
            }

            self.wait_for_background();
            let previous_status = self.previous_status;
            self.exit(previous_status);
        } else {
            self.execute_interactive();
        }
    }

    fn display_version(&self) {
        println!("{}", include!(concat!(env!("OUT_DIR"), "/version_string")));
        process::exit(0);
    }
}

fn word_divide(buf: &Buffer) -> Vec<(usize, usize)> {
    let mut res = Vec::new();
    let mut word_start = None;

    macro_rules! check_boundary {
        ($c:expr, $index:expr, $escaped:expr) => {{
            if let Some(start) = word_start {
                if $c == ' ' && !$escaped {
                    res.push((start, $index));
                    word_start = None;
                }
            } else {
                if $c != ' ' {
                    word_start = Some($index);
                }
            }
        }}
    }

    let mut iter = buf.chars().enumerate();
    while let Some((i, &c)) = iter.next() {
        match c {
            '\\' => {
                if let Some((_, &cnext)) = iter.next() {
                    // We use `i` in order to include the backslash as part of the word
                    check_boundary!(cnext, i, true);
                }
            }
            c => check_boundary!(c, i, false),
        }
    }
    if let Some(start) = word_start {
        // When start has been set, that means we have encountered a full word.
        res.push((start, buf.num_chars()));
    }
    res
}

/// Infer if the given filename is actually a partial filename
fn complete_as_file(current_dir: PathBuf, filename: String, index: usize) -> bool {
    let filename = filename.trim();
    let mut file = current_dir.clone();
    file.push(&filename);
    // If the user explicitly requests a file through this syntax then complete as a file
    if filename.trim().starts_with(".") {
        return true;
    }
    // If the file starts with a dollar sign, it's a variable, not a file
    if filename.trim().starts_with("$") {
        return false;
    }
    // Once we are beyond the first string, assume its a file
    if index > 0 {
        return true;
    }
    // If we are referencing a file that exists then just complete to that file
    if file.exists() {
        return true;
    }
    // If we have a partial file inside an existing directory, e.g. /foo/b when /foo/bar
    // exists, then treat it as file as long as `foo` isn't the current directory, otherwise
    // this would apply to any string `foo`
    if let Some(parent) = file.parent() {
        return parent.exists() && parent != current_dir;
    }
    // By default assume its not a file
    false
}
<|endoftext|>