<commit_before>use crate::ignore::{Ignore, IgnoreGit, IgnoreVcs};
use crate::pipeline::{PipelineFork, PipelineInfo};
use crossbeam_channel::{Receiver, Sender};
use std::fs;
use std::path::PathBuf;
use std::time::{Duration, Instant};

// ---------------------------------------------------------------------------------------------------------------------
// PathInfo
// ---------------------------------------------------------------------------------------------------------------------

#[derive(Debug, Clone)]
pub struct PathInfo {
    pub path: PathBuf,
}

// ---------------------------------------------------------------------------------------------------------------------
// PipelineFinder
// ---------------------------------------------------------------------------------------------------------------------

pub struct PipelineFinder {
    pub is_recursive: bool,
    pub follow_symlink: bool,
    pub skip_vcs: bool,
    pub skip_gitignore: bool,
    pub skip_hgignore: bool,
    pub skip_ambignore: bool,
    pub print_skipped: bool,
    pub find_parent_ignore: bool,
    pub infos: Vec<String>,
    pub errors: Vec<String>,
    time_beg: Instant,
    time_bsy: Duration,
    seq_no: usize,
    current_tx: usize,
    ignore_vcs: IgnoreVcs,
    ignore_git: Vec<IgnoreGit>,
}

impl PipelineFinder {
    pub fn new() -> Self {
        PipelineFinder {
            is_recursive: true,
            follow_symlink: true,
            skip_vcs: true,
            skip_gitignore: true,
            skip_hgignore: true,
            skip_ambignore: true,
            print_skipped: false,
            find_parent_ignore: true,
            infos: Vec::new(),
            errors: Vec::new(),
            time_beg: Instant::now(),
            time_bsy: Duration::new(0, 0),
            seq_no: 0,
            current_tx: 0,
            ignore_vcs: IgnoreVcs::new(),
            ignore_git: Vec::new(),
        }
    }

    fn find_path(&mut self, base: PathBuf, tx: &Vec<Sender<PipelineInfo<PathInfo>>>, is_symlink: bool) {
        let attr = match fs::metadata(&base) {
            Ok(x) => x,
            Err(e) => {
                if !is_symlink {
                    self.errors.push(format!("Error: {} @ {}", e, base.to_str().unwrap()));
                }
                return;
            }
        };

        if attr.is_file() {
            if attr.len() != 0 {
                self.send_path(base, &tx);
            }
        } else {
            let reader = match fs::read_dir(&base) {
                Ok(x) => x,
                Err(e) => {
                    self.errors.push(format!("Error: {} @ {}", e, base.to_str().unwrap()));
                    return;
                }
            };

            let gitignore_exist = self.push_gitignore(&base);

            for i in reader {
                match i {
                    Ok(entry) => {
                        let file_type = match entry.file_type() {
                            Ok(x) => x,
                            Err(e) => {
                                self.errors.push(format!("Error: {}", e));
                                continue;
                            }
                        };
                        if file_type.is_file() {
                            self.send_path(entry.path(), &tx);
                        } else {
                            let find_dir = file_type.is_dir() & self.is_recursive;
                            let find_symlink = file_type.is_symlink() & self.is_recursive & self.follow_symlink;
                            if (find_dir | find_symlink) & self.check_path(&entry.path(), true) {
                                self.find_path(entry.path(), &tx, find_symlink);
                            }
                        }
                    }
                    Err(e) => self.errors.push(format!("Error: {}", e)),
                };
            }

            self.pop_gitignore(gitignore_exist)
        }
    }

    fn send_path(&mut self, path: PathBuf, tx: &Vec<Sender<PipelineInfo<PathInfo>>>) {
        if self.check_path(&path, false) {
            let _ = tx[self.current_tx].send(PipelineInfo::SeqDat(self.seq_no, PathInfo { path: path }));
            self.seq_no += 1;
            self.current_tx = if self.current_tx == tx.len() - 1 {
                0
            } else {
                self.current_tx + 1
            };
        }
    }

    fn push_gitignore(&mut self, path: &PathBuf) -> bool {
        if !self.skip_gitignore {
            return false;
        }

        let reader = fs::read_dir(&path).unwrap();
        for i in reader {
            match i {
                Ok(entry) => {
                    if entry.path().ends_with(".gitignore") {
                        self.ignore_git.push(IgnoreGit::new(&entry.path()));
                        return true;
                    }
                }
                Err(e) => self.errors.push(format!("Error: {}", e)),
            }
        }
        false
    }

    fn pop_gitignore(&mut self, exist: bool) {
        if exist {
            let _ = self.ignore_git.pop();
        }
    }

    fn check_path(&mut self, path: &PathBuf, is_dir: bool) -> bool {
        let ok_vcs = if self.skip_vcs {
            !self.ignore_vcs.is_ignore(&path, is_dir)
        } else {
            true
        };

        let ok_git = if self.skip_gitignore && !self.ignore_git.is_empty() {
            !self.ignore_git.last().unwrap().is_ignore(&path, is_dir)
        } else {
            true
        };

        if !ok_vcs & self.print_skipped {
            self.infos.push(format!("Skipped: {:?} ( vcs file )\n", path));
        }

        if !ok_git & self.print_skipped {
            self.infos.push(format!("Skipped: {:?} ( .gitignore )\n", path));
        }

        ok_vcs && ok_git
    }

    fn set_default_gitignore(&mut self, base: &PathBuf) -> PathBuf {
        if !self.skip_gitignore {
            return base.clone();
        }
        if !self.find_parent_ignore {
            return base.clone();
        }

        let base_abs = match base.canonicalize() {
            Ok(x) => x,
            Err(e) => {
                self.errors.push(format!("Error: {} @ {}", e, base.to_str().unwrap()));
                return base.clone();
            }
        };

        let mut parent = base_abs.parent();
        while parent.is_some() {
            if self.push_gitignore(&PathBuf::from(parent.unwrap())) {
                self.infos.push(format!(
                    "Found .gitignore at the parent directory: {:?}\n",
                    parent.unwrap()
                ));
                return base_abs.clone();
            }
            parent = parent.unwrap().parent();
        }

        return base.clone();
    }
}

impl PipelineFork<PathBuf, PathInfo> for PipelineFinder {
    fn setup(&mut self, id: usize, rx: Receiver<PipelineInfo<PathBuf>>, tx: Vec<Sender<PipelineInfo<PathInfo>>>) {
        self.infos = Vec::new();
        self.errors = Vec::new();
        let mut seq_beg_arrived = false;

        loop {
            match rx.recv() {
                Ok(PipelineInfo::SeqDat(_, p)) => {
                    watch_time!(self.time_bsy, {
                        let p = self.set_default_gitignore(&p);
                        self.find_path(p, &tx, false);
                    });
                }

                Ok(PipelineInfo::SeqBeg(x)) => {
                    if !seq_beg_arrived {
                        self.seq_no = x;
                        self.time_beg = Instant::now();

                        for tx in &tx {
                            let _ = tx.send(PipelineInfo::SeqBeg(x));
                        }

                        seq_beg_arrived = true;
                    }
                }

                Ok(PipelineInfo::SeqEnd(_)) => {
                    for i in &self.infos {
                        let _ = tx[0].send(PipelineInfo::MsgInfo(id, i.clone()));
                    }
                    for e in &self.errors {
                        let _ = tx[0].send(PipelineInfo::MsgErr(id, e.clone()));
                    }

                    let _ = tx[0].send(PipelineInfo::MsgTime(id, self.time_bsy, self.time_beg.elapsed()));

                    for tx in &tx {
                        let _ = tx.send(PipelineInfo::SeqEnd(self.seq_no));
                    }

                    break;
                }

                Ok(PipelineInfo::MsgInfo(i, e)) => {
                    let _ = tx[0].send(PipelineInfo::MsgInfo(i, e));
                }
                Ok(PipelineInfo::MsgErr(i, e)) => {
                    let _ = tx[0].send(PipelineInfo::MsgErr(i, e));
                }
                Ok(PipelineInfo::MsgTime(i, t0, t1)) => {
                    let _ = tx[0].send(PipelineInfo::MsgTime(i, t0, t1));
                }
                Err(_) => break,
            }
        }
    }
}

// ---------------------------------------------------------------------------------------------------------------------
// Test
// ---------------------------------------------------------------------------------------------------------------------

#[cfg(test)]
mod tests {
    use super::*;
    use crate::pipeline::{PipelineFork, PipelineInfo};
    use crossbeam_channel::unbounded;
    use std::path::{Path,PathBuf};
    use std::thread;

    fn test<T: 'static + PipelineFork<PathBuf, PathInfo> + Send>(mut finder: T, path: String) -> Vec<PathInfo> {
        let (in_tx, in_rx) = unbounded();
        let (out_tx, out_rx) = unbounded();
        thread::spawn(move || {
            finder.setup(0, in_rx, vec![out_tx]);
        });
        let _ = in_tx.send(PipelineInfo::SeqBeg(0));
        let _ = in_tx.send(PipelineInfo::SeqDat(0, PathBuf::from(path)));
        let _ = in_tx.send(PipelineInfo::SeqEnd(1));

        let mut ret = Vec::new();
        loop {
            match out_rx.recv().unwrap() {
                PipelineInfo::SeqDat(_, x) => ret.push(x),
                PipelineInfo::SeqEnd(_) => break,
                _ => (),
            }
        }

        ret
    }

    #[test]
    fn pipeline_finder_default() {
        if !Path::new("./.git/config").exists() {
            fs::create_dir_all("./.git").unwrap();
            fs::File::create("./.git/config").unwrap();
        }

        let finder = PipelineFinder::new();
        let ret = test(finder, "./".to_string());

        assert!(ret.iter().any(|x| x.path == PathBuf::from("./Cargo.toml")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/ambr.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/ambs.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/console.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/lib.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/matcher.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/util.rs")));
        assert!(!ret.iter().any(|x| x.path == PathBuf::from("./.git/config")));
    }

    #[test]
    fn pipeline_finder_not_skip_vcs() {
        if !Path::new("./.git/config").exists() {
            fs::create_dir_all("./.git").unwrap();
            fs::File::create("./.git/config").unwrap();
        }

        let mut finder = PipelineFinder::new();
        finder.skip_vcs = false;
        let ret = test(finder, "./".to_string());

        assert!(ret.iter().any(|x| x.path == PathBuf::from("./Cargo.toml")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/ambr.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/ambs.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/console.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/lib.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/matcher.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/util.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./.git/config")));
    }
}
<commit_msg>Update cargo-fmt<commit_after>use crate::ignore::{Ignore, IgnoreGit, IgnoreVcs};
use crate::pipeline::{PipelineFork, PipelineInfo};
use crossbeam_channel::{Receiver, Sender};
use std::fs;
use std::path::PathBuf;
use std::time::{Duration, Instant};

// ---------------------------------------------------------------------------------------------------------------------
// PathInfo
// ---------------------------------------------------------------------------------------------------------------------

#[derive(Debug, Clone)]
pub struct PathInfo {
    pub path: PathBuf,
}

// ---------------------------------------------------------------------------------------------------------------------
// PipelineFinder
// ---------------------------------------------------------------------------------------------------------------------

pub struct PipelineFinder {
    pub is_recursive: bool,
    pub follow_symlink: bool,
    pub skip_vcs: bool,
    pub skip_gitignore: bool,
    pub skip_hgignore: bool,
    pub skip_ambignore: bool,
    pub print_skipped: bool,
    pub find_parent_ignore: bool,
    pub infos: Vec<String>,
    pub errors: Vec<String>,
    time_beg: Instant,
    time_bsy: Duration,
    seq_no: usize,
    current_tx: usize,
    ignore_vcs: IgnoreVcs,
    ignore_git: Vec<IgnoreGit>,
}

impl PipelineFinder {
    pub fn new() -> Self {
        PipelineFinder {
            is_recursive: true,
            follow_symlink: true,
            skip_vcs: true,
            skip_gitignore: true,
            skip_hgignore: true,
            skip_ambignore: true,
            print_skipped: false,
            find_parent_ignore: true,
            infos: Vec::new(),
            errors: Vec::new(),
            time_beg: Instant::now(),
            time_bsy: Duration::new(0, 0),
            seq_no: 0,
            current_tx: 0,
            ignore_vcs: IgnoreVcs::new(),
            ignore_git: Vec::new(),
        }
    }

    fn find_path(&mut self, base: PathBuf, tx: &Vec<Sender<PipelineInfo<PathInfo>>>, is_symlink: bool) {
        let attr = match fs::metadata(&base) {
            Ok(x) => x,
            Err(e) => {
                if !is_symlink {
                    self.errors.push(format!("Error: {} @ {}", e, base.to_str().unwrap()));
                }
                return;
            }
        };

        if attr.is_file() {
            if attr.len() != 0 {
                self.send_path(base, &tx);
            }
        } else {
            let reader = match fs::read_dir(&base) {
                Ok(x) => x,
                Err(e) => {
                    self.errors.push(format!("Error: {} @ {}", e, base.to_str().unwrap()));
                    return;
                }
            };

            let gitignore_exist = self.push_gitignore(&base);

            for i in reader {
                match i {
                    Ok(entry) => {
                        let file_type = match entry.file_type() {
                            Ok(x) => x,
                            Err(e) => {
                                self.errors.push(format!("Error: {}", e));
                                continue;
                            }
                        };
                        if file_type.is_file() {
                            self.send_path(entry.path(), &tx);
                        } else {
                            let find_dir = file_type.is_dir() & self.is_recursive;
                            let find_symlink = file_type.is_symlink() & self.is_recursive & self.follow_symlink;
                            if (find_dir | find_symlink) & self.check_path(&entry.path(), true) {
                                self.find_path(entry.path(), &tx, find_symlink);
                            }
                        }
                    }
                    Err(e) => self.errors.push(format!("Error: {}", e)),
                };
            }

            self.pop_gitignore(gitignore_exist)
        }
    }

    fn send_path(&mut self, path: PathBuf, tx: &Vec<Sender<PipelineInfo<PathInfo>>>) {
        if self.check_path(&path, false) {
            let _ = tx[self.current_tx].send(PipelineInfo::SeqDat(self.seq_no, PathInfo { path: path }));
            self.seq_no += 1;
            self.current_tx = if self.current_tx == tx.len() - 1 {
                0
            } else {
                self.current_tx + 1
            };
        }
    }

    fn push_gitignore(&mut self, path: &PathBuf) -> bool {
        if !self.skip_gitignore {
            return false;
        }

        let reader = fs::read_dir(&path).unwrap();
        for i in reader {
            match i {
                Ok(entry) => {
                    if entry.path().ends_with(".gitignore") {
                        self.ignore_git.push(IgnoreGit::new(&entry.path()));
                        return true;
                    }
                }
                Err(e) => self.errors.push(format!("Error: {}", e)),
            }
        }
        false
    }

    fn pop_gitignore(&mut self, exist: bool) {
        if exist {
            let _ = self.ignore_git.pop();
        }
    }

    fn check_path(&mut self, path: &PathBuf, is_dir: bool) -> bool {
        let ok_vcs = if self.skip_vcs {
            !self.ignore_vcs.is_ignore(&path, is_dir)
        } else {
            true
        };

        let ok_git = if self.skip_gitignore && !self.ignore_git.is_empty() {
            !self.ignore_git.last().unwrap().is_ignore(&path, is_dir)
        } else {
            true
        };

        if !ok_vcs & self.print_skipped {
            self.infos.push(format!("Skipped: {:?} ( vcs file )\n", path));
        }

        if !ok_git & self.print_skipped {
            self.infos.push(format!("Skipped: {:?} ( .gitignore )\n", path));
        }

        ok_vcs && ok_git
    }

    fn set_default_gitignore(&mut self, base: &PathBuf) -> PathBuf {
        if !self.skip_gitignore {
            return base.clone();
        }
        if !self.find_parent_ignore {
            return base.clone();
        }

        let base_abs = match base.canonicalize() {
            Ok(x) => x,
            Err(e) => {
                self.errors.push(format!("Error: {} @ {}", e, base.to_str().unwrap()));
                return base.clone();
            }
        };

        let mut parent = base_abs.parent();
        while parent.is_some() {
            if self.push_gitignore(&PathBuf::from(parent.unwrap())) {
                self.infos.push(format!(
                    "Found .gitignore at the parent directory: {:?}\n",
                    parent.unwrap()
                ));
                return base_abs.clone();
            }
            parent = parent.unwrap().parent();
        }

        return base.clone();
    }
}

impl PipelineFork<PathBuf, PathInfo> for PipelineFinder {
    fn setup(&mut self, id: usize, rx: Receiver<PipelineInfo<PathBuf>>, tx: Vec<Sender<PipelineInfo<PathInfo>>>) {
        self.infos = Vec::new();
        self.errors = Vec::new();
        let mut seq_beg_arrived = false;

        loop {
            match rx.recv() {
                Ok(PipelineInfo::SeqDat(_, p)) => {
                    watch_time!(self.time_bsy, {
                        let p = self.set_default_gitignore(&p);
                        self.find_path(p, &tx, false);
                    });
                }

                Ok(PipelineInfo::SeqBeg(x)) => {
                    if !seq_beg_arrived {
                        self.seq_no = x;
                        self.time_beg = Instant::now();

                        for tx in &tx {
                            let _ = tx.send(PipelineInfo::SeqBeg(x));
                        }

                        seq_beg_arrived = true;
                    }
                }

                Ok(PipelineInfo::SeqEnd(_)) => {
                    for i in &self.infos {
                        let _ = tx[0].send(PipelineInfo::MsgInfo(id, i.clone()));
                    }
                    for e in &self.errors {
                        let _ = tx[0].send(PipelineInfo::MsgErr(id, e.clone()));
                    }

                    let _ = tx[0].send(PipelineInfo::MsgTime(id, self.time_bsy, self.time_beg.elapsed()));

                    for tx in &tx {
                        let _ = tx.send(PipelineInfo::SeqEnd(self.seq_no));
                    }

                    break;
                }

                Ok(PipelineInfo::MsgInfo(i, e)) => {
                    let _ = tx[0].send(PipelineInfo::MsgInfo(i, e));
                }
                Ok(PipelineInfo::MsgErr(i, e)) => {
                    let _ = tx[0].send(PipelineInfo::MsgErr(i, e));
                }
                Ok(PipelineInfo::MsgTime(i, t0, t1)) => {
                    let _ = tx[0].send(PipelineInfo::MsgTime(i, t0, t1));
                }
                Err(_) => break,
            }
        }
    }
}

// ---------------------------------------------------------------------------------------------------------------------
// Test
// ---------------------------------------------------------------------------------------------------------------------

#[cfg(test)]
mod tests {
    use super::*;
    use crate::pipeline::{PipelineFork, PipelineInfo};
    use crossbeam_channel::unbounded;
    use std::path::{Path, PathBuf};
    use std::thread;

    fn test<T: 'static + PipelineFork<PathBuf, PathInfo> + Send>(mut finder: T, path: String) -> Vec<PathInfo> {
        let (in_tx, in_rx) = unbounded();
        let (out_tx, out_rx) = unbounded();
        thread::spawn(move || {
            finder.setup(0, in_rx, vec![out_tx]);
        });
        let _ = in_tx.send(PipelineInfo::SeqBeg(0));
        let _ = in_tx.send(PipelineInfo::SeqDat(0, PathBuf::from(path)));
        let _ = in_tx.send(PipelineInfo::SeqEnd(1));

        let mut ret = Vec::new();
        loop {
            match out_rx.recv().unwrap() {
                PipelineInfo::SeqDat(_, x) => ret.push(x),
                PipelineInfo::SeqEnd(_) => break,
                _ => (),
            }
        }

        ret
    }

    #[test]
    fn pipeline_finder_default() {
        if !Path::new("./.git/config").exists() {
            fs::create_dir_all("./.git").unwrap();
            fs::File::create("./.git/config").unwrap();
        }

        let finder = PipelineFinder::new();
        let ret = test(finder, "./".to_string());

        assert!(ret.iter().any(|x| x.path == PathBuf::from("./Cargo.toml")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/ambr.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/ambs.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/console.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/lib.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/matcher.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/util.rs")));
        assert!(!ret.iter().any(|x| x.path == PathBuf::from("./.git/config")));
    }

    #[test]
    fn pipeline_finder_not_skip_vcs() {
        if !Path::new("./.git/config").exists() {
            fs::create_dir_all("./.git").unwrap();
            fs::File::create("./.git/config").unwrap();
        }

        let mut finder = PipelineFinder::new();
        finder.skip_vcs = false;
        let ret = test(finder, "./".to_string());

        assert!(ret.iter().any(|x| x.path == PathBuf::from("./Cargo.toml")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/ambr.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/ambs.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/console.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/lib.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/matcher.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./src/util.rs")));
        assert!(ret.iter().any(|x| x.path == PathBuf::from("./.git/config")));
    }
}
<|endoftext|>


<commit_before>/// This file contains commands to generally control and administer the bot.
use client::PluginRegister;
use events::CommandEvent;

fn say(event: &CommandEvent) {
    if !event.client.is_admin(event) {
        return;
    }
    if event.args[0].starts_with("#") {
        event.client.send_message(event.args[0].as_slice(), event.args.slice_from(1).connect(" ").as_slice());
    } else {
        event.client.send_message(event.channel(), event.args.connect(" ").as_slice());
    }
}

fn quit(event: &CommandEvent) {
    if !event.client.is_admin(event) {
        return;
    }
    if event.args.len() != 0 {
        event.client.quit(Some(event.args.connect(" ").as_slice()));
    } else {
        event.client.quit(None);
    }
}

fn raw(event: &CommandEvent) {
    event.client.send_raw(event.args.connect(" "));
    event.client.send_message(event.channel(), "Sent raw message.");
}

fn join(event: &CommandEvent) {
    if !event.client.is_admin(event) {
        return;
    }
    event.client.join(event.args[0].as_slice());
    event.client.send_message(event.channel(), format!("Joined {}.", event.args[0]).as_slice());
}

fn part(event: &CommandEvent) {
    if !event.client.is_admin(event) {
        return;
    }
    if event.args.len() > 1 {
        event.client.part(event.args[0].as_slice(), Some(event.args.slice_from(1).connect(" ").as_slice()));
    } else {
        event.client.part(event.args[0].as_slice(), None);
    }

    event.client.send_message(event.channel(), format!("Parted {}.", event.args[0]).as_slice());
}

fn message(event: &CommandEvent) {
    if !event.client.is_admin(event) {
        return;
    }
    event.client.send_message(event.args[0].as_slice(), event.args.slice_from(1).connect(" ").as_slice());
    event.client.send_message(event.channel(), format!("Sent message to {}.", event.args[0]).as_slice());
}

pub fn register(register: &mut PluginRegister) {
    register.register_command("say", say);
    register.register_command("message", message);
    register.register_command("raw", raw);
    register.register_command("join", join);
    register.register_command("part", part);
    register.register_command("leave", part);
    register.register_command("quit", quit);
}
<commit_msg>Fix raw command being accessible without admin! important fix!<commit_after>/// This file contains commands to generally control and administer the bot.
use client::PluginRegister;
use events::CommandEvent;

fn say(event: &CommandEvent) {
    if !event.client.is_admin(event) {
        return;
    }
    if event.args[0].starts_with("#") {
        event.client.send_message(event.args[0].as_slice(), event.args.slice_from(1).connect(" ").as_slice());
    } else {
        event.client.send_message(event.channel(), event.args.connect(" ").as_slice());
    }
}

fn quit(event: &CommandEvent) {
    if !event.client.is_admin(event) {
        return;
    }
    if event.args.len() != 0 {
        event.client.quit(Some(event.args.connect(" ").as_slice()));
    } else {
        event.client.quit(None);
    }
}

fn raw(event: &CommandEvent) {
    if !event.client.is_admin(event) {
        return;
    }
    event.client.send_raw(event.args.connect(" "));
    event.client.send_message(event.channel(), "Sent raw message.");
}

fn join(event: &CommandEvent) {
    if !event.client.is_admin(event) {
        return;
    }
    event.client.join(event.args[0].as_slice());
    event.client.send_message(event.channel(), format!("Joined {}.", event.args[0]).as_slice());
}

fn part(event: &CommandEvent) {
    if !event.client.is_admin(event) {
        return;
    }
    if event.args.len() > 1 {
        event.client.part(event.args[0].as_slice(), Some(event.args.slice_from(1).connect(" ").as_slice()));
    } else {
        event.client.part(event.args[0].as_slice(), None);
    }

    event.client.send_message(event.channel(), format!("Parted {}.", event.args[0]).as_slice());
}

fn message(event: &CommandEvent) {
    if !event.client.is_admin(event) {
        return;
    }
    event.client.send_message(event.args[0].as_slice(), event.args.slice_from(1).connect(" ").as_slice());
    event.client.send_message(event.channel(), format!("Sent message to {}.", event.args[0]).as_slice());
}

pub fn register(register: &mut PluginRegister) {
    register.register_command("say", say);
    register.register_command("message", message);
    register.register_command("raw", raw);
    register.register_command("join", join);
    register.register_command("part", part);
    register.register_command("leave", part);
    register.register_command("quit", quit);
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#[doc = "Rust wrappers around the raw JS apis"];

use std::libc::types::os::arch::c95::{size_t, c_uint};
use std::libc::{c_char, uintptr_t};
use std::hashmap::HashMap;
use jsapi::*;
use default_stacksize;
use default_heapsize;
use JSOPTION_VAROBJFIX;
use JSOPTION_METHODJIT;
use JSOPTION_TYPE_INFERENCE;
use JSVAL_NULL;
use ERR;
use name_pool::*;
use global::global_class;
use std::ptr;
use std::ptr::null;
use result;
use result_obj;
use std::uint;
use std::str::raw::from_c_str;
use std::cast;

// ___________________________________________________________________________
// friendly Rustic API to runtimes

pub type rt = @rt_rsrc;

pub struct rt_rsrc {
    ptr : *JSRuntime,
}

impl Drop for rt_rsrc {
    #[fixed_stack_segment]
    fn drop(&self) {
        unsafe {
            JS_Finish(self.ptr);
        }
    }
}

pub fn new_runtime(p: *JSRuntime) -> rt {
    return @rt_rsrc {
        ptr: p
    }
}

impl rt_rsrc {
    #[fixed_stack_segment]
    pub fn cx(@self) -> @Cx {
        unsafe {
            new_context(JS_NewContext(self.ptr, default_stacksize as size_t), self)
        }
    }
}

// FIXME: Is this safe once we have more than one stack segment?
extern fn gc_callback(rt: *JSRuntime, _status: JSGCStatus) {
    use std::rt::local::Local;
    use std::rt::task::Task;
    unsafe {
        do Local::borrow |task: &mut Task| {
            match task.coroutine {
                Some(ref c) => {
                    let start = c.current_stack_segment.start() as uintptr_t;
                    let end = c.current_stack_segment.end() as uintptr_t;
                    JS_SetNativeStackBounds(rt, uint::min(start, end), uint::max(start, end));
                }
                None => {
                    fail!("gc_callback: cannot get stack for JS_SetNativeStackBounds");
                }
            }
        }
    }
}

#[fixed_stack_segment]
pub fn rt() -> rt {
    unsafe {
        let runtime = JS_Init(default_heapsize);
        JS_SetGCCallback(runtime, gc_callback);
        return new_runtime(runtime);
    }
}

// ___________________________________________________________________________
// contexts

pub struct Cx {
    ptr: *JSContext,
    rt: rt,
    classes: @mut HashMap<~str, @JSClass>,
}

#[unsafe_destructor]
impl Drop for Cx {
    #[fixed_stack_segment]
    fn drop(&self) {
        unsafe {
            JS_DestroyContext(self.ptr);
        }
    }
}

pub fn new_context(ptr: *JSContext, rt: rt) -> @Cx {
    return @Cx {
        ptr: ptr,
        rt: rt,
        classes: @mut HashMap::new()
    }
}

impl Cx {
    #[fixed_stack_segment]
    pub fn rooted_obj(@self, obj: *JSObject) -> jsobj {
        let jsobj = @jsobj_rsrc {cx: self, cxptr: self.ptr, ptr: obj};
        unsafe {
            JS_AddObjectRoot(self.ptr, ptr::to_unsafe_ptr(&jsobj.ptr));
        }
        jsobj
    }

    pub fn set_default_options_and_version(@self) {
        self.set_options(JSOPTION_VAROBJFIX | JSOPTION_METHODJIT |
                         JSOPTION_TYPE_INFERENCE);
        self.set_version(JSVERSION_LATEST);
    }

    #[fixed_stack_segment]
    pub fn set_options(@self, v: c_uint) {
        unsafe {
            JS_SetOptions(self.ptr, v);
        }
    }

    #[fixed_stack_segment]
    pub fn set_version(@self, v: i32) {
        unsafe {
            JS_SetVersion(self.ptr, v);
        }
    }

    #[fixed_stack_segment]
    pub fn set_logging_error_reporter(@self) {
        unsafe {
            JS_SetErrorReporter(self.ptr, reportError);
        }
    }

    #[fixed_stack_segment]
    pub fn set_error_reporter(@self, reportfn: extern "C" fn(*JSContext, *c_char, *JSErrorReport)) {
        unsafe {
            JS_SetErrorReporter(self.ptr, reportfn);
        }
    }

    #[fixed_stack_segment]
    pub fn new_compartment(@self,
                       globclsfn: &fn(@mut NamePool) -> JSClass)
                    -> Result<@mut Compartment,()> {
        unsafe {
            let np = NamePool();
            let globcls = @globclsfn(np);
            let globobj = JS_NewGlobalObject(self.ptr, ptr::to_unsafe_ptr(&*globcls), null());
            result(JS_InitStandardClasses(self.ptr, globobj)).chain(|_ok| {
                let compartment = @mut Compartment {
                    cx: self,
                    name_pool: np,
                    global_funcs: ~[],
                    global_props: ~[],
                    global_class: globcls,
                    global_obj: self.rooted_obj(globobj),
                    global_protos: @mut HashMap::new()
                };
                self.set_cx_private(ptr::to_unsafe_ptr(&*compartment) as *());
                Ok(compartment)
            })
        }
    }

    #[fixed_stack_segment]
    pub fn new_compartment_with_global(@self, global: *JSObject) -> Result<@mut Compartment,()> {
        let np = NamePool();
        let compartment = @mut Compartment {
            cx: self,
            name_pool: np,
            global_funcs: ~[],
            global_props: ~[],
            global_class: @global_class(np),
            global_obj: self.rooted_obj(global),
            global_protos: @mut HashMap::new()
        };
        unsafe {
            self.set_cx_private(ptr::to_unsafe_ptr(&*compartment) as *());
        }
        Ok(compartment)
    }

    #[fixed_stack_segment]
    pub fn evaluate_script(@self, glob: jsobj, script: ~str, filename: ~str, line_num: uint)
                    -> Result<(),()> {
        do script.to_utf16().as_imm_buf |script_ptr, script_len| {
            do filename.to_c_str().with_ref |filename_cstr| {
                let rval: JSVal = JSVAL_NULL;
                debug!("Evaluating script from %s with content %?", filename, script);
                unsafe {
                    if ERR == JS_EvaluateUCScript(self.ptr, glob.ptr,
                                                  script_ptr, script_len as c_uint,
                                                  filename_cstr, line_num as c_uint,
                                                  ptr::to_unsafe_ptr(&rval)) {
                        debug!("...err!");
                        Err(())
                    } else {
                        // we could return the script result but then we'd have
                        // to root it and so forth and, really, who cares?
                        debug!("...ok!");
                        Ok(())
                    }
                }
            }
        }
    }

    pub fn lookup_class_name(@self, s: ~str) ->  @JSClass {
        // FIXME: expect should really take a lambda...
        let error_msg = fmt!("class %s not found in class table", s);
        let name = self.classes.find(&s);
        *(name.expect(error_msg))
    }

    #[fixed_stack_segment]
    pub unsafe fn get_cx_private(@self) -> *() {
        cast::transmute(JS_GetContextPrivate(self.ptr))
    }

    #[fixed_stack_segment]
    pub unsafe fn set_cx_private(@self, data: *()) {
        JS_SetContextPrivate(self.ptr, cast::transmute(data));
    }

    #[fixed_stack_segment]
    pub unsafe fn get_obj_private(@self, obj: *JSObject) -> *() {
        cast::transmute(JS_GetPrivate(obj))
    }

    #[fixed_stack_segment]
    pub unsafe fn set_obj_private(@self, obj: *JSObject, data: *()) {
        JS_SetPrivate(obj, cast::transmute(data));
    }
}

pub extern fn reportError(_cx: *JSContext, msg: *c_char, report: *JSErrorReport) {
    unsafe {
        let fnptr = (*report).filename;
        let fname = if fnptr.is_not_null() {from_c_str(fnptr)} else {~"none"};
        let lineno = (*report).lineno;
        let msg = from_c_str(msg);
        error!("Error at %s:%?: %s\n", fname, lineno, msg);
    }
}

// ___________________________________________________________________________
// compartment

pub struct Compartment {
    cx: @Cx,
    name_pool: @mut NamePool,
    global_funcs: ~[@~[JSFunctionSpec]],
    global_props: ~[@~[JSPropertySpec]],
    global_class: @JSClass,
    global_obj: jsobj,
    global_protos: @mut HashMap<~str, jsobj>
}

impl Compartment {
    #[fixed_stack_segment]
    pub fn define_functions(@mut self,
                        specfn: &fn(@mut NamePool) -> ~[JSFunctionSpec])
                     -> Result<(),()> {
        let specvec = @specfn(self.name_pool);
        self.global_funcs.push(specvec);
        do specvec.as_imm_buf |specs, _len| {
            unsafe {
                result(JS_DefineFunctions(self.cx.ptr, self.global_obj.ptr, specs))
            }
        }
    }
    #[fixed_stack_segment]
    pub fn define_properties(@mut self, specfn: &fn() -> ~[JSPropertySpec]) -> Result<(),()> {
        let specvec = @specfn();
        self.global_props.push(specvec);
        do specvec.as_imm_buf |specs, _len| {
            unsafe {
                result(JS_DefineProperties(self.cx.ptr, self.global_obj.ptr, specs))
            }
        }
    }
    #[fixed_stack_segment]
    pub fn define_property(@mut self,
                       name: ~str,
                       value: JSVal,
                       getter: JSPropertyOp, setter: JSStrictPropertyOp,
                       attrs: c_uint)
                    -> Result<(),()> {
        unsafe {
            result(JS_DefineProperty(self.cx.ptr,
                                     self.global_obj.ptr,
                                     self.add_name(name),
                                     value,
                                     Some(getter),
                                     Some(setter),
                                     attrs))
        }
    }
    #[fixed_stack_segment]
    pub fn new_object(@mut self, class_name: ~str, proto: *JSObject, parent: *JSObject)
               -> Result<jsobj, ()> {
        unsafe {
            let classptr = self.cx.lookup_class_name(class_name);
            let obj = self.cx.rooted_obj(JS_NewObject(self.cx.ptr, &*classptr, proto, parent));
            result_obj(obj)
        }
    }
    #[fixed_stack_segment]
    pub fn new_object_with_proto(@mut self, class_name: ~str, proto_name: ~str, parent: *JSObject)
                          -> Result<jsobj, ()> {
        let classptr = self.cx.lookup_class_name(class_name);
        let proto = self.global_protos.find(&proto_name.clone()).expect(
            fmt!("new_object_with_proto: expected to find %s in the proto \
                  table", proto_name));
        unsafe {
            let obj = self.cx.rooted_obj(JS_NewObject(self.cx.ptr, ptr::to_unsafe_ptr(&*classptr),
                                                      proto.ptr, parent));
            result_obj(obj)
        }
    }
    pub fn get_global_proto(@mut self, name: ~str) -> jsobj {
        let proto = self.global_protos.get(&name);
        *proto
    }
    pub fn stash_global_proto(@mut self, name: ~str, proto: jsobj) {
        let global_protos = self.global_protos;
        if !global_protos.insert(name, proto) {
            fail!(~"Duplicate global prototype registered; you're gonna have a bad time.")
        }
    }
    pub fn register_class(@mut self, class_fn: &fn(x: @mut Compartment) -> JSClass) {
        let classptr = @class_fn(self);
        if !self.cx.classes.insert(
            unsafe { from_c_str(classptr.name) },
            classptr) {
            fail!(~"Duplicate JSClass registered; you're gonna have a bad time.")
        }
    }
    pub fn add_name(@mut self, name: ~str) -> *c_char {
        self.name_pool.add(name.clone())
    }
}

// ___________________________________________________________________________
// objects

pub type jsobj = @jsobj_rsrc;

pub struct jsobj_rsrc {
    cx: @Cx,
    cxptr: *JSContext,
    ptr: *JSObject,
}

#[unsafe_destructor]
impl Drop for jsobj_rsrc {
    #[fixed_stack_segment]
    fn drop(&self) {
        unsafe {
            JS_RemoveObjectRoot(self.cxptr, ptr::to_unsafe_ptr(&self.ptr));
        }
    }
}

impl jsobj_rsrc {
    fn new_object(&self, cx: @Cx, cxptr: *JSContext, ptr: *JSObject) -> jsobj {
        return @jsobj_rsrc {
            cx: cx,
            cxptr: cxptr,
            ptr: ptr
        }
    }
}

// ___________________________________________________________________________
// random utilities

pub trait to_jsstr {
    fn to_jsstr(self, cx: @Cx) -> *JSString;
}

impl to_jsstr for ~str {
    #[fixed_stack_segment]
    fn to_jsstr(self, cx: @Cx) -> *JSString {
        do self.as_imm_buf |buf, len| {
            unsafe {
                let cbuf = cast::transmute(buf);
                JS_NewStringCopyN(cx.ptr, cbuf, len as size_t)
            }
        }
    }
}

#[cfg(test)]
pub mod test {
    use super::rt;
    use super::super::global;
    use super::super::jsapi::{JS_GC, JS_GetRuntime};

    #[test]
    #[fixed_stack_segment]
    pub fn dummy() {
        let rt = rt();
        let cx = rt.cx();
        cx.set_default_options_and_version();
        cx.set_logging_error_reporter();
        cx.new_compartment(global::global_class).chain(|comp| {
            unsafe { JS_GC(JS_GetRuntime(comp.cx.ptr)); }

            comp.define_functions(global::debug_fns);

            let s = ~"debug(22);";
            let bytes = s.as_bytes().to_owned();
            cx.evaluate_script(comp.global_obj, bytes, ~"test", 1u)
        });
    }

}
<commit_msg>Fix broken test case<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#[doc = "Rust wrappers around the raw JS apis"];

use std::libc::types::os::arch::c95::{size_t, c_uint};
use std::libc::{c_char, uintptr_t};
use std::hashmap::HashMap;
use jsapi::*;
use default_stacksize;
use default_heapsize;
use JSOPTION_VAROBJFIX;
use JSOPTION_METHODJIT;
use JSOPTION_TYPE_INFERENCE;
use JSVAL_NULL;
use ERR;
use name_pool::*;
use global::global_class;
use std::ptr;
use std::ptr::null;
use result;
use result_obj;
use std::uint;
use std::str::raw::from_c_str;
use std::cast;

// ___________________________________________________________________________
// friendly Rustic API to runtimes

pub type rt = @rt_rsrc;

pub struct rt_rsrc {
    ptr : *JSRuntime,
}

impl Drop for rt_rsrc {
    #[fixed_stack_segment]
    fn drop(&self) {
        unsafe {
            JS_Finish(self.ptr);
        }
    }
}

pub fn new_runtime(p: *JSRuntime) -> rt {
    return @rt_rsrc {
        ptr: p
    }
}

impl rt_rsrc {
    #[fixed_stack_segment]
    pub fn cx(@self) -> @Cx {
        unsafe {
            new_context(JS_NewContext(self.ptr, default_stacksize as size_t), self)
        }
    }
}

// FIXME: Is this safe once we have more than one stack segment?
extern fn gc_callback(rt: *JSRuntime, _status: JSGCStatus) {
    use std::rt::local::Local;
    use std::rt::task::Task;
    unsafe {
        do Local::borrow |task: &mut Task| {
            match task.coroutine {
                Some(ref c) => {
                    let start = c.current_stack_segment.start() as uintptr_t;
                    let end = c.current_stack_segment.end() as uintptr_t;
                    JS_SetNativeStackBounds(rt, uint::min(start, end), uint::max(start, end));
                }
                None => {
                    fail!("gc_callback: cannot get stack for JS_SetNativeStackBounds");
                }
            }
        }
    }
}

#[fixed_stack_segment]
pub fn rt() -> rt {
    unsafe {
        let runtime = JS_Init(default_heapsize);
        JS_SetGCCallback(runtime, gc_callback);
        return new_runtime(runtime);
    }
}

// ___________________________________________________________________________
// contexts

pub struct Cx {
    ptr: *JSContext,
    rt: rt,
    classes: @mut HashMap<~str, @JSClass>,
}

#[unsafe_destructor]
impl Drop for Cx {
    #[fixed_stack_segment]
    fn drop(&self) {
        unsafe {
            JS_DestroyContext(self.ptr);
        }
    }
}

pub fn new_context(ptr: *JSContext, rt: rt) -> @Cx {
    return @Cx {
        ptr: ptr,
        rt: rt,
        classes: @mut HashMap::new()
    }
}

impl Cx {
    #[fixed_stack_segment]
    pub fn rooted_obj(@self, obj: *JSObject) -> jsobj {
        let jsobj = @jsobj_rsrc {cx: self, cxptr: self.ptr, ptr: obj};
        unsafe {
            JS_AddObjectRoot(self.ptr, ptr::to_unsafe_ptr(&jsobj.ptr));
        }
        jsobj
    }

    pub fn set_default_options_and_version(@self) {
        self.set_options(JSOPTION_VAROBJFIX | JSOPTION_METHODJIT |
                         JSOPTION_TYPE_INFERENCE);
        self.set_version(JSVERSION_LATEST);
    }

    #[fixed_stack_segment]
    pub fn set_options(@self, v: c_uint) {
        unsafe {
            JS_SetOptions(self.ptr, v);
        }
    }

    #[fixed_stack_segment]
    pub fn set_version(@self, v: i32) {
        unsafe {
            JS_SetVersion(self.ptr, v);
        }
    }

    #[fixed_stack_segment]
    pub fn set_logging_error_reporter(@self) {
        unsafe {
            JS_SetErrorReporter(self.ptr, reportError);
        }
    }

    #[fixed_stack_segment]
    pub fn set_error_reporter(@self, reportfn: extern "C" fn(*JSContext, *c_char, *JSErrorReport)) {
        unsafe {
            JS_SetErrorReporter(self.ptr, reportfn);
        }
    }

    #[fixed_stack_segment]
    pub fn new_compartment(@self,
                       globclsfn: &fn(@mut NamePool) -> JSClass)
                    -> Result<@mut Compartment,()> {
        unsafe {
            let np = NamePool();
            let globcls = @globclsfn(np);
            let globobj = JS_NewGlobalObject(self.ptr, ptr::to_unsafe_ptr(&*globcls), null());
            result(JS_InitStandardClasses(self.ptr, globobj)).chain(|_ok| {
                let compartment = @mut Compartment {
                    cx: self,
                    name_pool: np,
                    global_funcs: ~[],
                    global_props: ~[],
                    global_class: globcls,
                    global_obj: self.rooted_obj(globobj),
                    global_protos: @mut HashMap::new()
                };
                self.set_cx_private(ptr::to_unsafe_ptr(&*compartment) as *());
                Ok(compartment)
            })
        }
    }

    #[fixed_stack_segment]
    pub fn new_compartment_with_global(@self, global: *JSObject) -> Result<@mut Compartment,()> {
        let np = NamePool();
        let compartment = @mut Compartment {
            cx: self,
            name_pool: np,
            global_funcs: ~[],
            global_props: ~[],
            global_class: @global_class(np),
            global_obj: self.rooted_obj(global),
            global_protos: @mut HashMap::new()
        };
        unsafe {
            self.set_cx_private(ptr::to_unsafe_ptr(&*compartment) as *());
        }
        Ok(compartment)
    }

    #[fixed_stack_segment]
    pub fn evaluate_script(@self, glob: jsobj, script: ~str, filename: ~str, line_num: uint)
                    -> Result<(),()> {
        do script.to_utf16().as_imm_buf |script_ptr, script_len| {
            do filename.to_c_str().with_ref |filename_cstr| {
                let rval: JSVal = JSVAL_NULL;
                debug!("Evaluating script from %s with content %?", filename, script);
                unsafe {
                    if ERR == JS_EvaluateUCScript(self.ptr, glob.ptr,
                                                  script_ptr, script_len as c_uint,
                                                  filename_cstr, line_num as c_uint,
                                                  ptr::to_unsafe_ptr(&rval)) {
                        debug!("...err!");
                        Err(())
                    } else {
                        // we could return the script result but then we'd have
                        // to root it and so forth and, really, who cares?
                        debug!("...ok!");
                        Ok(())
                    }
                }
            }
        }
    }

    pub fn lookup_class_name(@self, s: ~str) ->  @JSClass {
        // FIXME: expect should really take a lambda...
        let error_msg = fmt!("class %s not found in class table", s);
        let name = self.classes.find(&s);
        *(name.expect(error_msg))
    }

    #[fixed_stack_segment]
    pub unsafe fn get_cx_private(@self) -> *() {
        cast::transmute(JS_GetContextPrivate(self.ptr))
    }

    #[fixed_stack_segment]
    pub unsafe fn set_cx_private(@self, data: *()) {
        JS_SetContextPrivate(self.ptr, cast::transmute(data));
    }

    #[fixed_stack_segment]
    pub unsafe fn get_obj_private(@self, obj: *JSObject) -> *() {
        cast::transmute(JS_GetPrivate(obj))
    }

    #[fixed_stack_segment]
    pub unsafe fn set_obj_private(@self, obj: *JSObject, data: *()) {
        JS_SetPrivate(obj, cast::transmute(data));
    }
}

pub extern fn reportError(_cx: *JSContext, msg: *c_char, report: *JSErrorReport) {
    unsafe {
        let fnptr = (*report).filename;
        let fname = if fnptr.is_not_null() {from_c_str(fnptr)} else {~"none"};
        let lineno = (*report).lineno;
        let msg = from_c_str(msg);
        error!("Error at %s:%?: %s\n", fname, lineno, msg);
    }
}

// ___________________________________________________________________________
// compartment

pub struct Compartment {
    cx: @Cx,
    name_pool: @mut NamePool,
    global_funcs: ~[@~[JSFunctionSpec]],
    global_props: ~[@~[JSPropertySpec]],
    global_class: @JSClass,
    global_obj: jsobj,
    global_protos: @mut HashMap<~str, jsobj>
}

impl Compartment {
    #[fixed_stack_segment]
    pub fn define_functions(@mut self,
                        specfn: &fn(@mut NamePool) -> ~[JSFunctionSpec])
                     -> Result<(),()> {
        let specvec = @specfn(self.name_pool);
        self.global_funcs.push(specvec);
        do specvec.as_imm_buf |specs, _len| {
            unsafe {
                result(JS_DefineFunctions(self.cx.ptr, self.global_obj.ptr, specs))
            }
        }
    }
    #[fixed_stack_segment]
    pub fn define_properties(@mut self, specfn: &fn() -> ~[JSPropertySpec]) -> Result<(),()> {
        let specvec = @specfn();
        self.global_props.push(specvec);
        do specvec.as_imm_buf |specs, _len| {
            unsafe {
                result(JS_DefineProperties(self.cx.ptr, self.global_obj.ptr, specs))
            }
        }
    }
    #[fixed_stack_segment]
    pub fn define_property(@mut self,
                       name: ~str,
                       value: JSVal,
                       getter: JSPropertyOp, setter: JSStrictPropertyOp,
                       attrs: c_uint)
                    -> Result<(),()> {
        unsafe {
            result(JS_DefineProperty(self.cx.ptr,
                                     self.global_obj.ptr,
                                     self.add_name(name),
                                     value,
                                     Some(getter),
                                     Some(setter),
                                     attrs))
        }
    }
    #[fixed_stack_segment]
    pub fn new_object(@mut self, class_name: ~str, proto: *JSObject, parent: *JSObject)
               -> Result<jsobj, ()> {
        unsafe {
            let classptr = self.cx.lookup_class_name(class_name);
            let obj = self.cx.rooted_obj(JS_NewObject(self.cx.ptr, &*classptr, proto, parent));
            result_obj(obj)
        }
    }
    #[fixed_stack_segment]
    pub fn new_object_with_proto(@mut self, class_name: ~str, proto_name: ~str, parent: *JSObject)
                          -> Result<jsobj, ()> {
        let classptr = self.cx.lookup_class_name(class_name);
        let proto = self.global_protos.find(&proto_name.clone()).expect(
            fmt!("new_object_with_proto: expected to find %s in the proto \
                  table", proto_name));
        unsafe {
            let obj = self.cx.rooted_obj(JS_NewObject(self.cx.ptr, ptr::to_unsafe_ptr(&*classptr),
                                                      proto.ptr, parent));
            result_obj(obj)
        }
    }
    pub fn get_global_proto(@mut self, name: ~str) -> jsobj {
        let proto = self.global_protos.get(&name);
        *proto
    }
    pub fn stash_global_proto(@mut self, name: ~str, proto: jsobj) {
        let global_protos = self.global_protos;
        if !global_protos.insert(name, proto) {
            fail!(~"Duplicate global prototype registered; you're gonna have a bad time.")
        }
    }
    pub fn register_class(@mut self, class_fn: &fn(x: @mut Compartment) -> JSClass) {
        let classptr = @class_fn(self);
        if !self.cx.classes.insert(
            unsafe { from_c_str(classptr.name) },
            classptr) {
            fail!(~"Duplicate JSClass registered; you're gonna have a bad time.")
        }
    }
    pub fn add_name(@mut self, name: ~str) -> *c_char {
        self.name_pool.add(name.clone())
    }
}

// ___________________________________________________________________________
// objects

pub type jsobj = @jsobj_rsrc;

pub struct jsobj_rsrc {
    cx: @Cx,
    cxptr: *JSContext,
    ptr: *JSObject,
}

#[unsafe_destructor]
impl Drop for jsobj_rsrc {
    #[fixed_stack_segment]
    fn drop(&self) {
        unsafe {
            JS_RemoveObjectRoot(self.cxptr, ptr::to_unsafe_ptr(&self.ptr));
        }
    }
}

impl jsobj_rsrc {
    fn new_object(&self, cx: @Cx, cxptr: *JSContext, ptr: *JSObject) -> jsobj {
        return @jsobj_rsrc {
            cx: cx,
            cxptr: cxptr,
            ptr: ptr
        }
    }
}

// ___________________________________________________________________________
// random utilities

pub trait to_jsstr {
    fn to_jsstr(self, cx: @Cx) -> *JSString;
}

impl to_jsstr for ~str {
    #[fixed_stack_segment]
    fn to_jsstr(self, cx: @Cx) -> *JSString {
        do self.as_imm_buf |buf, len| {
            unsafe {
                let cbuf = cast::transmute(buf);
                JS_NewStringCopyN(cx.ptr, cbuf, len as size_t)
            }
        }
    }
}

#[cfg(test)]
pub mod test {
    use super::rt;
    use super::super::global;
    use super::super::jsapi::{JS_GC, JS_GetRuntime};

    #[test]
    #[fixed_stack_segment]
    pub fn dummy() {
        let rt = rt();
        let cx = rt.cx();
        cx.set_default_options_and_version();
        cx.set_logging_error_reporter();
        cx.new_compartment(global::global_class).chain(|comp| {
            unsafe { JS_GC(JS_GetRuntime(comp.cx.ptr)); }

            comp.define_functions(global::debug_fns);

            let s = ~"debug(22);";
            cx.evaluate_script(comp.global_obj, s, ~"test", 1u)
        });
    }

}
<|endoftext|>


<commit_before>use actix_files as fs;
use actix_web::http::{Method, StatusCode};
use actix_web::{get, Either, HttpResponse, Responder, Result};
use std::env;

use crate::utils;

#[get("/favicon.ico")]
pub async fn favicon() -> Result<fs::NamedFile> {
    let mut path = utils::static_path();
    path.push_str("/favicon.ico");

    Ok(fs::NamedFile::open(path)?)
}

#[get("/robots.txt")]
pub async fn robots_txt() -> Result<fs::NamedFile> {
    let mut path = utils::static_path();
    path.push_str("/robots.txt");

    Ok(fs::NamedFile::open(path)?)
}

#[get("/A99A9D73.asc")]
pub async fn gpgkey() -> Result<fs::NamedFile> {
    let mut path = utils::static_path();
    path.push_str("/A99A9D73.asc");

    Ok(fs::NamedFile::open(path)?)
}

#[get("/humans.txt")]
pub async fn humans_txt() -> Result<fs::NamedFile> {
    let mut path = utils::static_path();
    path.push_str("/humans.txt");

    Ok(fs::NamedFile::open(path)?)
}

#[get("/.well-known/security.txt")]
pub async fn well_known_security_txt() -> Result<fs::NamedFile> {
    let mut path = utils::static_path();
    path.push_str("/security.txt");

    Ok(fs::NamedFile::open(path)?)
}

#[get("/security.txt")]
pub async fn security_txt() -> Result<fs::NamedFile> {
    let mut path = utils::static_path();
    path.push_str("/security.txt");

    Ok(fs::NamedFile::open(path)?)
}

#[get("/.well-known/keybase.txt")]
pub async fn keybase_txt() -> Result<fs::NamedFile> {
    let path = env::var("KEYBASE_TXT").expect("KEYBASE_TXT is not set");

    Ok(fs::NamedFile::open(path)?)
}

pub async fn p404(req_method: Method) -> Result<impl Responder> {
    match req_method {
        Method::GET => {
            let mut path = utils::static_path();
            path.push_str("404.html");

            let file = fs::NamedFile::open(path)?.set_status_code(StatusCode::NOT_FOUND);

            Ok(Either::Left(file))
        }
        _ => Ok(Either::Right(HttpResponse::MethodNotAllowed().finish())),
    }
}
<commit_msg>`set_status_code` is deprecated<commit_after>use actix_files as fs;
use actix_web::http::{Method, StatusCode};
use actix_web::{get, Either, HttpResponse, Responder, Result};
use std::env;

use crate::utils;

#[get("/favicon.ico")]
pub async fn favicon() -> Result<fs::NamedFile> {
    let mut path = utils::static_path();
    path.push_str("/favicon.ico");

    Ok(fs::NamedFile::open(path)?)
}

#[get("/robots.txt")]
pub async fn robots_txt() -> Result<fs::NamedFile> {
    let mut path = utils::static_path();
    path.push_str("/robots.txt");

    Ok(fs::NamedFile::open(path)?)
}

#[get("/A99A9D73.asc")]
pub async fn gpgkey() -> Result<fs::NamedFile> {
    let mut path = utils::static_path();
    path.push_str("/A99A9D73.asc");

    Ok(fs::NamedFile::open(path)?)
}

#[get("/humans.txt")]
pub async fn humans_txt() -> Result<fs::NamedFile> {
    let mut path = utils::static_path();
    path.push_str("/humans.txt");

    Ok(fs::NamedFile::open(path)?)
}

#[get("/.well-known/security.txt")]
pub async fn well_known_security_txt() -> Result<fs::NamedFile> {
    let mut path = utils::static_path();
    path.push_str("/security.txt");

    Ok(fs::NamedFile::open(path)?)
}

#[get("/security.txt")]
pub async fn security_txt() -> Result<fs::NamedFile> {
    let mut path = utils::static_path();
    path.push_str("/security.txt");

    Ok(fs::NamedFile::open(path)?)
}

#[get("/.well-known/keybase.txt")]
pub async fn keybase_txt() -> Result<fs::NamedFile> {
    let path = env::var("KEYBASE_TXT").expect("KEYBASE_TXT is not set");

    Ok(fs::NamedFile::open(path)?)
}

pub async fn p404(req_method: Method) -> Result<impl Responder> {
    match req_method {
        Method::GET => {
            let mut path = utils::static_path();
            path.push_str("404.html");

            let file = fs::NamedFile::open(path)?
                .customize()
                .with_status(StatusCode::NOT_FOUND);

            Ok(Either::Left(file))
        }
        _ => Ok(Either::Right(HttpResponse::MethodNotAllowed().finish())),
    }
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Result as FMTResult;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;
use std::fs::File as FSFile;
use std::io::Read;
use std::io::Write;
use std::vec::IntoIter;

use glob::glob;
use glob::Paths;

use storage::file::File;
use storage::file_id::*;
use storage::parser::{FileHeaderParser, Parser, ParserError};

use module::Module;
use runtime::Runtime;

pub type BackendOperationResult = Result<(), StorageBackendError>;

pub struct StorageBackend {
    basepath: String,
}

impl StorageBackend {

    pub fn new(basepath: String) -> StorageBackend {
        StorageBackend {
            basepath: basepath,
        }
    }

    fn build<M: Module>(rt: &Runtime, m: &M) -> StorageBackend {
        let path = rt.get_rtp() + m.name() + "/store";
        // TODO: Don't use "/store" but value from configuration
        debug!("Building StorageBackend for {}", path);
        StorageBackend::new(path)
    }

    fn get_file_ids(&self) -> Option<Vec<FileID>> {
        debug!("Getting files from {}", self.basepath);
        let list = glob(&self.basepath[..]);

        if let Ok(globlist) = list {
            let mut v = vec![];
            for entry in globlist {
                if let Ok(path) = entry {
                    debug!(" - File: {:?}", path);
                    v.push(from_pathbuf(&path));
                } else {
                    // Entry is not a path
                }
            }

            Some(v)
        } else {
            None
        }
    }

    pub fn iter_ids(&self, m: &Module) -> Option<IntoIter<FileID>>
    {
        glob(&self.prefix_of_files_for_module(m)[..]).and_then(|globlist| {
            let v = globlist.filter_map(Result::ok)
                            .map(|pbuf| from_pathbuf(&pbuf))
                            .collect::<Vec<FileID>>()
                            .into_iter();
            Ok(v)
        }).ok()
    }

    pub fn iter_files<'a, HP>(&self, m: &'a Module, p: &Parser<HP>)
        -> Option<IntoIter<File<'a>>>
        where HP: FileHeaderParser
    {
        self.iter_ids(m).and_then(|ids| {
            Some(ids.filter_map(|id| self.get_file_by_id(m, &id, p))
                    .collect::<Vec<File>>()
                    .into_iter())
        })
    }

    /*
     * Write a file to disk.
     *
     * The file is moved to this function as the file won't be edited afterwards
     */
    pub fn put_file<HP>(&self, f: File, p: &Parser<HP>) -> BackendOperationResult
        where HP: FileHeaderParser
    {
        let written = write_with_parser(&f, p);
        if written.is_err() { return Err(written.err().unwrap()); }
        let string = written.unwrap();

        let path = self.build_filepath(&f);
        debug!("Writing file: {}", path);
        debug!("      string: {}", string);

        FSFile::create(&path).map(|mut file| {
            debug!("Created file at '{}'", path);
            file.write_all(&string.clone().into_bytes())
                .map_err(|ioerr| {
                    debug!("Could not write file");
                    let mut err = StorageBackendError::build(
                            "File::write_all()",
                            "Could not write out File contents",
                            None
                        );
                    err.caused_by = Some(Box::new(ioerr));
                    err
                })
        }).map_err(|writeerr| {
            debug!("Could not create file at '{}'", path);
            let mut err = StorageBackendError::build(
                "File::create()",
                "Creating file on disk failed",
                None
            );
            err.caused_by = Some(Box::new(writeerr));
            err
        }).and(Ok(()))
    }

    /*
     * Update a file. We have the UUID and can find the file on FS with it and
     * then replace its contents with the contents of the passed file object
     */
    pub fn update_file<HP>(&self, f: File, p: &Parser<HP>) -> BackendOperationResult
        where HP: FileHeaderParser
    {
        let contents = write_with_parser(&f, p);
        if contents.is_err() { return Err(contents.err().unwrap()); }
        let string = contents.unwrap();

        let path = self.build_filepath(&f);
        debug!("Writing file: {}", path);
        debug!("      string: {}", string);

        FSFile::open(&path).map(|mut file| {
            debug!("Open file at '{}'", path);
            file.write_all(&string.clone().into_bytes())
                .map_err(|ioerr| {
                    debug!("Could not write file");
                    let mut err = StorageBackendError::build(
                            "File::write()",
                            "Tried to write contents of this file, though operation did not succeed",
                            Some(string)
                        );
                    err.caused_by = Some(Box::new(ioerr));
                    err
                })
        }).map_err(|writeerr| {
            debug!("Could not write file at '{}'", path);
            let mut err = StorageBackendError::build(
                "File::open()",
                "Tried to update contents of this file, though file doesn't exist",
                None
            );
            err.caused_by = Some(Box::new(writeerr));
            err
        }).and(Ok(()))
    }

    /*
     * Find a file by its ID and return it if found. Return nothing if not
     * found, of course.
     *
     * TODO: Needs refactoring, as there might be an error when reading from
     * disk OR the id just does not exist.
     */
    pub fn get_file_by_id<'a, HP>(&self, id: FileID, p: &Parser<HP>) -> Option<File>
        where HP: FileHeaderParser<'a>
    {
        debug!("Searching for file with id '{}'", id);
        if let Ok(mut fs) = FSFile::open(self.build_filepath_with_id(id.clone())) {
            let mut s = String::new();
            fs.read_to_string(&mut s);
            debug!("Success reading file with id '{}'", id);
            debug!("Parsing to internal structure now");
            p.read(s).and_then(|(h, d)| Ok(File::from_parser_result(id, h, d))).ok()
        } else {
            debug!("No file with id '{}'", id);
            None
        }
    }

    fn build_filepath(&self, f: &File) -> String {
        self.build_filepath_with_id(f.id())
    }

    fn build_filepath_with_id(&self, id: FileID) -> String {
        debug!("Building filepath for id '{}'", id);
        self.basepath.clone() + &id[..]
    }

}

#[derive(Debug)]
pub struct StorageBackendError {
    pub action: String,             // The file system action in words
    pub desc: String,               // A short description
    pub data_dump: Option<String>,  // Data dump, if any
    pub caused_by: Option<Box<Error>>,  // caused from this error
}

impl StorageBackendError {
    fn new(action: String,
           desc  : String,
           data  : Option<String>) -> StorageBackendError
    {
        StorageBackendError {
            action:         action,
            desc:           desc,
            data_dump:      data,
            caused_by:      None,
        }
    }

    fn build(action: &'static str,
             desc:   &'static str,
           data  : Option<String>) -> StorageBackendError
    {
        StorageBackendError {
            action:         String::from(action),
            desc:           String::from(desc),
            dataDump:       data,
            caused_by:      None,
        }
    }

}

impl Error for StorageBackendError {

    fn description(&self) -> &str {
        &self.desc[..]
    }

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

}

impl Display for StorageBackendError {
    fn fmt(&self, f: &mut Formatter) -> FMTResult {
        write!(f, "StorageBackendError[{}]: {}",
               self.action, self.desc)
    }
}

<commit_msg>Implement helper write_with_parser()<commit_after>use std::error::Error;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Result as FMTResult;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;
use std::fs::File as FSFile;
use std::io::Read;
use std::io::Write;
use std::vec::IntoIter;

use glob::glob;
use glob::Paths;

use storage::file::File;
use storage::file_id::*;
use storage::parser::{FileHeaderParser, Parser, ParserError};

use module::Module;
use runtime::Runtime;

pub type BackendOperationResult = Result<(), StorageBackendError>;

pub struct StorageBackend {
    basepath: String,
}

impl StorageBackend {

    pub fn new(basepath: String) -> StorageBackend {
        StorageBackend {
            basepath: basepath,
        }
    }

    fn build<M: Module>(rt: &Runtime, m: &M) -> StorageBackend {
        let path = rt.get_rtp() + m.name() + "/store";
        // TODO: Don't use "/store" but value from configuration
        debug!("Building StorageBackend for {}", path);
        StorageBackend::new(path)
    }

    fn get_file_ids(&self) -> Option<Vec<FileID>> {
        debug!("Getting files from {}", self.basepath);
        let list = glob(&self.basepath[..]);

        if let Ok(globlist) = list {
            let mut v = vec![];
            for entry in globlist {
                if let Ok(path) = entry {
                    debug!(" - File: {:?}", path);
                    v.push(from_pathbuf(&path));
                } else {
                    // Entry is not a path
                }
            }

            Some(v)
        } else {
            None
        }
    }

    pub fn iter_ids(&self, m: &Module) -> Option<IntoIter<FileID>>
    {
        glob(&self.prefix_of_files_for_module(m)[..]).and_then(|globlist| {
            let v = globlist.filter_map(Result::ok)
                            .map(|pbuf| from_pathbuf(&pbuf))
                            .collect::<Vec<FileID>>()
                            .into_iter();
            Ok(v)
        }).ok()
    }

    pub fn iter_files<'a, HP>(&self, m: &'a Module, p: &Parser<HP>)
        -> Option<IntoIter<File<'a>>>
        where HP: FileHeaderParser
    {
        self.iter_ids(m).and_then(|ids| {
            Some(ids.filter_map(|id| self.get_file_by_id(m, &id, p))
                    .collect::<Vec<File>>()
                    .into_iter())
        })
    }

    /*
     * Write a file to disk.
     *
     * The file is moved to this function as the file won't be edited afterwards
     */
    pub fn put_file<HP>(&self, f: File, p: &Parser<HP>) -> BackendOperationResult
        where HP: FileHeaderParser
    {
        let written = write_with_parser(&f, p);
        if written.is_err() { return Err(written.err().unwrap()); }
        let string = written.unwrap();

        let path = self.build_filepath(&f);
        debug!("Writing file: {}", path);
        debug!("      string: {}", string);

        FSFile::create(&path).map(|mut file| {
            debug!("Created file at '{}'", path);
            file.write_all(&string.clone().into_bytes())
                .map_err(|ioerr| {
                    debug!("Could not write file");
                    let mut err = StorageBackendError::build(
                            "File::write_all()",
                            "Could not write out File contents",
                            None
                        );
                    err.caused_by = Some(Box::new(ioerr));
                    err
                })
        }).map_err(|writeerr| {
            debug!("Could not create file at '{}'", path);
            let mut err = StorageBackendError::build(
                "File::create()",
                "Creating file on disk failed",
                None
            );
            err.caused_by = Some(Box::new(writeerr));
            err
        }).and(Ok(()))
    }

    /*
     * Update a file. We have the UUID and can find the file on FS with it and
     * then replace its contents with the contents of the passed file object
     */
    pub fn update_file<HP>(&self, f: File, p: &Parser<HP>) -> BackendOperationResult
        where HP: FileHeaderParser
    {
        let contents = write_with_parser(&f, p);
        if contents.is_err() { return Err(contents.err().unwrap()); }
        let string = contents.unwrap();

        let path = self.build_filepath(&f);
        debug!("Writing file: {}", path);
        debug!("      string: {}", string);

        FSFile::open(&path).map(|mut file| {
            debug!("Open file at '{}'", path);
            file.write_all(&string.clone().into_bytes())
                .map_err(|ioerr| {
                    debug!("Could not write file");
                    let mut err = StorageBackendError::build(
                            "File::write()",
                            "Tried to write contents of this file, though operation did not succeed",
                            Some(string)
                        );
                    err.caused_by = Some(Box::new(ioerr));
                    err
                })
        }).map_err(|writeerr| {
            debug!("Could not write file at '{}'", path);
            let mut err = StorageBackendError::build(
                "File::open()",
                "Tried to update contents of this file, though file doesn't exist",
                None
            );
            err.caused_by = Some(Box::new(writeerr));
            err
        }).and(Ok(()))
    }

    /*
     * Find a file by its ID and return it if found. Return nothing if not
     * found, of course.
     *
     * TODO: Needs refactoring, as there might be an error when reading from
     * disk OR the id just does not exist.
     */
    pub fn get_file_by_id<'a, HP>(&self, id: FileID, p: &Parser<HP>) -> Option<File>
        where HP: FileHeaderParser<'a>
    {
        debug!("Searching for file with id '{}'", id);
        if let Ok(mut fs) = FSFile::open(self.build_filepath_with_id(id.clone())) {
            let mut s = String::new();
            fs.read_to_string(&mut s);
            debug!("Success reading file with id '{}'", id);
            debug!("Parsing to internal structure now");
            p.read(s).and_then(|(h, d)| Ok(File::from_parser_result(id, h, d))).ok()
        } else {
            debug!("No file with id '{}'", id);
            None
        }
    }

    fn build_filepath(&self, f: &File) -> String {
        self.build_filepath_with_id(f.id())
    }

    fn build_filepath_with_id(&self, id: FileID) -> String {
        debug!("Building filepath for id '{}'", id);
        self.basepath.clone() + &id[..]
    }

}

#[derive(Debug)]
pub struct StorageBackendError {
    pub action: String,             // The file system action in words
    pub desc: String,               // A short description
    pub data_dump: Option<String>,  // Data dump, if any
    pub caused_by: Option<Box<Error>>,  // caused from this error
}

impl StorageBackendError {
    fn new(action: String,
           desc  : String,
           data  : Option<String>) -> StorageBackendError
    {
        StorageBackendError {
            action:         action,
            desc:           desc,
            data_dump:      data,
            caused_by:      None,
        }
    }

    fn build(action: &'static str,
             desc:   &'static str,
           data  : Option<String>) -> StorageBackendError
    {
        StorageBackendError {
            action:         String::from(action),
            desc:           String::from(desc),
            dataDump:       data,
            caused_by:      None,
        }
    }

}

impl Error for StorageBackendError {

    fn description(&self) -> &str {
        &self.desc[..]
    }

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

}

impl Display for StorageBackendError {
    fn fmt(&self, f: &mut Formatter) -> FMTResult {
        write!(f, "StorageBackendError[{}]: {}",
               self.action, self.desc)
    }
}

fn write_with_parser<'a, HP>(f: &File, p: &Parser<HP>) -> Result<String, StorageBackendError>
    where HP: FileHeaderParser
{
    p.write(f.contents())
        .or_else(|err| {
            let mut serr = StorageBackendError::build(
                "Parser::write()",
                "Cannot translate internal representation of file contents into on-disk representation",
                None
            );
            serr.caused_by = Some(Box::new(err));
            Err(serr)
        })
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Result as FMTResult;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;
use std::fs::File as FSFile;
use std::fs::create_dir_all;
use std::fs::remove_file;
use std::io::Read;
use std::io::Write;
use std::vec::IntoIter;

use glob::glob;
use glob::Paths;

use storage::file::File;
use storage::file_id::*;
use storage::parser::{FileHeaderParser, Parser, ParserError};

use module::Module;
use runtime::Runtime;

pub type BackendOperationResult<T = ()> = Result<T, StorageBackendError>;

pub struct StorageBackend {
    basepath: String,
    storepath: String,
}

impl StorageBackend {

    pub fn new(rt: &Runtime) -> BackendOperationResult<StorageBackend> {
        let storepath = rt.get_rtp() + "/store/";
        debug!("Trying to create {}", storepath);
        create_dir_all(&storepath).and_then(|_| {
            debug!("Creating succeeded, constructing backend instance");
            Ok(StorageBackend {
                basepath: rt.get_rtp(),
                storepath: storepath.clone(),
            })
        }).or_else(|e| {
            debug!("Creating failed, constructing error instance");
            let mut serr = StorageBackendError::build(
                "create_dir_all()",
                "Could not create store directories",
                Some(storepath)
            );
            serr.caused_by = Some(Box::new(e));
            Err(serr)
        })
    }

    fn get_file_ids(&self, m: &Module) -> Option<Vec<FileID>> {
        let list = glob(&self.prefix_of_files_for_module(m)[..]);

        if let Ok(globlist) = list {
            let mut v = vec![];
            for entry in globlist {
                if let Ok(path) = entry {
                    debug!(" - File: {:?}", path);
                    v.push(FileID::from(&path));
                } else {
                    // Entry is not a path
                }
            }

            Some(v)
        } else {
            None
        }
    }

    pub fn iter_ids(&self, m: &Module) -> Result<IntoIter<FileID>, StorageBackendError>
    {
        glob(&self.prefix_of_files_for_module(m)[..])
            .and_then(|globlist| {
                let v = globlist.filter_map(Result::ok)
                                .map(|pbuf| FileID::from(&pbuf))
                                .collect::<Vec<FileID>>()
                                .into_iter();
                Ok(v)
            })
            .map_err(|e| {
                let serr = StorageBackendError::build(
                        "iter_ids()",
                        "Cannot iter on file ids",
                        None);
                serr
            })
    }

    pub fn iter_files<'a, HP>(&self, m: &'a Module, p: &Parser<HP>)
        -> Option<IntoIter<File<'a>>>
        where HP: FileHeaderParser
    {
        self.iter_ids(m).and_then(|ids| {
            Some(ids.filter_map(|id| self.get_file_by_id(m, &id, p))
                    .collect::<Vec<File>>()
                    .into_iter())
        })
    }

    /*
     * Write a file to disk.
     *
     * The file is moved to this function as the file won't be edited afterwards
     */
    pub fn put_file<HP>(&self, f: File, p: &Parser<HP>) -> BackendOperationResult
        where HP: FileHeaderParser
    {
        let written = write_with_parser(&f, p);
        if written.is_err() { return Err(written.err().unwrap()); }
        let string = written.unwrap();

        let path = self.build_filepath(&f);
        debug!("Writing file: {}", path);
        debug!("      string: {}", string);

        FSFile::create(&path).map(|mut file| {
            debug!("Created file at '{}'", path);
            file.write_all(&string.clone().into_bytes())
                .map_err(|ioerr| {
                    debug!("Could not write file");
                    let mut err = StorageBackendError::build(
                            "File::write_all()",
                            "Could not write out File contents",
                            None
                        );
                    err.caused_by = Some(Box::new(ioerr));
                    err
                })
        }).map_err(|writeerr| {
            debug!("Could not create file at '{}'", path);
            let mut err = StorageBackendError::build(
                "File::create()",
                "Creating file on disk failed",
                None
            );
            err.caused_by = Some(Box::new(writeerr));
            err
        }).and(Ok(()))
    }

    /*
     * Update a file. We have the UUID and can find the file on FS with it and
     * then replace its contents with the contents of the passed file object
     */
    pub fn update_file<HP>(&self, f: File, p: &Parser<HP>) -> BackendOperationResult
        where HP: FileHeaderParser
    {
        let contents = write_with_parser(&f, p);
        if contents.is_err() { return Err(contents.err().unwrap()); }
        let string = contents.unwrap();

        let path = self.build_filepath(&f);
        debug!("Writing file: {}", path);
        debug!("      string: {}", string);

        FSFile::open(&path).map(|mut file| {
            debug!("Open file at '{}'", path);
            file.write_all(&string.clone().into_bytes())
                .map_err(|ioerr| {
                    debug!("Could not write file");
                    let mut err = StorageBackendError::build(
                            "File::write()",
                            "Tried to write contents of this file, though operation did not succeed",
                            Some(string)
                        );
                    err.caused_by = Some(Box::new(ioerr));
                    err
                })
        }).map_err(|writeerr| {
            debug!("Could not write file at '{}'", path);
            let mut err = StorageBackendError::build(
                "File::open()",
                "Tried to update contents of this file, though file doesn't exist",
                None
            );
            err.caused_by = Some(Box::new(writeerr));
            err
        }).and(Ok(()))
    }

    /*
     * Find a file by its ID and return it if found. Return nothing if not
     * found, of course.
     *
     * TODO: Needs refactoring, as there might be an error when reading from
     * disk OR the id just does not exist.
     */
    pub fn get_file_by_id<'a, HP>(&self, m: &'a Module, id: &FileID, p: &Parser<HP>) -> Option<File<'a>>
        where HP: FileHeaderParser
    {
        debug!("Searching for file with id '{}'", id);
        if let Ok(mut fs) = FSFile::open(self.build_filepath_with_id(m, id.clone())) {
            let mut s = String::new();
            fs.read_to_string(&mut s);
            debug!("Success reading file with id '{}'", id);
            debug!("Parsing to internal structure now");
            p.read(s).and_then(|(h, d)| Ok(File::from_parser_result(m, id.clone(), h, d))).ok()
        } else {
            debug!("No file with id '{}'", id);
            None
        }
    }

    pub fn remove_file(&self, m: &Module, file: File, checked: bool) -> BackendOperationResult {
        if checked {
            error!("Checked remove not implemented yet. I will crash now");
            unimplemented!()
        }

        debug!("Doing unchecked remove");
        info!("Going to remove file: {}", file);

        let fp = self.build_filepath(&file);
        remove_file(fp).map_err(|e| {
            let mut serr = StorageBackendError::build(
                "remove_file()",
                "File removal failed",
                Some(format!("{}", file))
            );
            serr.caused_by = Some(Box::new(e));
            serr
        })
    }

    fn build_filepath(&self, f: &File) -> String {
        self.build_filepath_with_id(f.owner(), f.id())
    }

    fn build_filepath_with_id(&self, owner: &Module, id: FileID) -> String {
        let idstr   : String        = id.clone().into();
        let idtype  : FileIDType    = id.into();
        let typestr : String        = idtype.into();

        debug!("Building filepath with id");
        debug!("  basepath: '{}'", self.basepath);
        debug!(" storepath: '{}'", self.storepath);
        debug!("        id: '{}'", idstr);
        debug!("      type: '{}'", typestr);

        self.prefix_of_files_for_module(owner) +
            "-" + &typestr[..] +
            "-" + &idstr[..] +
            ".imag"
    }

    fn prefix_of_files_for_module(&self, m: &Module) -> String {
        self.storepath.clone() + m.name()
    }

}

#[derive(Debug)]
pub struct StorageBackendError {
    pub action: String,             // The file system action in words
    pub desc: String,               // A short description
    pub data_dump: Option<String>,  // Data dump, if any
    pub caused_by: Option<Box<Error>>,  // caused from this error
}

impl StorageBackendError {
    fn new(action: String,
           desc  : String,
           data  : Option<String>) -> StorageBackendError
    {
        StorageBackendError {
            action:         action,
            desc:           desc,
            data_dump:      data,
            caused_by:      None,
        }
    }

    fn build(action: &'static str,
             desc:   &'static str,
             data  : Option<String>) -> StorageBackendError
    {
        StorageBackendError {
            action:         String::from(action),
            desc:           String::from(desc),
            data_dump:      data,
            caused_by:      None,
        }
    }

}

impl Error for StorageBackendError {

    fn description(&self) -> &str {
        &self.desc[..]
    }

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

}

impl<'a> Display for StorageBackendError {
    fn fmt(&self, f: &mut Formatter) -> FMTResult {
        write!(f, "StorageBackendError[{}]: {}",
               self.action, self.desc)
    }
}

fn write_with_parser<'a, HP>(f: &File, p: &Parser<HP>) -> Result<String, StorageBackendError>
    where HP: FileHeaderParser
{
    p.write(f.contents())
        .or_else(|err| {
            let mut serr = StorageBackendError::build(
                "Parser::write()",
                "Cannot translate internal representation of file contents into on-disk representation",
                None
            );
            serr.caused_by = Some(Box::new(err));
            Err(serr)
        })
}
<commit_msg>StorageBackend::iter_files() return Result<> with StorageBackendError on error<commit_after>use std::error::Error;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Result as FMTResult;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;
use std::fs::File as FSFile;
use std::fs::create_dir_all;
use std::fs::remove_file;
use std::io::Read;
use std::io::Write;
use std::vec::IntoIter;

use glob::glob;
use glob::Paths;

use storage::file::File;
use storage::file_id::*;
use storage::parser::{FileHeaderParser, Parser, ParserError};

use module::Module;
use runtime::Runtime;

pub type BackendOperationResult<T = ()> = Result<T, StorageBackendError>;

pub struct StorageBackend {
    basepath: String,
    storepath: String,
}

impl StorageBackend {

    pub fn new(rt: &Runtime) -> BackendOperationResult<StorageBackend> {
        let storepath = rt.get_rtp() + "/store/";
        debug!("Trying to create {}", storepath);
        create_dir_all(&storepath).and_then(|_| {
            debug!("Creating succeeded, constructing backend instance");
            Ok(StorageBackend {
                basepath: rt.get_rtp(),
                storepath: storepath.clone(),
            })
        }).or_else(|e| {
            debug!("Creating failed, constructing error instance");
            let mut serr = StorageBackendError::build(
                "create_dir_all()",
                "Could not create store directories",
                Some(storepath)
            );
            serr.caused_by = Some(Box::new(e));
            Err(serr)
        })
    }

    fn get_file_ids(&self, m: &Module) -> Option<Vec<FileID>> {
        let list = glob(&self.prefix_of_files_for_module(m)[..]);

        if let Ok(globlist) = list {
            let mut v = vec![];
            for entry in globlist {
                if let Ok(path) = entry {
                    debug!(" - File: {:?}", path);
                    v.push(FileID::from(&path));
                } else {
                    // Entry is not a path
                }
            }

            Some(v)
        } else {
            None
        }
    }

    pub fn iter_ids(&self, m: &Module) -> Result<IntoIter<FileID>, StorageBackendError>
    {
        glob(&self.prefix_of_files_for_module(m)[..])
            .and_then(|globlist| {
                let v = globlist.filter_map(Result::ok)
                                .map(|pbuf| FileID::from(&pbuf))
                                .collect::<Vec<FileID>>()
                                .into_iter();
                Ok(v)
            })
            .map_err(|e| {
                let serr = StorageBackendError::build(
                        "iter_ids()",
                        "Cannot iter on file ids",
                        None);
                serr
            })
    }

    pub fn iter_files<'a, HP>(&self, m: &'a Module, p: &Parser<HP>)
        -> Result<IntoIter<File<'a>>, StorageBackendError>
        where HP: FileHeaderParser
    {
        self.iter_ids(m)
            .and_then(|ids| {
                Ok(ids.filter_map(|id| self.get_file_by_id(m, &id, p))
                        .collect::<Vec<File>>()
                        .into_iter())
            })
            .map_err(|e| {
                let serr = StorageBackendError::build(
                        "iter_files()",
                        "Cannot iter on files",
                        None);
                serr
            })
    }

    /*
     * Write a file to disk.
     *
     * The file is moved to this function as the file won't be edited afterwards
     */
    pub fn put_file<HP>(&self, f: File, p: &Parser<HP>) -> BackendOperationResult
        where HP: FileHeaderParser
    {
        let written = write_with_parser(&f, p);
        if written.is_err() { return Err(written.err().unwrap()); }
        let string = written.unwrap();

        let path = self.build_filepath(&f);
        debug!("Writing file: {}", path);
        debug!("      string: {}", string);

        FSFile::create(&path).map(|mut file| {
            debug!("Created file at '{}'", path);
            file.write_all(&string.clone().into_bytes())
                .map_err(|ioerr| {
                    debug!("Could not write file");
                    let mut err = StorageBackendError::build(
                            "File::write_all()",
                            "Could not write out File contents",
                            None
                        );
                    err.caused_by = Some(Box::new(ioerr));
                    err
                })
        }).map_err(|writeerr| {
            debug!("Could not create file at '{}'", path);
            let mut err = StorageBackendError::build(
                "File::create()",
                "Creating file on disk failed",
                None
            );
            err.caused_by = Some(Box::new(writeerr));
            err
        }).and(Ok(()))
    }

    /*
     * Update a file. We have the UUID and can find the file on FS with it and
     * then replace its contents with the contents of the passed file object
     */
    pub fn update_file<HP>(&self, f: File, p: &Parser<HP>) -> BackendOperationResult
        where HP: FileHeaderParser
    {
        let contents = write_with_parser(&f, p);
        if contents.is_err() { return Err(contents.err().unwrap()); }
        let string = contents.unwrap();

        let path = self.build_filepath(&f);
        debug!("Writing file: {}", path);
        debug!("      string: {}", string);

        FSFile::open(&path).map(|mut file| {
            debug!("Open file at '{}'", path);
            file.write_all(&string.clone().into_bytes())
                .map_err(|ioerr| {
                    debug!("Could not write file");
                    let mut err = StorageBackendError::build(
                            "File::write()",
                            "Tried to write contents of this file, though operation did not succeed",
                            Some(string)
                        );
                    err.caused_by = Some(Box::new(ioerr));
                    err
                })
        }).map_err(|writeerr| {
            debug!("Could not write file at '{}'", path);
            let mut err = StorageBackendError::build(
                "File::open()",
                "Tried to update contents of this file, though file doesn't exist",
                None
            );
            err.caused_by = Some(Box::new(writeerr));
            err
        }).and(Ok(()))
    }

    /*
     * Find a file by its ID and return it if found. Return nothing if not
     * found, of course.
     *
     * TODO: Needs refactoring, as there might be an error when reading from
     * disk OR the id just does not exist.
     */
    pub fn get_file_by_id<'a, HP>(&self, m: &'a Module, id: &FileID, p: &Parser<HP>) -> Option<File<'a>>
        where HP: FileHeaderParser
    {
        debug!("Searching for file with id '{}'", id);
        if let Ok(mut fs) = FSFile::open(self.build_filepath_with_id(m, id.clone())) {
            let mut s = String::new();
            fs.read_to_string(&mut s);
            debug!("Success reading file with id '{}'", id);
            debug!("Parsing to internal structure now");
            p.read(s).and_then(|(h, d)| Ok(File::from_parser_result(m, id.clone(), h, d))).ok()
        } else {
            debug!("No file with id '{}'", id);
            None
        }
    }

    pub fn remove_file(&self, m: &Module, file: File, checked: bool) -> BackendOperationResult {
        if checked {
            error!("Checked remove not implemented yet. I will crash now");
            unimplemented!()
        }

        debug!("Doing unchecked remove");
        info!("Going to remove file: {}", file);

        let fp = self.build_filepath(&file);
        remove_file(fp).map_err(|e| {
            let mut serr = StorageBackendError::build(
                "remove_file()",
                "File removal failed",
                Some(format!("{}", file))
            );
            serr.caused_by = Some(Box::new(e));
            serr
        })
    }

    fn build_filepath(&self, f: &File) -> String {
        self.build_filepath_with_id(f.owner(), f.id())
    }

    fn build_filepath_with_id(&self, owner: &Module, id: FileID) -> String {
        let idstr   : String        = id.clone().into();
        let idtype  : FileIDType    = id.into();
        let typestr : String        = idtype.into();

        debug!("Building filepath with id");
        debug!("  basepath: '{}'", self.basepath);
        debug!(" storepath: '{}'", self.storepath);
        debug!("        id: '{}'", idstr);
        debug!("      type: '{}'", typestr);

        self.prefix_of_files_for_module(owner) +
            "-" + &typestr[..] +
            "-" + &idstr[..] +
            ".imag"
    }

    fn prefix_of_files_for_module(&self, m: &Module) -> String {
        self.storepath.clone() + m.name()
    }

}

#[derive(Debug)]
pub struct StorageBackendError {
    pub action: String,             // The file system action in words
    pub desc: String,               // A short description
    pub data_dump: Option<String>,  // Data dump, if any
    pub caused_by: Option<Box<Error>>,  // caused from this error
}

impl StorageBackendError {
    fn new(action: String,
           desc  : String,
           data  : Option<String>) -> StorageBackendError
    {
        StorageBackendError {
            action:         action,
            desc:           desc,
            data_dump:      data,
            caused_by:      None,
        }
    }

    fn build(action: &'static str,
             desc:   &'static str,
             data  : Option<String>) -> StorageBackendError
    {
        StorageBackendError {
            action:         String::from(action),
            desc:           String::from(desc),
            data_dump:      data,
            caused_by:      None,
        }
    }

}

impl Error for StorageBackendError {

    fn description(&self) -> &str {
        &self.desc[..]
    }

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

}

impl<'a> Display for StorageBackendError {
    fn fmt(&self, f: &mut Formatter) -> FMTResult {
        write!(f, "StorageBackendError[{}]: {}",
               self.action, self.desc)
    }
}

fn write_with_parser<'a, HP>(f: &File, p: &Parser<HP>) -> Result<String, StorageBackendError>
    where HP: FileHeaderParser
{
    p.write(f.contents())
        .or_else(|err| {
            let mut serr = StorageBackendError::build(
                "Parser::write()",
                "Cannot translate internal representation of file contents into on-disk representation",
                None
            );
            serr.caused_by = Some(Box::new(err));
            Err(serr)
        })
}
<|endoftext|>