<commit_before>//! hotwatch is a Rust library for conveniently watching and handling file changes.
//!
//! Watching is done on a separate thread to avoid blocking your enjoyment of life.
//! All handlers are run on that thread as well, so keep that in mind when attempting to access
//! outside data from within a handler.
//!
//! Nightly Rust is required, as hotwatch uses the box keyword internally.

#![feature(box_syntax)]
#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

extern crate notify;
extern crate parking_lot;

use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;
use std::sync::mpsc::{channel, Receiver};
use parking_lot::Mutex;
use notify::{RecommendedWatcher, Watcher};

#[derive(Debug)]
pub struct Event {
    pub path: String,
    op_flags: u32
}

impl Event {
    fn check(&self, op: notify::op::Op) -> bool {
        let flag = op.bits();
        self.op_flags & flag == flag
    }

    pub fn chmoded(&self) -> bool {
        self.check(notify::op::CHMOD)
    }

    pub fn created(&self) -> bool {
        self.check(notify::op::CREATE)
    }

    pub fn changed(&self) -> bool {
        self.check(notify::op::WRITE)
    }

    pub fn removed(&self) -> bool {
        self.check(notify::op::REMOVE)
    }

    pub fn renamed(&self) -> bool {
        self.check(notify::op::RENAME)
    }

    pub fn ignored(&self) -> bool {
        self.check(notify::op::IGNORED)
    }
}

fn event_from_notify(e: notify::Event) -> Option<Event> {
    if let notify::Event { path: Some(path), op: Ok(op) } = e {
        if cfg!(debug_assertions) {
            println!("{:?} {:?}", op, path);
        }
        path.to_str()
            .map(|s| s.to_string())
            .map(|s| Event {
                path: s,
                op_flags: op.bits()
            })
    } else {
        None
    }
}

#[derive(Debug)]
pub enum Error {
    Io(std::io::Error),
    Notify(notify::Error)
}

type HotwatchResult<T> = Result<T, Error>;

type Handler = Box<Fn(Event) + Send>;
type HandlerMapMutex = Arc<Mutex<HashMap<String, Handler>>>;

pub struct Hotwatch {
    watcher: RecommendedWatcher,
    handler_map_mutex: HandlerMapMutex,
}

impl Hotwatch {
    /// Creates a new hotwatch instance.
    ///
    /// # Errors
    ///
    /// This function can fail if the underlying [notify](https://github.com/passcod/rsnotify)
    /// instance fails to initialize. This will unfortunately expose you to notify's own error
    /// type; hotwatch doesn't perfectly encapsulate this.
    ///
    /// # Examples
    ///
    /// ```
    /// use hotwatch::Hotwatch;
    ///
    /// let hotwatch = Hotwatch::new().expect("Hotwatch failed to initialize.");
    /// ```
    pub fn new() -> HotwatchResult<Self> {
        let (tx, rx) = channel();
        let handler_map_mutex = Arc::new(Mutex::new(HashMap::new()));
        Hotwatch::run(handler_map_mutex.clone(), rx);
        Watcher::new(tx)
            .map_err(Error::Notify)
            .map(|watcher| Hotwatch {
                watcher: watcher,
                handler_map_mutex: handler_map_mutex,
            })
    }

    /// Watch a path and register a handler to it.
    ///
    /// Note that handlers will be run in hotwatch's watch thread, so you'll have to use `move`
    /// if the closure captures anything.
    ///
    /// # Examples
    ///
    /// ```
    /// use hotwatch::Hotwatch;
    ///
    /// let mut hotwatch = Hotwatch::new().expect("Hotwatch failed to initialize.");
    /// hotwatch.watch("README.md", |e: hotwatch::Event| {
    ///   if e.changed() {
    ///     println!("{} changed!", e.path);
    ///   }
    /// }).expect("Failed to watch file!");
    /// ```
    pub fn watch<F>(&mut self, path: &str, handler: F) -> HotwatchResult<()>
    where F: 'static + Fn(Event) + Send {
        let mut handlers = self.handler_map_mutex.lock();
        self.watcher.watch(Path::new(path))
            .map_err(|e| match e {
                notify::Error::Io(e) => Error::Io(e),
                _ => Error::Notify(e)
            })
            .map(|_| {
                (*handlers).insert(path.to_string(), box handler);
            })
    }

    fn run(handler_map_mutex: HandlerMapMutex, rx: Receiver<notify::Event>) {
        std::thread::spawn(move || {
            loop {
                match rx.recv() {
                    Ok(notify_event) => {
                        if let Some(event) = event_from_notify(notify_event) {
                            let handlers = handler_map_mutex.lock();
                            if let Some(handler) = (*handlers).get(&event.path) {
                                handler(event);
                            }
                        }
                    },
                    Err(e) => {
                        if cfg!(debug_assertions) {
                            println!("Receiver error: {:?}", e);
                        }
                    }
                }
            }
        });
    }
}
<commit_msg>Detailed potential errors in the watch function.<commit_after>//! hotwatch is a Rust library for conveniently watching and handling file changes.
//!
//! Watching is done on a separate thread to avoid blocking your enjoyment of life.
//! All handlers are run on that thread as well, so keep that in mind when attempting to access
//! outside data from within a handler.
//!
//! Nightly Rust is required, as hotwatch uses the box keyword internally.

#![feature(box_syntax)]
#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

extern crate notify;
extern crate parking_lot;

use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;
use std::sync::mpsc::{channel, Receiver};
use parking_lot::Mutex;
use notify::{RecommendedWatcher, Watcher};

#[derive(Debug)]
pub struct Event {
    pub path: String,
    op_flags: u32
}

impl Event {
    fn check(&self, op: notify::op::Op) -> bool {
        let flag = op.bits();
        self.op_flags & flag == flag
    }

    pub fn chmoded(&self) -> bool {
        self.check(notify::op::CHMOD)
    }

    pub fn created(&self) -> bool {
        self.check(notify::op::CREATE)
    }

    pub fn changed(&self) -> bool {
        self.check(notify::op::WRITE)
    }

    pub fn removed(&self) -> bool {
        self.check(notify::op::REMOVE)
    }

    pub fn renamed(&self) -> bool {
        self.check(notify::op::RENAME)
    }

    pub fn ignored(&self) -> bool {
        self.check(notify::op::IGNORED)
    }
}

fn event_from_notify(e: notify::Event) -> Option<Event> {
    if let notify::Event { path: Some(path), op: Ok(op) } = e {
        if cfg!(debug_assertions) {
            println!("{:?} {:?}", op, path);
        }
        path.to_str()
            .map(|s| s.to_string())
            .map(|s| Event {
                path: s,
                op_flags: op.bits()
            })
    } else {
        None
    }
}

#[derive(Debug)]
pub enum Error {
    Io(std::io::Error),
    Notify(notify::Error)
}

type HotwatchResult<T> = Result<T, Error>;

type Handler = Box<Fn(Event) + Send>;
type HandlerMapMutex = Arc<Mutex<HashMap<String, Handler>>>;

pub struct Hotwatch {
    watcher: RecommendedWatcher,
    handler_map_mutex: HandlerMapMutex,
}

impl Hotwatch {
    /// Creates a new hotwatch instance.
    ///
    /// # Errors
    ///
    /// This function can fail if the underlying [notify](https://github.com/passcod/rsnotify)
    /// instance fails to initialize. This will unfortunately expose you to notify's own error
    /// type; hotwatch doesn't perfectly encapsulate this.
    ///
    /// # Examples
    ///
    /// ```
    /// use hotwatch::Hotwatch;
    ///
    /// let hotwatch = Hotwatch::new().expect("Hotwatch failed to initialize.");
    /// ```
    pub fn new() -> HotwatchResult<Self> {
        let (tx, rx) = channel();
        let handler_map_mutex = Arc::new(Mutex::new(HashMap::new()));
        Hotwatch::run(handler_map_mutex.clone(), rx);
        Watcher::new(tx)
            .map_err(Error::Notify)
            .map(|watcher| Hotwatch {
                watcher: watcher,
                handler_map_mutex: handler_map_mutex,
            })
    }

    /// Watch a path and register a handler to it.
    ///
    /// Note that handlers will be run in hotwatch's watch thread, so you'll have to use `move`
    /// if the closure captures anything.
    ///
    /// # Errors
    ///
    /// Watching will fail if the path can't be read, thus returning
    /// a `hotwatch::Error::Io(std::io::Error)`.
    ///
    /// # Examples
    ///
    /// ```
    /// use hotwatch::Hotwatch;
    ///
    /// let mut hotwatch = Hotwatch::new().expect("Hotwatch failed to initialize.");
    /// hotwatch.watch("README.md", |e: hotwatch::Event| {
    ///   if e.changed() {
    ///     println!("{} changed!", e.path);
    ///   }
    /// }).expect("Failed to watch file!");
    /// ```
    pub fn watch<F>(&mut self, path: &str, handler: F) -> HotwatchResult<()>
    where F: 'static + Fn(Event) + Send {
        let mut handlers = self.handler_map_mutex.lock();
        self.watcher.watch(Path::new(path))
            .map_err(|e| match e {
                notify::Error::Io(e) => Error::Io(e),
                _ => Error::Notify(e)
            })
            .map(|_| {
                (*handlers).insert(path.to_string(), box handler);
            })
    }

    fn run(handler_map_mutex: HandlerMapMutex, rx: Receiver<notify::Event>) {
        std::thread::spawn(move || {
            loop {
                match rx.recv() {
                    Ok(notify_event) => {
                        if let Some(event) = event_from_notify(notify_event) {
                            let handlers = handler_map_mutex.lock();
                            if let Some(handler) = (*handlers).get(&event.path) {
                                handler(event);
                            }
                        }
                    },
                    Err(e) => {
                        if cfg!(debug_assertions) {
                            println!("Receiver error: {:?}", e);
                        }
                    }
                }
            }
        });
    }
}
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Andres Vahter (andres.vahter@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#[macro_use]
extern crate log;
extern crate libc;

#[allow(non_camel_case_types)]
pub mod ffiairspy;

use std::mem;
use std::ptr;

#[derive(Debug)]
pub enum AirspyError {
    InvalidParam       = ffiairspy::AIRSPY_ERROR_INVALID_PARAM as isize,
    NotFound           = ffiairspy::AIRSPY_ERROR_NOT_FOUND as isize,
    Busy               = ffiairspy::AIRSPY_ERROR_BUSY as isize,
    NoMem              = ffiairspy::AIRSPY_ERROR_NO_MEM as isize,
    LibUsb             = ffiairspy::AIRSPY_ERROR_LIBUSB as isize,
    Thread             = ffiairspy::AIRSPY_ERROR_THREAD as isize,
    StreamingThreadErr = ffiairspy::AIRSPY_ERROR_STREAMING_THREAD_ERR as isize,
    StreamingStopped   = ffiairspy::AIRSPY_ERROR_STREAMING_STOPPED as isize,
    Other              = ffiairspy::AIRSPY_ERROR_OTHER as isize,
}

#[derive(Debug, Copy, Clone)]
pub enum AirspySamplerate {
    Sps10000000 = 10_000_000,
    Sps2500000  =  2_500_000,
}

#[derive(Debug)]
pub enum AirspySampleType {
    /// 2 * 32bit float per sample
    Float32IQ   = ffiairspy::AIRSPY_SAMPLE_FLOAT32_IQ as isize,
    /// 1 * 32bit float per sample
    Float32Real = ffiairspy::AIRSPY_SAMPLE_FLOAT32_REAL as isize,
    /// 2 * 16bit int per sample
    Int16IQ     = ffiairspy::AIRSPY_SAMPLE_INT16_IQ as isize,
    /// 1 * 16bit int per sample
    Int16Real   = ffiairspy::AIRSPY_SAMPLE_INT16_REAL as isize,
    /// 1 * 16bit unsigned int per sample
    Uint16Real  = ffiairspy::AIRSPY_SAMPLE_UINT16_REAL as isize,
}

pub struct Airspy {
    device: *mut ffiairspy::Struct_airspy_device,
}

impl Airspy {

    pub fn new() -> Result<Airspy, AirspyError> {
        let mut device = ptr::null_mut();

        let error = unsafe {ffiairspy::airspy_open(&mut device)};
        match error {
            ffiairspy::AIRSPY_SUCCESS => Ok(Airspy {device: device}),
            ffiairspy::AIRSPY_ERROR_NO_MEM => Err(AirspyError::NoMem),
            ffiairspy::AIRSPY_ERROR_LIBUSB => Err(AirspyError::LibUsb),
            err => panic!("airspy_open returned error ({:})", err),
        }
    }

    pub fn set_samplerate(&self, samplerate: AirspySamplerate) -> Result<(), AirspyError> {
        let mut supported_samplerates_count: u32 = 0;

        // get count of supported samplerates
        let mut error = unsafe {ffiairspy::airspy_get_samplerates(self.device, &mut supported_samplerates_count as *mut u32, 0)};
        // anything except SUCCESS is library failure, hence panic!
        match error {
            ffiairspy::AIRSPY_SUCCESS => {},
            err => panic!("airspy_get_samplerates returned error ({:})", err),
        }

        assert!(supported_samplerates_count == ffiairspy::AIRSPY_SAMPLERATE_END);

        // get list of supported samplerates
        let mut samplerates: Vec<u32> = vec![0_u32; supported_samplerates_count as usize];
        error = unsafe {ffiairspy::airspy_get_samplerates(self.device, mem::transmute::<&mut u32, *mut u32>(&mut samplerates[0]), supported_samplerates_count)};
        // supported_samplerates_count must be right because we just asked for it from library, there must be lib error if anything other than SUCCESS is returned
        match error {
            ffiairspy::AIRSPY_SUCCESS => {},
            err => panic!("airspy_get_samplerates returned error ({:})", err),
        }

        let sr = samplerate.clone() as u32;
        let mut index: Option<u32> = None;
        for (i, rate) in samplerates.iter().enumerate() {
            if *rate == sr {
                index = Some(i as u32);
                break;
            }
        }

        if index.is_some() {
            let error = unsafe {ffiairspy::airspy_set_samplerate(self.device, index.unwrap())};
            match error {
                ffiairspy::AIRSPY_SUCCESS => Ok(()),
                ffiairspy::AIRSPY_ERROR_LIBUSB => Err(AirspyError::LibUsb),
                err => panic!("airspy_set_samplerate returned error ({:})", err),
            }
        }
        else {
            panic!("samplerate {:?} not supported by libairspy, supported samplerates are {:?}", samplerate, samplerates);
        }
    }

    pub fn set_sample_type(&self, sampletype: AirspySampleType) -> Result<(), AirspyError> {
        match unsafe {ffiairspy::airspy_set_sample_type(self.device, sampletype as u32)} {
            ffiairspy::AIRSPY_SUCCESS => {
                Ok(())
            },
            err => {
                // airspy_set_sample_type should only return SUCCESS
                // however just in case panic here if for some reason it does anything else
                panic!("airspy_set_sample_type returned error {}", err)
            },
        }
    }

    pub fn set_bias_tee(&self, onoff: bool) -> Result<(), AirspyError> {
        let c_onoff = match onoff {
            true => 1,
            false => 0,
        };

        match unsafe {ffiairspy::airspy_set_rf_bias(self.device, c_onoff)} {
            ffiairspy::AIRSPY_SUCCESS => Ok(()),
            ffiairspy::AIRSPY_ERROR_LIBUSB => Err(AirspyError::LibUsb),
            err => {
                panic!("airspy_set_rf_bias returned error {}", err)
            }
        }
    }
}

impl Drop for Airspy {
    fn drop(&mut self) {
        unsafe{ffiairspy::airspy_close(self.device)};
        unsafe{ffiairspy::airspy_exit()};
    }
}
<commit_msg>add set_lna_gain, set_mixer_gain and set_vga_gain<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Andres Vahter (andres.vahter@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#[macro_use]
extern crate log;
extern crate libc;

#[allow(non_camel_case_types)]
pub mod ffiairspy;

use std::mem;
use std::ptr;

#[derive(Debug)]
pub enum AirspyError {
    InvalidParam       = ffiairspy::AIRSPY_ERROR_INVALID_PARAM as isize,
    NotFound           = ffiairspy::AIRSPY_ERROR_NOT_FOUND as isize,
    Busy               = ffiairspy::AIRSPY_ERROR_BUSY as isize,
    NoMem              = ffiairspy::AIRSPY_ERROR_NO_MEM as isize,
    LibUsb             = ffiairspy::AIRSPY_ERROR_LIBUSB as isize,
    Thread             = ffiairspy::AIRSPY_ERROR_THREAD as isize,
    StreamingThreadErr = ffiairspy::AIRSPY_ERROR_STREAMING_THREAD_ERR as isize,
    StreamingStopped   = ffiairspy::AIRSPY_ERROR_STREAMING_STOPPED as isize,
    Other              = ffiairspy::AIRSPY_ERROR_OTHER as isize,
}

#[derive(Debug, Copy, Clone)]
pub enum AirspySamplerate {
    Sps10000000 = 10_000_000,
    Sps2500000  =  2_500_000,
}

#[derive(Debug)]
pub enum AirspySampleType {
    /// 2 * 32bit float per sample
    Float32IQ   = ffiairspy::AIRSPY_SAMPLE_FLOAT32_IQ as isize,
    /// 1 * 32bit float per sample
    Float32Real = ffiairspy::AIRSPY_SAMPLE_FLOAT32_REAL as isize,
    /// 2 * 16bit int per sample
    Int16IQ     = ffiairspy::AIRSPY_SAMPLE_INT16_IQ as isize,
    /// 1 * 16bit int per sample
    Int16Real   = ffiairspy::AIRSPY_SAMPLE_INT16_REAL as isize,
    /// 1 * 16bit unsigned int per sample
    Uint16Real  = ffiairspy::AIRSPY_SAMPLE_UINT16_REAL as isize,
}

pub struct Airspy {
    device: *mut ffiairspy::Struct_airspy_device,
}

impl Airspy {

    pub fn new() -> Result<Airspy, AirspyError> {
        let mut device = ptr::null_mut();

        let error = unsafe {ffiairspy::airspy_open(&mut device)};
        match error {
            ffiairspy::AIRSPY_SUCCESS => Ok(Airspy {device: device}),
            ffiairspy::AIRSPY_ERROR_NO_MEM => Err(AirspyError::NoMem),
            ffiairspy::AIRSPY_ERROR_LIBUSB => Err(AirspyError::LibUsb),
            err => panic!("airspy_open returned error ({:})", err),
        }
    }

    pub fn set_samplerate(&self, samplerate: AirspySamplerate) -> Result<(), AirspyError> {
        let mut supported_samplerates_count: u32 = 0;

        // get count of supported samplerates
        let mut error = unsafe {ffiairspy::airspy_get_samplerates(self.device, &mut supported_samplerates_count as *mut u32, 0)};
        // anything except SUCCESS is library failure, hence panic!
        match error {
            ffiairspy::AIRSPY_SUCCESS => {},
            err => panic!("airspy_get_samplerates returned error ({:})", err),
        }

        assert!(supported_samplerates_count == ffiairspy::AIRSPY_SAMPLERATE_END);

        // get list of supported samplerates
        let mut samplerates: Vec<u32> = vec![0_u32; supported_samplerates_count as usize];
        error = unsafe {ffiairspy::airspy_get_samplerates(self.device, mem::transmute::<&mut u32, *mut u32>(&mut samplerates[0]), supported_samplerates_count)};
        // supported_samplerates_count must be right because we just asked for it from library, there must be lib error if anything other than SUCCESS is returned
        match error {
            ffiairspy::AIRSPY_SUCCESS => {},
            err => panic!("airspy_get_samplerates returned error ({:})", err),
        }

        let sr = samplerate.clone() as u32;
        let mut index: Option<u32> = None;
        for (i, rate) in samplerates.iter().enumerate() {
            if *rate == sr {
                index = Some(i as u32);
                break;
            }
        }

        if index.is_some() {
            let error = unsafe {ffiairspy::airspy_set_samplerate(self.device, index.unwrap())};
            match error {
                ffiairspy::AIRSPY_SUCCESS => Ok(()),
                ffiairspy::AIRSPY_ERROR_LIBUSB => Err(AirspyError::LibUsb),
                err => panic!("airspy_set_samplerate returned error ({:})", err),
            }
        }
        else {
            panic!("samplerate {:?} not supported by libairspy, supported samplerates are {:?}", samplerate, samplerates);
        }
    }

    pub fn set_sample_type(&self, sampletype: AirspySampleType) -> Result<(), AirspyError> {
        match unsafe {ffiairspy::airspy_set_sample_type(self.device, sampletype as u32)} {
            ffiairspy::AIRSPY_SUCCESS => {
                Ok(())
            },
            err => {
                // airspy_set_sample_type should only return SUCCESS
                // however just in case panic here if for some reason it does anything else
                panic!("airspy_set_sample_type returned error {}", err)
            },
        }
    }

    pub fn set_bias_tee(&self, onoff: bool) -> Result<(), AirspyError> {
        let c_onoff = match onoff {
            true => 1,
            false => 0,
        };

        match unsafe {ffiairspy::airspy_set_rf_bias(self.device, c_onoff)} {
            ffiairspy::AIRSPY_SUCCESS => Ok(()),
            ffiairspy::AIRSPY_ERROR_LIBUSB => Err(AirspyError::LibUsb),
            err => {
                panic!("airspy_set_rf_bias returned error {}", err)
            }
        }
    }

    pub fn set_lna_gain(&self, gain: u8) -> Result<(), AirspyError> {
        if gain > 15 {
            Err(AirspyError::InvalidParam)
        }
        else {
            match unsafe {ffiairspy::airspy_set_lna_gain(self.device, gain)} {
                ffiairspy::AIRSPY_SUCCESS => Ok(()),
                ffiairspy::AIRSPY_ERROR_LIBUSB => Err(AirspyError::LibUsb),
                err => {
                    panic!("airspy_set_lna_gain returned error {}", err)
                }
            }
        }
    }

    pub fn set_mixer_gain(&self, gain: u8) -> Result<(), AirspyError> {
        if gain > 15 {
            Err(AirspyError::InvalidParam)
        }
        else {
            match unsafe {ffiairspy::airspy_set_mixer_gain(self.device, gain)} {
                ffiairspy::AIRSPY_SUCCESS => Ok(()),
                ffiairspy::AIRSPY_ERROR_LIBUSB => Err(AirspyError::LibUsb),
                err => {
                    panic!("airspy_set_mixer_gain returned error {}", err)
                }
            }
        }
    }

    pub fn set_vga_gain(&self, gain: u8) -> Result<(), AirspyError> {
        if gain > 15 {
            Err(AirspyError::InvalidParam)
        }
        else {
            match unsafe {ffiairspy::airspy_set_vga_gain(self.device, gain)} {
                ffiairspy::AIRSPY_SUCCESS => Ok(()),
                ffiairspy::AIRSPY_ERROR_LIBUSB => Err(AirspyError::LibUsb),
                err => {
                    panic!("airspy_set_vga_gain returned error {}", err)
                }
            }
        }
    }
}

impl Drop for Airspy {
    fn drop(&mut self) {
        unsafe{ffiairspy::airspy_close(self.device)};
        unsafe{ffiairspy::airspy_exit()};
    }
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate energymon_sys;

use libc::{c_int, c_ulonglong, c_char};
use std::mem;
use std::ptr;
use energymon_sys::*;

/// A basic energy monitor.
pub struct EnergyMon {
    /// The native C struct.
    em: em_impl,
}

impl EnergyMon {
    /// Create and initialize an `EnergyMon`.
    pub fn new() -> Result<EnergyMon, &'static str> {
    	unsafe {
            let mut em: em_impl = mem::uninitialized();
            match em_impl_get(&mut em) {
                0 => (),
                _ => return Err("Failed to create em_impl"),
            }
            match (em.finit)(&mut em) {
            	0 => Ok(EnergyMon{ em: em }),
            	_ => Err("Failed to initialize em_impl"),
        	}
        }
    }

    /// Read the total energy from the `EnergyMon`.
    pub fn read(&self) -> u64 {
        (self.em.fread)(&self.em)
    }

    /// Get a human-readable name of the `EnergyMon`'s source.
    pub fn source(&mut self) -> String {
        const BUFSIZE: usize = 100;
        let mut buf: [c_char; BUFSIZE] = [0; BUFSIZE];
        let ret: *mut c_char = (self.em.fsource)(buf.as_mut_ptr());
        if ret.is_null() {
            return "UNKNOWN".to_owned();
        }
        let buf: &[u8] = unsafe {
            mem::transmute::<&[c_char], &[u8]>(&buf)
        };
        String::from_utf8_lossy(buf).into_owned()
    }

    /// Get the refresh interval for the `EnergyMon`.
    pub fn interval(&self) -> u64 {
        (self.em.finterval)(&self.em)
    }

    /// Cleanup the `EnergyMon`.
    fn finish(&mut self) -> i32 {
    	(self.em.ffinish)(&mut self.em)
    }
}

impl Drop for EnergyMon {
    fn drop(&mut self) {
        self.finish();
    }
}

impl Default for EnergyMon {
    /// Returns a dummy `EnergyMon`.
    fn default() -> EnergyMon {
        extern fn default_init(_impl: *mut em_impl) -> c_int { 0 };
        extern fn default_read_total(_impl: *const em_impl) -> c_ulonglong { 0 };
        extern fn default_finish(_impl: *mut em_impl) -> c_int { 0 };
        extern fn default_get_source(_impl: *mut c_char) -> *mut c_char { ptr::null_mut() };
        extern fn default_get_interval(_impl: *const em_impl) -> c_ulonglong { 1 };
        EnergyMon {
            em: em_impl {
                finit: default_init,
                fread: default_read_total,
                ffinish: default_finish,
                fsource: default_get_source,
                finterval:default_get_interval,
                state: ptr::null_mut(),
            }
        }
    }
}

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

    #[test]
    fn test_interface() {
        let mut em: EnergyMon = EnergyMon::new().unwrap();
        let val = em.read();
        println!("Read {} from {} with refresh interval {}", val, em.source(), em.interval());
    }

    #[test]
    fn test_default() {
        let mut em: EnergyMon = Default::default();
        assert!(em.read() == 0);
        assert!(em.interval() == 1);
        assert!(em.finish() == 0);
        assert!(em.em.state.is_null())
    }

}
<commit_msg>Test source fn for default<commit_after>extern crate libc;
extern crate energymon_sys;

use libc::{c_int, c_ulonglong, c_char};
use std::mem;
use std::ptr;
use energymon_sys::*;

/// A basic energy monitor.
pub struct EnergyMon {
    /// The native C struct.
    em: em_impl,
}

impl EnergyMon {
    /// Create and initialize an `EnergyMon`.
    pub fn new() -> Result<EnergyMon, &'static str> {
    	unsafe {
            let mut em: em_impl = mem::uninitialized();
            match em_impl_get(&mut em) {
                0 => (),
                _ => return Err("Failed to create em_impl"),
            }
            match (em.finit)(&mut em) {
            	0 => Ok(EnergyMon{ em: em }),
            	_ => Err("Failed to initialize em_impl"),
        	}
        }
    }

    /// Read the total energy from the `EnergyMon`.
    pub fn read(&self) -> u64 {
        (self.em.fread)(&self.em)
    }

    /// Get a human-readable name of the `EnergyMon`'s source.
    pub fn source(&mut self) -> String {
        const BUFSIZE: usize = 100;
        let mut buf: [c_char; BUFSIZE] = [0; BUFSIZE];
        let ret: *mut c_char = (self.em.fsource)(buf.as_mut_ptr());
        if ret.is_null() {
            return "UNKNOWN".to_owned();
        }
        let buf: &[u8] = unsafe {
            mem::transmute::<&[c_char], &[u8]>(&buf)
        };
        String::from_utf8_lossy(buf).into_owned()
    }

    /// Get the refresh interval for the `EnergyMon`.
    pub fn interval(&self) -> u64 {
        (self.em.finterval)(&self.em)
    }

    /// Cleanup the `EnergyMon`.
    fn finish(&mut self) -> i32 {
    	(self.em.ffinish)(&mut self.em)
    }
}

impl Drop for EnergyMon {
    fn drop(&mut self) {
        self.finish();
    }
}

impl Default for EnergyMon {
    /// Returns a dummy `EnergyMon`.
    fn default() -> EnergyMon {
        extern fn default_init(_impl: *mut em_impl) -> c_int { 0 };
        extern fn default_read_total(_impl: *const em_impl) -> c_ulonglong { 0 };
        extern fn default_finish(_impl: *mut em_impl) -> c_int { 0 };
        extern fn default_get_source(_impl: *mut c_char) -> *mut c_char { ptr::null_mut() };
        extern fn default_get_interval(_impl: *const em_impl) -> c_ulonglong { 1 };
        EnergyMon {
            em: em_impl {
                finit: default_init,
                fread: default_read_total,
                ffinish: default_finish,
                fsource: default_get_source,
                finterval:default_get_interval,
                state: ptr::null_mut(),
            }
        }
    }
}

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

    #[test]
    fn test_interface() {
        let mut em: EnergyMon = EnergyMon::new().unwrap();
        let val = em.read();
        println!("Read {} from {} with refresh interval {}", val, em.source(), em.interval());
    }

    #[test]
    fn test_default() {
        let mut em: EnergyMon = Default::default();
        assert!(em.read() == 0);
        assert!(em.interval() == 1);
        assert!(em.finish() == 0);
        assert!(em.source().eq("UNKNOWN"));
        assert!(em.em.state.is_null())
    }

}
<|endoftext|>


<commit_before>//! robots.txt parser for Rust
//!
//! The robots.txt Exclusion Protocol is implemented as specified in
//! http://www.robotstxt.org/norobots-rfc.txt
//!
//! # Installation
//!
//! Add it to your ``Cargo.toml``:
//!
//! ```toml
//! [dependencies]
//! robotparser = "0.4"
//! ```
//!
//! Add ``extern crate robotparser`` to your crate root and your're good to go!
//!
//! # Examples
//!
//! ```
//! extern crate robotparser;
//!
//! use robotparser::RobotFileParser;
//!
//! fn main() {
//!     let parser = RobotFileParser::new("http://www.python.org/robots.txt");
//!     parser.read();
//!     assert!(parser.can_fetch("*", "http://www.python.org/robots.txt"));
//! }
//! ```

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
#![cfg_attr(feature="clippy", warn(cyclomatic_complexity))]

extern crate url;
extern crate hyper;

use std::io::Read;
use std::cell::{Cell, RefCell};
use std::borrow::Cow;
use std::time::{Duration, SystemTime, UNIX_EPOCH};

use url::Url;
use hyper::Client;
use hyper::header::UserAgent;
use hyper::status::StatusCode;
use hyper::client::Response;

const USER_AGENT: &'static str = "robotparser-rs (https://crates.io/crates/robotparser)";

/// A rule line is a single "Allow:" (allowance==True) or "Disallow:"
/// (allowance==False) followed by a path."""
#[derive(Debug, Eq, PartialEq, Clone)]
struct RuleLine<'a> {
    path: Cow<'a, str>,
    allowance: bool,
}

#[derive(Debug, Eq, PartialEq, Clone)]
pub struct RequestRate {
    pub requests: usize,
    pub seconds: usize,
}

/// An entry has one or more user-agents and zero or more rulelines
#[derive(Debug, Eq, PartialEq, Clone)]
struct Entry<'a> {
    useragents: RefCell<Vec<String>>,
    rulelines: RefCell<Vec<RuleLine<'a>>>,
    crawl_delay: Option<Duration>,
    sitemaps: Vec<Url>,
    req_rate: Option<RequestRate>,
}

/// robots.txt file parser
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct RobotFileParser<'a> {
    entries: RefCell<Vec<Entry<'a>>>,
    default_entry: RefCell<Entry<'a>>,
    disallow_all: Cell<bool>,
    allow_all: Cell<bool>,
    url: Url,
    host: String,
    path: String,
    last_checked: Cell<i64>,
}

impl<'a> RuleLine<'a> {
    fn new<S>(path: S, allowance: bool) -> RuleLine<'a>
        where S: Into<Cow<'a, str>>
    {
        let path = path.into();
        let mut allow = allowance;
        if path == "" && !allowance {
            // an empty value means allow all
            allow = true;
        }
        RuleLine {
            path: path,
            allowance: allow,
        }
    }

    fn applies_to(&self, filename: &str) -> bool {
        self.path == "*" || filename.starts_with(&self.path[..])
    }
}

impl<'a> Entry<'a> {
    fn new() -> Entry<'a> {
        Entry {
            useragents: RefCell::new(vec![]),
            rulelines: RefCell::new(vec![]),
            crawl_delay: None,
            sitemaps: Vec::new(),
            req_rate: None,
        }
    }

    /// check if this entry applies to the specified agent
    fn applies_to(&self, useragent: &str) -> bool {
        let ua = useragent.split('/').nth(0).unwrap_or("").to_lowercase();
        let useragents = self.useragents.borrow();
        for agent in &*useragents {
            if agent == "*" {
                return true;
            }
            if ua.contains(agent) {
                return true;
            }
        }
        false
    }

    /// Preconditions:
    /// - our agent applies to this entry
    /// - filename is URL decoded
    fn allowance(&self, filename: &str) -> bool {
        let rulelines = self.rulelines.borrow();
        for line in &*rulelines {
            if line.applies_to(filename) {
                return line.allowance;
            }
        }
        true
    }

    fn push_useragent(&self, useragent: &str) {
        let mut useragents = self.useragents.borrow_mut();
        useragents.push(useragent.to_lowercase().to_owned());
    }

    fn push_ruleline(&self, ruleline: RuleLine<'a>) {
        let mut rulelines = self.rulelines.borrow_mut();
        rulelines.push(ruleline);
    }

    fn has_useragent(&self, useragent: &str) -> bool {
        let useragents = self.useragents.borrow();
        useragents.contains(&useragent.to_owned())
    }

    fn is_empty(&self) -> bool {
        let useragents = self.useragents.borrow();
        let rulelines = self.rulelines.borrow();
        useragents.is_empty() && rulelines.is_empty()
    }

    fn set_crawl_delay(&mut self, delay: Duration) {
        self.crawl_delay = Some(delay);
    }

    fn get_crawl_delay(&self) -> Option<Duration> {
        self.crawl_delay
    }

    fn add_sitemap(&mut self, url: &str) {
        if let Ok(url) = Url::parse(url) {
            self.sitemaps.push(url);
        }
    }

    fn get_sitemaps(&self) -> Vec<Url> {
        self.sitemaps.clone()
    }

    fn set_req_rate(&mut self, req_rate: RequestRate) {
        self.req_rate = Some(req_rate);
    }

    fn get_req_rate(&self) -> Option<RequestRate> {
        self.req_rate.clone()
    }
}

impl<'a> Default for Entry<'a> {
    fn default() -> Entry<'a> {
        Entry::new()
    }
}

impl<'a> RobotFileParser<'a> {
    pub fn new<T: AsRef<str>>(url: T) -> RobotFileParser<'a> {
        let parsed_url = Url::parse(url.as_ref()).unwrap();
        RobotFileParser {
            entries: RefCell::new(vec![]),
            default_entry: RefCell::new(Entry::new()),
            disallow_all: Cell::new(false),
            allow_all: Cell::new(false),
            url: parsed_url.clone(),
            host: parsed_url.domain().unwrap().to_owned(),
            path: parsed_url.path().to_owned(),
            last_checked: Cell::new(0i64),
        }
    }

    /// Returns the time the robots.txt file was last fetched.
    ///
    /// This is useful for long-running web spiders that need to
    /// check for new robots.txt files periodically.
    pub fn mtime(&self) -> i64 {
        self.last_checked.get()
    }

    /// Sets the time the robots.txt file was last fetched to the
    /// current time.
    pub fn modified(&self) {
        let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64;
        self.last_checked.set(now);
    }

    /// Sets the URL referring to a robots.txt file.
    pub fn set_url<T: AsRef<str>>(&mut self, url: T) {
        let parsed_url = Url::parse(url.as_ref()).unwrap();
        self.url = parsed_url.clone();
        self.host = parsed_url.domain().unwrap().to_owned();
        self.path = parsed_url.path().to_owned();
        self.last_checked.set(0i64);
    }

    /// Reads the robots.txt URL and feeds it to the parser.
    pub fn read(&self) {
        let client = Client::new();
        let request = client.get(self.url.clone())
            .header(UserAgent(USER_AGENT.to_owned()));
        let res = match request.send() {
            Ok(res) => res,
            Err(_) => {
                return;
            }
        };
        match res.status {
            StatusCode::Unauthorized | StatusCode::Forbidden => {
                self.disallow_all.set(true);
            }
            status if status >= StatusCode::BadRequest && status < StatusCode::InternalServerError => {
                self.allow_all.set(true);
            }
            StatusCode::Ok => self.from_response(res),
            _ => {}
        }
    }

    /// Reads the HTTP response and feeds it to the parser.
    pub fn from_response(&self, mut response: Response) {
        let mut buf = String::new();
        response.read_to_string(&mut buf).unwrap();
        let lines: Vec<&str> = buf.split('\n').collect();
        self.parse(&lines);
    }

    fn _add_entry(&self, entry: Entry<'a>) {
        if entry.has_useragent("*") {
            // the default entry is considered last
            let mut default_entry = self.default_entry.borrow_mut();
            if default_entry.is_empty() {
                // the first default entry wins
                *default_entry = entry;
            }
        } else {
            let mut entries = self.entries.borrow_mut();
            entries.push(entry);
        }
    }

    ///
    /// Parse the input lines from a robots.txt file
    ///
    /// We allow that a user-agent: line is not preceded by
    /// one or more blank lines.
    ///
    pub fn parse<T: AsRef<str>>(&self, lines: &[T]) {
        use url::percent_encoding::percent_decode;

        // states:
        //   0: start state
        //   1: saw user-agent line
        //   2: saw an allow or disallow line
        let mut state = 0;
        let mut entry = Entry::new();

        self.modified();
        for line in lines {
            let mut ln = line.as_ref();
            if ln.is_empty() {
                match state {
                    1 => {
                        entry = Entry::new();
                        state = 0;
                    }
                    2 => {
                        self._add_entry(entry);
                        entry = Entry::new();
                        state = 0;
                    }
                    _ => {}
                }
            }
            // remove optional comment and strip line
            if let Some(i) = ln.find('#') {
                ln = &ln[0..i];
            }
            ln = ln.trim();
            if ln.is_empty() {
                continue;
            }
            let parts: Vec<&str> = ln.splitn(2, ':').collect();
            if parts.len() == 2 {
                let part0 = parts[0].trim().to_lowercase();
                let part1 = String::from_utf8(percent_decode(parts[1].trim().as_bytes()).collect())
                    .unwrap_or("".to_owned());
                match part0 {
                    ref x if x == "user-agent" => {
                        if state == 2 {
                            self._add_entry(entry);
                            entry = Entry::new();
                        }
                        entry.push_useragent(&part1);
                        state = 1;
                    }
                    ref x if x == "disallow" => {
                        if state != 0 {
                            entry.push_ruleline(RuleLine::new(part1, false));
                            state = 2;
                        }
                    }
                    ref x if x == "allow" => {
                        if state != 0 {
                            entry.push_ruleline(RuleLine::new(part1, true));
                            state = 2;
                        }
                    }
                    ref x if x == "crawl-delay" => {
                        if state != 0 {
                            if let Ok(delay) = part1.parse::<f64>() {
                                let delay_seconds = delay.trunc();
                                let delay_nanoseconds = delay.fract() * 10f64.powi(9);
                                let delay = Duration::new(delay_seconds as u64, delay_nanoseconds as u32);
                                entry.set_crawl_delay(delay);
                            }
                            state = 2;
                        }
                    }
                    ref x if x == "sitemap" => {
                        if state != 0 {
                            entry.add_sitemap(&part1);
                            state = 2;
                        }
                    }
                    ref x if x == "request-rate" => {
                        if state != 0 {
                            let numbers: Vec<Result<usize, _>> = part1.split('/').map(|x| x.parse::<usize>()).collect();
                            if numbers.len() == 2 && numbers[0].is_ok() && numbers[1].is_ok() {
                                let req_rate = RequestRate {
                                    requests: numbers[0].clone().unwrap(),
                                    seconds: numbers[1].clone().unwrap(),
                                };
                                entry.set_req_rate(req_rate);
                            }
                            state = 2;
                        }
                    }
                    _ => {}
                }
            }
        }
        if state == 2 {
            self._add_entry(entry);
        }
    }

    /// Using the parsed robots.txt decide if useragent can fetch url
    pub fn can_fetch<T: AsRef<str>>(&self, useragent: T, url: T) -> bool {
        use url::percent_encoding::percent_decode;

        let useragent = useragent.as_ref();
        let url = url.as_ref();

        if self.disallow_all.get() {
            return false;
        }
        if self.allow_all.get() {
            return true;
        }
        // Until the robots.txt file has been read or found not
        // to exist, we must assume that no url is allowable.
        // This prevents false positives when a user erronenously
        // calls can_fetch() before calling read().
        if self.last_checked.get() == 0 {
            return false;
        }
        // search for given user agent matches
        // the first match counts
        let decoded_url = String::from_utf8(percent_decode(url.trim().as_bytes()).collect()).unwrap_or("".to_owned());
        let url_str = match decoded_url {
            ref u if !u.is_empty() => u.to_owned(),
            _ => "/".to_owned(),
        };
        let entries = self.entries.borrow();
        for entry in &*entries {
            if entry.applies_to(useragent) {
                return entry.allowance(&url_str);
            }
        }
        // try the default entry last
        let default_entry = self.default_entry.borrow();
        if !default_entry.is_empty() {
            return default_entry.allowance(&url_str);
        }
        // agent not found ==> access granted
        true
    }

    /// Returns the crawl delay for this user agent as a `Duration`, or None if no crawl delay is defined.
    pub fn get_crawl_delay<T: AsRef<str>>(&self, useragent: T) -> Option<Duration> {
        let useragent = useragent.as_ref();
        if self.last_checked.get() == 0 {
            return None;
        }
        let entries = self.entries.borrow();
        for entry in &*entries {
            if entry.applies_to(useragent) {
                return entry.get_crawl_delay();
            }
        }
        None
    }

    /// Returns the sitemaps for this user agent as a `Vec<Url>`.
    pub fn get_sitemaps<T: AsRef<str>>(&self, useragent: T) -> Vec<Url> {
        let useragent = useragent.as_ref();
        if self.last_checked.get() == 0 {
            return Vec::new();
        }
        let entries = self.entries.borrow();
        for entry in &*entries {
            if entry.applies_to(useragent) {
                return entry.get_sitemaps();
            }
        }
        vec![]
    }

    /// Returns the request rate for this user agent as a `RequestRate`, or None if not request rate is defined
    pub fn get_req_rate<T: AsRef<str>>(&self, useragent: T) -> Option<RequestRate> {
        let useragent = useragent.as_ref();
        if self.last_checked.get() == 0 {
            return None;
        }
        let entries = self.entries.borrow();
        for entry in &*entries {
            if entry.applies_to(useragent) {
                return entry.get_req_rate();
            }
        }
        None
    }
}
<commit_msg>Change from_response method signature<commit_after>//! robots.txt parser for Rust
//!
//! The robots.txt Exclusion Protocol is implemented as specified in
//! http://www.robotstxt.org/norobots-rfc.txt
//!
//! # Installation
//!
//! Add it to your ``Cargo.toml``:
//!
//! ```toml
//! [dependencies]
//! robotparser = "0.4"
//! ```
//!
//! Add ``extern crate robotparser`` to your crate root and your're good to go!
//!
//! # Examples
//!
//! ```
//! extern crate robotparser;
//!
//! use robotparser::RobotFileParser;
//!
//! fn main() {
//!     let parser = RobotFileParser::new("http://www.python.org/robots.txt");
//!     parser.read();
//!     assert!(parser.can_fetch("*", "http://www.python.org/robots.txt"));
//! }
//! ```

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
#![cfg_attr(feature="clippy", warn(cyclomatic_complexity))]

extern crate url;
extern crate hyper;

use std::io::Read;
use std::cell::{Cell, RefCell};
use std::borrow::Cow;
use std::time::{Duration, SystemTime, UNIX_EPOCH};

use url::Url;
use hyper::Client;
use hyper::header::UserAgent;
use hyper::status::StatusCode;
use hyper::client::Response;

const USER_AGENT: &'static str = "robotparser-rs (https://crates.io/crates/robotparser)";

/// A rule line is a single "Allow:" (allowance==True) or "Disallow:"
/// (allowance==False) followed by a path."""
#[derive(Debug, Eq, PartialEq, Clone)]
struct RuleLine<'a> {
    path: Cow<'a, str>,
    allowance: bool,
}

#[derive(Debug, Eq, PartialEq, Clone)]
pub struct RequestRate {
    pub requests: usize,
    pub seconds: usize,
}

/// An entry has one or more user-agents and zero or more rulelines
#[derive(Debug, Eq, PartialEq, Clone)]
struct Entry<'a> {
    useragents: RefCell<Vec<String>>,
    rulelines: RefCell<Vec<RuleLine<'a>>>,
    crawl_delay: Option<Duration>,
    sitemaps: Vec<Url>,
    req_rate: Option<RequestRate>,
}

/// robots.txt file parser
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct RobotFileParser<'a> {
    entries: RefCell<Vec<Entry<'a>>>,
    default_entry: RefCell<Entry<'a>>,
    disallow_all: Cell<bool>,
    allow_all: Cell<bool>,
    url: Url,
    host: String,
    path: String,
    last_checked: Cell<i64>,
}

impl<'a> RuleLine<'a> {
    fn new<S>(path: S, allowance: bool) -> RuleLine<'a>
        where S: Into<Cow<'a, str>>
    {
        let path = path.into();
        let mut allow = allowance;
        if path == "" && !allowance {
            // an empty value means allow all
            allow = true;
        }
        RuleLine {
            path: path,
            allowance: allow,
        }
    }

    fn applies_to(&self, filename: &str) -> bool {
        self.path == "*" || filename.starts_with(&self.path[..])
    }
}

impl<'a> Entry<'a> {
    fn new() -> Entry<'a> {
        Entry {
            useragents: RefCell::new(vec![]),
            rulelines: RefCell::new(vec![]),
            crawl_delay: None,
            sitemaps: Vec::new(),
            req_rate: None,
        }
    }

    /// check if this entry applies to the specified agent
    fn applies_to(&self, useragent: &str) -> bool {
        let ua = useragent.split('/').nth(0).unwrap_or("").to_lowercase();
        let useragents = self.useragents.borrow();
        for agent in &*useragents {
            if agent == "*" {
                return true;
            }
            if ua.contains(agent) {
                return true;
            }
        }
        false
    }

    /// Preconditions:
    /// - our agent applies to this entry
    /// - filename is URL decoded
    fn allowance(&self, filename: &str) -> bool {
        let rulelines = self.rulelines.borrow();
        for line in &*rulelines {
            if line.applies_to(filename) {
                return line.allowance;
            }
        }
        true
    }

    fn push_useragent(&self, useragent: &str) {
        let mut useragents = self.useragents.borrow_mut();
        useragents.push(useragent.to_lowercase().to_owned());
    }

    fn push_ruleline(&self, ruleline: RuleLine<'a>) {
        let mut rulelines = self.rulelines.borrow_mut();
        rulelines.push(ruleline);
    }

    fn has_useragent(&self, useragent: &str) -> bool {
        let useragents = self.useragents.borrow();
        useragents.contains(&useragent.to_owned())
    }

    fn is_empty(&self) -> bool {
        let useragents = self.useragents.borrow();
        let rulelines = self.rulelines.borrow();
        useragents.is_empty() && rulelines.is_empty()
    }

    fn set_crawl_delay(&mut self, delay: Duration) {
        self.crawl_delay = Some(delay);
    }

    fn get_crawl_delay(&self) -> Option<Duration> {
        self.crawl_delay
    }

    fn add_sitemap(&mut self, url: &str) {
        if let Ok(url) = Url::parse(url) {
            self.sitemaps.push(url);
        }
    }

    fn get_sitemaps(&self) -> Vec<Url> {
        self.sitemaps.clone()
    }

    fn set_req_rate(&mut self, req_rate: RequestRate) {
        self.req_rate = Some(req_rate);
    }

    fn get_req_rate(&self) -> Option<RequestRate> {
        self.req_rate.clone()
    }
}

impl<'a> Default for Entry<'a> {
    fn default() -> Entry<'a> {
        Entry::new()
    }
}

impl<'a> RobotFileParser<'a> {
    pub fn new<T: AsRef<str>>(url: T) -> RobotFileParser<'a> {
        let parsed_url = Url::parse(url.as_ref()).unwrap();
        RobotFileParser {
            entries: RefCell::new(vec![]),
            default_entry: RefCell::new(Entry::new()),
            disallow_all: Cell::new(false),
            allow_all: Cell::new(false),
            url: parsed_url.clone(),
            host: parsed_url.domain().unwrap().to_owned(),
            path: parsed_url.path().to_owned(),
            last_checked: Cell::new(0i64),
        }
    }

    /// Returns the time the robots.txt file was last fetched.
    ///
    /// This is useful for long-running web spiders that need to
    /// check for new robots.txt files periodically.
    pub fn mtime(&self) -> i64 {
        self.last_checked.get()
    }

    /// Sets the time the robots.txt file was last fetched to the
    /// current time.
    pub fn modified(&self) {
        let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64;
        self.last_checked.set(now);
    }

    /// Sets the URL referring to a robots.txt file.
    pub fn set_url<T: AsRef<str>>(&mut self, url: T) {
        let parsed_url = Url::parse(url.as_ref()).unwrap();
        self.url = parsed_url.clone();
        self.host = parsed_url.domain().unwrap().to_owned();
        self.path = parsed_url.path().to_owned();
        self.last_checked.set(0i64);
    }

    /// Reads the robots.txt URL and feeds it to the parser.
    pub fn read(&self) {
        let client = Client::new();
        let request = client.get(self.url.clone())
            .header(UserAgent(USER_AGENT.to_owned()));
        let mut res = match request.send() {
            Ok(res) => res,
            Err(_) => {
                return;
            }
        };
        match res.status {
            StatusCode::Unauthorized | StatusCode::Forbidden => {
                self.disallow_all.set(true);
            }
            status if status >= StatusCode::BadRequest && status < StatusCode::InternalServerError => {
                self.allow_all.set(true);
            }
            StatusCode::Ok => self.from_response(&mut res),
            _ => {}
        }
    }

    /// Reads the HTTP response and feeds it to the parser.
    pub fn from_response(&self, response: &mut Response) {
        let mut buf = String::new();
        response.read_to_string(&mut buf).unwrap();
        let lines: Vec<&str> = buf.split('\n').collect();
        self.parse(&lines);
    }

    fn _add_entry(&self, entry: Entry<'a>) {
        if entry.has_useragent("*") {
            // the default entry is considered last
            let mut default_entry = self.default_entry.borrow_mut();
            if default_entry.is_empty() {
                // the first default entry wins
                *default_entry = entry;
            }
        } else {
            let mut entries = self.entries.borrow_mut();
            entries.push(entry);
        }
    }

    ///
    /// Parse the input lines from a robots.txt file
    ///
    /// We allow that a user-agent: line is not preceded by
    /// one or more blank lines.
    ///
    pub fn parse<T: AsRef<str>>(&self, lines: &[T]) {
        use url::percent_encoding::percent_decode;

        // states:
        //   0: start state
        //   1: saw user-agent line
        //   2: saw an allow or disallow line
        let mut state = 0;
        let mut entry = Entry::new();

        self.modified();
        for line in lines {
            let mut ln = line.as_ref();
            if ln.is_empty() {
                match state {
                    1 => {
                        entry = Entry::new();
                        state = 0;
                    }
                    2 => {
                        self._add_entry(entry);
                        entry = Entry::new();
                        state = 0;
                    }
                    _ => {}
                }
            }
            // remove optional comment and strip line
            if let Some(i) = ln.find('#') {
                ln = &ln[0..i];
            }
            ln = ln.trim();
            if ln.is_empty() {
                continue;
            }
            let parts: Vec<&str> = ln.splitn(2, ':').collect();
            if parts.len() == 2 {
                let part0 = parts[0].trim().to_lowercase();
                let part1 = String::from_utf8(percent_decode(parts[1].trim().as_bytes()).collect())
                    .unwrap_or("".to_owned());
                match part0 {
                    ref x if x == "user-agent" => {
                        if state == 2 {
                            self._add_entry(entry);
                            entry = Entry::new();
                        }
                        entry.push_useragent(&part1);
                        state = 1;
                    }
                    ref x if x == "disallow" => {
                        if state != 0 {
                            entry.push_ruleline(RuleLine::new(part1, false));
                            state = 2;
                        }
                    }
                    ref x if x == "allow" => {
                        if state != 0 {
                            entry.push_ruleline(RuleLine::new(part1, true));
                            state = 2;
                        }
                    }
                    ref x if x == "crawl-delay" => {
                        if state != 0 {
                            if let Ok(delay) = part1.parse::<f64>() {
                                let delay_seconds = delay.trunc();
                                let delay_nanoseconds = delay.fract() * 10f64.powi(9);
                                let delay = Duration::new(delay_seconds as u64, delay_nanoseconds as u32);
                                entry.set_crawl_delay(delay);
                            }
                            state = 2;
                        }
                    }
                    ref x if x == "sitemap" => {
                        if state != 0 {
                            entry.add_sitemap(&part1);
                            state = 2;
                        }
                    }
                    ref x if x == "request-rate" => {
                        if state != 0 {
                            let numbers: Vec<Result<usize, _>> = part1.split('/').map(|x| x.parse::<usize>()).collect();
                            if numbers.len() == 2 && numbers[0].is_ok() && numbers[1].is_ok() {
                                let req_rate = RequestRate {
                                    requests: numbers[0].clone().unwrap(),
                                    seconds: numbers[1].clone().unwrap(),
                                };
                                entry.set_req_rate(req_rate);
                            }
                            state = 2;
                        }
                    }
                    _ => {}
                }
            }
        }
        if state == 2 {
            self._add_entry(entry);
        }
    }

    /// Using the parsed robots.txt decide if useragent can fetch url
    pub fn can_fetch<T: AsRef<str>>(&self, useragent: T, url: T) -> bool {
        use url::percent_encoding::percent_decode;

        let useragent = useragent.as_ref();
        let url = url.as_ref();

        if self.disallow_all.get() {
            return false;
        }
        if self.allow_all.get() {
            return true;
        }
        // Until the robots.txt file has been read or found not
        // to exist, we must assume that no url is allowable.
        // This prevents false positives when a user erronenously
        // calls can_fetch() before calling read().
        if self.last_checked.get() == 0 {
            return false;
        }
        // search for given user agent matches
        // the first match counts
        let decoded_url = String::from_utf8(percent_decode(url.trim().as_bytes()).collect()).unwrap_or("".to_owned());
        let url_str = match decoded_url {
            ref u if !u.is_empty() => u.to_owned(),
            _ => "/".to_owned(),
        };
        let entries = self.entries.borrow();
        for entry in &*entries {
            if entry.applies_to(useragent) {
                return entry.allowance(&url_str);
            }
        }
        // try the default entry last
        let default_entry = self.default_entry.borrow();
        if !default_entry.is_empty() {
            return default_entry.allowance(&url_str);
        }
        // agent not found ==> access granted
        true
    }

    /// Returns the crawl delay for this user agent as a `Duration`, or None if no crawl delay is defined.
    pub fn get_crawl_delay<T: AsRef<str>>(&self, useragent: T) -> Option<Duration> {
        let useragent = useragent.as_ref();
        if self.last_checked.get() == 0 {
            return None;
        }
        let entries = self.entries.borrow();
        for entry in &*entries {
            if entry.applies_to(useragent) {
                return entry.get_crawl_delay();
            }
        }
        None
    }

    /// Returns the sitemaps for this user agent as a `Vec<Url>`.
    pub fn get_sitemaps<T: AsRef<str>>(&self, useragent: T) -> Vec<Url> {
        let useragent = useragent.as_ref();
        if self.last_checked.get() == 0 {
            return Vec::new();
        }
        let entries = self.entries.borrow();
        for entry in &*entries {
            if entry.applies_to(useragent) {
                return entry.get_sitemaps();
            }
        }
        vec![]
    }

    /// Returns the request rate for this user agent as a `RequestRate`, or None if not request rate is defined
    pub fn get_req_rate<T: AsRef<str>>(&self, useragent: T) -> Option<RequestRate> {
        let useragent = useragent.as_ref();
        if self.last_checked.get() == 0 {
            return None;
        }
        let entries = self.entries.borrow();
        for entry in &*entries {
            if entry.applies_to(useragent) {
                return entry.get_req_rate();
            }
        }
        None
    }
}
<|endoftext|>


<commit_before>#![crate_name="orchestrate"]

#![feature(globs, phase)]

extern crate serialize;
extern crate hyper;
extern crate url;

pub use error::OrchestrateError;

use client::Client;
use key_value::{
    GetKeyValue, CreateKeyValue, UpdateKeyValue, DeleteKeyValue, ListReader,
};
use search::SearchBuilder;
use events::{GetEvents, CreateEvent, DeleteEvent};
use graph::{GetRelations, PutRelation, DeleteRelation};
use serialize::{json, Decoder, Decodable};
use hyper::method::Head;

pub trait RepresentsJSON : Decodable<json::Decoder, json::DecoderError> {}
impl<T: Decodable<json::Decoder, json::DecoderError>> RepresentsJSON for T {}

pub struct Orchestrate {
    client: Client
}

impl Orchestrate {

    // create a new orchestrate client
    pub fn new(token: &str) -> Orchestrate {
        Orchestrate {
            client: Client::new(token)
        }
    }

    pub fn ping(&mut self) -> Result<bool, OrchestrateError> {
        let mut res = try!(self.client.trailing("").method(Head).exec());

        if (res.status as i32) != 200 {
            return Err(error::RequestError(try!(res.read_to_string())));
        }

        Ok(true)
    }

    pub fn get<'a>(&'a mut self, collection: &str, key: &str)
                    -> GetKeyValue<'a> {
        GetKeyValue::new(&mut self.client, collection, key)
    }

    pub fn post<'a>(&'a mut self, collection: &str) -> CreateKeyValue<'a> {
        CreateKeyValue::new(&mut self.client, collection)
    }

    pub fn put<'a>(&'a mut self, collection: &str, key: &str)
                   -> UpdateKeyValue<'a> {
        UpdateKeyValue::new(&mut self.client, collection, key)
    }

    pub fn delete<'a>(&'a mut self, collection: &str, key: &str)
                      -> DeleteKeyValue<'a> {
        DeleteKeyValue::new(&mut self.client, collection, key)
    }

    pub fn list<'a>(&'a mut self, collection: &str) -> ListReader<'a> {
        ListReader::new(&mut self.client, collection)
    }

    pub fn search<'a>(&'a mut self, collection: &str) -> SearchBuilder<'a> {
        SearchBuilder::new(&mut self.client, collection)
    }

    pub fn get_events<'a>(&'a mut self, collection: &str, key: &str, kind: &str)
                          -> GetEvents<'a> {
        GetEvents::new(&mut self.client, collection, key, kind)
    }

    pub fn create_event<'a>(&'a mut self, collection: &str, key: &str,
                            kind: &str) -> CreateEvent<'a> {
        CreateEvent::new(&mut self.client, collection, key, kind)
    }

    pub fn delete_event<'a>(&'a mut self, collection: &str, key: &str,
                            kind: &str) -> DeleteEvent<'a> {
        DeleteEvent::new(&mut self.client, collection, key, kind)
    }

    pub fn get_relations<'a>(&'a mut self, collection: &str, key: &str,
                             hops: Vec<&str>) -> GetRelations<'a> {
        GetRelations::new(&mut self.client, collection, key, hops)
    }

    pub fn put_relation<'a>(&'a mut self, collection: &str, key: &str,
                            kind: &str, to_collection: &str, to_key: &str)
                            -> PutRelation<'a> {
        PutRelation::new(&mut self.client, collection, key, kind, to_collection,
                         to_key)
    }

    pub fn delete_relation<'a>(&'a mut self, collection: &str, key: &str,
                            kind: &str, to_collection: &str, to_key: &str)
                            -> DeleteRelation<'a> {
        DeleteRelation::new(&mut self.client, collection, key, kind,
                            to_collection, to_key)
    }
}

mod client;
mod error;
mod path;

pub mod key_value;
pub mod search;
pub mod events;
pub mod graph;
<commit_msg>add a "delete collection" method<commit_after>#![crate_name="orchestrate"]

#![feature(globs, phase)]

extern crate serialize;
extern crate hyper;
extern crate url;

pub use error::OrchestrateError;

use client::Client;
use key_value::{
    GetKeyValue, CreateKeyValue, UpdateKeyValue, DeleteKeyValue, ListReader,
};
use search::SearchBuilder;
use events::{GetEvents, CreateEvent, DeleteEvent};
use graph::{GetRelations, PutRelation, DeleteRelation};
use serialize::{json, Decoder, Decodable};
use hyper::method::{Head, Delete};

pub trait RepresentsJSON : Decodable<json::Decoder, json::DecoderError> {}
impl<T: Decodable<json::Decoder, json::DecoderError>> RepresentsJSON for T {}

pub struct Orchestrate {
    client: Client
}

impl Orchestrate {

    // create a new orchestrate client
    pub fn new(token: &str) -> Orchestrate {
        Orchestrate {
            client: Client::new(token)
        }
    }

    pub fn ping(&mut self) -> Result<bool, OrchestrateError> {
        let mut res = try!(self.client.trailing("").method(Head).exec());

        if (res.status as i32) != 200 {
            return Err(error::RequestError(try!(res.read_to_string())));
        }

        Ok(true)
    }

    pub fn delete_collection(&mut self, collection: &str)
                             -> Result<bool, OrchestrateError> {
        let mut res = try!(self.client.trailing(collection)
                                      .query("force", "true")
                                      .method(Delete)
                                      .exec());

        if (res.status as i32) != 204 {
            return Err(error::RequestError(try!(res.read_to_string())));
        }

        Ok(true)
    }

    pub fn get<'a>(&'a mut self, collection: &str, key: &str)
                    -> GetKeyValue<'a> {
        GetKeyValue::new(&mut self.client, collection, key)
    }

    pub fn post<'a>(&'a mut self, collection: &str) -> CreateKeyValue<'a> {
        CreateKeyValue::new(&mut self.client, collection)
    }

    pub fn put<'a>(&'a mut self, collection: &str, key: &str)
                   -> UpdateKeyValue<'a> {
        UpdateKeyValue::new(&mut self.client, collection, key)
    }

    pub fn delete<'a>(&'a mut self, collection: &str, key: &str)
                      -> DeleteKeyValue<'a> {
        DeleteKeyValue::new(&mut self.client, collection, key)
    }

    pub fn list<'a>(&'a mut self, collection: &str) -> ListReader<'a> {
        ListReader::new(&mut self.client, collection)
    }

    pub fn search<'a>(&'a mut self, collection: &str) -> SearchBuilder<'a> {
        SearchBuilder::new(&mut self.client, collection)
    }

    pub fn get_events<'a>(&'a mut self, collection: &str, key: &str, kind: &str)
                          -> GetEvents<'a> {
        GetEvents::new(&mut self.client, collection, key, kind)
    }

    pub fn create_event<'a>(&'a mut self, collection: &str, key: &str,
                            kind: &str) -> CreateEvent<'a> {
        CreateEvent::new(&mut self.client, collection, key, kind)
    }

    pub fn delete_event<'a>(&'a mut self, collection: &str, key: &str,
                            kind: &str) -> DeleteEvent<'a> {
        DeleteEvent::new(&mut self.client, collection, key, kind)
    }

    pub fn get_relations<'a>(&'a mut self, collection: &str, key: &str,
                             hops: Vec<&str>) -> GetRelations<'a> {
        GetRelations::new(&mut self.client, collection, key, hops)
    }

    pub fn put_relation<'a>(&'a mut self, collection: &str, key: &str,
                            kind: &str, to_collection: &str, to_key: &str)
                            -> PutRelation<'a> {
        PutRelation::new(&mut self.client, collection, key, kind, to_collection,
                         to_key)
    }

    pub fn delete_relation<'a>(&'a mut self, collection: &str, key: &str,
                            kind: &str, to_collection: &str, to_key: &str)
                            -> DeleteRelation<'a> {
        DeleteRelation::new(&mut self.client, collection, key, kind,
                            to_collection, to_key)
    }
}

mod client;
mod error;
mod path;

pub mod key_value;
pub mod search;
pub mod events;
pub mod graph;
<|endoftext|>


<commit_before>//! atty is a simple utility that answers one question
//! > is this a tty?
//!
//! usage is just as simple
//!
//! ```
//! if atty::is(atty::Stream::Stdout) {
//!   println!("i'm a tty")
//! }
//! ```
//!
//! ```
//! if atty::isnt(atty::Stream::Stdout) {
//!   println!("i'm not a tty")
//! }
//! ```

#![cfg_attr(unix, no_std)]

#[cfg(windows)]
extern crate kernel32;
#[cfg(unix)]
extern crate libc;
#[cfg(windows)]
extern crate winapi;
#[cfg(target_os = "redox")]
extern crate termion;

#[cfg(windows)]
use winapi::minwindef::DWORD;

/// possible stream sources
#[derive(Clone, Copy, Debug)]
pub enum Stream {
    Stdout,
    Stderr,
    Stdin,
}

/// returns true if this is a tty
#[cfg(unix)]
pub fn is(stream: Stream) -> bool {
    extern crate libc;

    let fd = match stream {
        Stream::Stdout => libc::STDOUT_FILENO,
        Stream::Stderr => libc::STDERR_FILENO,
        Stream::Stdin => libc::STDIN_FILENO,
    };
    unsafe { libc::isatty(fd) != 0 }
}

/// returns true if this is a tty
#[cfg(windows)]
pub fn is(stream: Stream) -> bool {
    use winapi::{STD_ERROR_HANDLE as STD_ERROR, STD_INPUT_HANDLE as STD_INPUT,
                 STD_OUTPUT_HANDLE as STD_OUTPUT};

    let (fd, others) = match stream {
        Stream::Stdin => (STD_INPUT, [STD_ERROR, STD_OUTPUT]),
        Stream::Stderr => (STD_ERROR, [STD_INPUT, STD_OUTPUT]),
        Stream::Stdout => (STD_OUTPUT, [STD_INPUT, STD_ERROR]),
    };
    if unsafe { console_on_any(&[fd]) } {
        // False positives aren't possible. If we got a console then
        // we definitely have a tty on stdin.
        return true;
    }

    // At this point, we *could* have a false negative. We can determine that
    // this is true negative if we can detect the presence of a console on
    // any of the other streams. If another stream has a console, then we know
    // we're in a Windows console and can therefore trust the negative.
    if unsafe { console_on_any(&others) } {
        return false;
    }

    // Otherwise, we fall back to a very strange msys hack to see if we can
    // sneakily detect the presence of a tty.
    unsafe { msys_tty_on(fd) }
}

/// returns true if this is _not_ a tty
pub fn isnt(stream: Stream) -> bool {
    !is(stream)
}

/// Returns true if any of the given fds are on a console.
#[cfg(windows)]
unsafe fn console_on_any(fds: &[DWORD]) -> bool {
    for &fd in fds {
        let mut out = 0;
        let handle = kernel32::GetStdHandle(fd);
        if kernel32::GetConsoleMode(handle, &mut out) != 0 {
            return true;
        }
    }
    false
}

/// Returns true if there is an MSYS tty on the given handle.
#[cfg(windows)]
unsafe fn msys_tty_on(fd: DWORD) -> bool {
    use std::ffi::OsString;
    use std::mem;
    use std::os::raw::c_void;
    use std::os::windows::ffi::OsStringExt;
    use std::slice;

    use kernel32::GetFileInformationByHandleEx;
    use winapi::fileapi::FILE_NAME_INFO;
    use winapi::minwinbase::FileNameInfo;
    use winapi::minwindef::MAX_PATH;

    let size = mem::size_of::<FILE_NAME_INFO>();
    let mut name_info_bytes = vec![0u8; size + MAX_PATH];
    let res = GetFileInformationByHandleEx(
        kernel32::GetStdHandle(fd),
        FileNameInfo,
        &mut *name_info_bytes as *mut _ as *mut c_void,
        name_info_bytes.len() as u32,
    );
    if res == 0 {
        return true;
    }
    let name_info: FILE_NAME_INFO = *(name_info_bytes[0..size].as_ptr() as
                                          *const FILE_NAME_INFO);
    let name_bytes =
        &name_info_bytes[size..size + name_info.FileNameLength as usize];
    let name_u16 = slice::from_raw_parts(
        name_bytes.as_ptr() as *const u16,
        name_bytes.len() / 2,
    );
    let name = OsString::from_wide(name_u16)
        .as_os_str()
        .to_string_lossy()
        .into_owned();
    name.contains("msys-") || name.contains("-pty")
}

/// returns true if this is a tty
#[cfg(target_os = "redox")]
pub fn is(stream: Stream) -> bool {
    use std::io;
    use termion::is_tty;

    match stream {
        Stream::Stdin => is_tty(&io::stdin()),
        Stream::Stdout => is_tty(&io::stdout()),
        Stream::Stderr => is_tty(&io::stderr()),
    }
}

#[cfg(test)]
mod tests {
    use super::{Stream, is};

    #[test]
    #[cfg(windows)]
    fn is_err() {
        // appveyor pipes its output
        assert!(!is(Stream::Stderr))
    }

    #[test]
    #[cfg(windows)]
    fn is_out() {
        // appveyor pipes its output
        assert!(!is(Stream::Stdout))
    }

    #[test]
    #[cfg(windows)]
    fn is_in() {
        assert!(is(Stream::Stdin))
    }

    #[test]
    #[cfg(unix)]
    fn is_err() {
        assert!(is(Stream::Stderr))
    }

    #[test]
    #[cfg(unix)]
    fn is_out() {
        assert!(is(Stream::Stdout))
    }

    #[test]
    #[cfg(target_os = "macos")]
    fn is_in() {
        // macos on travis seems to pipe its input
        assert!(is(Stream::Stdin))
    }

    #[test]
    #[cfg(all(not(target_os = "macos"), unix))]
    fn is_in() {
        assert!(is(Stream::Stdin))
    }
}
<commit_msg>Fix #16: compile with wasm target<commit_after>//! atty is a simple utility that answers one question
//! > is this a tty?
//!
//! usage is just as simple
//!
//! ```
//! if atty::is(atty::Stream::Stdout) {
//!   println!("i'm a tty")
//! }
//! ```
//!
//! ```
//! if atty::isnt(atty::Stream::Stdout) {
//!   println!("i'm not a tty")
//! }
//! ```

#![cfg_attr(unix, no_std)]

#[cfg(windows)]
extern crate kernel32;
#[cfg(unix)]
extern crate libc;
#[cfg(windows)]
extern crate winapi;
#[cfg(target_os = "redox")]
extern crate termion;

#[cfg(windows)]
use winapi::minwindef::DWORD;

/// possible stream sources
#[derive(Clone, Copy, Debug)]
pub enum Stream {
    Stdout,
    Stderr,
    Stdin,
}

/// returns true if this is a tty
#[cfg(unix)]
pub fn is(stream: Stream) -> bool {
    extern crate libc;

    let fd = match stream {
        Stream::Stdout => libc::STDOUT_FILENO,
        Stream::Stderr => libc::STDERR_FILENO,
        Stream::Stdin => libc::STDIN_FILENO,
    };
    unsafe { libc::isatty(fd) != 0 }
}

/// returns true if this is a tty
#[cfg(windows)]
pub fn is(stream: Stream) -> bool {
    use winapi::{STD_ERROR_HANDLE as STD_ERROR, STD_INPUT_HANDLE as STD_INPUT,
                 STD_OUTPUT_HANDLE as STD_OUTPUT};

    let (fd, others) = match stream {
        Stream::Stdin => (STD_INPUT, [STD_ERROR, STD_OUTPUT]),
        Stream::Stderr => (STD_ERROR, [STD_INPUT, STD_OUTPUT]),
        Stream::Stdout => (STD_OUTPUT, [STD_INPUT, STD_ERROR]),
    };
    if unsafe { console_on_any(&[fd]) } {
        // False positives aren't possible. If we got a console then
        // we definitely have a tty on stdin.
        return true;
    }

    // At this point, we *could* have a false negative. We can determine that
    // this is true negative if we can detect the presence of a console on
    // any of the other streams. If another stream has a console, then we know
    // we're in a Windows console and can therefore trust the negative.
    if unsafe { console_on_any(&others) } {
        return false;
    }

    // Otherwise, we fall back to a very strange msys hack to see if we can
    // sneakily detect the presence of a tty.
    unsafe { msys_tty_on(fd) }
}

/// returns true if this is _not_ a tty
pub fn isnt(stream: Stream) -> bool {
    !is(stream)
}

/// Returns true if any of the given fds are on a console.
#[cfg(windows)]
unsafe fn console_on_any(fds: &[DWORD]) -> bool {
    for &fd in fds {
        let mut out = 0;
        let handle = kernel32::GetStdHandle(fd);
        if kernel32::GetConsoleMode(handle, &mut out) != 0 {
            return true;
        }
    }
    false
}

/// Returns true if there is an MSYS tty on the given handle.
#[cfg(windows)]
unsafe fn msys_tty_on(fd: DWORD) -> bool {
    use std::ffi::OsString;
    use std::mem;
    use std::os::raw::c_void;
    use std::os::windows::ffi::OsStringExt;
    use std::slice;

    use kernel32::GetFileInformationByHandleEx;
    use winapi::fileapi::FILE_NAME_INFO;
    use winapi::minwinbase::FileNameInfo;
    use winapi::minwindef::MAX_PATH;

    let size = mem::size_of::<FILE_NAME_INFO>();
    let mut name_info_bytes = vec![0u8; size + MAX_PATH];
    let res = GetFileInformationByHandleEx(
        kernel32::GetStdHandle(fd),
        FileNameInfo,
        &mut *name_info_bytes as *mut _ as *mut c_void,
        name_info_bytes.len() as u32,
    );
    if res == 0 {
        return true;
    }
    let name_info: FILE_NAME_INFO = *(name_info_bytes[0..size].as_ptr() as
                                          *const FILE_NAME_INFO);
    let name_bytes =
        &name_info_bytes[size..size + name_info.FileNameLength as usize];
    let name_u16 = slice::from_raw_parts(
        name_bytes.as_ptr() as *const u16,
        name_bytes.len() / 2,
    );
    let name = OsString::from_wide(name_u16)
        .as_os_str()
        .to_string_lossy()
        .into_owned();
    name.contains("msys-") || name.contains("-pty")
}

/// returns true if this is a tty
#[cfg(target_os = "redox")]
pub fn is(stream: Stream) -> bool {
    use std::io;
    use termion::is_tty;

    match stream {
        Stream::Stdin => is_tty(&io::stdin()),
        Stream::Stdout => is_tty(&io::stdout()),
        Stream::Stderr => is_tty(&io::stderr()),
    }
}

/// returns true if this is a tty
#[cfg(target_arch = "wasm32")]
pub fn is(_stream: Stream) -> bool {
    false
}

#[cfg(test)]
mod tests {
    use super::{Stream, is};

    #[test]
    #[cfg(windows)]
    fn is_err() {
        // appveyor pipes its output
        assert!(!is(Stream::Stderr))
    }

    #[test]
    #[cfg(windows)]
    fn is_out() {
        // appveyor pipes its output
        assert!(!is(Stream::Stdout))
    }

    #[test]
    #[cfg(windows)]
    fn is_in() {
        assert!(is(Stream::Stdin))
    }

    #[test]
    #[cfg(unix)]
    fn is_err() {
        assert!(is(Stream::Stderr))
    }

    #[test]
    #[cfg(unix)]
    fn is_out() {
        assert!(is(Stream::Stdout))
    }

    #[test]
    #[cfg(target_os = "macos")]
    fn is_in() {
        // macos on travis seems to pipe its input
        assert!(is(Stream::Stdin))
    }

    #[test]
    #[cfg(all(not(target_os = "macos"), unix))]
    fn is_in() {
        assert!(is(Stream::Stdin))
    }
}
<|endoftext|>


<commit_before>#![crate_type = "rlib"]

//! Macro to create a "view box", a box containing data plus
//! a "view" struct that can have interior references into the data.
//! The view box can be moved around as an atomic unit.
#![feature(macro_rules, globs, unboxed_closures)]

#[macro_export]
macro_rules! viewbox {
    (struct $name:ident<$d:ty, $v:ident>;) => (
        pub struct $name {
            view: $v<'static>,
            #[allow(dead_code)]
            data: Box<$d>
        }

        #[allow(dead_code)]
        impl $name {
            pub fn new<F>(data: $d, f: F) -> $name
                          where F: for<'a> FnOnce(&'a mut $d) -> $v<'a> {
                let mut d = box data;
                let v = unsafe { ::std::mem::transmute(f(&mut *d)) };

                $name { data: d, view: v }
            }

            pub fn new_result<E,F>(data: $d,
                                   f: F)
                                   -> ::std::result::Result<$name,($d,E)>
                                   where F: for<'a> FnOnce(&'a mut $d) -> ::std::result::Result<$v<'a>,E> {
                let mut d = box data;
                match f(&mut *d).map(|v| unsafe { ::std::mem::transmute(v) }) {
                    Ok(v) => Ok($name { data: d, view: v }),
                    Err(e) => Err((*d,e))
                }
            }

            pub fn into_inner(self)-> $d {
                let $name { data: box data, .. } = self;
                data
            }

            pub fn view<'a>(&'a self) -> &'a $v<'a> {
                unsafe { ::std::mem::transmute(&self.view) }
            }

            pub fn view_mut<'a>(&'a mut self) -> &'a mut $v<'a> {
                unsafe { ::std::mem::transmute(&mut self.view) }
            }
        }
    );
    (#[deriving(PartialEq $(,$derive:ident)*)] struct $name:ident<$d:ty, $v:ident>;) => (
        impl ::std::cmp::PartialEq for $name {
            fn eq(&self, other: &$name) -> bool { self.view() == other.view(); }
        }
        viewbox!(struct $name<$d,$v>;)
    );
    (#[deriving(Show $(,$derive:ident)*)] struct $name:ident<$d:ty, $v:ident>;) => (
        impl ::std::fmt::Show for $name {
            fn fmt(&self, fmt: &mut ::std::fmt::Formatter)
                   -> ::std::result::Result<(), ::std::fmt::FormatError> {
                self.view().fmt(fmt)
            }
        }
        viewbox!(struct $name<$d,$v>;)
    );
    (#[deriving()] struct $name:ident<$d:ty, $v:ident>;) => (
        viewbox!(struct $name<$d,$v>;)
    );
}

#[cfg(test)]
mod test {
    extern crate arena;
    use self::arena::TypedArena;
    use std::thread::Thread;

    // Test data structure
    #[deriving(PartialEq,Show)]
    struct TestData {
        foo: i32,
        bar: String
    }

    // View structure that has references into the boxed data
    struct TestView<'a> {
        x: &'a i32,
        y: &'a str
    }

    // Create TestBox which combines TestData and TestView
    viewbox! {
        struct TestBox<TestData,TestView>;
    }

    // Mutable view into TestData
    struct MutView<'a> {
        x: &'a mut i32,
        y: &'a mut String
    }

    // MutBox combines TestData and MutView
    viewbox! {
        struct MutBox<TestData, MutView>;
    }

    #[test]
    fn basic() {
        // Create some data
        let t = TestData { foo: 42, bar: "Hello".to_string() };
        // Move it into box, creating a view with interior references
        let v = TestBox::new(t, |d| TestView { x: &d.foo, y: d.bar.as_slice() });

        // We can access the boxed data via the view
        assert_eq!(v.view().x, &42i32);
        assert_eq!(v.view().y, "Hello");
        // We can unwrap the view to get the data back
        let t = v.into_inner();
        assert_eq!(t.foo, 42i32);
        assert_eq!(t.bar, "Hello".to_string());
    }

    #[test]
    fn mutation() {
        let t = TestData { foo: 42, bar: "Hello".to_string() };
        let mut v = MutBox::new(t, |d| MutView { x: &mut d.foo, y: &mut d.bar });

        *v.view_mut().x = 5;
        *v.view_mut().y = "Goodbye".to_string();
        let t = v.into_inner();
        assert_eq!(t, TestData { foo: 5, bar: "Goodbye".to_string() });
    }

    #[test]
    fn error() {
        let t = TestData { foo: 42, bar: "Hello".to_string() };
        let v = MutBox::new_result(t, |_| Err("Nope")).err().unwrap();
        assert_eq!(v, (TestData { foo: 42, bar: "Hello".to_string() }, "Nope"))
    }

    struct ArenaView<'a> {
        arena: &'a TypedArena<int>,
        vec: Vec<&'a int>
    }

    viewbox! {
        struct ArenaBox<TypedArena<int>, ArenaView>;
    }

    #[test]
    fn arenaview() {
        let mut vb = ArenaBox::new(
            TypedArena::new(),
            |a| ArenaView { arena: a, vec: Vec::new()});

        Thread::spawn(move || {
            let v = vb.view_mut();
            let a = v.arena.alloc(1);
            let b = v.arena.alloc(2);
            let c = v.arena.alloc(3);
            v.vec.push(a);
            v.vec.push(b);
            v.vec.push(c);
        }).join().ok().unwrap();
    }
}
<commit_msg>Fix build breaks<commit_after>#![crate_type = "rlib"]

//! Macro to create a "view box", a box containing data plus
//! a "view" struct that can have interior references into the data.
//! The view box be can moved around as an atomic unit.

#[macro_export]
macro_rules! viewbox {
    (struct $name:ident<$d:ty, $v:ident>;) => (
        pub struct $name {
            view: $v<'static>,
            #[allow(dead_code)]
            data: Box<$d>
        }

        #[allow(dead_code)]
        impl $name {
            pub fn new<F>(data: $d, f: F) -> $name
                          where F: for<'a> FnOnce(&'a mut $d) -> $v<'a> {
                let mut d = box data;
                let v = unsafe { ::std::mem::transmute(f(&mut *d)) };

                $name { data: d, view: v }
            }

            pub fn new_result<E,F>(data: $d,
                                   f: F)
                                   -> ::std::result::Result<$name,($d,E)>
                                   where F: for<'a> FnOnce(&'a mut $d) -> ::std::result::Result<$v<'a>,E> {
                let mut d = box data;
                match f(&mut *d).map(|v| unsafe { ::std::mem::transmute(v) }) {
                    Ok(v) => Ok($name { data: d, view: v }),
                    Err(e) => Err((*d,e))
                }
            }

            pub fn into_inner(self)-> $d {
                let $name { data: box data, .. } = self;
                data
            }

            pub fn view<'a>(&'a self) -> &'a $v<'a> {
                unsafe { ::std::mem::transmute(&self.view) }
            }

            pub fn view_mut<'a>(&'a mut self) -> &'a mut $v<'a> {
                unsafe { ::std::mem::transmute(&mut self.view) }
            }
        }
    );
    (#[derive(PartialEq $(,$derive:ident)*)] struct $name:ident<$d:ty, $v:ident>;) => (
        impl ::std::cmp::PartialEq for $name {
            fn eq(&self, other: &$name) -> bool { self.view() == other.view(); }
        }
        viewbox!(struct $name<$d,$v>;)
    );
    (#[derive(Debug $(,$derive:ident)*)] struct $name:ident<$d:ty, $v:ident>;) => (
        impl ::std::fmt::Debug for $name {
            fn fmt(&self, fmt: &mut ::std::fmt::Formatter)
                   -> ::std::result::Result<(), ::std::fmt::FormatError> {
                self.view().fmt(fmt)
            }
        }
        viewbox!(struct $name<$d,$v>;)
    );
    (#[derive()] struct $name:ident<$d:ty, $v:ident>;) => (
        viewbox!(struct $name<$d,$v>;)
    );
}

#[cfg(test)]
mod test {
    // Test data structure
    #[derive(PartialEq,Debug)]
    struct TestData {
        foo: i32,
        bar: String
    }

    // View structure that has references into the boxed data
    struct TestView<'a> {
        x: &'a i32,
        y: &'a str
    }

    // Create TestBox which combines TestData and TestView
    viewbox! {
        struct TestBox<TestData,TestView>;
    }

    // Mutable view into TestData
    struct MutView<'a> {
        x: &'a mut i32,
        y: &'a mut String
    }

    // MutBox combines TestData and MutView
    viewbox! {
        struct MutBox<TestData, MutView>;
    }

    #[test]
    fn basic() {
        // Create some data
        let t = TestData { foo: 42, bar: "Hello".to_string() };
        // Move it into box, creating a view with interior references
        let v = TestBox::new(t, |d| TestView { x: &d.foo, y: d.bar.as_slice() });

        // We can access the boxed data via the view
        assert_eq!(v.view().x, &42i32);
        assert_eq!(v.view().y, "Hello");
        // We can unwrap the view to get the data back
        let t = v.into_inner();
        assert_eq!(t.foo, 42i32);
        assert_eq!(t.bar, "Hello".to_string());
    }

    #[test]
    fn mutation() {
        let t = TestData { foo: 42, bar: "Hello".to_string() };
        let mut v = MutBox::new(t, |d| MutView { x: &mut d.foo, y: &mut d.bar });

        *v.view_mut().x = 5;
        *v.view_mut().y = "Goodbye".to_string();
        let t = v.into_inner();
        assert_eq!(t, TestData { foo: 5, bar: "Goodbye".to_string() });
    }

    #[test]
    fn error() {
        let t = TestData { foo: 42, bar: "Hello".to_string() };
        let v = MutBox::new_result(t, |_| Err("Nope")).err().unwrap();
        assert_eq!(v, (TestData { foo: 42, bar: "Hello".to_string() }, "Nope"))
    }
}
<|endoftext|>