<commit_before>extern crate env_logger;
#[macro_use(quick_main)]
extern crate error_chain;
extern crate futures;
extern crate hubcaps;
extern crate tokio_core;

use std::env;

use futures::Stream;
use tokio_core::reactor::Core;

use hubcaps::{Credentials, Github, Result};

quick_main!(run);

fn run() -> Result<()> {
    drop(env_logger::init());
    match env::var("GITHUB_TOKEN").ok() {
        Some(token) => {
            let mut core = Core::new()?;
            let github = Github::new(
                concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")),
                Some(Credentials::Token(token)),
                &core.handle(),
            );
            // get labels associated with a pull
            println!(
                "{:#?}",
                core.run(
                    github
                        .repo("softprops", "hubcaps")
                        .pulls()
                        .get(121)
                        .labels()
                        .add(vec!["enhancement"])
                )?
            );
            // stream over all labels defined for a repo
            core.run(
                github
                    .repo("rust-lang", "cargo")
                    .labels()
                    .iter()
                    .for_each(move |label| Ok(println!("{}", label.name))),
            )?;
            Ok(())
        }
        _ => Err("example missing GITHUB_TOKEN".into()),
    }
}
<commit_msg>update comment<commit_after>extern crate env_logger;
#[macro_use(quick_main)]
extern crate error_chain;
extern crate futures;
extern crate hubcaps;
extern crate tokio_core;

use std::env;

use futures::Stream;
use tokio_core::reactor::Core;

use hubcaps::{Credentials, Github, Result};

quick_main!(run);

fn run() -> Result<()> {
    drop(env_logger::init());
    match env::var("GITHUB_TOKEN").ok() {
        Some(token) => {
            let mut core = Core::new()?;
            let github = Github::new(
                concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")),
                Some(Credentials::Token(token)),
                &core.handle(),
            );
            // add labels associated with a pull
            println!(
                "{:#?}",
                core.run(
                    github
                        .repo("softprops", "hubcaps")
                        .pulls()
                        .get(121)
                        .labels()
                        .add(vec!["enhancement"])
                )?
            );
            // stream over all labels defined for a repo
            core.run(
                github
                    .repo("rust-lang", "cargo")
                    .labels()
                    .iter()
                    .for_each(move |label| Ok(println!("{}", label.name))),
            )?;
            Ok(())
        }
        _ => Err("example missing GITHUB_TOKEN".into()),
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate polydraw;

use std::ptr;

use polydraw::platform::x11::xlib::ffi::XOpenDisplay;

fn main() {
   let display = unsafe { XOpenDisplay(ptr::null()) };

   let screen = DefaultScreen!(display);

   println!("{:?}", screen)
}
<commit_msg>Checking for null display in the window example<commit_after>#[macro_use]
extern crate polydraw;

use std::ptr;

use polydraw::platform::x11::xlib::ffi::XOpenDisplay;

fn main() {
   let display = unsafe { XOpenDisplay(ptr::null()) };
   if display.is_null() {
      println!("Can't open display");
      return;
   }

   let default_screen = DefaultScreen!(display);

   println!("{:?}", default_screen)
}
<|endoftext|>


<commit_before>extern crate serde_json;
extern crate zmq;

use std::cell::RefCell;
use std::collections::HashMap;
use std::{fs, thread};
use std::io::{Error, ErrorKind, Write};
use std::path::{Path, PathBuf};

use commands::{Command, CommandExecutor};
use commands::pool::PoolCommand;
use errors::pool::PoolError;
use utils::sequence::SequenceUtils;
use utils::environment::EnvironmentUtils;

pub struct PoolService {
    pools: RefCell<HashMap<i32, Pool>>,
}

struct Pool {
    name: String,
    id: i32,
    send_sock: zmq::Socket,
    worker: Option<thread::JoinHandle<()>>,
}

impl Pool {
    pub fn new(name: &str, cmd_id: i32) -> Result<Pool, PoolError> {
        let zmq_ctx = zmq::Context::new();
        let zmq_ctx = zmq::Context::new();
        let recv_cmd_sock = zmq_ctx.socket(zmq::SocketType::PAIR)?;
        let send_cmd_sock = zmq_ctx.socket(zmq::SocketType::PAIR)?;
        let inproc_sock_name: String = format!("inproc://pool_{}", name);

        recv_cmd_sock.bind(inproc_sock_name.as_str())?;

        send_cmd_sock.connect(inproc_sock_name.as_str())?;
        let pool_id = SequenceUtils::get_next_id();

        Ok(Pool {
            name: name.to_string(),
            id: pool_id,
            send_sock: send_cmd_sock,
            worker: Some(thread::spawn(move || {
                let mut socks_to_poll: [zmq::PollItem; 1] = [
                    recv_cmd_sock.as_poll_item(zmq::POLLIN),
                ];
                CommandExecutor::instance().send(Command::Pool(
                    PoolCommand::OpenAck(cmd_id, Ok(pool_id)))); //TODO send only after catch-up?
                loop {
                    trace!("zmq poll loop >>");
                    let r = zmq::poll(&mut socks_to_poll, -1);
                    //FIXME implement
                    trace!("zmq poll loop << ret {:?}, at cmd sock {:?}", r, recv_cmd_sock.recv_string(0));
                }
            })),
        })
    }
}

impl Drop for Pool {
    fn drop(&mut self) {
        let target = format!("pool{}", self.name);
        info!(target: target.as_str(), "Drop started");
        self.send_sock.send("exit".as_bytes(), 0); //TODO
        // Option worker type and this kludge is workaround for rust
        self.worker.take().unwrap().join().unwrap();
        info!(target: target.as_str(), "Drop finished");
    }
}

#[derive(Serialize, Deserialize)]
struct PoolConfig {
    genesis_txn: String
}

impl PoolConfig {
    fn default(name: &str) -> PoolConfig {
        let mut txn = name.to_string();
        txn += ".txn";
        PoolConfig { genesis_txn: txn }
    }
}

impl PoolService {
    pub fn new() -> PoolService {
        PoolService {
            pools: RefCell::new(HashMap::new()),
        }
    }

    pub fn create(&self, name: &str, config: Option<&str>) -> Result<(), PoolError> {
        let mut path = EnvironmentUtils::pool_path(name);
        let pool_config: PoolConfig = match config {
            Some(config) => serde_json::from_str(config)?,
            None => PoolConfig::default(name)
        };

        if path.as_path().exists() {
            return Err(PoolError::NotCreated("Already created".to_string()));
        }

        fs::create_dir_all(path.as_path());

        path.push(name);
        path.set_extension("txn");
        fs::copy(&pool_config.genesis_txn, path.as_path())?;
        path.pop();

        path.push("config");
        path.set_extension("json");
        let mut f: fs::File = fs::File::create(path.as_path())?;
        f.write(serde_json::to_string(&pool_config)?.as_bytes())?;
        f.flush()?;

        // TODO probably create another one file pool.json with pool description,
        // but now there is no info to save (except name witch equal to directory)

        Ok(())
    }

    pub fn delete(&self, name: &str) -> Result<(), PoolError> {
        unimplemented!()
    }

    pub fn open(&self, name: &str, config: Option<&str>) -> Result<i32, PoolError> {
        for pool in self.pools.borrow().values() {
            if name.eq(pool.name.as_str()) {
                //TODO change error
                return Err(PoolError::InvalidHandle("Already opened".to_string()));
            }
        }

        let cmd_id: i32 = SequenceUtils::get_next_id();
        let new_pool = Pool::new(name, cmd_id)?;
        //FIXME process config: check None (use default), transfer to Pool instance

        self.pools.borrow_mut().insert(new_pool.id, new_pool);
        return Ok(cmd_id);
    }

    pub fn close(&self, handle: i32) -> Result<(), PoolError> {
        unimplemented!()
    }

    pub fn refresh(&self, handle: i32) -> Result<(), PoolError> {
        unimplemented!()
    }

    pub fn get_pool_name(&self, handle: i32) -> Result<String, PoolError> {
        self.pools.borrow().get(&handle).map_or(
            Err(PoolError::InvalidHandle("Doesn't exists".to_string())),
            |pool: &Pool| Ok(pool.name.clone()))
    }
}

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

    use std::cell::RefCell;

    pub struct PoolService {
        create_results: RefCell<Vec<Result<(), PoolError>>>,
        delete_results: RefCell<Vec<Result<(), PoolError>>>,
        open_results: RefCell<Vec<Result<i32, PoolError>>>,
        close_results: RefCell<Vec<Result<(), PoolError>>>,
        refresh_results: RefCell<Vec<Result<(), PoolError>>>
    }

    impl PoolService {
        pub fn new() -> PoolService {
            PoolService {
                create_results: RefCell::new(Vec::new()),
                delete_results: RefCell::new(Vec::new()),
                open_results: RefCell::new(Vec::new()),
                close_results: RefCell::new(Vec::new()),
                refresh_results: RefCell::new(Vec::new())
            }
        }

        pub fn create(&self, name: &str, config: &str) -> Result<(), PoolError> {
            //self.create_results.pop().unwrap()
            unimplemented!()
        }

        pub fn delete(&self, name: &str) -> Result<(), PoolError> {
            //self.delete_results.pop().unwrap()
            unimplemented!()
        }

        pub fn open(&self, name: &str, config: &str) -> Result<i32, PoolError> {
            //self.open_results.pop().unwrap()
            unimplemented!()
        }

        pub fn close(&self, handle: i32) -> Result<(), PoolError> {
            //self.close_results.pop().unwrap()
            unimplemented!()
        }

        pub fn refresh(&self, handle: i32) -> Result<(), PoolError> {
            //self.refresh_results.pop().unwrap()
            unimplemented!()
        }
    }
}

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

    #[test]
    fn pool_service_can_be_created() {
        let pool_service = PoolService::new();
        assert!(true, "No crashes on PoolService::new");
    }

    #[test]
    fn pool_service_can_be_dropped() {
        fn drop_test() {
            let pool_service = PoolService::new();
        }

        drop_test();
        assert!(true, "No crashes on PoolService::drop");
    }
}<commit_msg>Clean-up: fix warnings.<commit_after>extern crate serde_json;
extern crate zmq;

use std::cell::RefCell;
use std::collections::HashMap;
use std::{fs, thread};
use std::io::Write;

use commands::{Command, CommandExecutor};
use commands::pool::PoolCommand;
use errors::pool::PoolError;
use utils::sequence::SequenceUtils;
use utils::environment::EnvironmentUtils;

pub struct PoolService {
    pools: RefCell<HashMap<i32, Pool>>,
}

struct Pool {
    name: String,
    id: i32,
    send_sock: zmq::Socket,
    worker: Option<thread::JoinHandle<()>>,
}

impl Pool {
    pub fn new(name: &str, cmd_id: i32) -> Result<Pool, PoolError> {
        let zmq_ctx = zmq::Context::new();
        let zmq_ctx = zmq::Context::new();
        let recv_cmd_sock = zmq_ctx.socket(zmq::SocketType::PAIR)?;
        let send_cmd_sock = zmq_ctx.socket(zmq::SocketType::PAIR)?;
        let inproc_sock_name: String = format!("inproc://pool_{}", name);

        recv_cmd_sock.bind(inproc_sock_name.as_str())?;

        send_cmd_sock.connect(inproc_sock_name.as_str())?;
        let pool_id = SequenceUtils::get_next_id();

        Ok(Pool {
            name: name.to_string(),
            id: pool_id,
            send_sock: send_cmd_sock,
            worker: Some(thread::spawn(move || {
                let mut socks_to_poll: [zmq::PollItem; 1] = [
                    recv_cmd_sock.as_poll_item(zmq::POLLIN),
                ];
                CommandExecutor::instance().send(Command::Pool(
                    PoolCommand::OpenAck(cmd_id, Ok(pool_id)))).expect("send ack cmd"); //TODO send only after catch-up?
                loop {
                    trace!("zmq poll loop >>");
                    let r = zmq::poll(&mut socks_to_poll, -1);
                    //FIXME implement
                    trace!("zmq poll loop << ret {:?}, at cmd sock {:?}", r, recv_cmd_sock.recv_string(0));
                }
            })),
        })
    }
}

impl Drop for Pool {
    fn drop(&mut self) {
        let target = format!("pool{}", self.name);
        info!(target: target.as_str(), "Drop started");
        self.send_sock.send("exit".as_bytes(), 0).expect("send exit command"); //TODO
        info!(target: target.as_str(), "Drop wait worker");
        // Option worker type and this kludge is workaround for rust
        self.worker.take().unwrap().join().unwrap();
        info!(target: target.as_str(), "Drop finished");
    }
}

#[derive(Serialize, Deserialize)]
struct PoolConfig {
    genesis_txn: String
}

impl PoolConfig {
    fn default(name: &str) -> PoolConfig {
        let mut txn = name.to_string();
        txn += ".txn";
        PoolConfig { genesis_txn: txn }
    }
}

impl PoolService {
    pub fn new() -> PoolService {
        PoolService {
            pools: RefCell::new(HashMap::new()),
        }
    }

    pub fn create(&self, name: &str, config: Option<&str>) -> Result<(), PoolError> {
        let mut path = EnvironmentUtils::pool_path(name);
        let pool_config: PoolConfig = match config {
            Some(config) => serde_json::from_str(config)?,
            None => PoolConfig::default(name)
        };

        if path.as_path().exists() {
            return Err(PoolError::NotCreated("Already created".to_string()));
        }

        fs::create_dir_all(path.as_path())?;

        path.push(name);
        path.set_extension("txn");
        fs::copy(&pool_config.genesis_txn, path.as_path())?;
        path.pop();

        path.push("config");
        path.set_extension("json");
        let mut f: fs::File = fs::File::create(path.as_path())?;
        f.write(serde_json::to_string(&pool_config)?.as_bytes())?;
        f.flush()?;

        // TODO probably create another one file pool.json with pool description,
        // but now there is no info to save (except name witch equal to directory)

        Ok(())
    }

    pub fn delete(&self, name: &str) -> Result<(), PoolError> {
        unimplemented!()
    }

    pub fn open(&self, name: &str, config: Option<&str>) -> Result<i32, PoolError> {
        for pool in self.pools.borrow().values() {
            if name.eq(pool.name.as_str()) {
                //TODO change error
                return Err(PoolError::InvalidHandle("Already opened".to_string()));
            }
        }

        let cmd_id: i32 = SequenceUtils::get_next_id();
        let new_pool = Pool::new(name, cmd_id)?;
        //FIXME process config: check None (use default), transfer to Pool instance

        self.pools.borrow_mut().insert(new_pool.id, new_pool);
        return Ok(cmd_id);
    }

    pub fn close(&self, handle: i32) -> Result<(), PoolError> {
        unimplemented!()
    }

    pub fn refresh(&self, handle: i32) -> Result<(), PoolError> {
        unimplemented!()
    }

    pub fn get_pool_name(&self, handle: i32) -> Result<String, PoolError> {
        self.pools.borrow().get(&handle).map_or(
            Err(PoolError::InvalidHandle("Doesn't exists".to_string())),
            |pool: &Pool| Ok(pool.name.clone()))
    }
}

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

    use std::cell::RefCell;

    pub struct PoolService {
        create_results: RefCell<Vec<Result<(), PoolError>>>,
        delete_results: RefCell<Vec<Result<(), PoolError>>>,
        open_results: RefCell<Vec<Result<i32, PoolError>>>,
        close_results: RefCell<Vec<Result<(), PoolError>>>,
        refresh_results: RefCell<Vec<Result<(), PoolError>>>
    }

    impl PoolService {
        pub fn new() -> PoolService {
            PoolService {
                create_results: RefCell::new(Vec::new()),
                delete_results: RefCell::new(Vec::new()),
                open_results: RefCell::new(Vec::new()),
                close_results: RefCell::new(Vec::new()),
                refresh_results: RefCell::new(Vec::new())
            }
        }

        pub fn create(&self, name: &str, config: &str) -> Result<(), PoolError> {
            //self.create_results.pop().unwrap()
            unimplemented!()
        }

        pub fn delete(&self, name: &str) -> Result<(), PoolError> {
            //self.delete_results.pop().unwrap()
            unimplemented!()
        }

        pub fn open(&self, name: &str, config: &str) -> Result<i32, PoolError> {
            //self.open_results.pop().unwrap()
            unimplemented!()
        }

        pub fn close(&self, handle: i32) -> Result<(), PoolError> {
            //self.close_results.pop().unwrap()
            unimplemented!()
        }

        pub fn refresh(&self, handle: i32) -> Result<(), PoolError> {
            //self.refresh_results.pop().unwrap()
            unimplemented!()
        }
    }
}

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

    #[test]
    fn pool_service_can_be_created() {
        let pool_service = PoolService::new();
        assert!(true, "No crashes on PoolService::new");
    }

    #[test]
    fn pool_service_can_be_dropped() {
        fn drop_test() {
            let pool_service = PoolService::new();
        }

        drop_test();
        assert!(true, "No crashes on PoolService::drop");
    }
}<|endoftext|>


<commit_before>#![feature(test)]

#[macro_use]
extern crate lazy_static;
extern crate serde_json;
extern crate test;

use std::fs::File;
use std::io::{BufRead, BufReader, Read};
use std::path::{Path, PathBuf};
use test::Bencher;

use serde_json::{self as json};

extern crate hyphenation;
use hyphenation::{load, Corpus, Hyphenation, Language};
use hyphenation::exception::{Exceptions};
use hyphenation::pattern::{Patterns};

fn fiat_io(lang: Language) -> Corpus {
    hyphenation::set_pattern_folder(&DATAPATH.as_path());
    load::language(lang).unwrap()
}

lazy_static! {
    static ref DATAPATH: PathBuf = {
        let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        path.push("patterns");

        path
    };

    static ref EN_US: Corpus = fiat_io(Language::English_US);

    static ref WORDS: Vec<String> = {
        let file = File::open(Path::new("/usr/share/dict/words")).unwrap();
        let words: Vec<_> = BufReader::new(file).lines().map(|l| l.unwrap()).collect();

        words
    };
}

#[bench]
fn parse_patterns_en_us(b: &mut Bencher) {
    hyphenation::set_pattern_folder(DATAPATH.as_path());
    let mut f = load::data_file(Language::English_US, "pat").unwrap();
    let mut buffer = Vec::new();
    f.read_to_end(&mut buffer);

    let mut ps = Patterns::empty();
    b.iter(|| {
        let pairs: Vec<(String, Vec<u32>)> = json::from_slice(&buffer).unwrap();
        for p in pairs {
            ps.insert(p);
        }
    });
}

#[bench]
fn parse_exceptions_en_us(b: &mut Bencher) {
    hyphenation::set_pattern_folder(DATAPATH.as_path());
    let mut f = load::data_file(Language::English_US, "hyp").unwrap();
    let mut buffer = Vec::new();
    f.read_to_end(&mut buffer);

    let mut exs = Exceptions::empty();
    b.iter(|| {
        let pairs: Vec<(String, Vec<u32>)> = json::from_slice(&buffer).unwrap();
        for ex in pairs {
            exs.insert(ex);
        }
    });
}

#[bench]
fn opportunities_words(b: &mut Bencher) {
    hyphenation::set_pattern_folder(DATAPATH.as_path());
    let mut ws = WORDS.iter();

    b.iter(|| {
        for w in ws.by_ref() {
            w.opportunities(&EN_US);
        }
    })
}

#[bench]
fn hyphenate_words(b: &mut Bencher) {
    hyphenation::set_pattern_folder(DATAPATH.as_path());
    let mut ws = WORDS.iter();

    b.iter(|| {
        for w in ws.by_ref() {
            w.hyphenate(&EN_US).count();
        }
    })
}
<commit_msg>Trivial renaming in benchmarks<commit_after>#![feature(test)]

#[macro_use]
extern crate lazy_static;
extern crate serde_json;
extern crate test;

use std::fs::File;
use std::io::{BufRead, BufReader, Read};
use std::path::{Path, PathBuf};
use test::Bencher;

use serde_json::{self as json};

extern crate hyphenation;
use hyphenation::{load, Corpus, Hyphenation, Language};
use hyphenation::exception::{Exceptions};
use hyphenation::pattern::{Patterns};

fn fiat_io(lang: Language) -> Corpus {
    hyphenation::set_pattern_folder(&DATAPATH.as_path());
    load::language(lang).unwrap()
}

lazy_static! {
    static ref DATAPATH: PathBuf = {
        let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        path.push("patterns");

        path
    };

    static ref EN_US: Corpus = fiat_io(Language::English_US);

    static ref WORDS: Vec<String> = {
        let file = File::open(Path::new("/usr/share/dict/words")).unwrap();
        let words: Vec<_> = BufReader::new(file).lines().map(|l| l.unwrap()).collect();

        words
    };
}

#[bench]
fn parse_patterns_en_us(b: &mut Bencher) {
    hyphenation::set_pattern_folder(DATAPATH.as_path());
    let mut f = load::data_file(Language::English_US, "pat").unwrap();
    let mut buffer = Vec::new();
    f.read_to_end(&mut buffer);

    let mut ps = Patterns::empty();
    b.iter(|| {
        let pairs: Vec<(String, Vec<u32>)> = json::from_slice(&buffer).unwrap();
        for p in pairs {
            ps.insert(p);
        }
    });
}

#[bench]
fn parse_exceptions_en_us(b: &mut Bencher) {
    hyphenation::set_pattern_folder(DATAPATH.as_path());
    let mut f = load::data_file(Language::English_US, "hyp").unwrap();
    let mut buffer = Vec::new();
    f.read_to_end(&mut buffer);

    let mut exs = Exceptions::empty();
    b.iter(|| {
        let pairs: Vec<(String, Vec<u32>)> = json::from_slice(&buffer).unwrap();
        for ex in pairs {
            exs.insert(ex);
        }
    });
}

#[bench]
fn opportunities_en_us(b: &mut Bencher) {
    hyphenation::set_pattern_folder(DATAPATH.as_path());
    let mut ws = WORDS.iter();

    b.iter(|| {
        for w in ws.by_ref() {
            w.opportunities(&EN_US);
        }
    })
}

#[bench]
fn hyphenate_en_us(b: &mut Bencher) {
    hyphenation::set_pattern_folder(DATAPATH.as_path());
    let mut ws = WORDS.iter();

    b.iter(|| {
        for w in ws.by_ref() {
            w.hyphenate(&EN_US).count();
        }
    })
}
<|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/. */

use dom::bindings::codegen::Bindings::PermissionStatusBinding::{PermissionDescriptor, PermissionName, PermissionState};
use dom::bindings::codegen::Bindings::PermissionsBinding::{self, PermissionsMethods};
use dom::bindings::error::Error;
use dom::bindings::js::Root;
use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
use dom::bluetooth::Bluetooth;
use dom::bluetoothpermissionresult::BluetoothPermissionResult;
use dom::globalscope::GlobalScope;
use dom::permissionstatus::PermissionStatus;
use dom::promise::Promise;
use js::conversions::ConversionResult;
use js::jsapi::{JSContext, JSObject};
use js::jsval::{ObjectValue, UndefinedValue};
use std::rc::Rc;
#[cfg(target_os = "linux")]
use tinyfiledialogs::{self, MessageBoxIcon, YesNo};

#[cfg(target_os = "linux")]
const DIALOG_TITLE: &'static str = "Permission request dialog";
#[cfg(target_os = "linux")]
const QUERY_DIALOG_MESSAGE: &'static str = "Can't guarantee, that the current context is secure.
\t\tStill grant permission for";
const ROOT_DESC_CONVERSION_ERROR: &'static str = "Can't convert to an IDL value of type PermissionDescriptor";

pub trait PermissionAlgorithm {
    type Descriptor;
    type Status;
    fn create_descriptor(cx: *mut JSContext,
                         permission_descriptor_obj: *mut JSObject)
                         -> Result<Self::Descriptor, Error>;
    fn permission_query(cx: *mut JSContext, promise: &Rc<Promise>,
                        descriptor: &Self::Descriptor, status: &Self::Status);
    fn permission_request(cx: *mut JSContext, promise: &Rc<Promise>,
                          descriptor: &Self::Descriptor, status: &Self::Status);
    fn permission_revoke(descriptor: &Self::Descriptor, status: &Self::Status);
}

// https://w3c.github.io/permissions/#permissions
#[dom_struct]
pub struct Permissions {
    reflector_: Reflector,
}

impl Permissions {
    pub fn new_inherited() -> Permissions {
        Permissions {
            reflector_: Reflector::new(),
        }
    }

    pub fn new(global: &GlobalScope) -> Root<Permissions> {
        reflect_dom_object(box Permissions::new_inherited(),
                           global,
                           PermissionsBinding::Wrap)
    }
}

impl PermissionsMethods for Permissions {
    #[allow(unrooted_must_root)]
    #[allow(unsafe_code)]
    // https://w3c.github.io/permissions/#dom-permissions-query
    unsafe fn Query(&self, cx: *mut JSContext, permissionDesc: *mut JSObject) -> Rc<Promise> {
        // Step 3.
        let p = Promise::new(&self.global());

        // Step 1.
        let root_desc = match Permissions::create_descriptor(cx, permissionDesc) {
            Ok(descriptor) => descriptor,
            Err(error) => {
                p.reject_error(cx, error);
                return p;
            },
        };

        // Step 5.
        let status = PermissionStatus::new(&self.global(), &root_desc);

        // Step 2.
        match root_desc.name {
            PermissionName::Bluetooth => {
                let bluetooth_desc = match Bluetooth::create_descriptor(cx, permissionDesc) {
                    Ok(descriptor) => descriptor,
                    Err(error) => {
                        p.reject_error(cx, error);
                        return p;
                    },
                };
                // Step 5.
                let result = BluetoothPermissionResult::new(&self.global(), &status);
                // Step 6.
                Bluetooth::permission_query(cx, &p, &bluetooth_desc, &result);
                // Step 7. in permission_query
            },
            _ => {
                // Step 6.
                Permissions::permission_query(cx, &p, &root_desc, &status);

                // Step 7.
                p.resolve_native(cx, &status);
            },
        };

        // Step 4.
        return p;
    }

    #[allow(unrooted_must_root)]
    #[allow(unsafe_code)]
    // https://w3c.github.io/permissions/#dom-permissions-request
    unsafe fn Request(&self, cx: *mut JSContext, permissionDesc: *mut JSObject) -> Rc<Promise> {
        // Step 3.
        let p = Promise::new(&self.global());

        // Step 1.
        let root_desc = match Permissions::create_descriptor(cx, permissionDesc) {
            Ok(descriptor) => descriptor,
            Err(error) => {
                p.reject_error(cx, error);
                return p;
            },
        };

        // Step 5.
        let status = PermissionStatus::new(&self.global(), &root_desc);

        // Step 2.
        match root_desc.name {
            PermissionName::Bluetooth => {
                let bluetooth_desc = match Bluetooth::create_descriptor(cx, permissionDesc) {
                    Ok(descriptor) => descriptor,
                    Err(error) => {
                        p.reject_error(cx, error);
                        return p;
                    },
                };
                // Step 5.
                let result = BluetoothPermissionResult::new(&self.global(), &status);
                // Step 6.
                Bluetooth::permission_request(cx, &p, &bluetooth_desc, &result);
                // Step 7 - 8. in permission_request
            },
            _ => {
                // Step 6.
                Permissions::permission_request(cx, &p, &root_desc, &status);

                // Step 7. The default algorithm always resolve

                // Step 8.
                p.resolve_native(cx, &status);
            },
        };
        // Step 4.
        return p;
    }

    #[allow(unrooted_must_root)]
    #[allow(unsafe_code)]
    // https://w3c.github.io/permissions/#dom-permissions-revoke
    unsafe fn Revoke(&self, cx: *mut JSContext, permissionDesc: *mut JSObject) -> Rc<Promise> {
        // Step 1.
        let root_desc = match Permissions::create_descriptor(cx, permissionDesc) {
            Ok(descriptor) => descriptor,
            Err(error) => {
                let p = Promise::new(&self.global());
                p.reject_error(cx, error);
                return p;
            },
        };

        let status = PermissionStatus::new(&self.global(), &root_desc);

        // Step 2.
        match root_desc.name {
            PermissionName::Bluetooth => {
                let bluetooth_desc = match Bluetooth::create_descriptor(cx, permissionDesc) {
                    Ok(descriptor) => descriptor,
                    Err(error) => {
                        let p = Promise::new(&self.global());
                        p.reject_error(cx, error);
                        return p;
                    },
                };
                let result = BluetoothPermissionResult::new(&self.global(), &status);
                // Step 3 - 4. in permission_revoke
                Bluetooth::permission_revoke(&bluetooth_desc, &result);
            },
            _ => {
                Permissions::permission_revoke(&root_desc, &status);
            },
        };

        // Step 5.
        return self.Query(cx, permissionDesc);
    }
}

impl PermissionAlgorithm for Permissions {
    type Descriptor = PermissionDescriptor;
    type Status = PermissionStatus;

    #[allow(unsafe_code)]
    fn create_descriptor(cx: *mut JSContext,
                         permission_descriptor_obj: *mut JSObject)
                         -> Result<PermissionDescriptor, Error> {
        rooted!(in(cx) let mut property = UndefinedValue());
        property.handle_mut().set(ObjectValue(permission_descriptor_obj));
        unsafe {
            match PermissionDescriptor::new(cx, property.handle()) {
                Ok(ConversionResult::Success(descriptor)) => Ok(descriptor),
                Ok(ConversionResult::Failure(error)) => Err(Error::Type(error.into_owned())),
                Err(_) => Err(Error::Type(String::from(ROOT_DESC_CONVERSION_ERROR))),
            }
        }
    }

    // https://w3c.github.io/permissions/#boolean-permission-query-algorithm
    fn permission_query(_cx: *mut JSContext,
                        _promise: &Rc<Promise>,
                        _descriptor: &PermissionDescriptor,
                        status: &PermissionStatus) {
        // Step 1.
        status.set_state(get_descriptor_permission_state(status.get_query(), None));
    }

    // https://w3c.github.io/permissions/#boolean-permission-request-algorithm
    fn permission_request(cx: *mut JSContext,
                          promise: &Rc<Promise>,
                          descriptor: &PermissionDescriptor,
                          status: &PermissionStatus) {
        // Step 1.
        Permissions::permission_query(cx, promise, descriptor, status);

        // TODO: Step 2 - 4: `environment settings object` is not implemented in Servo yet.
        // For this reason in the `get_descriptor_permission_state` function we can't decide
        // if we have a secure context or not, or store the previous invocation results.
        // Without these the remaining steps can't be implemented properly.
    }

    fn permission_revoke(_descriptor: &PermissionDescriptor, _status: &PermissionStatus) {}
}

// https://w3c.github.io/permissions/#permission-state
pub fn get_descriptor_permission_state(permission_name: PermissionName ,
                                       _env_settings_obj: Option<*mut JSObject>)
                                       -> PermissionState {
    // TODO: Step 1: If settings wasn’t passed, set it to the current settings object.
    // TODO: `environment settings object` is not implemented in Servo yet.

    // Step 2.
    // TODO: The `is the environment settings object a non-secure context` check is missing.
    // The current solution is a workaround with a message box to warn about this,
    // if the feature is not allowed in non-secure contexcts,
    // and let the user decide to grant the permission or not.
    if !allowed_in_nonsecure_contexts(&permission_name) {
        return prompt_user(permission_name);
    }

    // TODO: Step 3: Store the invocation results
    // TODO: `environment settings object` is not implemented in Servo yet.

    // Step 4.
    PermissionState::Granted
}

#[cfg(target_os = "linux")]
fn prompt_user(permission_name: PermissionName) -> PermissionState {
    match tinyfiledialogs::message_box_yes_no(DIALOG_TITLE,
                                              &format!("{} {:?} ?", QUERY_DIALOG_MESSAGE, permission_name),
                                              MessageBoxIcon::Question,
                                              YesNo::No) {
        YesNo::Yes => return PermissionState::Granted,
        YesNo::No => return PermissionState::Denied,
    };
}

#[cfg(not(target_os = "linux"))]
fn prompt_user(_permission_name: PermissionName) -> PermissionState {
    // TODO popup only supported on linux
    PermissionState::Denied
}

// https://w3c.github.io/permissions/#allowed-in-non-secure-contexts
fn allowed_in_nonsecure_contexts(permission_name: &PermissionName) -> bool {
    match *permission_name {
        // https://w3c.github.io/permissions/#dom-permissionname-geolocation
        PermissionName::Geolocation => true,
        // https://w3c.github.io/permissions/#dom-permissionname-notifications
        PermissionName::Notifications => true,
        // https://w3c.github.io/permissions/#dom-permissionname-push
        PermissionName::Push => false,
        // https://w3c.github.io/permissions/#dom-permissionname-midi
        PermissionName::Midi => true,
        // https://w3c.github.io/permissions/#dom-permissionname-camera
        PermissionName::Camera => false,
        // https://w3c.github.io/permissions/#dom-permissionname-microphone
        PermissionName::Microphone => false,
        // https://w3c.github.io/permissions/#dom-permissionname-speaker
        PermissionName::Speaker => false,
        // https://w3c.github.io/permissions/#dom-permissionname-device-info
        PermissionName::Device_info => false,
        // https://w3c.github.io/permissions/#dom-permissionname-background-sync
        PermissionName::Background_sync => false,
        // https://webbluetoothcg.github.io/web-bluetooth/#dom-permissionname-bluetooth
        PermissionName::Bluetooth => false,
        // https://storage.spec.whatwg.org/#dom-permissionname-persistent-storage
        PermissionName::Persistent_storage => false,
    }
}
<commit_msg>Refactor permission operations into one function<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/. */

use dom::bindings::codegen::Bindings::PermissionStatusBinding::{PermissionDescriptor, PermissionName, PermissionState};
use dom::bindings::codegen::Bindings::PermissionsBinding::{self, PermissionsMethods};
use dom::bindings::error::Error;
use dom::bindings::js::Root;
use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
use dom::bluetooth::Bluetooth;
use dom::bluetoothpermissionresult::BluetoothPermissionResult;
use dom::globalscope::GlobalScope;
use dom::permissionstatus::PermissionStatus;
use dom::promise::Promise;
use js::conversions::ConversionResult;
use js::jsapi::{JSContext, JSObject};
use js::jsval::{ObjectValue, UndefinedValue};
use std::rc::Rc;
#[cfg(target_os = "linux")]
use tinyfiledialogs::{self, MessageBoxIcon, YesNo};

#[cfg(target_os = "linux")]
const DIALOG_TITLE: &'static str = "Permission request dialog";
#[cfg(target_os = "linux")]
const QUERY_DIALOG_MESSAGE: &'static str = "Can't guarantee, that the current context is secure.
\t\tStill grant permission for";
const ROOT_DESC_CONVERSION_ERROR: &'static str = "Can't convert to an IDL value of type PermissionDescriptor";

pub trait PermissionAlgorithm {
    type Descriptor;
    type Status;
    fn create_descriptor(cx: *mut JSContext,
                         permission_descriptor_obj: *mut JSObject)
                         -> Result<Self::Descriptor, Error>;
    fn permission_query(cx: *mut JSContext, promise: &Rc<Promise>,
                        descriptor: &Self::Descriptor, status: &Self::Status);
    fn permission_request(cx: *mut JSContext, promise: &Rc<Promise>,
                          descriptor: &Self::Descriptor, status: &Self::Status);
    fn permission_revoke(descriptor: &Self::Descriptor, status: &Self::Status);
}

enum Operation {
    Query,
    Request,
    Revoke,
}

// https://w3c.github.io/permissions/#permissions
#[dom_struct]
pub struct Permissions {
    reflector_: Reflector,
}

impl Permissions {
    pub fn new_inherited() -> Permissions {
        Permissions {
            reflector_: Reflector::new(),
        }
    }

    pub fn new(global: &GlobalScope) -> Root<Permissions> {
        reflect_dom_object(box Permissions::new_inherited(),
                           global,
                           PermissionsBinding::Wrap)
    }

    #[allow(unrooted_must_root)]
    // https://w3c.github.io/permissions/#dom-permissions-query
    // https://w3c.github.io/permissions/#dom-permissions-request
    // https://w3c.github.io/permissions/#dom-permissions-revoke
    fn manipulate(&self,
                  op: Operation,
                  cx: *mut JSContext,
                  permissionDesc: *mut JSObject,
                  promise: Option<Rc<Promise>>)
                  -> Rc<Promise> {
        // (Query, Request) Step 3.
        let p = match promise {
            Some(promise) => promise,
            None => Promise::new(&self.global()),
        };

        // (Query, Request, Revoke) Step 1.
        let root_desc = match Permissions::create_descriptor(cx, permissionDesc) {
            Ok(descriptor) => descriptor,
            Err(error) => {
                p.reject_error(cx, error);
                return p;
            },
        };

        // (Query, Request) Step 5.
        let status = PermissionStatus::new(&self.global(), &root_desc);

        // (Query, Request, Revoke) Step 2.
        match root_desc.name {
            PermissionName::Bluetooth => {
                let bluetooth_desc = match Bluetooth::create_descriptor(cx, permissionDesc) {
                    Ok(descriptor) => descriptor,
                    Err(error) => {
                        p.reject_error(cx, error);
                        return p;
                    },
                };

                // (Query, Request) Step 5.
                let result = BluetoothPermissionResult::new(&self.global(), &status);

                match &op {
                    // (Request) Step 6 - 8.
                    &Operation::Request => Bluetooth::permission_request(cx, &p, &bluetooth_desc, &result),

                    // (Query) Step 6 - 7.
                    &Operation::Query => Bluetooth::permission_query(cx, &p, &bluetooth_desc, &result),

                    // (Revoke) Step 3 - 4.
                    &Operation::Revoke => Bluetooth::permission_revoke(&bluetooth_desc, &result),
                }
            },
            _ => {
                match &op {
                    &Operation::Request => {
                        // (Request) Step 6.
                        Permissions::permission_request(cx, &p, &root_desc, &status);

                        // (Request) Step 7. The default algorithm always resolve

                        // (Request) Step 8.
                        p.resolve_native(cx, &status);
                    },
                    &Operation::Query => {
                        // (Query) Step 6.
                        Permissions::permission_query(cx, &p, &root_desc, &status);

                        // (Query) Step 7.
                        p.resolve_native(cx, &status);
                    },
                    // (Revoke) Step 3 - 4.
                    &Operation::Revoke => Permissions::permission_revoke(&root_desc, &status),
                }
            },
        };
        match op {
            // (Revoke) Step 5.
            Operation::Revoke => self.manipulate(Operation::Query, cx, permissionDesc, Some(p)),

            // (Query, Request) Step 4.
            _ => p,
        }
    }
}

impl PermissionsMethods for Permissions {
    #[allow(unrooted_must_root)]
    #[allow(unsafe_code)]
    // https://w3c.github.io/permissions/#dom-permissions-query
    unsafe fn Query(&self, cx: *mut JSContext, permissionDesc: *mut JSObject) -> Rc<Promise> {
        self.manipulate(Operation::Query, cx, permissionDesc, None)
    }

    #[allow(unrooted_must_root)]
    #[allow(unsafe_code)]
    // https://w3c.github.io/permissions/#dom-permissions-request
    unsafe fn Request(&self, cx: *mut JSContext, permissionDesc: *mut JSObject) -> Rc<Promise> {
        self.manipulate(Operation::Request, cx, permissionDesc, None)
    }

    #[allow(unrooted_must_root)]
    #[allow(unsafe_code)]
    // https://w3c.github.io/permissions/#dom-permissions-revoke
    unsafe fn Revoke(&self, cx: *mut JSContext, permissionDesc: *mut JSObject) -> Rc<Promise> {
        self.manipulate(Operation::Revoke, cx, permissionDesc, None)
    }
}

impl PermissionAlgorithm for Permissions {
    type Descriptor = PermissionDescriptor;
    type Status = PermissionStatus;

    #[allow(unsafe_code)]
    fn create_descriptor(cx: *mut JSContext,
                         permission_descriptor_obj: *mut JSObject)
                         -> Result<PermissionDescriptor, Error> {
        rooted!(in(cx) let mut property = UndefinedValue());
        property.handle_mut().set(ObjectValue(permission_descriptor_obj));
        unsafe {
            match PermissionDescriptor::new(cx, property.handle()) {
                Ok(ConversionResult::Success(descriptor)) => Ok(descriptor),
                Ok(ConversionResult::Failure(error)) => Err(Error::Type(error.into_owned())),
                Err(_) => Err(Error::Type(String::from(ROOT_DESC_CONVERSION_ERROR))),
            }
        }
    }

    // https://w3c.github.io/permissions/#boolean-permission-query-algorithm
    fn permission_query(_cx: *mut JSContext,
                        _promise: &Rc<Promise>,
                        _descriptor: &PermissionDescriptor,
                        status: &PermissionStatus) {
        // Step 1.
        status.set_state(get_descriptor_permission_state(status.get_query(), None));
    }

    // https://w3c.github.io/permissions/#boolean-permission-request-algorithm
    fn permission_request(cx: *mut JSContext,
                          promise: &Rc<Promise>,
                          descriptor: &PermissionDescriptor,
                          status: &PermissionStatus) {
        // Step 1.
        Permissions::permission_query(cx, promise, descriptor, status);

        // TODO: Step 2 - 4: `environment settings object` is not implemented in Servo yet.
        // For this reason in the `get_descriptor_permission_state` function we can't decide
        // if we have a secure context or not, or store the previous invocation results.
        // Without these the remaining steps can't be implemented properly.
    }

    fn permission_revoke(_descriptor: &PermissionDescriptor, _status: &PermissionStatus) {}
}

// https://w3c.github.io/permissions/#permission-state
pub fn get_descriptor_permission_state(permission_name: PermissionName ,
                                       _env_settings_obj: Option<*mut JSObject>)
                                       -> PermissionState {
    // TODO: Step 1: If settings wasn’t passed, set it to the current settings object.
    // TODO: `environment settings object` is not implemented in Servo yet.

    // Step 2.
    // TODO: The `is the environment settings object a non-secure context` check is missing.
    // The current solution is a workaround with a message box to warn about this,
    // if the feature is not allowed in non-secure contexcts,
    // and let the user decide to grant the permission or not.
    if !allowed_in_nonsecure_contexts(&permission_name) {
        return prompt_user(permission_name);
    }

    // TODO: Step 3: Store the invocation results
    // TODO: `environment settings object` is not implemented in Servo yet.

    // Step 4.
    PermissionState::Granted
}

#[cfg(target_os = "linux")]
fn prompt_user(permission_name: PermissionName) -> PermissionState {
    match tinyfiledialogs::message_box_yes_no(DIALOG_TITLE,
                                              &format!("{} {:?} ?", QUERY_DIALOG_MESSAGE, permission_name),
                                              MessageBoxIcon::Question,
                                              YesNo::No) {
        YesNo::Yes => return PermissionState::Granted,
        YesNo::No => return PermissionState::Denied,
    };
}

#[cfg(not(target_os = "linux"))]
fn prompt_user(_permission_name: PermissionName) -> PermissionState {
    // TODO popup only supported on linux
    PermissionState::Denied
}

// https://w3c.github.io/permissions/#allowed-in-non-secure-contexts
fn allowed_in_nonsecure_contexts(permission_name: &PermissionName) -> bool {
    match *permission_name {
        // https://w3c.github.io/permissions/#dom-permissionname-geolocation
        PermissionName::Geolocation => true,
        // https://w3c.github.io/permissions/#dom-permissionname-notifications
        PermissionName::Notifications => true,
        // https://w3c.github.io/permissions/#dom-permissionname-push
        PermissionName::Push => false,
        // https://w3c.github.io/permissions/#dom-permissionname-midi
        PermissionName::Midi => true,
        // https://w3c.github.io/permissions/#dom-permissionname-camera
        PermissionName::Camera => false,
        // https://w3c.github.io/permissions/#dom-permissionname-microphone
        PermissionName::Microphone => false,
        // https://w3c.github.io/permissions/#dom-permissionname-speaker
        PermissionName::Speaker => false,
        // https://w3c.github.io/permissions/#dom-permissionname-device-info
        PermissionName::Device_info => false,
        // https://w3c.github.io/permissions/#dom-permissionname-background-sync
        PermissionName::Background_sync => false,
        // https://webbluetoothcg.github.io/web-bluetooth/#dom-permissionname-bluetooth
        PermissionName::Bluetooth => false,
        // https://storage.spec.whatwg.org/#dom-permissionname-persistent-storage
        PermissionName::Persistent_storage => false,
    }
}
<|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/. */

use cow_rc_str::CowRcStr;
use std::borrow::Cow;
use tokenizer::Token;

#[macro_export]
macro_rules! size_of_test {
    ($testname: ident, $t: ty, $expected_size: expr) => {
        #[test]
        fn $testname() {
            let new = ::std::mem::size_of::<$t>();
            let old = $expected_size;
            if new < old {
                panic!(
                    "Your changes have decreased the stack size of {} from {} to {}. \
                     Good work! Please update the expected size in {}.",
                    stringify!($t), old, new, file!()
                )
            } else if new > old {
                panic!(
                    "Your changes have increased the stack size of {} from {} to {}. \
                     Please consider choosing a design which avoids this increase. \
                     If you feel that the increase is necessary, update the size in {}.",
                    stringify!($t), old, new, file!()
                )
            }
        }
    }
}

// These assume 64-bit
size_of_test!(token, Token, 32);
size_of_test!(std_cow_str, Cow<'static, str>, 32);
size_of_test!(cow_rc_str, CowRcStr, 16);
<commit_msg>Add more size_of tests<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/. */

use cow_rc_str::CowRcStr;
use std::borrow::Cow;
use tokenizer::Token;

#[macro_export]
macro_rules! size_of_test {
    ($testname: ident, $t: ty, $expected_size: expr) => {
        #[test]
        fn $testname() {
            let new = ::std::mem::size_of::<$t>();
            let old = $expected_size;
            if new < old {
                panic!(
                    "Your changes have decreased the stack size of {} from {} to {}. \
                     Good work! Please update the expected size in {}.",
                    stringify!($t), old, new, file!()
                )
            } else if new > old {
                panic!(
                    "Your changes have increased the stack size of {} from {} to {}. \
                     Please consider choosing a design which avoids this increase. \
                     If you feel that the increase is necessary, update the size in {}.",
                    stringify!($t), old, new, file!()
                )
            }
        }
    }
}

// Some of these assume 64-bit
size_of_test!(token, Token, 32);
size_of_test!(std_cow_str, Cow<'static, str>, 32);
size_of_test!(cow_rc_str, CowRcStr, 16);

size_of_test!(tokenizer, ::tokenizer::Tokenizer, 48);
size_of_test!(parser_input, ::parser::ParserInput, 104);
size_of_test!(parser, ::parser::Parser, 16);
size_of_test!(tokenizer_source_position, ::tokenizer::SourcePosition, 8);
size_of_test!(source_position, ::parser::SourcePosition, 16);
size_of_test!(source_location, ::SourceLocation, 8);

size_of_test!(basic_parse_error, ::BasicParseError, 40);
size_of_test!(parse_error_lower_bound, ::ParseError<()>, 48);
size_of_test!(precise_parse_error_lower_bound, ::PreciseParseError<()>, 80);
<|endoftext|>


<commit_before>pub mod route_guide;
pub mod route_guide_grpc;
pub mod client;
pub mod server;
mod test;

pub const DEFAULT_PORT: u16 = 10000;
<commit_msg>cargo fmt<commit_after>pub mod client;
pub mod route_guide;
pub mod route_guide_grpc;
pub mod server;
mod test;

pub const DEFAULT_PORT: u16 = 10000;
<|endoftext|>


<commit_before>// Copyright (c) 2017 Fabian Schuiki

//! The high-level intermediate representation for SystemVerilog.
//!
//! After parsing the AST is lowered into this representation, eliminating a lot
//! of syntactic sugar and resolving any syntactic ambiguities.

use crate::crate_prelude::*;
use std::{collections::BTreeSet, sync::Arc};

mod lowering;
mod nodes;
mod visit;

pub(crate) use self::lowering::hir_of;
pub use self::lowering::Hint;
pub use self::nodes::*;
pub use self::visit::*;

make_arenas!(
    /// An arena to allocate HIR nodes into.
    pub struct Arena<'hir> {
        modules: Module<'hir>,
        ports: Port,
        types: Type,
        exprs: Expr,
        inst_target: InstTarget,
        insts: Inst<'hir>,
        type_params: TypeParam,
        value_params: ValueParam,
        var_decls: VarDecl,
        procs: Proc,
        stmts: Stmt,
        event_exprs: EventExpr,
        gens: Gen,
        genvar_decls: GenvarDecl,
        typedefs: Typedef,
        assigns: Assign,
    }
);

/// Determine the nodes accessed by another node.
pub(crate) fn accessed_nodes<'gcx>(
    cx: &impl Context<'gcx>,
    node_id: NodeId,
) -> Result<Arc<AccessTable>> {
    let mut k = AccessTableCollector {
        cx,
        table: AccessTable {
            node_id,
            read: Default::default(),
            written: Default::default(),
        },
    };
    k.visit_node_with_id(node_id, false);
    Ok(Arc::new(k.table))
}

/// A table of accessed nodes.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AccessTable {
    /// The node for which the analysis was performed.
    pub node_id: NodeId,
    /// All nodes being read.
    pub read: BTreeSet<NodeId>,
    /// All nodes being written.
    pub written: BTreeSet<NodeId>,
}

/// A visitor for the HIR that populates an access table.
struct AccessTableCollector<'a, C> {
    cx: &'a C,
    table: AccessTable,
}

impl<'a, 'gcx: 'a, C> Visitor<'gcx> for AccessTableCollector<'a, C>
where
    C: Context<'gcx>,
{
    type Context = C;
    fn context(&self) -> &C {
        self.cx
    }

    fn visit_expr(&mut self, expr: &'gcx Expr, lvalue: bool) {
        match expr.kind {
            ExprKind::Ident(name) => match self.cx.resolve_upwards_or_error(name, expr.id) {
                Ok(binding) => {
                    if !self.cx.is_parent_of(self.table.node_id, binding) {
                        if lvalue {
                            self.table.written.insert(binding);
                        } else {
                            self.table.read.insert(binding);
                        }
                    }
                }
                Err(()) => (),
            },
            _ => (),
        }
        walk_expr(self, expr, lvalue)
    }
}
<commit_msg>hir: filter access tables<commit_after>// Copyright (c) 2017 Fabian Schuiki

//! The high-level intermediate representation for SystemVerilog.
//!
//! After parsing the AST is lowered into this representation, eliminating a lot
//! of syntactic sugar and resolving any syntactic ambiguities.

use crate::crate_prelude::*;
use std::{collections::BTreeSet, sync::Arc};

mod lowering;
mod nodes;
mod visit;

pub(crate) use self::lowering::hir_of;
pub use self::lowering::Hint;
pub use self::nodes::*;
pub use self::visit::*;

make_arenas!(
    /// An arena to allocate HIR nodes into.
    pub struct Arena<'hir> {
        modules: Module<'hir>,
        ports: Port,
        types: Type,
        exprs: Expr,
        inst_target: InstTarget,
        insts: Inst<'hir>,
        type_params: TypeParam,
        value_params: ValueParam,
        var_decls: VarDecl,
        procs: Proc,
        stmts: Stmt,
        event_exprs: EventExpr,
        gens: Gen,
        genvar_decls: GenvarDecl,
        typedefs: Typedef,
        assigns: Assign,
    }
);

/// Determine the nodes accessed by another node.
pub(crate) fn accessed_nodes<'gcx>(
    cx: &impl Context<'gcx>,
    node_id: NodeId,
) -> Result<Arc<AccessTable>> {
    let mut k = AccessTableCollector {
        cx,
        table: AccessTable {
            node_id,
            read: Default::default(),
            written: Default::default(),
        },
    };
    k.visit_node_with_id(node_id, false);
    Ok(Arc::new(k.table))
}

/// A table of accessed nodes.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AccessTable {
    /// The node for which the analysis was performed.
    pub node_id: NodeId,
    /// All nodes being read.
    pub read: BTreeSet<NodeId>,
    /// All nodes being written.
    pub written: BTreeSet<NodeId>,
}

/// A visitor for the HIR that populates an access table.
struct AccessTableCollector<'a, C> {
    cx: &'a C,
    table: AccessTable,
}

impl<'a, 'gcx: 'a, C> Visitor<'gcx> for AccessTableCollector<'a, C>
where
    C: Context<'gcx>,
{
    type Context = C;
    fn context(&self) -> &C {
        self.cx
    }

    fn visit_expr(&mut self, expr: &'gcx Expr, lvalue: bool) {
        match expr.kind {
            ExprKind::Ident(name) => match self.cx.resolve_upwards_or_error(name, expr.id) {
                Ok(binding) => {
                    if self.is_binding_interesting(binding) {
                        if lvalue {
                            self.table.written.insert(binding);
                        } else {
                            self.table.read.insert(binding);
                        }
                    }
                }
                Err(()) => (),
            },
            _ => (),
        }
        walk_expr(self, expr, lvalue)
    }
}

impl<'a, 'gcx: 'a, C> AccessTableCollector<'a, C>
where
    C: Context<'gcx>,
{
    fn is_binding_interesting(&self, binding: NodeId) -> bool {
        if self.cx.is_parent_of(self.table.node_id, binding) {
            return false;
        }
        match self.cx.hir_of(binding) {
            Ok(HirNode::ValueParam(..)) => return false,
            Ok(HirNode::TypeParam(..)) => return false,
            Ok(HirNode::GenvarDecl(..)) => return false,
            Err(_) => return false,
            _ => (),
        }
        true
    }
}
<|endoftext|>


<commit_before>// This module defines constants and functions for working with zakocmp
// configuration files.

extern crate yaml_rust;

use std::io::{Error, ErrorKind};
use std::result::Result;
use std::string::String;
use std::vec::Vec;
use yaml_rust::{Yaml, YamlLoader};

const DEFAULT_POLICY_KEY: &'static str = "default-policy";
const POLICIES_KEY: &'static str = "policies";

// Enumerates the string representations of known policies.
const POLICY_REPR_IGNORE: &'static str = "ignore";
const POLICY_REPR_NOADD: &'static str = "noadd";
const POLICY_REPR_NODELETE: &'static str = "nodelete";
const POLICY_REPR_NOMODIFY: &'static str = "nomodify";
const POLICY_REPR_IMMUTABLE: &'static str = "immutable";

// Represents known policies as integers.
pub const POLICY_IGNORE: i32 = 0;
pub const POLICY_NOADD: i32 = 1 << 0;
pub const POLICY_NODELETE: i32 = 1 << 1;
pub const POLICY_NOMODIFY: i32 = 1 << 2;
pub const POLICY_IMMUTABLE: i32 = POLICY_NOADD | POLICY_NODELETE | POLICY_NOMODIFY;

// Represents a sorted vector of zakopane config rules, each mapping a
// path (prefix) to a policy. This type alias is provided for ease of
// coding.
type Policies = Vec<(String, i32)>;

// Represents a zakopane config. Please consult the documentation.
pub struct Config {
    default_policy: i32,
    policies: Policies,
}

// Borrows the string representation of one policy `token` and returns
// the equivalent integral representation.
fn policy_token_as_int(token: &str) -> Result<i32, Error> {
    match token {
        POLICY_REPR_IGNORE => Ok(POLICY_IGNORE),
        POLICY_REPR_NOADD => Ok(POLICY_NOADD),
        POLICY_REPR_NODELETE => Ok(POLICY_NODELETE),
        POLICY_REPR_NOMODIFY => Ok(POLICY_NOMODIFY),
        POLICY_REPR_IMMUTABLE => Ok(POLICY_IMMUTABLE),
        _ => Err(Error::new(
            ErrorKind::InvalidInput,
            format!("bad token: ``{}''", token),
        )),
    }
}

// Borrows the string representation of a combined `policy` and returns
// the equivalent integral representation. This function expects
// `policy` to comprise one or more policy tokens separated by commas.
fn policy_tokens_as_int(policy: &str) -> Result<i32, Error> {
    let policy_ints: Vec<i32> = policy
        .split(",")
        .map(|tok| policy_token_as_int(tok))
        .collect::<Result<Vec<i32>, Error>>()?;
    return Ok(policy_ints
        .iter()
        .fold(POLICY_IGNORE, |accum, elem| accum | elem));
}

// Borrows yaml representations of one line of zakopane policy and
// returns the corresponding valid tuple suitable for use in building a
// Policies object.
fn extract_policy(ypath: &Yaml, policy_tokens: &Yaml) -> Result<(String, i32), Error> {
    let path: String = match ypath.as_str() {
        Some(string) => string.to_owned(),
        None => return Err(Error::new(ErrorKind::InvalidData, "malformed path?")),
    };
    let policy: i32 = match policy_tokens.as_str() {
        Some(string) => policy_tokens_as_int(string)?,
        None => return Err(Error::new(ErrorKind::InvalidData, "malformed policy?")),
    };
    Ok((path, policy))
}

// Borrows the YAML representation of a zakopane config and returns the
// corresponding Policies. The return value can be benignly
// empty (e.g. if the present config elects not to specify any rules).
fn policies_from_yaml(doc: &Yaml) -> Result<Policies, Error> {
    let policies_map_yaml = &doc[POLICIES_KEY];
    if policies_map_yaml.is_badvalue() {
        // Assumes the config may be benignly devoid of specific
        // policies, returning Ok.
        return Ok(vec![]);
    }
    // Otherwise, iterates over the policies map. Each entry in the
    // policies map correlates a path prefix to a comma-separated list
    // of policies.
    let policies_map: &yaml_rust::yaml::Hash = match policies_map_yaml.as_hash() {
        Some(map) => map,
        None => return Err(Error::new(ErrorKind::InvalidData, "malformed policies")),
    };
    let mut policies: Policies = policies_map
        .into_iter()
        .map(|pair| extract_policy(&pair.0, &pair.1))
        .collect::<Result<Policies, Error>>()?;
    policies.sort_unstable_by_key(|pair| pair.0.to_owned());
    Ok(policies)
}

// Borrows the YAML representation of a zakopane config and returns the
// integral default-policy defined within.
fn default_policy_from_yaml(doc: &Yaml) -> Result<i32, Error> {
    let default_policy_yaml = &doc[DEFAULT_POLICY_KEY];
    if default_policy_yaml.is_badvalue() {
        return Err(Error::new(ErrorKind::InvalidData, DEFAULT_POLICY_KEY));
    }
    let default_policy: i32 = match default_policy_yaml.as_str() {
        None => return Err(Error::new(ErrorKind::InvalidData, DEFAULT_POLICY_KEY)),
        Some(token) => policy_tokens_as_int(&token),
    }?;
    Ok(default_policy)
}

impl Config {
    // Borrows the string representation of a zakopane config and
    // returns a corresponding Config.
    pub fn new(config: &str) -> Result<Config, Error> {
        let docs: Vec<Yaml> = match YamlLoader::load_from_str(config) {
            Ok(val) => val,
            Err(scan_error) => return Err(Error::new(ErrorKind::InvalidData, scan_error)),
        };
        if docs.len() == 0 {
            return Err(Error::new(ErrorKind::InvalidData, "empty zakopane config"));
        }
        let doc = &docs[0];

        let default_policy = default_policy_from_yaml(&doc)?;
        let policies: Policies = policies_from_yaml(&doc)?;

        Ok(Config {
            default_policy: default_policy,
            policies: policies,
        })
    }

    // Returns how many rules this config contains.
    // This is never less than 1 as a default-policy is always required.
    pub fn rules(&self) -> usize {
        1 + self.policies.len()
    }

    // Borrows a `path` and returns the best-matched policy that
    // applies. This function returns an owned tuple of the
    // (closest-matched path expression, integral policy).
    //
    // This function represents the default-policy fallback by
    // returning the tuple consisting of an empty &str and the
    // default policy.
    pub fn match_policy(&self, path: &str) -> (&str, i32) {
        let mut best_match_path: &str = "";
        let mut best_match_policy: i32 = 0;
        for (prefix, policy) in self.policies.iter() {
            if path.starts_with(prefix) && prefix.len() > best_match_path.len() {
                best_match_path = prefix;
                best_match_policy = *policy;
            }
        }
        if best_match_path.len() == 0 {
            return ("", self.default_policy);
        }
        return (best_match_path, best_match_policy);
    }
}

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

    #[test]
    fn policy_token_bare() {
        let policy: i32 = policy_tokens_as_int(&"noadd").unwrap();
        assert_eq!(policy, POLICY_NOADD);

        let policy: i32 = policy_tokens_as_int(&"nodelete").unwrap();
        assert_eq!(policy, POLICY_NODELETE);

        let policy: i32 = policy_tokens_as_int(&"nomodify").unwrap();
        assert_eq!(policy, POLICY_NOMODIFY);
    }

    #[test]
    fn policy_tokens_can_combo() {
        let policy: i32 = policy_tokens_as_int(&"noadd,nodelete").unwrap();
        assert_eq!(policy, POLICY_NOADD | POLICY_NODELETE);
    }

    #[test]
    fn policy_tokens_can_repeat() {
        let policy: i32 =
            policy_tokens_as_int(&"noadd,noadd,noadd,noadd,nodelete,nodelete,nodelete,noadd")
                .unwrap();
        assert_eq!(policy, POLICY_NOADD | POLICY_NODELETE);
    }

    #[test]
    fn config_must_not_be_empty() {
        let config = "";
        assert!(Config::new(&config).is_err());
    }

    #[test]
    fn config_must_not_be_obviously_malformed() {
        let config = r#"
This is not a zakopane config -
rather, it's two lines of text.
        "#;
        assert!(Config::new(&config).is_err());
    }

    #[test]
    fn config_requires_default_policy() {
        let config = r#"
policies:
    hello-there: nomodify
        "#;
        assert!(Config::new(&config).is_err());

        let mut config_with_default_policy: String = r#"
default-policy: immutable
        "#
        .to_string();
        config_with_default_policy.push_str(&config);
        assert!(Config::new(&config_with_default_policy).is_ok())
    }

    #[test]
    fn config_might_not_have_specific_policies() {
        let config = r#"
default-policy: nodelete
one-irrelevant-key: it doesn't matter what we put here
another-irrelevant-key: this doesn't invalidate the YAML
third-irrelevant-key: so long as it contains a default-policy
        "#;
        assert!(Config::new(&config).is_ok());
    }

    #[test]
    fn config_policies_must_be_a_map() {
        let config = r#"
default-policy: noadd
policies:
    -   eh?
    -   this ain't a map
        "#;
        assert!(Config::new(&config).is_err());
    }

    #[test]
    fn config_can_have_several_policies() {
        let config = r#"
default-policy: immutable
policies:
    hello-there: noadd
    general-kenobi: nodelete
        "#;
        assert!(Config::new(&config).is_ok());
    }

    #[test]
    fn match_default_policy() {
        let config_yaml = r#"
default-policy: noadd
        "#;
        let config = Config::new(&config_yaml).unwrap();

        // With only a default policy, this config has just 1 rule.
        assert_eq!(config.rules(), 1);

        // Any path prefix we throw at match_policy() shall come up
        // as the default policy.
        let (_path, policy) = config.match_policy("./Documents/hello/there.txt");
        assert_eq!(policy, POLICY_NOADD);
        let (_path, policy) = config.match_policy("./Music/general/kenobi.txt");
        assert_eq!(policy, POLICY_NOADD);
    }

    #[test]
    fn match_nondefault_policies() {
        let config_yaml = r#"
default-policy: immutable
policies:
    ./Pictures/: noadd
    ./Pictures/2019/third-party/: nodelete
    ./Pictures/2020/: nomodify
    ./Pictures/2020/food/: nodelete,nomodify
        "#;
        let config = Config::new(&config_yaml).unwrap();

        assert_eq!(config.rules(), 5);

        // Falls back on the default-policy absent any specific policy
        // defined for this file.
        let (_path, policy) = config.match_policy("./Documents/catch-me-senpai.txt");
        assert_eq!(policy, POLICY_IMMUTABLE);
        // Matches only ``./Pictures.''
        let (_path, policy) = config.match_policy("./Pictures/2016/yano.jpg");
        assert_eq!(policy, POLICY_NOADD);
        // As above and does _not_ match ``./Pictures/2019/third-party/.''
        let (_path, policy) = config.match_policy("./Pictures/2019/first-party.jpg");
        assert_eq!(policy, POLICY_NOADD);
        // Does match ``./Pictures/2019/third-party/.''
        let (_path, policy) = config.match_policy("./Pictures/2019/third-party/yano.jpg");
        assert_eq!(policy, POLICY_NODELETE);

        // Path prefix matching is done strictly and exactly;
        // ``food.md'' doesn't match ``food/,'' so there's no risk of
        // zakopane confusing cohabiting entities with similar basenames.
        let (path, policy) = config.match_policy("./Pictures/2020/food.md");
        assert_eq!(policy, POLICY_NOMODIFY);
        assert_eq!(path, "./Pictures/2020/");
        let (path, policy) = config.match_policy("./Pictures/2020/food/tacos.jpg");
        assert_eq!(policy, POLICY_NODELETE | POLICY_NOMODIFY);
        assert_eq!(path, "./Pictures/2020/food/");
    }
}
<commit_msg>config: un-use std::io::{Error, ErrorKind}<commit_after>// This module defines constants and functions for working with zakocmp
// configuration files.

extern crate yaml_rust;

use std::result::Result;
use std::string::String;
use std::vec::Vec;
use yaml_rust::{Yaml, YamlLoader};

const DEFAULT_POLICY_KEY: &'static str = "default-policy";
const POLICIES_KEY: &'static str = "policies";

// Enumerates the string representations of known policies.
const POLICY_REPR_IGNORE: &'static str = "ignore";
const POLICY_REPR_NOADD: &'static str = "noadd";
const POLICY_REPR_NODELETE: &'static str = "nodelete";
const POLICY_REPR_NOMODIFY: &'static str = "nomodify";
const POLICY_REPR_IMMUTABLE: &'static str = "immutable";

// Represents known policies as integers.
pub const POLICY_IGNORE: i32 = 0;
pub const POLICY_NOADD: i32 = 1 << 0;
pub const POLICY_NODELETE: i32 = 1 << 1;
pub const POLICY_NOMODIFY: i32 = 1 << 2;
pub const POLICY_IMMUTABLE: i32 = POLICY_NOADD | POLICY_NODELETE | POLICY_NOMODIFY;

// Represents a sorted vector of zakopane config rules, each mapping a
// path (prefix) to a policy. This type alias is provided for ease of
// coding.
type Policies = Vec<(String, i32)>;

// Represents a zakopane config. Please consult the documentation.
pub struct Config {
    default_policy: i32,
    policies: Policies,
}

// Borrows the string representation of one policy `token` and returns
// the equivalent integral representation.
fn policy_token_as_int(token: &str) -> Result<i32, std::io::Error> {
    match token {
        POLICY_REPR_IGNORE => Ok(POLICY_IGNORE),
        POLICY_REPR_NOADD => Ok(POLICY_NOADD),
        POLICY_REPR_NODELETE => Ok(POLICY_NODELETE),
        POLICY_REPR_NOMODIFY => Ok(POLICY_NOMODIFY),
        POLICY_REPR_IMMUTABLE => Ok(POLICY_IMMUTABLE),
        _ => Err(std::io::Error::new(
            std::io::ErrorKind::InvalidInput,
            format!("bad token: ``{}''", token),
        )),
    }
}

// Borrows the string representation of a combined `policy` and returns
// the equivalent integral representation. This function expects
// `policy` to comprise one or more policy tokens separated by commas.
fn policy_tokens_as_int(policy: &str) -> Result<i32, std::io::Error> {
    let policy_ints: Vec<i32> = policy
        .split(",")
        .map(|tok| policy_token_as_int(tok))
        .collect::<Result<Vec<i32>, std::io::Error>>()?;
    return Ok(policy_ints
        .iter()
        .fold(POLICY_IGNORE, |accum, elem| accum | elem));
}

// Borrows yaml representations of one line of zakopane policy and
// returns the corresponding valid tuple suitable for use in building a
// Policies object.
fn extract_policy(ypath: &Yaml, policy_tokens: &Yaml) -> Result<(String, i32), std::io::Error> {
    let path: String = match ypath.as_str() {
        Some(string) => string.to_owned(),
        None => {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                "malformed path?",
            ))
        }
    };
    let policy: i32 = match policy_tokens.as_str() {
        Some(string) => policy_tokens_as_int(string)?,
        None => {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                "malformed policy?",
            ))
        }
    };
    Ok((path, policy))
}

// Borrows the YAML representation of a zakopane config and returns the
// corresponding Policies. The return value can be benignly
// empty (e.g. if the present config elects not to specify any rules).
fn policies_from_yaml(doc: &Yaml) -> Result<Policies, std::io::Error> {
    let policies_map_yaml = &doc[POLICIES_KEY];
    if policies_map_yaml.is_badvalue() {
        // Assumes the config may be benignly devoid of specific
        // policies, returning Ok.
        return Ok(vec![]);
    }
    // Otherwise, iterates over the policies map. Each entry in the
    // policies map correlates a path prefix to a comma-separated list
    // of policies.
    let policies_map: &yaml_rust::yaml::Hash = match policies_map_yaml.as_hash() {
        Some(map) => map,
        None => {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                "malformed policies",
            ))
        }
    };
    let mut policies: Policies = policies_map
        .into_iter()
        .map(|pair| extract_policy(&pair.0, &pair.1))
        .collect::<Result<Policies, std::io::Error>>()?;
    policies.sort_unstable_by_key(|pair| pair.0.to_owned());
    Ok(policies)
}

// Borrows the YAML representation of a zakopane config and returns the
// integral default-policy defined within.
fn default_policy_from_yaml(doc: &Yaml) -> Result<i32, std::io::Error> {
    let default_policy_yaml = &doc[DEFAULT_POLICY_KEY];
    if default_policy_yaml.is_badvalue() {
        return Err(std::io::Error::new(
            std::io::ErrorKind::InvalidData,
            DEFAULT_POLICY_KEY,
        ));
    }
    let default_policy: i32 = match default_policy_yaml.as_str() {
        None => {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                DEFAULT_POLICY_KEY,
            ))
        }
        Some(token) => policy_tokens_as_int(&token),
    }?;
    Ok(default_policy)
}

impl Config {
    // Borrows the string representation of a zakopane config and
    // returns a corresponding Config.
    pub fn new(config: &str) -> Result<Config, std::io::Error> {
        let docs: Vec<Yaml> = match YamlLoader::load_from_str(config) {
            Ok(val) => val,
            Err(scan_error) => {
                return Err(std::io::Error::new(
                    std::io::ErrorKind::InvalidData,
                    scan_error,
                ))
            }
        };
        if docs.len() == 0 {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                "empty zakopane config",
            ));
        }
        let doc = &docs[0];

        let default_policy = default_policy_from_yaml(&doc)?;
        let policies: Policies = policies_from_yaml(&doc)?;

        Ok(Config {
            default_policy: default_policy,
            policies: policies,
        })
    }

    // Returns how many rules this config contains.
    // This is never less than 1 as a default-policy is always required.
    pub fn rules(&self) -> usize {
        1 + self.policies.len()
    }

    // Borrows a `path` and returns the best-matched policy that
    // applies. This function returns an owned tuple of the
    // (closest-matched path expression, integral policy).
    //
    // This function represents the default-policy fallback by
    // returning the tuple consisting of an empty &str and the
    // default policy.
    pub fn match_policy(&self, path: &str) -> (&str, i32) {
        let mut best_match_path: &str = "";
        let mut best_match_policy: i32 = 0;
        for (prefix, policy) in self.policies.iter() {
            if path.starts_with(prefix) && prefix.len() > best_match_path.len() {
                best_match_path = prefix;
                best_match_policy = *policy;
            }
        }
        if best_match_path.len() == 0 {
            return ("", self.default_policy);
        }
        return (best_match_path, best_match_policy);
    }
}

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

    #[test]
    fn policy_token_bare() {
        let policy: i32 = policy_tokens_as_int(&"noadd").unwrap();
        assert_eq!(policy, POLICY_NOADD);

        let policy: i32 = policy_tokens_as_int(&"nodelete").unwrap();
        assert_eq!(policy, POLICY_NODELETE);

        let policy: i32 = policy_tokens_as_int(&"nomodify").unwrap();
        assert_eq!(policy, POLICY_NOMODIFY);
    }

    #[test]
    fn policy_tokens_can_combo() {
        let policy: i32 = policy_tokens_as_int(&"noadd,nodelete").unwrap();
        assert_eq!(policy, POLICY_NOADD | POLICY_NODELETE);
    }

    #[test]
    fn policy_tokens_can_repeat() {
        let policy: i32 =
            policy_tokens_as_int(&"noadd,noadd,noadd,noadd,nodelete,nodelete,nodelete,noadd")
                .unwrap();
        assert_eq!(policy, POLICY_NOADD | POLICY_NODELETE);
    }

    #[test]
    fn config_must_not_be_empty() {
        let config = "";
        assert!(Config::new(&config).is_err());
    }

    #[test]
    fn config_must_not_be_obviously_malformed() {
        let config = r#"
This is not a zakopane config -
rather, it's two lines of text.
        "#;
        assert!(Config::new(&config).is_err());
    }

    #[test]
    fn config_requires_default_policy() {
        let config = r#"
policies:
    hello-there: nomodify
        "#;
        assert!(Config::new(&config).is_err());

        let mut config_with_default_policy: String = r#"
default-policy: immutable
        "#
        .to_string();
        config_with_default_policy.push_str(&config);
        assert!(Config::new(&config_with_default_policy).is_ok())
    }

    #[test]
    fn config_might_not_have_specific_policies() {
        let config = r#"
default-policy: nodelete
one-irrelevant-key: it doesn't matter what we put here
another-irrelevant-key: this doesn't invalidate the YAML
third-irrelevant-key: so long as it contains a default-policy
        "#;
        assert!(Config::new(&config).is_ok());
    }

    #[test]
    fn config_policies_must_be_a_map() {
        let config = r#"
default-policy: noadd
policies:
    -   eh?
    -   this ain't a map
        "#;
        assert!(Config::new(&config).is_err());
    }

    #[test]
    fn config_can_have_several_policies() {
        let config = r#"
default-policy: immutable
policies:
    hello-there: noadd
    general-kenobi: nodelete
        "#;
        assert!(Config::new(&config).is_ok());
    }

    #[test]
    fn match_default_policy() {
        let config_yaml = r#"
default-policy: noadd
        "#;
        let config = Config::new(&config_yaml).unwrap();

        // With only a default policy, this config has just 1 rule.
        assert_eq!(config.rules(), 1);

        // Any path prefix we throw at match_policy() shall come up
        // as the default policy.
        let (_path, policy) = config.match_policy("./Documents/hello/there.txt");
        assert_eq!(policy, POLICY_NOADD);
        let (_path, policy) = config.match_policy("./Music/general/kenobi.txt");
        assert_eq!(policy, POLICY_NOADD);
    }

    #[test]
    fn match_nondefault_policies() {
        let config_yaml = r#"
default-policy: immutable
policies:
    ./Pictures/: noadd
    ./Pictures/2019/third-party/: nodelete
    ./Pictures/2020/: nomodify
    ./Pictures/2020/food/: nodelete,nomodify
        "#;
        let config = Config::new(&config_yaml).unwrap();

        assert_eq!(config.rules(), 5);

        // Falls back on the default-policy absent any specific policy
        // defined for this file.
        let (_path, policy) = config.match_policy("./Documents/catch-me-senpai.txt");
        assert_eq!(policy, POLICY_IMMUTABLE);
        // Matches only ``./Pictures.''
        let (_path, policy) = config.match_policy("./Pictures/2016/yano.jpg");
        assert_eq!(policy, POLICY_NOADD);
        // As above and does _not_ match ``./Pictures/2019/third-party/.''
        let (_path, policy) = config.match_policy("./Pictures/2019/first-party.jpg");
        assert_eq!(policy, POLICY_NOADD);
        // Does match ``./Pictures/2019/third-party/.''
        let (_path, policy) = config.match_policy("./Pictures/2019/third-party/yano.jpg");
        assert_eq!(policy, POLICY_NODELETE);

        // Path prefix matching is done strictly and exactly;
        // ``food.md'' doesn't match ``food/,'' so there's no risk of
        // zakopane confusing cohabiting entities with similar basenames.
        let (path, policy) = config.match_policy("./Pictures/2020/food.md");
        assert_eq!(policy, POLICY_NOMODIFY);
        assert_eq!(path, "./Pictures/2020/");
        let (path, policy) = config.match_policy("./Pictures/2020/food/tacos.jpg");
        assert_eq!(policy, POLICY_NODELETE | POLICY_NOMODIFY);
        assert_eq!(path, "./Pictures/2020/food/");
    }
}
<|endoftext|>


<commit_before>// This module defines constants and functions for working with zakocmp
// configuration files.

use std::io::{Error, ErrorKind};
use std::result::Result;

// Enumerates the string representations of known policies.
const POLICY_REPR_IGNORE: &str = "ignore";
const POLICY_REPR_NOADD: &str = "noadd";
const POLICY_REPR_NODELETE: &str = "nodelete";
const POLICY_REPR_NOMODIFY: &str = "nomodify";
const POLICY_REPR_IMMUTABLE: &str = "immutable";

// Represents known policies as integers.
pub const POLICY_IGNORE: i32 = 0;
pub const POLICY_NOADD: i32 = 1 << 0;
pub const POLICY_NODELETE: i32 = 1 << 1;
pub const POLICY_NOMODIFY: i32 = 1 << 2;
pub const POLICY_IMMUTABLE: i32 = POLICY_NOADD & POLICY_NODELETE & POLICY_NOMODIFY;

// Borrows the string representation of one policy `token` and returns
// the equivalent integral representation.
fn policy_token_as_int(token: &str) -> Result<i32, Error> {
    match token {
        POLICY_REPR_IGNORE => Ok(POLICY_IGNORE),
        POLICY_REPR_NOADD => Ok(POLICY_NOADD),
        POLICY_REPR_NODELETE => Ok(POLICY_NODELETE),
        POLICY_REPR_NOMODIFY => Ok(POLICY_NOMODIFY),
        POLICY_REPR_IMMUTABLE => Ok(POLICY_IMMUTABLE),
        _ => Err(Error::new(
            ErrorKind::InvalidInput,
            format!("bad token: ``{}''", token),
        )),
    }
}

// Borrows the string representation of a combined `policy` and returns
// the equivalent integral representation. This function expects
// `policy` to comprise one or more policy tokens separated by commas.
pub fn policy_repr_as_int(policy: &str) -> Result<i32, Error> {
    let mut policy_int: i32 = POLICY_IGNORE;
    for token in policy.split(",") {
        policy_int |= policy_token_as_int(token)?;
    }
    return Ok(policy_int);
}
<commit_msg>zakocmp: add documentation tests for config module<commit_after>// This module defines constants and functions for working with zakocmp
// configuration files.

use std::io::{Error, ErrorKind};
use std::result::Result;

// Enumerates the string representations of known policies.
const POLICY_REPR_IGNORE: &str = "ignore";
const POLICY_REPR_NOADD: &str = "noadd";
const POLICY_REPR_NODELETE: &str = "nodelete";
const POLICY_REPR_NOMODIFY: &str = "nomodify";
const POLICY_REPR_IMMUTABLE: &str = "immutable";

// Represents known policies as integers.
pub const POLICY_IGNORE: i32 = 0;
pub const POLICY_NOADD: i32 = 1 << 0;
pub const POLICY_NODELETE: i32 = 1 << 1;
pub const POLICY_NOMODIFY: i32 = 1 << 2;
pub const POLICY_IMMUTABLE: i32 = POLICY_NOADD | POLICY_NODELETE | POLICY_NOMODIFY;

// Borrows the string representation of one policy `token` and returns
// the equivalent integral representation.
fn policy_token_as_int(token: &str) -> Result<i32, Error> {
    match token {
        POLICY_REPR_IGNORE => Ok(POLICY_IGNORE),
        POLICY_REPR_NOADD => Ok(POLICY_NOADD),
        POLICY_REPR_NODELETE => Ok(POLICY_NODELETE),
        POLICY_REPR_NOMODIFY => Ok(POLICY_NOMODIFY),
        POLICY_REPR_IMMUTABLE => Ok(POLICY_IMMUTABLE),
        _ => Err(Error::new(
            ErrorKind::InvalidInput,
            format!("bad token: ``{}''", token),
        )),
    }
}

/// Borrows the string representation of a combined `policy` and returns
/// the equivalent integral representation. This function expects
/// `policy` to comprise one or more policy tokens separated by commas.
///
/// # Examples
///
/// ```
/// # mod zakocmp;
/// let policy: i32 = config::policy_repr_as_int(&"immutable").unwrap();
/// assert!(policy == config::POLICY_IMMUTABLE);
/// ```
///
/// ```
/// # mod zakocmp;
/// let policy: i32 = config::policy_repr_as_int(&"noadd").unwrap();
/// assert!(policy == config::POLICY_NOADD);
/// ```
///
/// ```
/// # mod zakocmp;
/// let policy: i32 = config::policy_repr_as_int(&"nomodify").unwrap();
/// assert!(policy == config::POLICY_NOMODIFY);
/// ```
///
/// ```
/// # mod zakocmp;
/// let policy: i32 = config::policy_repr_as_int(&"nodelete").unwrap();
/// assert!(policy == config::POLICY_NODELETE);
/// ```
///
/// ```
/// # mod zakocmp;
/// // While it's pointless to repeat oneself,
/// // it's not invalid to do so.
/// let policy: i32 = config::policy_repr_as_int(
///     &"immutable,immutable,immutable").unwrap();
/// assert!(policy == config::POLICY_IMMUTABLE);
/// ```
///
/// ```
/// # mod zakocmp;
/// let policy: i32 = config::policy_repr_as_int(
///     &"noadd,nodelete,nomodify").unwrap();
/// assert!(policy == config::POLICY_IMMUTABLE);
/// ```
///
/// ```
/// # mod zakocmp;
/// let policy: i32 = config::policy_repr_as_int(
///     &"noadd,nomodify").unwrap();
/// assert!(policy == config::POLICY_NOADD | config::POLICY_NOMODIFY);
/// ```
///
/// # Failures
///
/// ```
/// # mod zakocmp;
/// // Obi-wan was witty, but not good at dictating zakocmp policy.
/// assert!(!config::policy_repr_as_int(&"hello there!").is_ok());
/// ```
///
pub fn policy_repr_as_int(policy: &str) -> Result<i32, Error> {
    let mut policy_int: i32 = POLICY_IGNORE;
    for token in policy.split(",") {
        policy_int |= policy_token_as_int(token)?;
    }
    return Ok(policy_int);
}
<|endoftext|>