<commit_before>use config::{PollerSettingsProvider, AwsCredentialsProviderType};
use std::result::Result as StdResult;
use std::error::Error as StdError;
use std::fmt;
use std::str::FromStr;
use std::sync::Arc;
use std::io::{stderr, Write};
use rusoto::{ProvideAwsCredentials, AwsCredentials, DefaultCredentialsProviderSync, EnvironmentProvider,
             ProfileProvider, InstanceMetadataProvider, ContainerProvider, CredentialsError,
             Region, ParseRegionError, HttpDispatchError};
use rusoto::ec2;
use rusoto::default_tls_client;
use hyper;
use std::ascii::AsciiExt;
use std::iter::{Iterator, IntoIterator};
use prometheus::{Opts, GaugeVec, Collector};
use prometheus::Error as PrometheusError;
use time::precise_time_s;
use std::collections::HashMap;

#[derive(Debug, Eq, PartialEq, Clone)]
pub enum PollerError {
    InvalidCredentials(String),
    InsufficientPermissions(String),
    BadRegion(String),
    NetworkError(String),
    UnknownError(String),
    NoError
}

impl fmt::Display for PollerError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}

impl From<CredentialsError> for PollerError {
    fn from(error: CredentialsError) -> Self {
        PollerError::InvalidCredentials(String::from(error.description()))
    }
}

impl From<ParseRegionError> for PollerError {
    fn from(error: ParseRegionError) -> Self {
        PollerError::BadRegion(String::from(error.description()))
    }
}

impl From<HttpDispatchError> for PollerError {
    fn from(error: HttpDispatchError) -> Self {
        PollerError::NetworkError(String::from(error.description()))
    }
}

impl From<PrometheusError> for PollerError {
    fn from(error: PrometheusError) -> Self {
        PollerError::UnknownError(String::from(error.description()))
    }
}

impl From<ec2::DescribeInstancesError> for PollerError {
    fn from(e: ec2::DescribeInstancesError) -> Self {
        match e {
            ec2::DescribeInstancesError::HttpDispatch(dpt) => PollerError::from(dpt),
            ec2::DescribeInstancesError::Credentials(crd) => PollerError::from(crd),
            ec2::DescribeInstancesError::Validation(s) => PollerError::InvalidCredentials(s),
            ec2::DescribeInstancesError::Unknown(s) => {
                if s.contains("DryRunOperation") {
                    PollerError::NoError
                } else if s.contains("UnauthorizedOperation") {
                    PollerError::InsufficientPermissions(String::from("DescribeInstances"))
                } else if s.contains("AuthFailure") {
                    PollerError::InvalidCredentials(s)
                } else {
                    PollerError::UnknownError(s)
                }
            }
        }
    }
}

impl StdError for PollerError {
    fn description(&self) -> &str {
        match *self {
            PollerError::InvalidCredentials(ref m) => &m,
            PollerError::InsufficientPermissions(ref m) => &m,
            PollerError::BadRegion(ref m) => &m,
            PollerError::NetworkError(ref m) => &m,
            PollerError::UnknownError(ref m) => &m,
            PollerError::NoError => "No error",
        }
    }
}

type PollerResult<T> = StdResult<T, PollerError>;

pub trait Poller : Sync + Send {
    fn poll(&self);
}

#[derive(Clone)]
struct CredentialsProviderWrapper {
    inner: Arc<ProvideAwsCredentials + Send + Sync>
}

impl ProvideAwsCredentials for CredentialsProviderWrapper {
    fn credentials(&self) -> StdResult<AwsCredentials, CredentialsError> {
        self.inner.credentials()
    }
}

type Ec2Client = ec2::Ec2Client<CredentialsProviderWrapper, hyper::Client>;

pub struct AwsPoller {
    credentials_provider: CredentialsProviderWrapper,
    region: Region,
    di_chunk_size: Option<i32>,
    expose_tags: Vec<String>,
    gauges: GaugeVec
}

impl AwsPoller {
    pub fn new(settings: &PollerSettingsProvider) -> PollerResult<AwsPoller> {
        let result = AwsPoller{
            credentials_provider: Self::new_credentials_provider(settings.credentials_provider())?,
            region : Region::from_str(settings.region())?,
            di_chunk_size: settings.describe_instances_chunk_size(),
            expose_tags: settings.expose_tags(),
            gauges: Self::new_gauges(settings.expose_tags())?
        };
        if let Some(e) = result.test_credentials() { Err(e)? }
        else if let Some(e) = result.test_describe_instances() { Err(e)? }
        Ok(result)
    }

    pub fn counters(&self) -> Box<Collector> {
        Box::new(self.gauges.clone())
    }

    fn new_credentials_provider(provider_type: AwsCredentialsProviderType)
        -> Result<CredentialsProviderWrapper, CredentialsError> {
        Ok(CredentialsProviderWrapper {
            inner: match provider_type {
                AwsCredentialsProviderType::Default =>
                    Arc::new(DefaultCredentialsProviderSync::new()?),
                AwsCredentialsProviderType::Environment =>
                    Arc::new(EnvironmentProvider {}),
                AwsCredentialsProviderType::Profile =>
                    Arc::new(ProfileProvider::new()?),
                AwsCredentialsProviderType::Instance =>
                    Arc::new(InstanceMetadataProvider {}),
                AwsCredentialsProviderType::Container =>
                    Arc::new(ContainerProvider {})
            }
        })
    }

    fn new_gauges(expose_tags: Vec<String>) -> Result<GaugeVec, PrometheusError> {
        let opts = Opts::new("AwsInstanceState", "Identifies a running AWS instance");
        let labels : Vec<&str> = vec!["id", "platform", "type", "lifecycle"].into_iter()
            .chain(expose_tags.iter().map(|s| &**s)).collect();
        GaugeVec::new(opts, labels.as_slice())
    }

    /// Try to retrieve credentials from provider to be able to fail-fast if the credentials
    /// are not available.
    fn test_credentials(&self) -> Option<PollerError> {
        self.credentials_provider.credentials().err().map(|e| PollerError::from(e))
    }

    fn get_ec2_client(&self) -> Ec2Client {
        Ec2Client::new(default_tls_client().unwrap(), self.credentials_provider.clone(), self.region)
    }

    fn test_describe_instances(&self) -> Option<PollerError> {
        let client = self.get_ec2_client();
        let mut req : ec2::DescribeInstancesRequest = Default::default();
        req.dry_run = Some(true);

        match client.describe_instances(&req) {
            Err(e) => {
                match PollerError::from(e) {
                    PollerError::NoError => None,
                    e => Some(e)
                }
            }
            _ => None
        }
    }
}

struct DescribeInstancesIterator<'a> {
    current_page: Option<Vec<ec2::Instance>>,
    client: Ec2Client,
    req: ec2::DescribeInstancesRequest,
    error: &'a mut Option<ec2::DescribeInstancesError>,
    first_chunk: bool,
}

impl<'a> DescribeInstancesIterator<'a> {
    fn new(client: Ec2Client, filters: Vec<ec2::Filter>, chunk_size: Option<i32>,
           error: &'a mut Option<ec2::DescribeInstancesError>) -> Self {
        let mut req: ec2::DescribeInstancesRequest = Default::default();
        req.filters = Some(filters);
        req.max_results = chunk_size;
        DescribeInstancesIterator {
            current_page: None,
            client: client,
            req: req,
            error: error,
            first_chunk: true,
        }
    }

    fn next_page(&mut self) -> Option<Vec<ec2::Instance>> {
        if self.req.next_token.is_none() && !self.first_chunk {
            return None;
        }
        self.first_chunk = false;
        match self.client.describe_instances(&self.req) {
            Ok(ref mut resp) => {
                let mut chunk = vec![];
                if let Some(ref mut reservations) = resp.reservations {
                    for r in reservations {
                        if let Some(ref mut instances) = r.instances {
                            chunk.append(instances);
                        }
                    }
                }
                self.req.next_token = resp.next_token.clone();
                Some(chunk.into_iter().rev().collect())
            }
            Err(e) => {
                *self.error = Some(e);
                None
            }
        }
    }
}

impl<'a> Iterator for DescribeInstancesIterator<'a> {
    type Item = ec2::Instance;

    fn next(&mut self) -> Option<Self::Item> {
        if self.current_page.is_none() {
            self.current_page = self.next_page();
            if self.current_page.is_none() {
                return None;
            }
        }
        match self.current_page.as_mut().unwrap().pop() {
            Some(i) => Some(i),
            None => {
                self.current_page = self.next_page();
                match self.current_page {
                    Some(_) => self.next(),
                    None => None
                }
            }
        }
    }
}

fn to_hashmap(labels: &Vec<(String, String)>) -> HashMap<&str, &str> {
    let literals : Vec<(&str, &str)> = labels.iter().map(|l| -> (&str, &str)
        { (&l.0, &l.1) }).collect();
    literals.iter().cloned().collect()
}

impl Poller for AwsPoller {
    fn poll(&self) {
        let running_filter = ec2::Filter{
            name: Some(String::from("instance-state-code")),
            values: Some(vec![String::from("16")])
        };
        let mut current_metrics : Vec<_> = self.gauges.collect().iter().next().unwrap().get_metric().iter()
            .map(|m| m.get_label().iter()
                .map(|l| (l.get_name().to_owned(), l.get_value().to_owned())).collect::<HashMap<_, _>>())
            .collect();
        let mut query_err = None;
        let started = precise_time_s();
        {
            let di = DescribeInstancesIterator::new(self.get_ec2_client(), vec![running_filter],
                                                    self.di_chunk_size, &mut query_err);
            for instance in di {
                if let Some(tags) = instance.tags {
                    let id = instance.instance_id.unwrap();
                    let mut subsidiary_labels = vec![
                        ("id".to_owned(), id.clone()),
                        ("platform".to_owned(), instance.platform.unwrap_or("linux".to_owned())),
                        ("type".to_owned(), instance.instance_type.unwrap()),
                        ("lifecycle".to_owned(), instance.instance_lifecycle.unwrap_or("ondemand".to_owned()))
                    ];
                    current_metrics.retain(|m| m[&"id".to_owned()] != id);
                    let mut labels = Vec::with_capacity(subsidiary_labels.len() + self.expose_tags.len());
                    labels.append(&mut subsidiary_labels);
                    for e in self.expose_tags.iter() {
                        let m = match tags.iter().find(|&t| e.eq_ignore_ascii_case(t.key.as_ref().unwrap())) {
                            Some(ft) => (e.clone(), ft.clone().value.unwrap()),
                            None => (e.clone(), "".to_owned())
                        };
                        labels.push(m);
                    }
                    match self.gauges.get_metric_with(&to_hashmap(&labels)) {
                        Ok(m) => m.set(1.0),
                        Err(e) => println!("Error {:?} on {:?}", e, labels)
                    }
                }
            }
        }
        if query_err.is_some() {
            let _ = writeln!(&mut stderr(), "Unexpected error during instance enumeration: {:?}",
                     query_err);
        } else {
            // Delete instances that are not in running state anymore
            for m in current_metrics.iter() {
                let labels = m.iter().map(|t| (t.0.as_str(), t.1.as_str())).collect::<HashMap<_, _>>();
                println!("Deleting {:?}", labels["id"]);
                if self.gauges.remove(&labels).is_err() {
                    let _ = writeln!(&mut stderr(), "Instance disappeared?");
                }
            }
        }
        println!("Total time spent on query: {:?} sec", precise_time_s() - started);

    }
}
<commit_msg>generic paginated iterator<commit_after>use config::{PollerSettingsProvider, AwsCredentialsProviderType};
use std::result::Result as StdResult;
use std::error::Error as StdError;
use std::fmt;
use std::str::FromStr;
use std::sync::Arc;
use std::io::{stderr, Write};
use rusoto::{ProvideAwsCredentials, AwsCredentials, DefaultCredentialsProviderSync, EnvironmentProvider,
             ProfileProvider, InstanceMetadataProvider, ContainerProvider, CredentialsError,
             Region, ParseRegionError, HttpDispatchError};
use rusoto::ec2;
use rusoto::default_tls_client;
use hyper;
use std::ascii::AsciiExt;
use std::iter::{Iterator, IntoIterator};
use prometheus::{Opts, GaugeVec, Collector};
use prometheus::Error as PrometheusError;
use time::precise_time_s;
use std::collections::HashMap;

#[derive(Debug, Eq, PartialEq, Clone)]
pub enum PollerError {
    InvalidCredentials(String),
    InsufficientPermissions(String),
    BadRegion(String),
    NetworkError(String),
    UnknownError(String),
    NoError
}

impl fmt::Display for PollerError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}

impl From<CredentialsError> for PollerError {
    fn from(error: CredentialsError) -> Self {
        PollerError::InvalidCredentials(String::from(error.description()))
    }
}

impl From<ParseRegionError> for PollerError {
    fn from(error: ParseRegionError) -> Self {
        PollerError::BadRegion(String::from(error.description()))
    }
}

impl From<HttpDispatchError> for PollerError {
    fn from(error: HttpDispatchError) -> Self {
        PollerError::NetworkError(String::from(error.description()))
    }
}

impl From<PrometheusError> for PollerError {
    fn from(error: PrometheusError) -> Self {
        PollerError::UnknownError(String::from(error.description()))
    }
}

impl From<ec2::DescribeInstancesError> for PollerError {
    fn from(e: ec2::DescribeInstancesError) -> Self {
        match e {
            ec2::DescribeInstancesError::HttpDispatch(dpt) => PollerError::from(dpt),
            ec2::DescribeInstancesError::Credentials(crd) => PollerError::from(crd),
            ec2::DescribeInstancesError::Validation(s) => PollerError::InvalidCredentials(s),
            ec2::DescribeInstancesError::Unknown(s) => {
                if s.contains("DryRunOperation") {
                    PollerError::NoError
                } else if s.contains("UnauthorizedOperation") {
                    PollerError::InsufficientPermissions(String::from("DescribeInstances"))
                } else if s.contains("AuthFailure") {
                    PollerError::InvalidCredentials(s)
                } else {
                    PollerError::UnknownError(s)
                }
            }
        }
    }
}

impl StdError for PollerError {
    fn description(&self) -> &str {
        match *self {
            PollerError::InvalidCredentials(ref m) => &m,
            PollerError::InsufficientPermissions(ref m) => &m,
            PollerError::BadRegion(ref m) => &m,
            PollerError::NetworkError(ref m) => &m,
            PollerError::UnknownError(ref m) => &m,
            PollerError::NoError => "No error",
        }
    }
}

type PollerResult<T> = StdResult<T, PollerError>;

pub trait Poller : Sync + Send {
    fn poll(&self);
}

#[derive(Clone)]
struct CredentialsProviderWrapper {
    inner: Arc<ProvideAwsCredentials + Send + Sync>
}

impl ProvideAwsCredentials for CredentialsProviderWrapper {
    fn credentials(&self) -> StdResult<AwsCredentials, CredentialsError> {
        self.inner.credentials()
    }
}

type Ec2Client = ec2::Ec2Client<CredentialsProviderWrapper, hyper::Client>;

pub struct AwsPoller {
    credentials_provider: CredentialsProviderWrapper,
    region: Region,
    di_chunk_size: Option<i32>,
    expose_tags: Vec<String>,
    gauges: GaugeVec
}

impl AwsPoller {
    pub fn new(settings: &PollerSettingsProvider) -> PollerResult<AwsPoller> {
        let result = AwsPoller{
            credentials_provider: Self::new_credentials_provider(settings.credentials_provider())?,
            region : Region::from_str(settings.region())?,
            di_chunk_size: settings.describe_instances_chunk_size(),
            expose_tags: settings.expose_tags(),
            gauges: Self::new_gauges(settings.expose_tags())?
        };
        if let Some(e) = result.test_credentials() { Err(e)? }
        else if let Some(e) = result.test_describe_instances() { Err(e)? }
        Ok(result)
    }

    pub fn counters(&self) -> Box<Collector> {
        Box::new(self.gauges.clone())
    }

    fn new_credentials_provider(provider_type: AwsCredentialsProviderType)
        -> Result<CredentialsProviderWrapper, CredentialsError> {
        Ok(CredentialsProviderWrapper {
            inner: match provider_type {
                AwsCredentialsProviderType::Default =>
                    Arc::new(DefaultCredentialsProviderSync::new()?),
                AwsCredentialsProviderType::Environment =>
                    Arc::new(EnvironmentProvider {}),
                AwsCredentialsProviderType::Profile =>
                    Arc::new(ProfileProvider::new()?),
                AwsCredentialsProviderType::Instance =>
                    Arc::new(InstanceMetadataProvider {}),
                AwsCredentialsProviderType::Container =>
                    Arc::new(ContainerProvider {})
            }
        })
    }

    fn new_gauges(expose_tags: Vec<String>) -> Result<GaugeVec, PrometheusError> {
        let opts = Opts::new("AwsInstanceState", "Identifies a running AWS instance");
        let labels : Vec<&str> = vec!["id", "platform", "type", "lifecycle"].into_iter()
            .chain(expose_tags.iter().map(|s| &**s)).collect();
        GaugeVec::new(opts, labels.as_slice())
    }

    /// Try to retrieve credentials from provider to be able to fail-fast if the credentials
    /// are not available.
    fn test_credentials(&self) -> Option<PollerError> {
        self.credentials_provider.credentials().err().map(|e| PollerError::from(e))
    }

    fn get_ec2_client(&self) -> Ec2Client {
        Ec2Client::new(default_tls_client().unwrap(), self.credentials_provider.clone(), self.region)
    }

    fn test_describe_instances(&self) -> Option<PollerError> {
        let client = self.get_ec2_client();
        let mut req : ec2::DescribeInstancesRequest = Default::default();
        req.dry_run = Some(true);

        match client.describe_instances(&req) {
            Err(e) => {
                match PollerError::from(e) {
                    PollerError::NoError => None,
                    e => Some(e)
                }
            }
            _ => None
        }
    }
}

trait PaginatedRequestor {
    type Item: 'static;
    type Error: 'static;
    fn next_page(&mut self) -> Result<Option<Vec<Self::Item>>, Self::Error>;
}

struct DescribeInstancesRequestor {
    client: Ec2Client,
    req: ec2::DescribeInstancesRequest,
    first_chunk: bool
}

impl PaginatedRequestor for DescribeInstancesRequestor {
    type Item = ec2::Instance;
    type Error = ec2::DescribeInstancesError;
    fn next_page(&mut self) -> Result<Option<Vec<Self::Item>>, Self::Error> {
        if self.req.next_token.is_none() && !self.first_chunk {
            return Ok(None);
        }
        self.first_chunk = false;
        match self.client.describe_instances(&self.req) {
            Ok(ref mut resp) => {
                let mut chunk = vec![];
                if let Some(ref mut reservations) = resp.reservations {
                    for r in reservations {
                        if let Some(ref mut instances) = r.instances {
                            chunk.append(instances);
                        }
                    }
                }
                self.req.next_token = resp.next_token.clone();
                Ok(Some(chunk.into_iter().rev().collect()))
            }
            Err(e) => {
                Err(e)
            }
        }
    }
}

impl DescribeInstancesRequestor {
    fn new(client: Ec2Client, filters: Vec<ec2::Filter>, chunk_size: Option<i32>) -> Self {
        let mut req: ec2::DescribeInstancesRequest = Default::default();
        req.filters = Some(filters);
        req.max_results = chunk_size;
        DescribeInstancesRequestor {
            client: client,
            req: req,
            first_chunk: true,
        }
    }
}

struct PaginatedIterator<'a, TR: PaginatedRequestor> {
    requestor: TR,
    current_page: Option<Vec<TR::Item>>,
    error: &'a mut Option<TR::Error>
}

impl<'a, TR: PaginatedRequestor> PaginatedIterator<'a, TR> {
    fn new(requestor: TR, error: &'a mut Option<TR::Error>) -> Self {
        PaginatedIterator {
            requestor: requestor,
            current_page: None,
            error: error
        }
    }

    fn advance_page(&mut self) {
        self.current_page = match self.requestor.next_page() {
            Ok(p) => p,
            Err(e) => { *self.error = Some(e); None }
        }
    }
}

impl<'a, TR: PaginatedRequestor> Iterator for PaginatedIterator<'a, TR> {
    type Item = TR::Item;

    fn next(&mut self) -> Option<Self::Item> {
        if self.current_page.is_none() {
            self.advance_page();
            if self.current_page.is_none() {
                return None;
            }
        }
        match self.current_page.as_mut().unwrap().pop() {
            Some(i) => Some(i),
            None => {
                self.advance_page();
                match self.current_page {
                    Some(_) => self.next(),
                    None => None
                }
            }
        }
    }
}

fn to_hashmap(labels: &Vec<(String, String)>) -> HashMap<&str, &str> {
    let literals : Vec<(&str, &str)> = labels.iter().map(|l| -> (&str, &str)
        { (&l.0, &l.1) }).collect();
    literals.iter().cloned().collect()
}

impl Poller for AwsPoller {
    fn poll(&self) {
        let running_filter = ec2::Filter{
            name: Some(String::from("instance-state-code")),
            values: Some(vec![String::from("16")])
        };
        let mut current_metrics : Vec<_> = self.gauges.collect().iter().next().unwrap().get_metric().iter()
            .map(|m| m.get_label().iter()
                .map(|l| (l.get_name().to_owned(), l.get_value().to_owned())).collect::<HashMap<_, _>>())
            .collect();
        let mut query_err = None;
        let started = precise_time_s();
        {
            let di = PaginatedIterator::new(
                DescribeInstancesRequestor::new(self.get_ec2_client(), vec![running_filter], self.di_chunk_size),
                &mut query_err);

            for instance in di {
                if let Some(tags) = instance.tags {
                    let id = instance.instance_id.unwrap();
                    let mut subsidiary_labels = vec![
                        ("id".to_owned(), id.clone()),
                        ("platform".to_owned(), instance.platform.unwrap_or("linux".to_owned())),
                        ("type".to_owned(), instance.instance_type.unwrap()),
                        ("lifecycle".to_owned(), instance.instance_lifecycle.unwrap_or("ondemand".to_owned()))
                    ];
                    current_metrics.retain(|m| m[&"id".to_owned()] != id);
                    let mut labels = Vec::with_capacity(subsidiary_labels.len() + self.expose_tags.len());
                    labels.append(&mut subsidiary_labels);
                    for e in self.expose_tags.iter() {
                        let m = match tags.iter().find(|&t| e.eq_ignore_ascii_case(t.key.as_ref().unwrap())) {
                            Some(ft) => (e.clone(), ft.clone().value.unwrap()),
                            None => (e.clone(), "".to_owned())
                        };
                        labels.push(m);
                    }
                    match self.gauges.get_metric_with(&to_hashmap(&labels)) {
                        Ok(m) => m.set(1.0),
                        Err(e) => println!("Error {:?} on {:?}", e, labels)
                    }
                }
            }
        }
        if query_err.is_some() {
            let _ = writeln!(&mut stderr(), "Unexpected error during instance enumeration: {:?}",
                     query_err);
        } else {
            // Delete instances that are not in running state anymore
            for m in current_metrics.iter() {
                let labels = m.iter().map(|t| (t.0.as_str(), t.1.as_str())).collect::<HashMap<_, _>>();
                println!("Deleting {:?}", labels["id"]);
                if self.gauges.remove(&labels).is_err() {
                    let _ = writeln!(&mut stderr(), "Instance disappeared?");
                }
            }
        }
        println!("Total time spent on query: {:?} sec", precise_time_s() - started);

    }
}
<|endoftext|>


<commit_before>#[cfg(feature = "rustc-serialize")]
use serialize::{Encodable, Encoder, Decodable, Decoder};

use std::hash;
use std::iter::FromIterator;
use std::iter::IntoIterator;
use std::ops::{
    Index,
    IndexMut,
};

use super::{
    Dimension, Ix,
    Elements,
    ElementsMut,
    ArrayBase,
    ArrayView,
    ArrayViewMut,
    Data,
    DataMut,
    DataOwned,
    NdIndex,
};

use numeric_util;

#[cold]
#[inline(never)]
fn array_out_of_bounds() -> ! {
    panic!("ndarray: index out of bounds");
}

/// Access the element at **index**.
///
/// **Panics** if index is out of bounds.
impl<S, D, I> Index<I> for ArrayBase<S, D>
    where D: Dimension,
          I: NdIndex<Dim=D>,
          S: Data,
{
    type Output = S::Elem;
    #[inline]
    fn index(&self, index: I) -> &S::Elem {
        self.get(index).unwrap_or_else(|| array_out_of_bounds())
    }
}

/// Access the element at **index** mutably.
///
/// **Panics** if index is out of bounds.
impl<S, D, I> IndexMut<I> for ArrayBase<S, D>
    where D: Dimension,
          I: NdIndex<Dim=D>,
          S: DataMut,
{
    #[inline]
    fn index_mut(&mut self, index: I) -> &mut S::Elem {
        self.get_mut(index).unwrap_or_else(|| array_out_of_bounds())
    }
}

/// Return `true` if the array shapes and all elements of `self` and
/// `rhs` are equal. Return `false` otherwise.
impl<S, S2, D> PartialEq<ArrayBase<S2, D>> for ArrayBase<S, D>
    where D: Dimension,
          S: Data,
          S2: Data<Elem = S::Elem>,
          S::Elem: PartialEq,
{
    fn eq(&self, rhs: &ArrayBase<S2, D>) -> bool
    {
        if self.shape() != rhs.shape() {
            return false;
        }
        if let Some(self_s) = self.as_slice() {
            if let Some(rhs_s) = rhs.as_slice() {
                return numeric_util::unrolled_eq(self_s, rhs_s);
            }
        }
        self.iter().zip(rhs.iter()).all(|(a, b)| a == b)
    }
}

impl<S, D> Eq for ArrayBase<S, D>
    where D: Dimension,
          S: Data,
          S::Elem: Eq,
{ }

impl<A, S> FromIterator<A> for ArrayBase<S, Ix>
    where S: DataOwned<Elem=A>
{
    fn from_iter<I>(iterable: I) -> ArrayBase<S, Ix>
        where I: IntoIterator<Item=A>,
    {
        ArrayBase::from_iter(iterable)
    }
}

impl<'a, S, D> IntoIterator for &'a ArrayBase<S, D>
    where D: Dimension,
          S: Data,
{
    type Item = &'a S::Elem;
    type IntoIter = Elements<'a, S::Elem, D>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

impl<'a, S, D> IntoIterator for &'a mut ArrayBase<S, D>
    where D: Dimension,
          S: DataMut,
{
    type Item = &'a mut S::Elem;
    type IntoIter = ElementsMut<'a, S::Elem, D>;

    fn into_iter(self) -> Self::IntoIter
    {
        self.iter_mut()
    }
}

impl<'a, A, D> IntoIterator for ArrayView<'a, A,  D>
    where D: Dimension,
{
    type Item = &'a A;
    type IntoIter = Elements<'a, A, D>;

    fn into_iter(self) -> Self::IntoIter {
        self.into_iter_()
    }
}

impl<'a, A, D> IntoIterator for ArrayViewMut<'a, A,  D>
    where D: Dimension,
{
    type Item = &'a mut A;
    type IntoIter = ElementsMut<'a, A, D>;

    fn into_iter(self) -> Self::IntoIter {
        self.into_iter_()
    }
}

impl<'a, S, D> hash::Hash for ArrayBase<S, D>
    where D: Dimension,
          S: Data,
          S::Elem: hash::Hash,
{
    fn hash<H: hash::Hasher>(&self, state: &mut H)
    {
        self.shape().hash(state);
        for elt in self.iter() {
            elt.hash(state)
        }
    }
}

// NOTE: ArrayBase keeps an internal raw pointer that always
// points into the storage. This is Sync & Send as long as we
// follow the usual inherited mutability rules, as we do with
// Vec, &[] and &mut []

/// `ArrayBase` is `Sync` when the storage type is.
unsafe impl<S, D> Sync for ArrayBase<S, D>
    where S: Sync + Data, D: Sync
{ }

/// `ArrayBase` is `Send` when the storage type is.
unsafe impl<S, D> Send for ArrayBase<S, D>
    where S: Send + Data, D: Send
{ }

#[cfg(feature = "rustc-serialize")]
// Use version number so we can add a packed format later.
static ARRAY_FORMAT_VERSION: u8 = 1u8;

/// **Requires `feature = "rustc-serialize"`**
#[cfg(feature = "rustc-serialize")]
impl<A, S, D> Encodable for ArrayBase<S, D>
    where A: Encodable, D: Dimension + Encodable,
          S: Data<Elem=A>,
{
    fn encode<E: Encoder>(&self, s: &mut E) -> Result<(), E::Error>
    {
        s.emit_struct("Array", 3, |e| {
            try!(e.emit_struct_field("v", 0, |e| {
                ARRAY_FORMAT_VERSION.encode(e)
            }));
            // FIXME: Write self.dim as a slice (self.shape)
            // The problem is decoding it.
            try!(e.emit_struct_field("dim", 1,
                                           |e| self.dim.encode(e)));
            try!(e.emit_struct_field("data", 2, |e| {
                let sz = self.dim.size();
                e.emit_seq(sz, |e| {
                    for (i, elt) in self.iter().enumerate() {
                        try!(e.emit_seq_elt(i, |e| {
                            elt.encode(e)
                        }))
                    }
                    Ok(())
                })
            }));
            Ok(())
        })
    }
}

/// **Requires `feature = "rustc-serialize"`**
#[cfg(feature = "rustc-serialize")]
impl<A, S, D> Decodable for ArrayBase<S, D>
    where A: Decodable, D: Dimension + Decodable,
          S: DataOwned<Elem=A>,
{
    fn decode<E: Decoder>(d: &mut E) -> Result<ArrayBase<S, D>, E::Error>
    {
        d.read_struct("Array", 3, |d| {
            let version: u8 = try!(d.read_struct_field("v", 0, Decodable::decode));
            if version > ARRAY_FORMAT_VERSION {
                return Err(d.error("unknown array version"))
            }
            let dim: D = try!(d.read_struct_field("dim", 1, |d| {
                Decodable::decode(d)
            }));
            let elements = try!(
                d.read_struct_field("data", 2, |d| {
                    d.read_seq(|d, len| {
                        if len != dim.size() {
                            Err(d.error("data and dimension must match in size"))
                        } else {
                            let mut elements = Vec::with_capacity(len);
                            for i in 0..len {
                                elements.push(try!(d.read_seq_elt::<A, _>(i, Decodable::decode)))
                            }
                            Ok(elements)
                        }
                    })
            }));
            unsafe {
                Ok(ArrayBase::from_vec_dim_unchecked(dim, elements))
            }
        })
    }
}
<commit_msg>Use hash_slice in Hash impl for ArrayBase<commit_after>#[cfg(feature = "rustc-serialize")]
use serialize::{Encodable, Encoder, Decodable, Decoder};

use std::hash;
use std::iter::FromIterator;
use std::iter::IntoIterator;
use std::ops::{
    Index,
    IndexMut,
};

use super::{
    Dimension, Ix,
    Elements,
    ElementsMut,
    ArrayBase,
    ArrayView,
    ArrayViewMut,
    Data,
    DataMut,
    DataOwned,
    NdIndex,
};

use numeric_util;

#[cold]
#[inline(never)]
fn array_out_of_bounds() -> ! {
    panic!("ndarray: index out of bounds");
}

/// Access the element at **index**.
///
/// **Panics** if index is out of bounds.
impl<S, D, I> Index<I> for ArrayBase<S, D>
    where D: Dimension,
          I: NdIndex<Dim=D>,
          S: Data,
{
    type Output = S::Elem;
    #[inline]
    fn index(&self, index: I) -> &S::Elem {
        self.get(index).unwrap_or_else(|| array_out_of_bounds())
    }
}

/// Access the element at **index** mutably.
///
/// **Panics** if index is out of bounds.
impl<S, D, I> IndexMut<I> for ArrayBase<S, D>
    where D: Dimension,
          I: NdIndex<Dim=D>,
          S: DataMut,
{
    #[inline]
    fn index_mut(&mut self, index: I) -> &mut S::Elem {
        self.get_mut(index).unwrap_or_else(|| array_out_of_bounds())
    }
}

/// Return `true` if the array shapes and all elements of `self` and
/// `rhs` are equal. Return `false` otherwise.
impl<S, S2, D> PartialEq<ArrayBase<S2, D>> for ArrayBase<S, D>
    where D: Dimension,
          S: Data,
          S2: Data<Elem = S::Elem>,
          S::Elem: PartialEq,
{
    fn eq(&self, rhs: &ArrayBase<S2, D>) -> bool
    {
        if self.shape() != rhs.shape() {
            return false;
        }
        if let Some(self_s) = self.as_slice() {
            if let Some(rhs_s) = rhs.as_slice() {
                return numeric_util::unrolled_eq(self_s, rhs_s);
            }
        }
        self.iter().zip(rhs.iter()).all(|(a, b)| a == b)
    }
}

impl<S, D> Eq for ArrayBase<S, D>
    where D: Dimension,
          S: Data,
          S::Elem: Eq,
{ }

impl<A, S> FromIterator<A> for ArrayBase<S, Ix>
    where S: DataOwned<Elem=A>
{
    fn from_iter<I>(iterable: I) -> ArrayBase<S, Ix>
        where I: IntoIterator<Item=A>,
    {
        ArrayBase::from_iter(iterable)
    }
}

impl<'a, S, D> IntoIterator for &'a ArrayBase<S, D>
    where D: Dimension,
          S: Data,
{
    type Item = &'a S::Elem;
    type IntoIter = Elements<'a, S::Elem, D>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

impl<'a, S, D> IntoIterator for &'a mut ArrayBase<S, D>
    where D: Dimension,
          S: DataMut,
{
    type Item = &'a mut S::Elem;
    type IntoIter = ElementsMut<'a, S::Elem, D>;

    fn into_iter(self) -> Self::IntoIter
    {
        self.iter_mut()
    }
}

impl<'a, A, D> IntoIterator for ArrayView<'a, A,  D>
    where D: Dimension,
{
    type Item = &'a A;
    type IntoIter = Elements<'a, A, D>;

    fn into_iter(self) -> Self::IntoIter {
        self.into_iter_()
    }
}

impl<'a, A, D> IntoIterator for ArrayViewMut<'a, A,  D>
    where D: Dimension,
{
    type Item = &'a mut A;
    type IntoIter = ElementsMut<'a, A, D>;

    fn into_iter(self) -> Self::IntoIter {
        self.into_iter_()
    }
}

impl<'a, S, D> hash::Hash for ArrayBase<S, D>
    where D: Dimension,
          S: Data,
          S::Elem: hash::Hash,
{
    fn hash<H: hash::Hasher>(&self, state: &mut H)
    {
        self.shape().hash(state);
        if let Some(self_s) = self.as_slice() {
            hash::Hash::hash_slice(self_s, state);
        } else {
            for row in self.inner_iter() {
                if let Some(row_s) = row.as_slice() {
                    hash::Hash::hash_slice(row_s, state);
                } else {
                    for elt in row {
                        elt.hash(state)
                    }
                }
            }
        }
    }
}

// NOTE: ArrayBase keeps an internal raw pointer that always
// points into the storage. This is Sync & Send as long as we
// follow the usual inherited mutability rules, as we do with
// Vec, &[] and &mut []

/// `ArrayBase` is `Sync` when the storage type is.
unsafe impl<S, D> Sync for ArrayBase<S, D>
    where S: Sync + Data, D: Sync
{ }

/// `ArrayBase` is `Send` when the storage type is.
unsafe impl<S, D> Send for ArrayBase<S, D>
    where S: Send + Data, D: Send
{ }

#[cfg(feature = "rustc-serialize")]
// Use version number so we can add a packed format later.
static ARRAY_FORMAT_VERSION: u8 = 1u8;

/// **Requires `feature = "rustc-serialize"`**
#[cfg(feature = "rustc-serialize")]
impl<A, S, D> Encodable for ArrayBase<S, D>
    where A: Encodable, D: Dimension + Encodable,
          S: Data<Elem=A>,
{
    fn encode<E: Encoder>(&self, s: &mut E) -> Result<(), E::Error>
    {
        s.emit_struct("Array", 3, |e| {
            try!(e.emit_struct_field("v", 0, |e| {
                ARRAY_FORMAT_VERSION.encode(e)
            }));
            // FIXME: Write self.dim as a slice (self.shape)
            // The problem is decoding it.
            try!(e.emit_struct_field("dim", 1,
                                           |e| self.dim.encode(e)));
            try!(e.emit_struct_field("data", 2, |e| {
                let sz = self.dim.size();
                e.emit_seq(sz, |e| {
                    for (i, elt) in self.iter().enumerate() {
                        try!(e.emit_seq_elt(i, |e| {
                            elt.encode(e)
                        }))
                    }
                    Ok(())
                })
            }));
            Ok(())
        })
    }
}

/// **Requires `feature = "rustc-serialize"`**
#[cfg(feature = "rustc-serialize")]
impl<A, S, D> Decodable for ArrayBase<S, D>
    where A: Decodable, D: Dimension + Decodable,
          S: DataOwned<Elem=A>,
{
    fn decode<E: Decoder>(d: &mut E) -> Result<ArrayBase<S, D>, E::Error>
    {
        d.read_struct("Array", 3, |d| {
            let version: u8 = try!(d.read_struct_field("v", 0, Decodable::decode));
            if version > ARRAY_FORMAT_VERSION {
                return Err(d.error("unknown array version"))
            }
            let dim: D = try!(d.read_struct_field("dim", 1, |d| {
                Decodable::decode(d)
            }));
            let elements = try!(
                d.read_struct_field("data", 2, |d| {
                    d.read_seq(|d, len| {
                        if len != dim.size() {
                            Err(d.error("data and dimension must match in size"))
                        } else {
                            let mut elements = Vec::with_capacity(len);
                            for i in 0..len {
                                elements.push(try!(d.read_seq_elt::<A, _>(i, Decodable::decode)))
                            }
                            Ok(elements)
                        }
                    })
            }));
            unsafe {
                Ok(ArrayBase::from_vec_dim_unchecked(dim, elements))
            }
        })
    }
}
<|endoftext|>


<commit_before>#![crate_name = "bindgen"]
#![crate_type = "bin"]

extern crate bindgen;
extern crate env_logger;
#[macro_use]
extern crate log;
extern crate clang_sys;
extern crate rustc_serialize;

use bindgen::{Bindings, BindgenOptions, LinkType};
use std::default::Default;
use std::io;
use std::path;
use std::process;
use std::env;
use std::fs;

const USAGE: &'static str = "
Usage:
    bindgen [options] \
        [--link=<lib>...] \
        [--static-link=<lib>...] \
        [--framework-link=<framework>...] \
        [--raw-line=<raw>...] \
        [--opaque-type=<type>...] \
        [--blacklist-type=<type>...] \
        [--whitelist-type=<type>...] \
        [--whitelist-function=<name>...] \
        [--whitelist-var=<name>...] \
        <input-header> \
        [-- <clang-args>...]

    bindgen (-h | --help)

Options:
    -h, --help                    Display this help message.

    -l=<lib>, --link=<lib>        Link to a dynamic library, can be provided
                                  multiple times.

    --static-link=<lib>           Link to a static library, can be provided
                                  multiple times.

    --framework-link=<framework>  Link to a framework.

    -o=<output-rust-file>         Write bindings to <output-rust-file>
                                  (defaults to stdout)

    --builtins                    Output bindings for builtin definitions (for
                                  example __builtin_va_list)

    --ignore-functions            Don't generate bindings for functions and
                                  methods. This is useful when you only care
                                  about struct layouts.

    --ignore-methods              Avoid generating all kind of methods.

    --enable-cxx-namespaces       Enable support for C++ namespaces.

    --no-type-renaming            Don't rename types.

    --emit-clang-ast              Output the ast (for debugging purposes)

    --use-msvc-mangling           Handle MSVC C++ ABI mangling; requires that
                                  target be set to (i686|x86_64)-pc-win32

    --raw-line=<raw>              Add a raw line at the beginning of the output.
    --no-unstable-rust            Avoid generating unstable rust.
    --no-bitfield-methods         Avoid generating methods for bitfield access.
    --opaque-type=<type>          Mark a type as opaque.
    --blacklist-type=<type>       Mark a type as hidden.
    --whitelist-type=<type>       Whitelist the type. If this set or any other
                                  of the whitelisting sets is not empty, then
                                  all the non-whitelisted types (or dependant)
                                  won't be generated.
    --whitelist-function=<regex>  Whitelist all the free-standing functions
                                  matching <regex>.  Same behavior on emptyness
                                  than the type whitelisting.
    --whitelist-var=<regex>       Whitelist all the free-standing variables
                                  matching <regex>.  Same behavior on emptyness
                                  than the type whitelisting.

    <clang-args>                  Options other than stated above are passed
                                  directly through to clang.
";

// FIXME(emilio): Replace this with docopt if/when they fix their exponential
// algorithm for argument parsing.
fn parse_args_or_exit(args: Vec<String>) -> (BindgenOptions, Box<io::Write>) {
    let mut options = BindgenOptions::default();
    let mut dest_file = None;
    let mut source_file = None;

    let mut iter = args.into_iter().skip(1);
    loop {
        let next = match iter.next() {
            Some(arg) => arg,
            _ => break,
        };

        match &*next {
            "-h" | "--help" => {
                println!("{}", USAGE);
                process::exit(0);
            }
            "-l" | "--link" => {
                let lib = iter.next().expect("--link needs an argument");
                options.links.push((lib, LinkType::Default));
            }
            "--static-link" => {
                let lib = iter.next().expect("--static-link needs an argument");
                options.links.push((lib, LinkType::Static));
            }
            "--framework-link" => {
                let lib = iter.next().expect("--framework-link needs an argument");
                options.links.push((lib, LinkType::Framework));
            }
            "--raw-line" => {
                let line = iter.next().expect("--raw-line needs an argument");
                options.raw_lines.push(line);
            }
            "--opaque-type" => {
                let ty_canonical_name = iter.next().expect("--opaque-type expects a type");
                options.opaque_types.insert(ty_canonical_name);
            }
            "--blacklist-type" => {
                let ty_canonical_name = iter.next().expect("--blacklist-type expects a type");
                options.hidden_types.insert(ty_canonical_name);
            }
            "--whitelist-type" => {
                let ty_pat = iter.next().expect("--whitelist-type expects a type pattern");
                options.whitelisted_types.insert(&ty_pat);
            }
            "--whitelist-function" => {
                let function_pat = iter.next().expect("--whitelist-function expects a pattern");
                options.whitelisted_functions.insert(&function_pat);
            }
            "--whitelist-var" => {
                let var_pat = iter.next().expect("--whitelist-var expects a pattern");
                options.whitelisted_vars.insert(&var_pat);
            }
            "--" => {
                while let Some(clang_arg) = iter.next() {
                    options.clang_args.push(clang_arg);
                }
            }
            "--output" | "-o" => {
                let out_name = iter.next().expect("-o expects a file name");
                dest_file = Some(out_name);
            }
            "--builtins" => {
                options.builtins = true;
            }
            "--ignore-functions" => {
                options.ignore_functions = true;
            }
            "--no-bitfield-methods" => {
                options.gen_bitfield_methods = false;
            }
            "--ignore-methods" => {
                options.ignore_methods = true;
            }
            "--enable-cxx-namespaces" => {
                options.enable_cxx_namespaces = true;
            }
            "--no-type-renaming" => {
                options.rename_types = false;
            }
            "--no-unstable-rust" => {
                options.unstable_rust = false;
            }
            "--emit-clang-ast" => {
                options.emit_ast = true;
            }
            "--use-msvc-mangling" => {
                options.msvc_mangling = true;
            }
            other if source_file.is_none() => {
                source_file = Some(other.into());
            }
            other => {
                panic!("Unknown option: \"{}\"", other);
            }
        }
    }

    if let Some(source_file) = source_file.take() {
        options.clang_args.push(source_file);
    }

    let out = if let Some(ref path_name) = dest_file {
        let path = path::Path::new(path_name);
        let file = fs::File::create(path).expect("Opening out file failed");
        Box::new(io::BufWriter::new(file)) as Box<io::Write>
    } else {
        Box::new(io::BufWriter::new(io::stdout())) as Box<io::Write>
    };

    (options, out)
}

pub fn main() {
    log::set_logger(|max_log_level| {
        use env_logger::Logger;
        let env_logger = Logger::new();
        max_log_level.set(env_logger.filter());
        Box::new(env_logger)
    }).expect("Failed to set logger.");

    let mut bind_args: Vec<_> = env::args().collect();

    if let Some(clang) = clang_sys::support::Clang::find(None) {
        let has_clang_args = bind_args.iter().rposition(|arg| *arg == "--").is_some();
        if !has_clang_args {
            bind_args.push("--".to_owned());
        }

        // If --target is specified, assume caller knows what they're doing and don't mess with
        // include paths for them
        let has_target_arg = bind_args.iter().rposition(|arg| arg.starts_with("--target")).is_some();
        if !has_target_arg {
            // TODO: distinguish C and C++ paths? C++'s should be enough, I guess.
            for path in clang.cpp_search_paths.into_iter() {
                if let Ok(path) = path.into_os_string().into_string() {
                    bind_args.push("-isystem".to_owned());
                    bind_args.push(path);
                }
            }
        }
    }

    let (options, out) = parse_args_or_exit(bind_args);

    let bindings = Bindings::generate(options, None)
                        .expect("Unable to generate bindings");

    bindings.write(out)
            .expect("Unable to write bindings to file.");
}
<commit_msg>Be consistent with blank lines in USAGE<commit_after>#![crate_name = "bindgen"]
#![crate_type = "bin"]

extern crate bindgen;
extern crate env_logger;
#[macro_use]
extern crate log;
extern crate clang_sys;
extern crate rustc_serialize;

use bindgen::{Bindings, BindgenOptions, LinkType};
use std::default::Default;
use std::io;
use std::path;
use std::process;
use std::env;
use std::fs;

const USAGE: &'static str = "
Usage:
    bindgen [options] \
        [--link=<lib>...] \
        [--static-link=<lib>...] \
        [--framework-link=<framework>...] \
        [--raw-line=<raw>...] \
        [--opaque-type=<type>...] \
        [--blacklist-type=<type>...] \
        [--whitelist-type=<type>...] \
        [--whitelist-function=<name>...] \
        [--whitelist-var=<name>...] \
        <input-header> \
        [-- <clang-args>...]

    bindgen (-h | --help)

Options:
    -h, --help                    Display this help message.

    -l=<lib>, --link=<lib>        Link to a dynamic library, can be provided
                                  multiple times.

    --static-link=<lib>           Link to a static library, can be provided
                                  multiple times.

    --framework-link=<framework>  Link to a framework.

    -o=<output-rust-file>         Write bindings to <output-rust-file>
                                  (defaults to stdout)

    --builtins                    Output bindings for builtin definitions (for
                                  example __builtin_va_list)

    --ignore-functions            Don't generate bindings for functions and
                                  methods. This is useful when you only care
                                  about struct layouts.

    --ignore-methods              Avoid generating all kind of methods.

    --enable-cxx-namespaces       Enable support for C++ namespaces.

    --no-type-renaming            Don't rename types.

    --emit-clang-ast              Output the ast (for debugging purposes)

    --use-msvc-mangling           Handle MSVC C++ ABI mangling; requires that
                                  target be set to (i686|x86_64)-pc-win32

    --raw-line=<raw>              Add a raw line at the beginning of the output.

    --no-unstable-rust            Avoid generating unstable rust.

    --no-bitfield-methods         Avoid generating methods for bitfield access.

    --opaque-type=<type>          Mark a type as opaque.

    --blacklist-type=<type>       Mark a type as hidden.

    --whitelist-type=<type>       Whitelist the type. If this set or any other
                                  of the whitelisting sets is not empty, then
                                  all the non-whitelisted types (or dependant)
                                  won't be generated.

    --whitelist-function=<regex>  Whitelist all the free-standing functions
                                  matching <regex>.  Same behavior on emptyness
                                  than the type whitelisting.

    --whitelist-var=<regex>       Whitelist all the free-standing variables
                                  matching <regex>.  Same behavior on emptyness
                                  than the type whitelisting.

    <clang-args>                  Options other than stated above are passed
                                  directly through to clang.
";

// FIXME(emilio): Replace this with docopt if/when they fix their exponential
// algorithm for argument parsing.
fn parse_args_or_exit(args: Vec<String>) -> (BindgenOptions, Box<io::Write>) {
    let mut options = BindgenOptions::default();
    let mut dest_file = None;
    let mut source_file = None;

    let mut iter = args.into_iter().skip(1);
    loop {
        let next = match iter.next() {
            Some(arg) => arg,
            _ => break,
        };

        match &*next {
            "-h" | "--help" => {
                println!("{}", USAGE);
                process::exit(0);
            }
            "-l" | "--link" => {
                let lib = iter.next().expect("--link needs an argument");
                options.links.push((lib, LinkType::Default));
            }
            "--static-link" => {
                let lib = iter.next().expect("--static-link needs an argument");
                options.links.push((lib, LinkType::Static));
            }
            "--framework-link" => {
                let lib = iter.next().expect("--framework-link needs an argument");
                options.links.push((lib, LinkType::Framework));
            }
            "--raw-line" => {
                let line = iter.next().expect("--raw-line needs an argument");
                options.raw_lines.push(line);
            }
            "--opaque-type" => {
                let ty_canonical_name = iter.next().expect("--opaque-type expects a type");
                options.opaque_types.insert(ty_canonical_name);
            }
            "--blacklist-type" => {
                let ty_canonical_name = iter.next().expect("--blacklist-type expects a type");
                options.hidden_types.insert(ty_canonical_name);
            }
            "--whitelist-type" => {
                let ty_pat = iter.next().expect("--whitelist-type expects a type pattern");
                options.whitelisted_types.insert(&ty_pat);
            }
            "--whitelist-function" => {
                let function_pat = iter.next().expect("--whitelist-function expects a pattern");
                options.whitelisted_functions.insert(&function_pat);
            }
            "--whitelist-var" => {
                let var_pat = iter.next().expect("--whitelist-var expects a pattern");
                options.whitelisted_vars.insert(&var_pat);
            }
            "--" => {
                while let Some(clang_arg) = iter.next() {
                    options.clang_args.push(clang_arg);
                }
            }
            "--output" | "-o" => {
                let out_name = iter.next().expect("-o expects a file name");
                dest_file = Some(out_name);
            }
            "--builtins" => {
                options.builtins = true;
            }
            "--ignore-functions" => {
                options.ignore_functions = true;
            }
            "--no-bitfield-methods" => {
                options.gen_bitfield_methods = false;
            }
            "--ignore-methods" => {
                options.ignore_methods = true;
            }
            "--enable-cxx-namespaces" => {
                options.enable_cxx_namespaces = true;
            }
            "--no-type-renaming" => {
                options.rename_types = false;
            }
            "--no-unstable-rust" => {
                options.unstable_rust = false;
            }
            "--emit-clang-ast" => {
                options.emit_ast = true;
            }
            "--use-msvc-mangling" => {
                options.msvc_mangling = true;
            }
            other if source_file.is_none() => {
                source_file = Some(other.into());
            }
            other => {
                panic!("Unknown option: \"{}\"", other);
            }
        }
    }

    if let Some(source_file) = source_file.take() {
        options.clang_args.push(source_file);
    }

    let out = if let Some(ref path_name) = dest_file {
        let path = path::Path::new(path_name);
        let file = fs::File::create(path).expect("Opening out file failed");
        Box::new(io::BufWriter::new(file)) as Box<io::Write>
    } else {
        Box::new(io::BufWriter::new(io::stdout())) as Box<io::Write>
    };

    (options, out)
}

pub fn main() {
    log::set_logger(|max_log_level| {
        use env_logger::Logger;
        let env_logger = Logger::new();
        max_log_level.set(env_logger.filter());
        Box::new(env_logger)
    }).expect("Failed to set logger.");

    let mut bind_args: Vec<_> = env::args().collect();

    if let Some(clang) = clang_sys::support::Clang::find(None) {
        let has_clang_args = bind_args.iter().rposition(|arg| *arg == "--").is_some();
        if !has_clang_args {
            bind_args.push("--".to_owned());
        }

        // If --target is specified, assume caller knows what they're doing and don't mess with
        // include paths for them
        let has_target_arg = bind_args.iter().rposition(|arg| arg.starts_with("--target")).is_some();
        if !has_target_arg {
            // TODO: distinguish C and C++ paths? C++'s should be enough, I guess.
            for path in clang.cpp_search_paths.into_iter() {
                if let Ok(path) = path.into_os_string().into_string() {
                    bind_args.push("-isystem".to_owned());
                    bind_args.push(path);
                }
            }
        }
    }

    let (options, out) = parse_args_or_exit(bind_args);

    let bindings = Bindings::generate(options, None)
                        .expect("Unable to generate bindings");

    bindings.write(out)
            .expect("Unable to write bindings to file.");
}
<|endoftext|>


<commit_before>#![feature(convert)]
#![feature(io)]

extern crate rustc_serialize;
extern crate docopt;
extern crate users;
extern crate glob;
extern crate tempfile;
extern crate libc;

use tempfile::NamedTempFile;
use docopt::Docopt;
use std::env;
use std::fs;
use std::io::{stdin, stdout, stderr, Write, Read, self};
use std::fs::File;
use std::os::unix::fs::PermissionsExt;
use std::path::{PathBuf, Path};
use std::process::{Command, exit};
use std::ops::Deref;
use std::ffi::CString;

extern "C" {
    fn chown(path: *const libc::c_char, owner: libc::uid_t, group: libc::gid_t) -> libc::c_int;
}

fn change_owner<P: AsRef<Path>>(path: P, owner: libc::uid_t, group: libc::gid_t) -> Result<(), io::Error> {
    match unsafe { chown(CString::new(path.as_ref().as_os_str().to_bytes().unwrap()).unwrap().as_ptr(), owner, group) } {
        0 => Ok(()),
        -1 => Err(io::Error::last_os_error()),
        _ => unreachable!()
    }
}

static CRONTAB_DIR: &'static str = "/var/spool/cron";
static REBOOT_FILE: &'static str = "/run/crond.reboot";

static USAGE: &'static str = r#"
Usage: crontab [-u <user>] -l
       crontab [-u <user>] -e [<file>]
       crontab [-u <user>] -s
       crontab [-u <user>] -r [-i]
       crontab -h | --help

Maintain crontab files for individual users

Options:

  -h, --help                Show this help message and exit.
  -u <user>, --user <user>  It specifies the name of the user whose crontab is to
                            be tweaked. If this option is not given, crontab
                            examines "your" crontab, i.e., the crontab of the
                            person executing the command. Note that su(8) can
                            confuse crontab and that if you are running inside of
                            su(8) you should always use the -u option for safety's
                            sake. The first form of this command is used to
                            install a new crontab from some named file or standard
                            input if the pseudo-filename "-" is given.
  -l, --list                The current crontab will be displayed on standard
                            output.
  -r, --remove              The current crontab will be removed.
  -e, --edit                This option is used to edit the current crontab using
                            the editor specified by the VISUAL or EDITOR
                            environment variables. After you exit from the editor,
                            the modified crontab will be installed automatically.
  -s, --show                Show all users who have a crontab.
  -i, --ask                 This option modifies the -r option to prompt the user
                            for a 'y/Y' response before actually removing the
                            crontab.
"#;

#[derive(Debug, RustcDecodable)]
#[allow(non_snake_case)]
struct Args {
    arg_file: Option<String>,
    flag_user: Option<String>,
    flag_list: bool,
    flag_remove: bool,
    flag_edit: bool,
    flag_show: bool,
    flag_ask: bool
}

fn get_editor() -> Option<String> {
    env::var("EDITOR").ok()
        .or_else(|| env::var("VISUAL").ok())
        .or_else(|| ["/usr/bin/editor", "/usr/bin/vim", "/usr/bin/nano", "/usr/bin/mcedit"].iter()
                 .filter(|editor| fs::metadata(editor).map(|meta| meta.is_file() && meta.permissions().mode() & 0o0111 != 0).unwrap_or(false))
                 .next()
                 .map(|&s| s.to_owned()))
}

fn confirm(msg: &str) -> bool {
    let mut stdin = stdin();
    let mut stdout = stdout();
    loop {
        stdout.write_all(msg.as_bytes()).unwrap();
        stdout.flush().unwrap();
        let mut buf = [0u8; 1024];
        match stdin.read(&mut buf) {
            Ok(n) if n > 0 && (buf[0] == 121 || buf[0] == 89) => return true,
            Ok(n) if n > 0 && (buf[0] == 110 || buf[0] == 78) => return false,
            _ => { stdout.write_all("Please reply \"y\" or \"n\"\n".as_bytes()).unwrap(); },
        }
    }
}

fn list(cron_file: &Path, args: &Args) -> i32 {
    if let Err(e) = File::open(cron_file).map(|file| file.tee(stdout()).bytes().count()) {
        use std::io::ErrorKind::*;
        match e.kind() {
            NotFound => println!("no crontab for {}", args.flag_user.as_ref().map(String::deref).unwrap_or("???")),
            _ => println!("failed to read {}: {}", cron_file.display(), e),
        }
        return 1;
    }
    0
}

fn remove(cron_file: &Path, args: &Args) -> i32 {
    let mut stderr = stderr();

    if !args.flag_ask || confirm(&*format!("Are you sure you want to delete {} (y/n)? ", cron_file.display())) {
        if let Err(e) = fs::remove_file(cron_file) {
            use std::io::ErrorKind::*;
            match e.kind() {
                NotFound => writeln!(stderr, "no crontab for {}", args.flag_user.as_ref().map(String::deref).unwrap_or("???")),
                _ => writeln!(stderr, "failed to remove {}: {}", cron_file.display(), e)
            }.unwrap();
            return 1;
        }
    }
    0
}

fn show(_cron_file: &Path, _args: &Args) -> i32 {
    let mut stderr = stderr();

    if let Ok(dir) = fs::read_dir(CRONTAB_DIR) {
        for item in dir {
            if let Ok(entry) = item {
                if let Some(name) = entry.file_name().to_str() {
                    if users::get_user_by_name(name).is_some() {
                        println!("{}", name);
                    } else {
                        writeln!(stderr, "WARNING: crontab found with no matching user: {}", name).unwrap();
                    }
                }
            }
        }
    }
    0
}

fn edit(cron_file: &Path, _args: &Args) -> i32 {
    use std::io::ErrorKind::*;

    let mut stderr = stderr();

    let editor = match get_editor() {
        None => {
            writeln!(stderr, "no editor found").unwrap();
            return 1;
        },
        Some(editor) => editor
    };

    let mut tmpfile = NamedTempFile::new_in(CRONTAB_DIR).unwrap();

    if let Err(e) = File::open(cron_file).map(|file| file.tee(&mut tmpfile).bytes().count()) {
        match e.kind() {
            NotFound => tmpfile.write_all("# min hour dom month dow command".as_bytes()).unwrap(),
            _ => {
                writeln!(stderr, "error copying crontab file {}: {}", cron_file.display(), e).unwrap();
                return 1;
            }
        }
    }

    tmpfile.flush().unwrap();
    {
        let (uid, gid) = (users::get_current_uid(), users::get_current_gid());
        change_owner(tmpfile.path(), uid, gid);
        let _guard = users::switch_user_group(uid, gid);
        match Command::new(editor).arg(tmpfile.path()).status() {
            Ok(status) if status.success() => (),
            _ => {
                writeln!(stderr, "edit aborted, your edit is kept here: {}", tmpfile.path().display()).unwrap();
                return 1;
            }
        }
    }

    // TODO: check tmpfile with parser

    if let Err(err) = tmpfile.persist(cron_file) {
        writeln!(stderr, "unexpected error: {}, your edit is kept here: {}", err.error, err.file.path().display()).unwrap();
        return 1;
    }

    0
}

fn replace(cron_file: &Path, args: &Args) -> i32 {
    unimplemented!();
}

fn main() {
    let mut stderr = stderr();
    let mut args: Args = Docopt::new(USAGE)
                            .and_then(|d| d.decode())
                            .unwrap_or_else(|e| e.exit());

    if args.flag_user.is_some() && users::get_current_uid() != 0 {
        writeln!(stderr, "must be privileged to use -u").unwrap();
        exit(1);
    }

    match fs::metadata(CRONTAB_DIR) {
        Ok(ref meta) if !meta.is_dir() => {
            writeln!(stderr, "{} is not a directory!", CRONTAB_DIR).unwrap();
            exit(1);
        },
        Err(_) => if let Err(_) = fs::create_dir_all(CRONTAB_DIR) {
            writeln!(stderr, "{} doesn't exist!", CRONTAB_DIR).unwrap();
            exit(1);
        },
        _ => ()
    }

    if args.flag_user.is_none() {
        args.flag_user = users::get_current_username();
    }
    let cron_file = PathBuf::from(CRONTAB_DIR).join(args.flag_user.clone().unwrap());

    exit(match args {
        Args { flag_show: true, .. } => show(&*cron_file, &args),
        Args { flag_list: true, .. } => list(&*cron_file, &args),
        Args { flag_edit: true, arg_file: None, .. } => edit(&*cron_file, &args),
        Args { flag_edit: true, .. } => replace(&*cron_file, &args),
        Args { flag_remove: true, .. } => remove(&*cron_file, &args),
        _ => unreachable!()
    })
}
<commit_msg>crontab: replace<commit_after>#![feature(convert)]
#![feature(io)]

extern crate rustc_serialize;
extern crate docopt;
extern crate users;
extern crate glob;
extern crate tempfile;
extern crate libc;

use tempfile::NamedTempFile;
use docopt::Docopt;
use std::env;
use std::fs;
use std::io::{stdin, stdout, stderr, Write, Read, self};
use std::fs::File;
use std::os::unix::fs::PermissionsExt;
use std::path::{PathBuf, Path};
use std::process::{Command, exit};
use std::ops::Deref;
use std::ffi::CString;

extern "C" {
    fn chown(path: *const libc::c_char, owner: libc::uid_t, group: libc::gid_t) -> libc::c_int;
}

fn change_owner<P: AsRef<Path>>(path: P, owner: libc::uid_t, group: libc::gid_t) -> Result<(), io::Error> {
    match unsafe { chown(CString::new(path.as_ref().as_os_str().to_bytes().unwrap()).unwrap().as_ptr(), owner, group) } {
        0 => Ok(()),
        -1 => Err(io::Error::last_os_error()),
        _ => unreachable!()
    }
}

static CRONTAB_DIR: &'static str = "/var/spool/cron";

static USAGE: &'static str = r#"
Usage: crontab [-u <user>] -l
       crontab [-u <user>] -e [<file>]
       crontab [-u <user>] -s
       crontab [-u <user>] -r [-i]
       crontab -h | --help

Maintain crontab files for individual users

Options:

  -h, --help                Show this help message and exit.
  -u <user>, --user <user>  It specifies the name of the user whose crontab is to
                            be tweaked. If this option is not given, crontab
                            examines "your" crontab, i.e., the crontab of the
                            person executing the command. Note that su(8) can
                            confuse crontab and that if you are running inside of
                            su(8) you should always use the -u option for safety's
                            sake. The first form of this command is used to
                            install a new crontab from some named file or standard
                            input if the pseudo-filename "-" is given.
  -l, --list                The current crontab will be displayed on standard
                            output.
  -r, --remove              The current crontab will be removed.
  -e, --edit                This option is used to edit the current crontab using
                            the editor specified by the VISUAL or EDITOR
                            environment variables. After you exit from the editor,
                            the modified crontab will be installed automatically.
  -s, --show                Show all users who have a crontab.
  -i, --ask                 This option modifies the -r option to prompt the user
                            for a 'y/Y' response before actually removing the
                            crontab.
"#;

#[derive(Debug, RustcDecodable)]
#[allow(non_snake_case)]
struct Args {
    arg_file: Option<String>,
    flag_user: Option<String>,
    flag_list: bool,
    flag_remove: bool,
    flag_edit: bool,
    flag_show: bool,
    flag_ask: bool
}

fn get_editor() -> Option<String> {
    env::var("EDITOR").ok()
        .or_else(|| env::var("VISUAL").ok())
        .or_else(|| ["/usr/bin/editor", "/usr/bin/vim", "/usr/bin/nano", "/usr/bin/mcedit"].iter()
                 .filter(|editor| fs::metadata(editor).map(|meta| meta.is_file() && meta.permissions().mode() & 0o0111 != 0).unwrap_or(false))
                 .next()
                 .map(|&s| s.to_owned()))
}

fn confirm(msg: &str) -> bool {
    let mut stdin = stdin();
    let mut stdout = stdout();
    loop {
        stdout.write_all(msg.as_bytes()).unwrap();
        stdout.flush().unwrap();
        let mut buf = [0u8; 1024];
        match stdin.read(&mut buf) {
            Ok(n) if n > 0 && (buf[0] == 121 || buf[0] == 89) => return true,
            Ok(n) if n > 0 && (buf[0] == 110 || buf[0] == 78) => return false,
            _ => { stdout.write_all("Please reply \"y\" or \"n\"\n".as_bytes()).unwrap(); },
        }
    }
}

fn list(cron_file: &Path, args: &Args) -> i32 {
    if let Err(e) = File::open(cron_file).map(|file| file.tee(stdout()).bytes().count()) {
        use std::io::ErrorKind::*;
        match e.kind() {
            NotFound => println!("no crontab for {}", args.flag_user.as_ref().map(String::deref).unwrap_or("???")),
            _ => println!("failed to read {}: {}", cron_file.display(), e),
        }
        return 1;
    }
    0
}

fn remove(cron_file: &Path, args: &Args) -> i32 {
    let mut stderr = stderr();

    if !args.flag_ask || confirm(&*format!("Are you sure you want to delete {} (y/n)? ", cron_file.display())) {
        if let Err(e) = fs::remove_file(cron_file) {
            use std::io::ErrorKind::*;
            match e.kind() {
                NotFound => writeln!(stderr, "no crontab for {}", args.flag_user.as_ref().map(String::deref).unwrap_or("???")),
                _ => writeln!(stderr, "failed to remove {}: {}", cron_file.display(), e)
            }.unwrap();
            return 1;
        }
    }
    0
}

fn show(_cron_file: &Path, _args: &Args) -> i32 {
    let mut stderr = stderr();

    if let Ok(dir) = fs::read_dir(CRONTAB_DIR) {
        for item in dir {
            if let Ok(entry) = item {
                if let Some(name) = entry.file_name().to_str() {
                    if users::get_user_by_name(name).is_some() {
                        println!("{}", name);
                    } else {
                        writeln!(stderr, "WARNING: crontab found with no matching user: {}", name).unwrap();
                    }
                }
            }
        }
    }
    0
}

fn edit(cron_file: &Path, _args: &Args) -> i32 {
    use std::io::ErrorKind::*;

    let mut stderr = stderr();

    let editor = match get_editor() {
        None => {
            writeln!(stderr, "no editor found").unwrap();
            return 1;
        },
        Some(editor) => editor
    };

    let mut tmpfile = NamedTempFile::new_in(CRONTAB_DIR).unwrap();

    if let Err(e) = File::open(cron_file).map(|file| file.tee(&mut tmpfile).bytes().count()) {
        match e.kind() {
            NotFound => tmpfile.write_all("# min hour dom month dow command".as_bytes()).unwrap(),
            _ => {
                writeln!(stderr, "error copying crontab file {}: {}", cron_file.display(), e).unwrap();
                return 1;
            }
        }
    }

    tmpfile.flush().unwrap();
    {
        let (uid, gid) = (users::get_current_uid(), users::get_current_gid());
        change_owner(tmpfile.path(), uid, gid).unwrap(); // TODO: user from args.flag_user
        let _guard = users::switch_user_group(uid, gid);
        match Command::new(editor).arg(tmpfile.path()).status() {
            Ok(status) if status.success() => (),
            _ => {
                writeln!(stderr, "edit aborted, your edit is kept here: {}", tmpfile.path().display()).unwrap();
                return 1;
            }
        }
    }

    // TODO: check tmpfile with parser

    if let Err(err) = tmpfile.persist(cron_file) {
        writeln!(stderr, "unexpected error: {}, your edit is kept here: {}", err.error, err.file.path().display()).unwrap();
        return 1;
    }

    0
}

fn replace(cron_file: &Path, args: &Args) -> i32 {
    let mut tmpfile = NamedTempFile::new_in(CRONTAB_DIR).unwrap();

    match args.arg_file {
        Some(ref name) if &**name == "-" => { stdin().tee(&mut tmpfile).bytes().count(); },
        Some(ref name) => { File::open(&**name).unwrap().tee(&mut tmpfile).bytes().count(); },
        None => unreachable!()
    }

    tmpfile.flush().unwrap();

    // TODO: check tmpfile syntax

    if let Err(e) = tmpfile.persist(cron_file) {
        writeln!(stderr(), "error renaming {} to {}: {}", e.file.path().display(), cron_file.display(), e.error).unwrap();
        return 1;
    }

    // TODO: user from args.flag_user
    change_owner(cron_file, users::get_current_uid(), users::get_current_gid()).unwrap();

    0
}

fn main() {
    let mut stderr = stderr();
    let mut args: Args = Docopt::new(USAGE)
                            .and_then(|d| d.decode())
                            .unwrap_or_else(|e| e.exit());

    if args.flag_user.is_some() && users::get_current_uid() != 0 {
        writeln!(stderr, "must be privileged to use -u").unwrap();
        exit(1);
    }

    match fs::metadata(CRONTAB_DIR) {
        Ok(ref meta) if !meta.is_dir() => {
            writeln!(stderr, "{} is not a directory!", CRONTAB_DIR).unwrap();
            exit(1);
        },
        Err(_) => if let Err(_) = fs::create_dir_all(CRONTAB_DIR) {
            writeln!(stderr, "{} doesn't exist!", CRONTAB_DIR).unwrap();
            exit(1);
        },
        _ => ()
    }

    if args.flag_user.is_none() {
        args.flag_user = users::get_current_username();
    }
    let cron_file = PathBuf::from(CRONTAB_DIR).join(args.flag_user.clone().unwrap());

    exit(match args {
        Args { flag_show: true, .. } => show(&*cron_file, &args),
        Args { flag_list: true, .. } => list(&*cron_file, &args),
        Args { flag_edit: true, arg_file: None, .. } => edit(&*cron_file, &args),
        Args { flag_edit: true, .. } => replace(&*cron_file, &args),
        Args { flag_remove: true, .. } => remove(&*cron_file, &args),
        _ => unreachable!()
    })
}
<|endoftext|>


<commit_before>#![feature(io)]

extern crate rustc_serialize;
extern crate docopt;
extern crate users;
extern crate glob;
extern crate tempfile;

use tempfile::NamedTempFile;
use docopt::Docopt;
use std::env;
use std::fs;
use std::io::{stdin, stdout, stderr, Write, Read};
use std::fs::File;
use std::os::unix::fs::PermissionsExt;
use std::path::{PathBuf, Path};
use std::process::{Command, exit};
use std::ops::Deref;

static CRONTAB_DIR: &'static str = "/var/spool/cron";
static REBOOT_FILE: &'static str = "/run/crond.reboot";

static USAGE: &'static str = r#"
Usage: crontab [-u USER] -l
       crontab [-u USER] -e [FILE]
       crontab [-u USER] -s
       crontab [-u USER] -r [-i]
       crontab -h | --help

Maintain crontab files for individual users

Options:

  -h, --help            Show this help message and exit.
  -u USER, --user USER  It specifies the name of the user whose crontab is to
                        be tweaked. If this option is not given, crontab
                        examines "your" crontab, i.e., the crontab of the
                        person executing the command. Note that su(8) can
                        confuse crontab and that if you are running inside of
                        su(8) you should always use the -u option for safety's
                        sake. The first form of this command is used to
                        install a new crontab from some named file or standard
                        input if the pseudo-filename "-" is given.
  -l, --list            The current crontab will be displayed on standard
                        output.
  -r, --remove          The current crontab will be removed.
  -e, --edit            This option is used to edit the current crontab using
                        the editor specified by the VISUAL or EDITOR
                        environment variables. After you exit from the editor,
                        the modified crontab will be installed automatically.
  -s, --show            Show all users who have a crontab.
  -i, --ask             This option modifies the -r option to prompt the user
                        for a 'y/Y' response before actually removing the
                        crontab.
"#;

#[derive(Debug, RustcDecodable)]
#[allow(non_snake_case)]
struct Args {
    arg_FILE: Option<String>,
    flag_user: Option<String>,
    flag_list: bool,
    flag_remove: bool,
    flag_edit: bool,
    flag_show: bool,
    flag_ask: bool
}

fn get_editor() -> Option<String> {
    env::var("EDITOR").ok()
        .or_else(|| env::var("VISUAL").ok())
        .or_else(|| ["/usr/bin/editor", "/usr/bin/vim", "/usr/bin/nano", "/usr/bin/mcedit"].iter()
                 .filter(|editor| fs::metadata(editor).map(|meta| meta.is_file() && meta.permissions().mode() & 0o0111 != 0).unwrap_or(false))
                 .next()
                 .map(|&s| s.to_owned()))
}

fn confirm(msg: &str) -> bool {
    let mut stdin = stdin();
    let mut stdout = stdout();
    loop {
        stdout.write_all(msg.as_bytes()).unwrap();
        stdout.flush().unwrap();
        let mut buf = [0u8; 1024];
        match stdin.read(&mut buf) {
            Ok(n) if n > 0 && (buf[0] == 121 || buf[0] == 89) => return true,
            Ok(n) if n > 0 && (buf[0] == 110 || buf[0] == 78) => return false,
            _ => { stdout.write_all("Please reply \"y\" or \"n\"\n".as_bytes()).unwrap(); },
        }
    }
}

fn list(cron_file: &Path, args: &Args) -> i32 {
    if let Err(e) = File::open(cron_file).map(|file| file.tee(stdout()).bytes().count()) {
        use std::io::ErrorKind::*;
        match e.kind() {
            NotFound => println!("no crontab for {}", args.flag_user.as_ref().map(String::deref).unwrap_or("???")),
            PermissionDenied => println!("you can not display {}'s crontab", args.flag_user.as_ref().map(String::deref).unwrap_or("???")),
            _ => println!("failed to read {}", cron_file.display()),
        }
        return 1;
    }
    0
}

fn remove(cron_file: &Path, args: &Args) -> i32 {
    let mut stderr = stderr();

    if !args.flag_ask || confirm(&*format!("Are you sure you want to delete {} (y/n)? ", cron_file.display())) {
        if let Err(e) = fs::remove_file(cron_file) {
            use std::io::ErrorKind::*;
            match e.kind() {
                NotFound => writeln!(stderr, "no crontab for {}", args.flag_user.as_ref().map(String::deref).unwrap_or("???")),
                PermissionDenied => match args.flag_user {
                    ref user @ Some(_) if user != &users::get_current_username() => writeln!(stderr, "you can not remove {}'s crontab", args.flag_user.as_ref().map(String::deref).unwrap_or("???")),
                    _ => match File::create(cron_file) {
                        Ok(_) => writeln!(stderr, "couldn't remove {}, wiped it instead", cron_file.display()),
                        Err(_) => writeln!(stderr, "failed to remove {}", cron_file.display()),
                    }
                },
                _ => writeln!(stderr, "failed to remove {}", cron_file.display())
            }.unwrap();
            return 1;
        }
    }
    0
}

fn show(cron_file: &Path, args: &Args) -> i32 {
    let mut stderr = stderr();

    if users::get_current_uid() != 0 {
        writeln!(stderr, "must be privileged to use -s");
        return 2;
    }

    if let Ok(dir) = fs::read_dir(CRONTAB_DIR) {
        for entry in dir {
            let name = entry.ok().and_then(|e| e.path().file_name().map(|s| s.to_string_lossy().into_owned()));
            if let Some(user) = name {
                if users::get_user_by_name(&*user).is_some() {
                    println!("{}", user);
                } else {
                    writeln!(stderr, "WARNING: crontab found with no matching user: {}", user);
                }
            }
        }
    }
    0
}

fn edit(cron_file: &Path, args: &Args) -> i32 {
    use std::io::ErrorKind::*;

    let mut stderr = stderr();

    let editor = match get_editor() {
        None => {
            writeln!(stderr, "no editor found");
            return 1;
        },
        Some(editor) => editor
    };

    let mut tmpfile = NamedTempFile::new_in(CRONTAB_DIR).unwrap();

    if let Err(e) = File::open(cron_file).map(|file| file.tee(&mut tmpfile).bytes().count()) {
        match e.kind() {
            NotFound => tmpfile.write_all("# min hour dom month dow command".as_bytes()).unwrap(),
            _ => {
                writeln!(stderr, "you can not edit {}'s crontab", args.flag_user.as_ref().map(String::deref).unwrap_or("???"));
                return 1;
            }
        }
    }

    tmpfile.flush().unwrap();
    match Command::new(editor).arg(tmpfile.path()).status() {
        Ok(status) if status.success() => (),
        _ => {
            writeln!(stderr, "edit aborted, your edit is kept here: {}", tmpfile.path().display());
            return 1;
        }
    }

    // TODO: check tmpfile with parser

    if let Err(err) = tmpfile.persist(cron_file) {
        match err.error.kind() {
            PermissionDenied => writeln!(stderr, "you can not edit {}'s crontab, your edit is kept here: {}",
                                         args.flag_user.as_ref().map(String::deref).unwrap_or("???"),
                                         err.file.path().display()),
            _ => writeln!(stderr, "unexpected error: {}, your edit is kept here: {}", err.error, err.file.path().display())
        }.unwrap();
        return 1;
    }

    0
}

fn replace(cron_file: &Path, args: &Args) -> i32 {
    // TODO
    0
}

fn main() {
    let mut args: Args = Docopt::new(USAGE)
                            .and_then(|d| d.decode())
                            .unwrap_or_else(|e| e.exit());

    let editor = get_editor();

    match fs::metadata(CRONTAB_DIR) {
        Ok(ref meta) if meta.is_dir() => (),
        Ok(_) => return println!("{} is not a directory!", CRONTAB_DIR),
        Err(_) => if let Err(_) = fs::create_dir_all(CRONTAB_DIR) {
            return println!("{} doesn't exist!", CRONTAB_DIR);
        }
    }

    if args.flag_user.is_none() {
        args.flag_user = users::get_current_username();
    }
    let cron_file = PathBuf::from(CRONTAB_DIR).join(args.flag_user.clone().unwrap());

    exit(
        if args.flag_show {
            show(&*cron_file, &args)
        } else if args.flag_list {
            list(&*cron_file, &args)
        } else if args.flag_edit {
            if args.arg_FILE.is_none() {
                edit(&*cron_file, &args)
            } else {
                replace(&*cron_file, &args)
            }
        } else if args.flag_remove {
            remove(&*cron_file, &args)
        } else {
            0
        })
}
<commit_msg>crontab: warnings cleanup<commit_after>#![feature(io)]

extern crate rustc_serialize;
extern crate docopt;
extern crate users;
extern crate glob;
extern crate tempfile;

use tempfile::NamedTempFile;
use docopt::Docopt;
use std::env;
use std::fs;
use std::io::{stdin, stdout, stderr, Write, Read};
use std::fs::File;
use std::os::unix::fs::PermissionsExt;
use std::path::{PathBuf, Path};
use std::process::{Command, exit};
use std::ops::Deref;

static CRONTAB_DIR: &'static str = "/var/spool/cron";
static REBOOT_FILE: &'static str = "/run/crond.reboot";

static USAGE: &'static str = r#"
Usage: crontab [-u USER] -l
       crontab [-u USER] -e [FILE]
       crontab [-u USER] -s
       crontab [-u USER] -r [-i]
       crontab -h | --help

Maintain crontab files for individual users

Options:

  -h, --help            Show this help message and exit.
  -u USER, --user USER  It specifies the name of the user whose crontab is to
                        be tweaked. If this option is not given, crontab
                        examines "your" crontab, i.e., the crontab of the
                        person executing the command. Note that su(8) can
                        confuse crontab and that if you are running inside of
                        su(8) you should always use the -u option for safety's
                        sake. The first form of this command is used to
                        install a new crontab from some named file or standard
                        input if the pseudo-filename "-" is given.
  -l, --list            The current crontab will be displayed on standard
                        output.
  -r, --remove          The current crontab will be removed.
  -e, --edit            This option is used to edit the current crontab using
                        the editor specified by the VISUAL or EDITOR
                        environment variables. After you exit from the editor,
                        the modified crontab will be installed automatically.
  -s, --show            Show all users who have a crontab.
  -i, --ask             This option modifies the -r option to prompt the user
                        for a 'y/Y' response before actually removing the
                        crontab.
"#;

#[derive(Debug, RustcDecodable)]
#[allow(non_snake_case)]
struct Args {
    arg_FILE: Option<String>,
    flag_user: Option<String>,
    flag_list: bool,
    flag_remove: bool,
    flag_edit: bool,
    flag_show: bool,
    flag_ask: bool
}

fn get_editor() -> Option<String> {
    env::var("EDITOR").ok()
        .or_else(|| env::var("VISUAL").ok())
        .or_else(|| ["/usr/bin/editor", "/usr/bin/vim", "/usr/bin/nano", "/usr/bin/mcedit"].iter()
                 .filter(|editor| fs::metadata(editor).map(|meta| meta.is_file() && meta.permissions().mode() & 0o0111 != 0).unwrap_or(false))
                 .next()
                 .map(|&s| s.to_owned()))
}

fn confirm(msg: &str) -> bool {
    let mut stdin = stdin();
    let mut stdout = stdout();
    loop {
        stdout.write_all(msg.as_bytes()).unwrap();
        stdout.flush().unwrap();
        let mut buf = [0u8; 1024];
        match stdin.read(&mut buf) {
            Ok(n) if n > 0 && (buf[0] == 121 || buf[0] == 89) => return true,
            Ok(n) if n > 0 && (buf[0] == 110 || buf[0] == 78) => return false,
            _ => { stdout.write_all("Please reply \"y\" or \"n\"\n".as_bytes()).unwrap(); },
        }
    }
}

fn list(cron_file: &Path, args: &Args) -> i32 {
    if let Err(e) = File::open(cron_file).map(|file| file.tee(stdout()).bytes().count()) {
        use std::io::ErrorKind::*;
        match e.kind() {
            NotFound => println!("no crontab for {}", args.flag_user.as_ref().map(String::deref).unwrap_or("???")),
            PermissionDenied => println!("you can not display {}'s crontab", args.flag_user.as_ref().map(String::deref).unwrap_or("???")),
            _ => println!("failed to read {}", cron_file.display()),
        }
        return 1;
    }
    0
}

fn remove(cron_file: &Path, args: &Args) -> i32 {
    let mut stderr = stderr();

    if !args.flag_ask || confirm(&*format!("Are you sure you want to delete {} (y/n)? ", cron_file.display())) {
        if let Err(e) = fs::remove_file(cron_file) {
            use std::io::ErrorKind::*;
            match e.kind() {
                NotFound => writeln!(stderr, "no crontab for {}", args.flag_user.as_ref().map(String::deref).unwrap_or("???")),
                PermissionDenied => match args.flag_user {
                    ref user @ Some(_) if user != &users::get_current_username() => writeln!(stderr, "you can not remove {}'s crontab", args.flag_user.as_ref().map(String::deref).unwrap_or("???")),
                    _ => match File::create(cron_file) {
                        Ok(_) => writeln!(stderr, "couldn't remove {}, wiped it instead", cron_file.display()),
                        Err(_) => writeln!(stderr, "failed to remove {}", cron_file.display()),
                    }
                },
                _ => writeln!(stderr, "failed to remove {}", cron_file.display())
            }.unwrap();
            return 1;
        }
    }
    0
}

fn show(cron_file: &Path, args: &Args) -> i32 {
    let mut stderr = stderr();

    if users::get_current_uid() != 0 {
        writeln!(stderr, "must be privileged to use -s").unwrap();
        return 2;
    }

    if let Ok(dir) = fs::read_dir(CRONTAB_DIR) {
        for entry in dir {
            let name = entry.ok().and_then(|e| e.path().file_name().map(|s| s.to_string_lossy().into_owned()));
            if let Some(user) = name {
                if users::get_user_by_name(&*user).is_some() {
                    println!("{}", user);
                } else {
                    writeln!(stderr, "WARNING: crontab found with no matching user: {}", user).unwrap();
                }
            }
        }
    }
    0
}

fn edit(cron_file: &Path, args: &Args) -> i32 {
    use std::io::ErrorKind::*;

    let mut stderr = stderr();

    let editor = match get_editor() {
        None => {
            writeln!(stderr, "no editor found").unwrap();
            return 1;
        },
        Some(editor) => editor
    };

    let mut tmpfile = NamedTempFile::new_in(CRONTAB_DIR).unwrap();

    if let Err(e) = File::open(cron_file).map(|file| file.tee(&mut tmpfile).bytes().count()) {
        match e.kind() {
            NotFound => tmpfile.write_all("# min hour dom month dow command".as_bytes()).unwrap(),
            _ => {
                writeln!(stderr, "you can not edit {}'s crontab", args.flag_user.as_ref().map(String::deref).unwrap_or("???")).unwrap();
                return 1;
            }
        }
    }

    tmpfile.flush().unwrap();
    match Command::new(editor).arg(tmpfile.path()).status() {
        Ok(status) if status.success() => (),
        _ => {
            writeln!(stderr, "edit aborted, your edit is kept here: {}", tmpfile.path().display()).unwrap();
            return 1;
        }
    }

    // TODO: check tmpfile with parser

    if let Err(err) = tmpfile.persist(cron_file) {
        match err.error.kind() {
            PermissionDenied => writeln!(stderr, "you can not edit {}'s crontab, your edit is kept here: {}",
                                         args.flag_user.as_ref().map(String::deref).unwrap_or("???"),
                                         err.file.path().display()),
            _ => writeln!(stderr, "unexpected error: {}, your edit is kept here: {}", err.error, err.file.path().display())
        }.unwrap();
        return 1;
    }

    0
}

fn replace(cron_file: &Path, args: &Args) -> i32 {
    // TODO
    0
}

fn main() {
    let mut args: Args = Docopt::new(USAGE)
                            .and_then(|d| d.decode())
                            .unwrap_or_else(|e| e.exit());

    match fs::metadata(CRONTAB_DIR) {
        Ok(ref meta) if meta.is_dir() => (),
        Ok(_) => return println!("{} is not a directory!", CRONTAB_DIR),
        Err(_) => if let Err(_) = fs::create_dir_all(CRONTAB_DIR) {
            return println!("{} doesn't exist!", CRONTAB_DIR);
        }
    }

    if args.flag_user.is_none() {
        args.flag_user = users::get_current_username();
    }
    let cron_file = PathBuf::from(CRONTAB_DIR).join(args.flag_user.clone().unwrap());

    exit(
        if args.flag_show {
            show(&*cron_file, &args)
        } else if args.flag_list {
            list(&*cron_file, &args)
        } else if args.flag_edit {
            if args.arg_FILE.is_none() {
                edit(&*cron_file, &args)
            } else {
                replace(&*cron_file, &args)
            }
        } else if args.flag_remove {
            remove(&*cron_file, &args)
        } else {
            0
        })
}
<|endoftext|>


<commit_before>//! Error types that can be emitted from this library

use std::io;
use std::error;

pub type ZipResult<T> = Result<T, ZipError>;

/// Error type for Zip
#[deriving(Show)]
pub enum ZipError
{
    /// An Error caused by I/O
    Io(io::IoError),

    /// This file is probably not a zipfile. The argument is enclosed.
    InvalidZipFile(&'static str),

    /// This file is unsupported. The reason is enclosed.
    UnsupportedZipFile(&'static str),

    /// The ZipReader is not available.
    ReaderUnavailable,
}

impl error::FromError<io::IoError> for ZipError
{
    fn from_error(err: io::IoError) -> ZipError
    {
        ZipError::Io(err)
    }
}

impl error::Error for ZipError
{
    fn description(&self) -> &str
    {
        match *self
        {
            ZipError::Io(ref io_err) => io_err.description(),
            ZipError::InvalidZipFile(..) => "Invalid Zip File",
            ZipError::UnsupportedZipFile(..) => "Unsupported Zip File",
            ZipError::ReaderUnavailable => "No reader available",
        }
    }

    fn detail(&self) -> Option<String>
    {
        match *self
        {
            ZipError::Io(ref io_err) => io_err.detail(),
            ZipError::InvalidZipFile(detail) |
            ZipError::UnsupportedZipFile(detail) => Some(detail.to_string()),
            ZipError::ReaderUnavailable => None,
        }
    }

    fn cause(&self) -> Option<&error::Error>
    {
        match *self
        {
            ZipError::Io(ref io_err) => Some(io_err as &error::Error),
            _ => None,
        }
    }
}
<commit_msg>Add missing documentation<commit_after>//! Error types that can be emitted from this library

use std::io;
use std::error;

/// Generic result type with ZipError as its error variant
pub type ZipResult<T> = Result<T, ZipError>;

/// Error type for Zip
#[deriving(Show)]
pub enum ZipError
{
    /// An Error caused by I/O
    Io(io::IoError),

    /// This file is probably not a zipfile. The argument is enclosed.
    InvalidZipFile(&'static str),

    /// This file is unsupported. The reason is enclosed.
    UnsupportedZipFile(&'static str),

    /// The ZipReader is not available.
    ReaderUnavailable,
}

impl error::FromError<io::IoError> for ZipError
{
    fn from_error(err: io::IoError) -> ZipError
    {
        ZipError::Io(err)
    }
}

impl error::Error for ZipError
{
    fn description(&self) -> &str
    {
        match *self
        {
            ZipError::Io(ref io_err) => io_err.description(),
            ZipError::InvalidZipFile(..) => "Invalid Zip File",
            ZipError::UnsupportedZipFile(..) => "Unsupported Zip File",
            ZipError::ReaderUnavailable => "No reader available",
        }
    }

    fn detail(&self) -> Option<String>
    {
        match *self
        {
            ZipError::Io(ref io_err) => io_err.detail(),
            ZipError::InvalidZipFile(detail) |
            ZipError::UnsupportedZipFile(detail) => Some(detail.to_string()),
            ZipError::ReaderUnavailable => None,
        }
    }

    fn cause(&self) -> Option<&error::Error>
    {
        match *self
        {
            ZipError::Io(ref io_err) => Some(io_err as &error::Error),
            _ => None,
        }
    }
}
<|endoftext|>


<commit_before>use std::fmt;

pub type NSSResult<T> = Result<T, NSSError>;

pub enum NSSError
{
    /// An error generated by NSPR or NSS
    NSS(i32),
}

impl fmt::Show for NSSError
{
    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::FormatError>
    {
        match *self
        {
            NSS(code) => {
                match ::ffi::nspr::get_error_message(code)
                {
                    Some(message) => formatter.write(message.as_bytes()),
                    None => formatter.write(format!("Unknown error {}", code).as_bytes()),
                }
            },
        }
    }
}
<commit_msg>Remove superfluous format!<commit_after>use std::fmt;

pub type NSSResult<T> = Result<T, NSSError>;

pub enum NSSError
{
    /// An error generated by NSPR or NSS
    NSS(i32),
}

impl fmt::Show for NSSError
{
    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::FormatError>
    {
        match *self
        {
            NSS(code) => {
                match ::ffi::nspr::get_error_message(code)
                {
                    Some(message) => formatter.write(message.as_bytes()),
                    None => write!(formatter, "Unknown error {}", code),
                }
            },
        }
    }
}
<|endoftext|>