<commit_before>// Copyright 2017 Serde Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::collections::HashSet;

use syn::{self, visit};
use syn::punctuated::Punctuated;

use internals::ast::{Data, Container};
use internals::attr;

use proc_macro2::{Span, Term};

// Remove the default from every type parameter because in the generated impls
// they look like associated types: "error: associated type bindings are not
// allowed here".
pub fn without_defaults(generics: &syn::Generics) -> syn::Generics {
    syn::Generics {
        params: generics
            .params
            .iter()
            .map(|param| match *param {
                syn::GenericParam::Type(ref param) => {
                    syn::GenericParam::Type(syn::TypeParam {
                        eq_token: None,
                        default: None,
                        ..param.clone()
                    })
                }
                _ => param.clone(),
            })
            .collect(),
        ..generics.clone()
    }
}

pub fn with_where_predicates(
    generics: &syn::Generics,
    predicates: &[syn::WherePredicate],
) -> syn::Generics {
    let mut generics = generics.clone();
    if generics.where_clause.is_none() {
        generics.where_clause = Some(syn::WhereClause {
            where_token: Default::default(),
            predicates: Punctuated::new(),
        });
    }
    generics.where_clause
        .as_mut()
        .unwrap()
        .predicates
        .extend(predicates.into_iter().cloned());
    generics
}

pub fn with_where_predicates_from_fields<F>(
    cont: &Container,
    generics: &syn::Generics,
    from_field: F,
) -> syn::Generics
where
    F: Fn(&attr::Field) -> Option<&[syn::WherePredicate]>,
{
    let predicates = cont.data
        .all_fields()
        .flat_map(|field| from_field(&field.attrs))
        .flat_map(|predicates| predicates.to_vec());

    let mut generics = generics.clone();
    if generics.where_clause.is_none() {
        generics.where_clause = Some(syn::WhereClause {
            where_token: Default::default(),
            predicates: Punctuated::new(),
        });
    }
    generics.where_clause
        .as_mut()
        .unwrap()
        .predicates
        .extend(predicates);
    generics
}

// Puts the given bound on any generic type parameters that are used in fields
// for which filter returns true.
//
// For example, the following struct needs the bound `A: Serialize, B: Serialize`.
//
//     struct S<'b, A, B: 'b, C> {
//         a: A,
//         b: Option<&'b B>
//         #[serde(skip_serializing)]
//         c: C,
//     }
pub fn with_bound<F>(
    cont: &Container,
    generics: &syn::Generics,
    filter: F,
    bound: &syn::Path,
) -> syn::Generics
where
    F: Fn(&attr::Field, Option<&attr::Variant>) -> bool,
{
    struct FindTyParams {
        // Set of all generic type parameters on the current struct (A, B, C in
        // the example). Initialized up front.
        all_ty_params: HashSet<syn::Ident>,
        // Set of generic type parameters used in fields for which filter
        // returns true (A and B in the example). Filled in as the visitor sees
        // them.
        relevant_ty_params: HashSet<syn::Ident>,
    }
    impl<'ast> visit::Visit<'ast> for FindTyParams {
        fn visit_path(&mut self, path: &syn::Path) {
            if let Some(seg) = path.segments.last() {
                if seg.into_value().ident == "PhantomData" {
                    // Hardcoded exception, because PhantomData<T> implements
                    // Serialize and Deserialize whether or not T implements it.
                    return;
                }
            }
            if path.leading_colon.is_none() && path.segments.len() == 1 {
                let id = path.segments[0].ident;
                if self.all_ty_params.contains(&id) {
                    self.relevant_ty_params.insert(id);
                }
            }
            visit::visit_path(self, path);
        }

        // Type parameter should not be considered used by a macro path.
        //
        //     struct TypeMacro<T> {
        //         mac: T!(),
        //         marker: PhantomData<T>,
        //     }
        fn visit_macro(&mut self, _mac: &syn::Macro) {}
    }

    let all_ty_params: HashSet<_> = generics
        .params
        .iter()
        .filter_map(|param| match *param {
            syn::GenericParam::Type(ref param) => Some(param.ident),
            _ => None,
        })
        .collect();

    let mut visitor = FindTyParams {
        all_ty_params: all_ty_params,
        relevant_ty_params: HashSet::new(),
    };
    match cont.data {
        Data::Enum(ref variants) => for variant in variants.iter() {
            let relevant_fields = variant
                .fields
                .iter()
                .filter(|field| filter(&field.attrs, Some(&variant.attrs)));
            for field in relevant_fields {
                visit::visit_type(&mut visitor, field.ty);
            }
        },
        Data::Struct(_, ref fields) => {
            for field in fields.iter().filter(|field| filter(&field.attrs, None)) {
                visit::visit_type(&mut visitor, field.ty);
            }
        }
    }

    let new_predicates = generics
        .params
        .iter()
        .filter_map(|param| match *param {
            syn::GenericParam::Type(ref param) => Some(param.ident),
            _ => None,
        })
        .filter(|id| visitor.relevant_ty_params.contains(id))
        .map(|id| {
            syn::WherePredicate::Type(syn::PredicateType {
                lifetimes: None,
                // the type parameter that is being bounded e.g. T
                bounded_ty: syn::Type::Path(syn::TypePath {
                    qself: None,
                    path: id.into(),
                }),
                colon_token: Default::default(),
                // the bound e.g. Serialize
                bounds: vec![
                    syn::TypeParamBound::Trait(syn::TraitBound {
                        modifier: syn::TraitBoundModifier::None,
                        lifetimes: None,
                        path: bound.clone(),
                    }),
                ].into_iter().collect(),
            })
        });

    let mut generics = generics.clone();
    if generics.where_clause.is_none() {
        generics.where_clause = Some(syn::WhereClause {
            where_token: Default::default(),
            predicates: Punctuated::new(),
        });
    }
    generics.where_clause
        .as_mut()
        .unwrap()
        .predicates
        .extend(new_predicates);
    generics
}

pub fn with_self_bound(
    cont: &Container,
    generics: &syn::Generics,
    bound: &syn::Path,
) -> syn::Generics {
    let mut generics = generics.clone();
    if generics.where_clause.is_none() {
        generics.where_clause = Some(syn::WhereClause {
            where_token: Default::default(),
            predicates: Punctuated::new(),
        });
    }
    generics.where_clause
        .as_mut()
        .unwrap()
        .predicates
        .push(syn::WherePredicate::Type(syn::PredicateType {
            lifetimes: None,
            // the type that is being bounded e.g. MyStruct<'a, T>
            bounded_ty: type_of_item(cont),
            colon_token: Default::default(),
            // the bound e.g. Default
            bounds: vec![
                syn::TypeParamBound::Trait(syn::TraitBound {
                    modifier: syn::TraitBoundModifier::None,
                    lifetimes: None,
                    path: bound.clone(),
                }),
            ].into_iter().collect(),
        }));
    generics
}

pub fn with_lifetime_bound(generics: &syn::Generics, lifetime: &str) -> syn::Generics {
    let bound = syn::Lifetime::new(Term::new(lifetime, Span::call_site()));
    let def = syn::LifetimeDef {
        attrs: Vec::new(),
        lifetime: bound,
        colon_token: None,
        bounds: Punctuated::new(),
    };

    let params = Some(syn::GenericParam::Lifetime(def))
        .into_iter()
        .chain(generics.params
            .iter()
            .cloned()
            .map(|mut param| {
                match param {
                    syn::GenericParam::Lifetime(ref mut param) => {
                        param.bounds.push(bound);
                    }
                    syn::GenericParam::Type(ref mut param) => {
                        param.bounds.push(syn::TypeParamBound::Lifetime(bound));
                    }
                    syn::GenericParam::Const(_) => {}
                }
                param
            }))
        .collect();

    syn::Generics {
        params: params,
        ..generics.clone()
    }
}

fn type_of_item(cont: &Container) -> syn::Type {
    syn::Type::Path(syn::TypePath {
        qself: None,
        path: syn::Path {
            leading_colon: None,
            segments: vec![
                syn::PathSegment {
                    ident: cont.ident,
                    arguments: syn::PathArguments::AngleBracketed(
                        syn::AngleBracketedGenericArguments {
                            colon2_token: None,
                            lt_token: Default::default(),
                            args: cont.generics
                                .params
                                .iter()
                                .map(|param| match *param {
                                    syn::GenericParam::Type(ref param) => {
                                        syn::GenericArgument::Type(syn::Type::Path(syn::TypePath {
                                            qself: None,
                                            path: param.ident.into(),
                                        }))
                                    }
                                    syn::GenericParam::Lifetime(ref param) => {
                                        syn::GenericArgument::Lifetime(param.lifetime)
                                    }
                                    syn::GenericParam::Const(_) => {
                                        panic!("Serde does not support const generics yet");
                                    }
                                })
                                .collect(),
                            gt_token: Default::default(),
                        },
                    ),
                },
            ].into_iter().collect(),
        },
    })
}
<commit_msg>Add parens to TraitBound<commit_after>// Copyright 2017 Serde Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::collections::HashSet;

use syn::{self, visit};
use syn::punctuated::Punctuated;

use internals::ast::{Data, Container};
use internals::attr;

use proc_macro2::{Span, Term};

// Remove the default from every type parameter because in the generated impls
// they look like associated types: "error: associated type bindings are not
// allowed here".
pub fn without_defaults(generics: &syn::Generics) -> syn::Generics {
    syn::Generics {
        params: generics
            .params
            .iter()
            .map(|param| match *param {
                syn::GenericParam::Type(ref param) => {
                    syn::GenericParam::Type(syn::TypeParam {
                        eq_token: None,
                        default: None,
                        ..param.clone()
                    })
                }
                _ => param.clone(),
            })
            .collect(),
        ..generics.clone()
    }
}

pub fn with_where_predicates(
    generics: &syn::Generics,
    predicates: &[syn::WherePredicate],
) -> syn::Generics {
    let mut generics = generics.clone();
    if generics.where_clause.is_none() {
        generics.where_clause = Some(syn::WhereClause {
            where_token: Default::default(),
            predicates: Punctuated::new(),
        });
    }
    generics.where_clause
        .as_mut()
        .unwrap()
        .predicates
        .extend(predicates.into_iter().cloned());
    generics
}

pub fn with_where_predicates_from_fields<F>(
    cont: &Container,
    generics: &syn::Generics,
    from_field: F,
) -> syn::Generics
where
    F: Fn(&attr::Field) -> Option<&[syn::WherePredicate]>,
{
    let predicates = cont.data
        .all_fields()
        .flat_map(|field| from_field(&field.attrs))
        .flat_map(|predicates| predicates.to_vec());

    let mut generics = generics.clone();
    if generics.where_clause.is_none() {
        generics.where_clause = Some(syn::WhereClause {
            where_token: Default::default(),
            predicates: Punctuated::new(),
        });
    }
    generics.where_clause
        .as_mut()
        .unwrap()
        .predicates
        .extend(predicates);
    generics
}

// Puts the given bound on any generic type parameters that are used in fields
// for which filter returns true.
//
// For example, the following struct needs the bound `A: Serialize, B: Serialize`.
//
//     struct S<'b, A, B: 'b, C> {
//         a: A,
//         b: Option<&'b B>
//         #[serde(skip_serializing)]
//         c: C,
//     }
pub fn with_bound<F>(
    cont: &Container,
    generics: &syn::Generics,
    filter: F,
    bound: &syn::Path,
) -> syn::Generics
where
    F: Fn(&attr::Field, Option<&attr::Variant>) -> bool,
{
    struct FindTyParams {
        // Set of all generic type parameters on the current struct (A, B, C in
        // the example). Initialized up front.
        all_ty_params: HashSet<syn::Ident>,
        // Set of generic type parameters used in fields for which filter
        // returns true (A and B in the example). Filled in as the visitor sees
        // them.
        relevant_ty_params: HashSet<syn::Ident>,
    }
    impl<'ast> visit::Visit<'ast> for FindTyParams {
        fn visit_path(&mut self, path: &syn::Path) {
            if let Some(seg) = path.segments.last() {
                if seg.into_value().ident == "PhantomData" {
                    // Hardcoded exception, because PhantomData<T> implements
                    // Serialize and Deserialize whether or not T implements it.
                    return;
                }
            }
            if path.leading_colon.is_none() && path.segments.len() == 1 {
                let id = path.segments[0].ident;
                if self.all_ty_params.contains(&id) {
                    self.relevant_ty_params.insert(id);
                }
            }
            visit::visit_path(self, path);
        }

        // Type parameter should not be considered used by a macro path.
        //
        //     struct TypeMacro<T> {
        //         mac: T!(),
        //         marker: PhantomData<T>,
        //     }
        fn visit_macro(&mut self, _mac: &syn::Macro) {}
    }

    let all_ty_params: HashSet<_> = generics
        .params
        .iter()
        .filter_map(|param| match *param {
            syn::GenericParam::Type(ref param) => Some(param.ident),
            _ => None,
        })
        .collect();

    let mut visitor = FindTyParams {
        all_ty_params: all_ty_params,
        relevant_ty_params: HashSet::new(),
    };
    match cont.data {
        Data::Enum(ref variants) => for variant in variants.iter() {
            let relevant_fields = variant
                .fields
                .iter()
                .filter(|field| filter(&field.attrs, Some(&variant.attrs)));
            for field in relevant_fields {
                visit::visit_type(&mut visitor, field.ty);
            }
        },
        Data::Struct(_, ref fields) => {
            for field in fields.iter().filter(|field| filter(&field.attrs, None)) {
                visit::visit_type(&mut visitor, field.ty);
            }
        }
    }

    let new_predicates = generics
        .params
        .iter()
        .filter_map(|param| match *param {
            syn::GenericParam::Type(ref param) => Some(param.ident),
            _ => None,
        })
        .filter(|id| visitor.relevant_ty_params.contains(id))
        .map(|id| {
            syn::WherePredicate::Type(syn::PredicateType {
                lifetimes: None,
                // the type parameter that is being bounded e.g. T
                bounded_ty: syn::Type::Path(syn::TypePath {
                    qself: None,
                    path: id.into(),
                }),
                colon_token: Default::default(),
                // the bound e.g. Serialize
                bounds: vec![
                    syn::TypeParamBound::Trait(syn::TraitBound {
                        paren_token: None,
                        modifier: syn::TraitBoundModifier::None,
                        lifetimes: None,
                        path: bound.clone(),
                    }),
                ].into_iter().collect(),
            })
        });

    let mut generics = generics.clone();
    if generics.where_clause.is_none() {
        generics.where_clause = Some(syn::WhereClause {
            where_token: Default::default(),
            predicates: Punctuated::new(),
        });
    }
    generics.where_clause
        .as_mut()
        .unwrap()
        .predicates
        .extend(new_predicates);
    generics
}

pub fn with_self_bound(
    cont: &Container,
    generics: &syn::Generics,
    bound: &syn::Path,
) -> syn::Generics {
    let mut generics = generics.clone();
    if generics.where_clause.is_none() {
        generics.where_clause = Some(syn::WhereClause {
            where_token: Default::default(),
            predicates: Punctuated::new(),
        });
    }
    generics.where_clause
        .as_mut()
        .unwrap()
        .predicates
        .push(syn::WherePredicate::Type(syn::PredicateType {
            lifetimes: None,
            // the type that is being bounded e.g. MyStruct<'a, T>
            bounded_ty: type_of_item(cont),
            colon_token: Default::default(),
            // the bound e.g. Default
            bounds: vec![
                syn::TypeParamBound::Trait(syn::TraitBound {
                    paren_token: None,
                    modifier: syn::TraitBoundModifier::None,
                    lifetimes: None,
                    path: bound.clone(),
                }),
            ].into_iter().collect(),
        }));
    generics
}

pub fn with_lifetime_bound(generics: &syn::Generics, lifetime: &str) -> syn::Generics {
    let bound = syn::Lifetime::new(Term::new(lifetime, Span::call_site()));
    let def = syn::LifetimeDef {
        attrs: Vec::new(),
        lifetime: bound,
        colon_token: None,
        bounds: Punctuated::new(),
    };

    let params = Some(syn::GenericParam::Lifetime(def))
        .into_iter()
        .chain(generics.params
            .iter()
            .cloned()
            .map(|mut param| {
                match param {
                    syn::GenericParam::Lifetime(ref mut param) => {
                        param.bounds.push(bound);
                    }
                    syn::GenericParam::Type(ref mut param) => {
                        param.bounds.push(syn::TypeParamBound::Lifetime(bound));
                    }
                    syn::GenericParam::Const(_) => {}
                }
                param
            }))
        .collect();

    syn::Generics {
        params: params,
        ..generics.clone()
    }
}

fn type_of_item(cont: &Container) -> syn::Type {
    syn::Type::Path(syn::TypePath {
        qself: None,
        path: syn::Path {
            leading_colon: None,
            segments: vec![
                syn::PathSegment {
                    ident: cont.ident,
                    arguments: syn::PathArguments::AngleBracketed(
                        syn::AngleBracketedGenericArguments {
                            colon2_token: None,
                            lt_token: Default::default(),
                            args: cont.generics
                                .params
                                .iter()
                                .map(|param| match *param {
                                    syn::GenericParam::Type(ref param) => {
                                        syn::GenericArgument::Type(syn::Type::Path(syn::TypePath {
                                            qself: None,
                                            path: param.ident.into(),
                                        }))
                                    }
                                    syn::GenericParam::Lifetime(ref param) => {
                                        syn::GenericArgument::Lifetime(param.lifetime)
                                    }
                                    syn::GenericParam::Const(_) => {
                                        panic!("Serde does not support const generics yet");
                                    }
                                })
                                .collect(),
                            gt_token: Default::default(),
                        },
                    ),
                },
            ].into_iter().collect(),
        },
    })
}
<|endoftext|>


<commit_before>use super::{MysqlType, MysqlTypeMetadata};
use crate::deserialize;
use mysqlclient_sys as ffi;
use std::error::Error;

/// Raw mysql value as received from the database
#[derive(Copy, Clone, Debug)]
pub struct MysqlValue<'a> {
    raw: &'a [u8],
    tpe: MysqlTypeMetadata,
}

impl<'a> MysqlValue<'a> {
    pub(crate) fn new(raw: &'a [u8], tpe: MysqlTypeMetadata) -> Self {
        Self { raw, tpe }
    }

    /// Get the underlying raw byte representation
    pub fn as_bytes(&self) -> &[u8] {
        self.raw
    }

    /// Checks that the type code is valid, and interprets the data as a
    /// `MYSQL_TIME` pointer
    #[allow(dead_code)]
    pub(crate) fn time_value(&self) -> deserialize::Result<ffi::MYSQL_TIME> {
        match self.tpe.data_type {
            MysqlType::Time | MysqlType::Date | MysqlType::DateTime | MysqlType::Timestamp => {
                Ok(*unsafe { &*(self.raw as *const _ as *const ffi::MYSQL_TIME) })
            }
            _ => Err(self.invalid_type_code("timestamp")),
        }
    }

    /// Returns the numeric representation of this value, based on the type code.
    /// Returns an error if the type code is not numeric.
    pub(crate) fn numeric_value(&self) -> deserialize::Result<NumericRepresentation> {
        use self::NumericRepresentation::*;
        use std::convert::TryInto;

        Ok(match self.tpe.data_type {
            MysqlType::Tiny => Tiny(self.raw[0] as i8),
            MysqlType::Short => Small(i16::from_ne_bytes(self.raw.try_into()?)),
            MysqlType::Long => Medium(i32::from_ne_bytes(self.raw.try_into()?)),
            MysqlType::LongLong => Big(i64::from_ne_bytes(self.raw.try_into()?)),
            MysqlType::Float => Float(f32::from_ne_bytes(self.raw.try_into()?)),
            MysqlType::Double => Double(f64::from_ne_bytes(self.raw.try_into()?)),

            MysqlType::Numeric => Decimal(self.raw),
            _ => return Err(self.invalid_type_code("number")),
        })
    }

    fn invalid_type_code(&self, expected: &str) -> Box<dyn Error + Send + Sync> {
        format!(
            "Invalid representation received for {}: {:?}",
            expected, self.tpe
        )
        .into()
    }
}

/// Represents all possible forms MySQL transmits integers
#[derive(Debug, Clone, Copy)]
pub enum NumericRepresentation<'a> {
    /// Correponds to `MYSQL_TYPE_TINY`
    Tiny(i8),
    /// Correponds to `MYSQL_TYPE_SHORT`
    Small(i16),
    /// Correponds to `MYSQL_TYPE_INT24` and `MYSQL_TYPE_LONG`
    Medium(i32),
    /// Correponds to `MYSQL_TYPE_LONGLONG`
    Big(i64),
    /// Correponds to `MYSQL_TYPE_FLOAT`
    Float(f32),
    /// Correponds to `MYSQL_TYPE_DOUBLE`
    Double(f64),
    /// Correponds to `MYSQL_TYPE_DECIMAL` and `MYSQL_TYPE_NEWDECIMAL`
    Decimal(&'a [u8]),
}
<commit_msg>Improve conversion of the received mysql value bytes to MYSQL_TIME<commit_after>use super::{MysqlType, MysqlTypeMetadata};
use crate::deserialize;
use mysqlclient_sys as ffi;
use std::error::Error;

/// Raw mysql value as received from the database
#[derive(Copy, Clone, Debug)]
pub struct MysqlValue<'a> {
    raw: &'a [u8],
    tpe: MysqlTypeMetadata,
}

impl<'a> MysqlValue<'a> {
    pub(crate) fn new(raw: &'a [u8], tpe: MysqlTypeMetadata) -> Self {
        Self { raw, tpe }
    }

    /// Get the underlying raw byte representation
    pub fn as_bytes(&self) -> &[u8] {
        self.raw
    }

    /// Checks that the type code is valid, and interprets the data as a
    /// `MYSQL_TIME` pointer
    // We use `ptr.read_unaligned()` to read the potential unaligned ptr,
    // so clippy is clearly wrong here
    // https://github.com/rust-lang/rust-clippy/issues/2881
    #[allow(dead_code, clippy::cast_ptr_alignment)]
    pub(crate) fn time_value(&self) -> deserialize::Result<ffi::MYSQL_TIME> {
        match self.tpe.data_type {
            MysqlType::Time | MysqlType::Date | MysqlType::DateTime | MysqlType::Timestamp => {
                let ptr = self.raw.as_ptr() as *const ffi::MYSQL_TIME;
                let result = unsafe { ptr.read_unaligned() };
                if result.neg == 0 {
                    Ok(result)
                } else {
                    Err("Negative dates/times are not yet supported".into())
                }
            }
            _ => Err(self.invalid_type_code("timestamp")),
        }
    }

    /// Returns the numeric representation of this value, based on the type code.
    /// Returns an error if the type code is not numeric.
    pub(crate) fn numeric_value(&self) -> deserialize::Result<NumericRepresentation> {
        use self::NumericRepresentation::*;
        use std::convert::TryInto;

        Ok(match self.tpe.data_type {
            MysqlType::Tiny => Tiny(self.raw[0] as i8),
            MysqlType::Short => Small(i16::from_ne_bytes(self.raw.try_into()?)),
            MysqlType::Long => Medium(i32::from_ne_bytes(self.raw.try_into()?)),
            MysqlType::LongLong => Big(i64::from_ne_bytes(self.raw.try_into()?)),
            MysqlType::Float => Float(f32::from_ne_bytes(self.raw.try_into()?)),
            MysqlType::Double => Double(f64::from_ne_bytes(self.raw.try_into()?)),

            MysqlType::Numeric => Decimal(self.raw),
            _ => return Err(self.invalid_type_code("number")),
        })
    }

    fn invalid_type_code(&self, expected: &str) -> Box<dyn Error + Send + Sync> {
        format!(
            "Invalid representation received for {}: {:?}",
            expected, self.tpe
        )
        .into()
    }
}

/// Represents all possible forms MySQL transmits integers
#[derive(Debug, Clone, Copy)]
pub enum NumericRepresentation<'a> {
    /// Correponds to `MYSQL_TYPE_TINY`
    Tiny(i8),
    /// Correponds to `MYSQL_TYPE_SHORT`
    Small(i16),
    /// Correponds to `MYSQL_TYPE_INT24` and `MYSQL_TYPE_LONG`
    Medium(i32),
    /// Correponds to `MYSQL_TYPE_LONGLONG`
    Big(i64),
    /// Correponds to `MYSQL_TYPE_FLOAT`
    Float(f32),
    /// Correponds to `MYSQL_TYPE_DOUBLE`
    Double(f64),
    /// Correponds to `MYSQL_TYPE_DECIMAL` and `MYSQL_TYPE_NEWDECIMAL`
    Decimal(&'a [u8]),
}
<|endoftext|>


<commit_before>#[macro_use] extern crate nom;
#[macro_use] extern crate clap;
#[macro_use] extern crate serde_derive;
extern crate mio;
extern crate mio_uds;
extern crate serde;
extern crate serde_json;
extern crate time;
extern crate libc;
extern crate slab;
extern crate rand;
extern crate nix;
#[macro_use] extern crate sozu_lib as sozu;
extern crate sozu_command_lib as sozu_command;

#[cfg(target_os = "linux")]
extern crate num_cpus;
#[cfg(target_os = "linux")]
extern crate procinfo;

mod command;
mod worker;
mod logging;
mod upgrade;

use std::net::ToSocketAddrs;
use std::env;
use sozu_command::config::Config;
use clap::{App,Arg,SubCommand};

#[cfg(target_os = "linux")]
use libc::{cpu_set_t,pid_t};

use command::Worker;
use worker::{begin_worker_process,start_workers};
use upgrade::begin_new_master_process;

fn main() {
  let matches = App::new("sozu")
                        .version(crate_version!())
                        .about("hot reconfigurable proxy")
                        .subcommand(SubCommand::with_name("start")
                                    .about("launch the master process")
                                    .arg(Arg::with_name("config")
                                        .short("c")
                                        .long("config")
                                        .value_name("FILE")
                                        .help("Sets a custom config file")
                                        .takes_value(true)
                                        .required(true)))
                        .subcommand(SubCommand::with_name("worker")
                                    .about("start a worker (internal command, should not be used directly)")
                                    .arg(Arg::with_name("id").long("id")
                                         .takes_value(true).required(true).help("worker identifier"))
                                    .arg(Arg::with_name("fd").long("fd")
                                         .takes_value(true).required(true).help("IPC file descriptor"))
                                    .arg(Arg::with_name("channel-buffer-size").long("channel-buffer-size")
                                         .takes_value(true).required(false).help("Worker's channel buffer size")))
                        .subcommand(SubCommand::with_name("upgrade")
                                    .about("start a new master process (internal command, should not be used directly)")
                                    .arg(Arg::with_name("fd").long("fd")
                                         .takes_value(true).required(true).help("IPC file descriptor"))
                                    .arg(Arg::with_name("channel-buffer-size").long("channel-buffer-size")
                                         .takes_value(true).required(false).help("Worker's channel buffer size")))
                        .get_matches();

  if let Some(matches) = matches.subcommand_matches("worker") {
    let fd  = matches.value_of("fd").expect("needs a file descriptor")
      .parse::<i32>().expect("the file descriptor must be a number");
    let id  = matches.value_of("id").expect("needs a worker id");
    let buffer_size = matches.value_of("channel-buffer-size")
      .and_then(|size| size.parse::<usize>().ok())
      .unwrap_or(10000);

    begin_worker_process(fd, id, buffer_size);
    return;
  }

  if let Some(matches) = matches.subcommand_matches("upgrade") {
    let fd  = matches.value_of("fd").expect("needs a file descriptor")
      .parse::<i32>().expect("the file descriptor must be a number");
    let buffer_size = matches.value_of("channel-buffer-size")
      .and_then(|size| size.parse::<usize>().ok())
      .unwrap_or(10000);

    begin_new_master_process(fd, buffer_size);
    return;
  }

  let submatches = matches.subcommand_matches("start").expect("unknown subcommand");
  let config_file = submatches.value_of("config").expect("required config file");

  if let Ok(config) = Config::load_from_path(config_file) {
    //FIXME: should have an id for the master too
    logging::setup("MASTER".to_string(), &config.log_level, &config.log_target);
    info!("starting up");

    metrics_set_up!(&config.metrics.address[..], config.metrics.port);
    gauge!("sozu.TEST", 42);

    if check_process_limits(config.clone()) {
      match start_workers(&config) {
        Ok(workers) => {
          info!("created workers: {:?}", workers);

          let handle_process_affinity = match config.handle_process_affinity {
            Some(val) => val,
            None => false
          };

          if cfg!(target_os = "linux") && handle_process_affinity {
            set_workers_affinity(&workers);
          }

          command::start(config, workers);
        },
        Err(e) => error!("Error while creating workers: {}", e)
      }
    }

    info!("master process stopped");
  } else {
    error!("could not load configuration file at '{}', stopping", config_file);
  }
}

/// Set workers process affinity, see man sched_setaffinity
/// Bind each worker (including the master) process to a CPU core.
/// Can bind multiple processes to a CPU core if there are more processes
/// than CPU cores. Only works on Linux.
#[cfg(target_os = "linux")]
fn set_workers_affinity(workers: &Vec<Worker>) {
  let mut cpu_count = 0;
  let max_cpu = num_cpus::get();

  // +1 for the master process that will also be bound to its CPU core
  if (workers.len() + 1) > max_cpu {
    warn!("There are more workers than available CPU cores, \
          multiple workers will be bound to the same CPU core. \
          This may impact performances");
  }

  let master_pid = unsafe { libc::getpid() };
  set_process_affinity(master_pid, cpu_count);
  cpu_count = cpu_count + 1;

  for ref worker in workers {
    if cpu_count >= max_cpu {
      cpu_count = 0;
    }

    set_process_affinity(worker.pid, cpu_count);

    cpu_count = cpu_count + 1;
  }
}

/// Set workers process affinity, see man sched_setaffinity
/// Bind each worker (including the master) process to a CPU core.
/// Can bind multiple processes to a CPU core if there are more processes
/// than CPU cores. Only works on Linux.
#[cfg(not(target_os = "linux"))]
fn set_workers_affinity(_: &Vec<Worker>) {
}

/// Set a specific process to run onto a specific CPU core
#[cfg(target_os = "linux")]
fn set_process_affinity(pid: pid_t, cpu: usize) {
  unsafe {
    let mut cpu_set: cpu_set_t = mem::zeroed();
    let size_cpu_set = mem::size_of::<cpu_set_t>();
    libc::CPU_SET(cpu, &mut cpu_set);
    libc::sched_setaffinity(pid, size_cpu_set, &mut cpu_set);

    debug!("Worker {} bound to CPU core {}", pid, cpu);
  };
}

#[cfg(target_os="linux")]
// We check the hard_limit. The soft_limit can be changed at runtime
// by the process or any user. hard_limit can only be changed by root
fn check_process_limits(config: Config) -> bool {
  let process_limits = procinfo::pid::limits_self()
    .expect("Couldn't read /proc/self/limits to determine max open file descriptors limit");

  // If limit is "unlimited"
  if process_limits.max_open_files.hard.is_none() {
    return true;
  }

  let hard_limit = process_limits.max_open_files.hard.unwrap();
  let http_max_cons = config.http.and_then(|proxy| Some(proxy.max_connections)).unwrap_or(0);
  let https_max_cons = config.https.and_then(|proxy| Some(proxy.max_connections)).unwrap_or(0);

  // check if all proxies are under the hard limit
  if http_max_cons > hard_limit || https_max_cons > hard_limit {
    error!("At least one proxy can't have that much of connections. \
            Current max file descriptor hard limit is: {}", hard_limit);
    return false;
  }

  let total_proxies_connections = http_max_cons + https_max_cons;
  let system_max_fd = procinfo::sys::fs::file_max::file_max()
    .expect("Couldn't read /proc/sys/fs/file-max") as usize;

  if total_proxies_connections > system_max_fd {
    error!("Proxies total max_connections can't be higher than system's file-max limit. \
            Current limit: {}, current value: {}", system_max_fd, total_proxies_connections);
    return false;
  }

  true
}

#[cfg(not(target_os = "linux"))]
fn check_process_limits(_: Config) -> bool {
  true
}
<commit_msg>Add `use std::mem` if target_os is linux. Fixes otherwise breaking linux build.<commit_after>#[macro_use] extern crate nom;
#[macro_use] extern crate clap;
#[macro_use] extern crate serde_derive;
extern crate mio;
extern crate mio_uds;
extern crate serde;
extern crate serde_json;
extern crate time;
extern crate libc;
extern crate slab;
extern crate rand;
extern crate nix;
#[macro_use] extern crate sozu_lib as sozu;
extern crate sozu_command_lib as sozu_command;

#[cfg(target_os = "linux")]
extern crate num_cpus;
#[cfg(target_os = "linux")]
extern crate procinfo;

mod command;
mod worker;
mod logging;
mod upgrade;

use std::net::ToSocketAddrs;
use std::env;
use sozu_command::config::Config;
use clap::{App,Arg,SubCommand};

#[cfg(target_os = "linux")]
use libc::{cpu_set_t,pid_t};

use command::Worker;
use worker::{begin_worker_process,start_workers};
use upgrade::begin_new_master_process;

fn main() {
  let matches = App::new("sozu")
                        .version(crate_version!())
                        .about("hot reconfigurable proxy")
                        .subcommand(SubCommand::with_name("start")
                                    .about("launch the master process")
                                    .arg(Arg::with_name("config")
                                        .short("c")
                                        .long("config")
                                        .value_name("FILE")
                                        .help("Sets a custom config file")
                                        .takes_value(true)
                                        .required(true)))
                        .subcommand(SubCommand::with_name("worker")
                                    .about("start a worker (internal command, should not be used directly)")
                                    .arg(Arg::with_name("id").long("id")
                                         .takes_value(true).required(true).help("worker identifier"))
                                    .arg(Arg::with_name("fd").long("fd")
                                         .takes_value(true).required(true).help("IPC file descriptor"))
                                    .arg(Arg::with_name("channel-buffer-size").long("channel-buffer-size")
                                         .takes_value(true).required(false).help("Worker's channel buffer size")))
                        .subcommand(SubCommand::with_name("upgrade")
                                    .about("start a new master process (internal command, should not be used directly)")
                                    .arg(Arg::with_name("fd").long("fd")
                                         .takes_value(true).required(true).help("IPC file descriptor"))
                                    .arg(Arg::with_name("channel-buffer-size").long("channel-buffer-size")
                                         .takes_value(true).required(false).help("Worker's channel buffer size")))
                        .get_matches();

  if let Some(matches) = matches.subcommand_matches("worker") {
    let fd  = matches.value_of("fd").expect("needs a file descriptor")
      .parse::<i32>().expect("the file descriptor must be a number");
    let id  = matches.value_of("id").expect("needs a worker id");
    let buffer_size = matches.value_of("channel-buffer-size")
      .and_then(|size| size.parse::<usize>().ok())
      .unwrap_or(10000);

    begin_worker_process(fd, id, buffer_size);
    return;
  }

  if let Some(matches) = matches.subcommand_matches("upgrade") {
    let fd  = matches.value_of("fd").expect("needs a file descriptor")
      .parse::<i32>().expect("the file descriptor must be a number");
    let buffer_size = matches.value_of("channel-buffer-size")
      .and_then(|size| size.parse::<usize>().ok())
      .unwrap_or(10000);

    begin_new_master_process(fd, buffer_size);
    return;
  }

  let submatches = matches.subcommand_matches("start").expect("unknown subcommand");
  let config_file = submatches.value_of("config").expect("required config file");

  if let Ok(config) = Config::load_from_path(config_file) {
    //FIXME: should have an id for the master too
    logging::setup("MASTER".to_string(), &config.log_level, &config.log_target);
    info!("starting up");

    metrics_set_up!(&config.metrics.address[..], config.metrics.port);
    gauge!("sozu.TEST", 42);

    if check_process_limits(config.clone()) {
      match start_workers(&config) {
        Ok(workers) => {
          info!("created workers: {:?}", workers);

          let handle_process_affinity = match config.handle_process_affinity {
            Some(val) => val,
            None => false
          };

          if cfg!(target_os = "linux") && handle_process_affinity {
            set_workers_affinity(&workers);
          }

          command::start(config, workers);
        },
        Err(e) => error!("Error while creating workers: {}", e)
      }
    }

    info!("master process stopped");
  } else {
    error!("could not load configuration file at '{}', stopping", config_file);
  }
}

/// Set workers process affinity, see man sched_setaffinity
/// Bind each worker (including the master) process to a CPU core.
/// Can bind multiple processes to a CPU core if there are more processes
/// than CPU cores. Only works on Linux.
#[cfg(target_os = "linux")]
fn set_workers_affinity(workers: &Vec<Worker>) {
  let mut cpu_count = 0;
  let max_cpu = num_cpus::get();

  // +1 for the master process that will also be bound to its CPU core
  if (workers.len() + 1) > max_cpu {
    warn!("There are more workers than available CPU cores, \
          multiple workers will be bound to the same CPU core. \
          This may impact performances");
  }

  let master_pid = unsafe { libc::getpid() };
  set_process_affinity(master_pid, cpu_count);
  cpu_count = cpu_count + 1;

  for ref worker in workers {
    if cpu_count >= max_cpu {
      cpu_count = 0;
    }

    set_process_affinity(worker.pid, cpu_count);

    cpu_count = cpu_count + 1;
  }
}

/// Set workers process affinity, see man sched_setaffinity
/// Bind each worker (including the master) process to a CPU core.
/// Can bind multiple processes to a CPU core if there are more processes
/// than CPU cores. Only works on Linux.
#[cfg(not(target_os = "linux"))]
fn set_workers_affinity(_: &Vec<Worker>) {
}

/// Set a specific process to run onto a specific CPU core
#[cfg(target_os = "linux")]
use std::mem;
#[cfg(target_os = "linux")]
fn set_process_affinity(pid: pid_t, cpu: usize) {
  unsafe {
    let mut cpu_set: cpu_set_t = mem::zeroed();
    let size_cpu_set = mem::size_of::<cpu_set_t>();
    libc::CPU_SET(cpu, &mut cpu_set);
    libc::sched_setaffinity(pid, size_cpu_set, &mut cpu_set);

    debug!("Worker {} bound to CPU core {}", pid, cpu);
  };
}

#[cfg(target_os="linux")]
// We check the hard_limit. The soft_limit can be changed at runtime
// by the process or any user. hard_limit can only be changed by root
fn check_process_limits(config: Config) -> bool {
  let process_limits = procinfo::pid::limits_self()
    .expect("Couldn't read /proc/self/limits to determine max open file descriptors limit");

  // If limit is "unlimited"
  if process_limits.max_open_files.hard.is_none() {
    return true;
  }

  let hard_limit = process_limits.max_open_files.hard.unwrap();
  let http_max_cons = config.http.and_then(|proxy| Some(proxy.max_connections)).unwrap_or(0);
  let https_max_cons = config.https.and_then(|proxy| Some(proxy.max_connections)).unwrap_or(0);

  // check if all proxies are under the hard limit
  if http_max_cons > hard_limit || https_max_cons > hard_limit {
    error!("At least one proxy can't have that much of connections. \
            Current max file descriptor hard limit is: {}", hard_limit);
    return false;
  }

  let total_proxies_connections = http_max_cons + https_max_cons;
  let system_max_fd = procinfo::sys::fs::file_max::file_max()
    .expect("Couldn't read /proc/sys/fs/file-max") as usize;

  if total_proxies_connections > system_max_fd {
    error!("Proxies total max_connections can't be higher than system's file-max limit. \
            Current limit: {}, current value: {}", system_max_fd, total_proxies_connections);
    return false;
  }

  true
}

#[cfg(not(target_os = "linux"))]
fn check_process_limits(_: Config) -> bool {
  true
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::env;
use std::fs as stdfs;
use std::ffi::OsString;
use std::path::PathBuf;

use common::ui::UI;
use hcore::crypto::CACHE_KEY_PATH_ENV_VAR;
use hcore::env as henv;
use hcore::fs;

use config;
use error::Result;

const ARTIFACT_PATH_ENVVAR: &'static str = "ARTIFACT_PATH";
const ORIGIN_ENVVAR: &'static str = "HAB_ORIGIN";
const STUDIO_CMD: &'static str = "hab-studio";
const STUDIO_CMD_ENVVAR: &'static str = "HAB_STUDIO_BINARY";
const STUDIO_PACKAGE_IDENT: &'static str = "core/hab-studio";

pub fn start(ui: &mut UI, args: Vec<OsString>) -> Result<()> {
    if henv::var(ORIGIN_ENVVAR).is_err() {
        let config = config::load()?;
        if let Some(default_origin) = config.origin {
            debug!("Setting default origin {} via CLI config", &default_origin);
            env::set_var("HAB_ORIGIN", default_origin);
        }
    }

    if henv::var(CACHE_KEY_PATH_ENV_VAR).is_err() {
        let path = fs::cache_key_path(None::<&str>);
        debug!("Setting {}={}", CACHE_KEY_PATH_ENV_VAR, path.display());
        env::set_var(CACHE_KEY_PATH_ENV_VAR, &path);
    };

    let artifact_path = match henv::var(ARTIFACT_PATH_ENVVAR) {
        Ok(p) => PathBuf::from(p),
        Err(_) => {
            let path = fs::cache_artifact_path(None::<&str>);
            debug!("Setting {}={}", ARTIFACT_PATH_ENVVAR, path.display());
            env::set_var(ARTIFACT_PATH_ENVVAR, &path);
            path
        }
    };
    if !artifact_path.is_dir() {
        debug!("Creating artifact_path at: {}", artifact_path.display());
        stdfs::create_dir_all(&artifact_path)?;
    }

    inner::start(ui, args)
}

#[cfg(target_os = "linux")]
mod inner {
    use std::env;
    use std::ffi::OsString;
    use std::path::PathBuf;
    use std::str::FromStr;

    use common::ui::UI;
    use hcore::crypto::{init, default_cache_key_path};
    use hcore::env as henv;
    use hcore::fs::{am_i_root, find_command};
    use hcore::os::process;
    use hcore::package::PackageIdent;

    use error::{Error, Result};
    use exec;
    use VERSION;

    const SUDO_CMD: &'static str = "sudo";

    pub fn start(ui: &mut UI, args: Vec<OsString>) -> Result<()> {
        rerun_with_sudo_if_needed(ui)?;

        let command = match henv::var(super::STUDIO_CMD_ENVVAR) {
            Ok(command) => PathBuf::from(command),
            Err(_) => {
                init();
                let version: Vec<&str> = VERSION.split("/").collect();
                let ident = PackageIdent::from_str(
                    &format!("{}/{}", super::STUDIO_PACKAGE_IDENT, version[0]),
                )?;
                exec::command_from_min_pkg(
                    ui,
                    super::STUDIO_CMD,
                    &ident,
                    &default_cache_key_path(None),
                    0,
                )?
            }
        };

        if let Some(cmd) = find_command(command.to_string_lossy().as_ref()) {
            Ok(process::become_command(cmd, args)?)
        } else {
            Err(Error::ExecCommandNotFound(command))
        }
    }

    fn rerun_with_sudo_if_needed(ui: &mut UI) -> Result<()> {
        // If I have root permissions, early return, we are done.
        if am_i_root() {
            return Ok(());
        }

        // Otherwise we will try to re-run this program using `sudo`
        match find_command(SUDO_CMD) {
            Some(sudo_prog) => {
                let mut args: Vec<OsString> = vec![
                    "-p".into(),
                    "[sudo hab-studio] password for %u: ".into(),
                    "-E".into(),
                ];
                args.append(&mut env::args_os().collect());
                Ok(process::become_command(sudo_prog, args)?)
            }
            None => {
                ui.warn(format!(
                    "Could not find the `{}' command, is it in your PATH?",
                    SUDO_CMD
                ))?;
                ui.warn(
                    "Running Habitat Studio requires root or administrator privileges. \
                              Please retry this command as a super user or use a \
                              privilege-granting facility such as sudo.",
                )?;
                ui.br()?;
                Err(Error::RootRequired)
            }
        }
    }
}

#[cfg(not(target_os = "linux"))]
mod inner {
    use std::env;
    use std::ffi::{OsStr, OsString};
    use std::path::{Path, PathBuf};
    use std::process::{Command, Stdio};
    use std::str::FromStr;

    use common::ui::UI;
    use hcore::crypto::{init, default_cache_key_path};
    use hcore::env as henv;
    use hcore::fs::{CACHE_ARTIFACT_PATH, CACHE_KEY_PATH, find_command};
    use hcore::os::process;
    use hcore::package::PackageIdent;

    use error::{Error, Result};
    use exec;
    use VERSION;

    const DOCKER_CMD: &'static str = "docker";
    const DOCKER_CMD_ENVVAR: &'static str = "HAB_DOCKER_BINARY";
    const DOCKER_IMAGE: &'static str = "habitat-docker-registry.bintray.io/studio";
    const DOCKER_IMAGE_ENVVAR: &'static str = "HAB_DOCKER_STUDIO_IMAGE";
    const DOCKER_OPTS_ENVVAR: &'static str = "HAB_DOCKER_OPTS";
    const DOCKER_SOCKET: &'static str = "/var/run/docker.sock";

    pub fn start(_ui: &mut UI, args: Vec<OsString>) -> Result<()> {
        if is_windows_studio(&args) {
            start_windows_studio(_ui, args)
        } else {
            start_docker_studio(_ui, args)
        }
    }

    pub fn start_windows_studio(ui: &mut UI, args: Vec<OsString>) -> Result<()> {
        let command = match henv::var(super::STUDIO_CMD_ENVVAR) {
            Ok(command) => PathBuf::from(command),
            Err(_) => {
                init();
                let version: Vec<&str> = VERSION.split("/").collect();
                let ident = PackageIdent::from_str(
                    &format!("{}/{}", super::STUDIO_PACKAGE_IDENT, version[0]),
                )?;
                exec::command_from_min_pkg(
                    ui,
                    super::STUDIO_CMD,
                    &ident,
                    &default_cache_key_path(None),
                    0,
                )?
            }
        };

        if let Some(cmd) = find_command(command.to_string_lossy().as_ref()) {
            process::become_command(cmd, args)?;
        } else {
            return Err(Error::ExecCommandNotFound(command));
        }
        Ok(())
    }

    pub fn start_docker_studio(_ui: &mut UI, args: Vec<OsString>) -> Result<()> {
        let docker_cmd = find_docker_cmd()?;

        if is_image_present(&docker_cmd) {
            debug!("Found Studio Docker image locally.");
        } else {
            debug!("Failed to find Studio Docker image locally.");
            pull_image(&docker_cmd)?;
        }

        let mut volumes = vec![
            format!("{}:/src", env::current_dir().unwrap().to_string_lossy()),
            format!(
                "{}:/{}",
                default_cache_key_path(None).to_string_lossy(),
                CACHE_KEY_PATH
            ),
        ];
        if let Ok(cache_artifact_path) = henv::var(super::ARTIFACT_PATH_ENVVAR) {
            volumes.push(format!("{}:/{}", cache_artifact_path, CACHE_ARTIFACT_PATH));
        }
        if Path::new(DOCKER_SOCKET).exists() {
            volumes.push(format!("{}:{}", DOCKER_SOCKET, DOCKER_SOCKET));
        }

        let env_vars = vec![
            "HAB_DEPOT_URL",
            "HAB_DEPOT_CHANNEL",
            "HAB_ORIGIN",
            "HAB_STUDIO_SUP",
            "HAB_UPDATE_STRATEGY_FREQUENCY_MS",
            "http_proxy",
            "https_proxy",
        ];

        check_mounts(&docker_cmd, volumes.iter())?;
        run_container(docker_cmd, args, volumes.iter(), env_vars.iter())
    }

    fn find_docker_cmd() -> Result<PathBuf> {
        let docker_cmd = henv::var(DOCKER_CMD_ENVVAR).unwrap_or(DOCKER_CMD.to_string());

        match find_command(&docker_cmd) {
            Some(docker_abs_path) => Ok(docker_abs_path),
            None => Err(Error::ExecCommandNotFound(docker_cmd.into())),
        }
    }

    fn is_image_present(docker_cmd: &Path) -> bool {
        let mut cmd = Command::new(docker_cmd);
        cmd.arg("images").arg(&image_identifier()).arg("-q");
        debug!("Running command: {:?}", cmd);
        let result = cmd.output().expect("Docker command failed to spawn");

        !String::from_utf8_lossy(&result.stdout).as_ref().is_empty()
    }

    fn pull_image(docker_cmd: &Path) -> Result<()> {
        let image = image_identifier();
        let mut cmd = Command::new(docker_cmd);
        cmd.arg("pull")
            .arg(&image)
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit());
        debug!("Running command: {:?}", cmd);
        let result = cmd.spawn()
            .expect("Docker command failed to spawn")
            .wait_with_output()
            .expect("Failed to wait on child process");

        if result.status.success() {
            debug!("Docker image '{}' is present locally.", &image);
        } else {
            debug!(
                "Pulling Docker image '{}' failed with exit code: {:?}",
                &image,
                result.status
            );

            let err_output = String::from_utf8_lossy(&result.stderr);

            if err_output.contains("image") && err_output.contains("not found") {
                return Err(Error::DockerImageNotFound(image_identifier().to_string()));
            } else if err_output.contains("Cannot connect to the Docker daemon") {
                return Err(Error::DockerDaemonDown);
            } else {
                return Err(Error::DockerNetworkDown(image_identifier().to_string()));
            }
        }

        Ok(())
    }

    /// Checks whether or not the volume mounts are working.
    ///
    /// We need to ensure that filesystem sharing has been enabled, otherwise the user will be
    /// greeted with a horrible error message that's difficult to make sense of. To mitigate this,
    /// we check the studio version. This will cause Docker to go through the mounting steps, so we
    /// can watch stderr for failure, but has the advantage of not requiring a TTY.
    fn check_mounts<I, S>(docker_cmd: &Path, volumes: I) -> Result<()>
    where
        I: IntoIterator<Item = S>,
        S: AsRef<OsStr>,
    {
        let mut volume_args: Vec<OsString> = Vec::new();
        for vol in volumes {
            volume_args.push("--volume".into());
            volume_args.push(vol.as_ref().into());
        }

        let version_output = Command::new(docker_cmd)
            .arg("run")
            .arg("--rm")
            .arg("--privileged")
            .args(volume_args)
            .arg(image_identifier())
            .arg("-V")
            .output()
            .expect("docker failed to start");

        let stderr = String::from_utf8(version_output.stderr).unwrap();
        if !stderr.is_empty() &&
            (stderr.as_str().contains("Mounts denied") ||
                 stderr.as_str().contains("drive is not shared"))
        {
            return Err(Error::DockerFileSharingNotEnabled);
        }
        Ok(())
    }

    fn run_container<I, J, S, T>(
        docker_cmd: PathBuf,
        args: Vec<OsString>,
        volumes: I,
        env_vars: J,
    ) -> Result<()>
    where
        I: IntoIterator<Item = S>,
        J: IntoIterator<Item = T>,
        S: AsRef<OsStr>,
        T: AsRef<str>,
    {
        let mut cmd_args: Vec<OsString> = vec![
            "run".into(),
            "--rm".into(),
            "--tty".into(),
            "--interactive".into(),
            "--privileged".into(),
        ];
        if let Ok(opts) = henv::var(DOCKER_OPTS_ENVVAR) {
            let opts = opts.split(" ")
                .map(|v| v.into())
                // Ensure we're not passing something like `--tty` again here.
                .filter(|v| !cmd_args.contains(v))
                .collect::<Vec<_>>();
            if !opts.is_empty() {
                debug!(
                    "Adding extra Docker options from {} = {:?}",
                    DOCKER_OPTS_ENVVAR,
                    opts
                );
                cmd_args.extend_from_slice(opts.as_slice());
            }
        }
        for var in env_vars {
            if let Ok(val) = henv::var(var.as_ref()) {
                debug!("Setting container env var: {:?}='{}'", var.as_ref(), val);
                cmd_args.push("--env".into());
                cmd_args.push(format!("{}={}", var.as_ref(), val).into());
            }
        }
        for vol in volumes {
            cmd_args.push("--volume".into());
            cmd_args.push(vol.as_ref().into());
        }
        cmd_args.push(image_identifier().into());
        cmd_args.extend_from_slice(args.as_slice());

        unset_proxy_env_vars();
        Ok(process::become_command(docker_cmd, cmd_args)?)
    }

    fn unset_proxy_env_vars() {
        for var in vec!["http_proxy", "https_proxy"] {
            if let Ok(_) = henv::var(var) {
                debug!("Unsetting process environment variable '{}'", var);
                env::remove_var(var);
            }
        }
    }

    /// Returns the Docker Studio image with tag for the desired version which corresponds to the
    /// same version (minus release) as this program.
    fn image_identifier() -> String {
        let version: Vec<&str> = VERSION.split("/").collect();
        henv::var(DOCKER_IMAGE_ENVVAR).unwrap_or(format!("{}:{}", DOCKER_IMAGE, version[0]))
    }

    fn is_windows_studio(args: &Vec<OsString>) -> bool {
        if cfg!(not(target_os = "windows")) {
            return false;
        }

        for arg in args.iter() {
            let str_arg = arg.to_string_lossy().to_lowercase();
            if str_arg == String::from("--windows") || str_arg == String::from("-w") {
                return true;
            }
        }

        return false;
    }

    #[cfg(test)]
    mod tests {
        use super::{image_identifier, DOCKER_IMAGE};
        use VERSION;

        #[test]
        fn retrieve_image_identifier() {
            assert_eq!(image_identifier(), format!("{}:{}", DOCKER_IMAGE, VERSION));
        }
    }
}
<commit_msg>mount docker.sock on windows host even if it is not a file<commit_after>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::env;
use std::fs as stdfs;
use std::ffi::OsString;
use std::path::PathBuf;

use common::ui::UI;
use hcore::crypto::CACHE_KEY_PATH_ENV_VAR;
use hcore::env as henv;
use hcore::fs;

use config;
use error::Result;

const ARTIFACT_PATH_ENVVAR: &'static str = "ARTIFACT_PATH";
const ORIGIN_ENVVAR: &'static str = "HAB_ORIGIN";
const STUDIO_CMD: &'static str = "hab-studio";
const STUDIO_CMD_ENVVAR: &'static str = "HAB_STUDIO_BINARY";
const STUDIO_PACKAGE_IDENT: &'static str = "core/hab-studio";

pub fn start(ui: &mut UI, args: Vec<OsString>) -> Result<()> {
    if henv::var(ORIGIN_ENVVAR).is_err() {
        let config = config::load()?;
        if let Some(default_origin) = config.origin {
            debug!("Setting default origin {} via CLI config", &default_origin);
            env::set_var("HAB_ORIGIN", default_origin);
        }
    }

    if henv::var(CACHE_KEY_PATH_ENV_VAR).is_err() {
        let path = fs::cache_key_path(None::<&str>);
        debug!("Setting {}={}", CACHE_KEY_PATH_ENV_VAR, path.display());
        env::set_var(CACHE_KEY_PATH_ENV_VAR, &path);
    };

    let artifact_path = match henv::var(ARTIFACT_PATH_ENVVAR) {
        Ok(p) => PathBuf::from(p),
        Err(_) => {
            let path = fs::cache_artifact_path(None::<&str>);
            debug!("Setting {}={}", ARTIFACT_PATH_ENVVAR, path.display());
            env::set_var(ARTIFACT_PATH_ENVVAR, &path);
            path
        }
    };
    if !artifact_path.is_dir() {
        debug!("Creating artifact_path at: {}", artifact_path.display());
        stdfs::create_dir_all(&artifact_path)?;
    }

    inner::start(ui, args)
}

#[cfg(target_os = "linux")]
mod inner {
    use std::env;
    use std::ffi::OsString;
    use std::path::PathBuf;
    use std::str::FromStr;

    use common::ui::UI;
    use hcore::crypto::{init, default_cache_key_path};
    use hcore::env as henv;
    use hcore::fs::{am_i_root, find_command};
    use hcore::os::process;
    use hcore::package::PackageIdent;

    use error::{Error, Result};
    use exec;
    use VERSION;

    const SUDO_CMD: &'static str = "sudo";

    pub fn start(ui: &mut UI, args: Vec<OsString>) -> Result<()> {
        rerun_with_sudo_if_needed(ui)?;

        let command = match henv::var(super::STUDIO_CMD_ENVVAR) {
            Ok(command) => PathBuf::from(command),
            Err(_) => {
                init();
                let version: Vec<&str> = VERSION.split("/").collect();
                let ident = PackageIdent::from_str(
                    &format!("{}/{}", super::STUDIO_PACKAGE_IDENT, version[0]),
                )?;
                exec::command_from_min_pkg(
                    ui,
                    super::STUDIO_CMD,
                    &ident,
                    &default_cache_key_path(None),
                    0,
                )?
            }
        };

        if let Some(cmd) = find_command(command.to_string_lossy().as_ref()) {
            Ok(process::become_command(cmd, args)?)
        } else {
            Err(Error::ExecCommandNotFound(command))
        }
    }

    fn rerun_with_sudo_if_needed(ui: &mut UI) -> Result<()> {
        // If I have root permissions, early return, we are done.
        if am_i_root() {
            return Ok(());
        }

        // Otherwise we will try to re-run this program using `sudo`
        match find_command(SUDO_CMD) {
            Some(sudo_prog) => {
                let mut args: Vec<OsString> = vec![
                    "-p".into(),
                    "[sudo hab-studio] password for %u: ".into(),
                    "-E".into(),
                ];
                args.append(&mut env::args_os().collect());
                Ok(process::become_command(sudo_prog, args)?)
            }
            None => {
                ui.warn(format!(
                    "Could not find the `{}' command, is it in your PATH?",
                    SUDO_CMD
                ))?;
                ui.warn(
                    "Running Habitat Studio requires root or administrator privileges. \
                              Please retry this command as a super user or use a \
                              privilege-granting facility such as sudo.",
                )?;
                ui.br()?;
                Err(Error::RootRequired)
            }
        }
    }
}

#[cfg(not(target_os = "linux"))]
mod inner {
    use std::env;
    use std::ffi::{OsStr, OsString};
    use std::path::{Path, PathBuf};
    use std::process::{Command, Stdio};
    use std::str::FromStr;

    use common::ui::UI;
    use hcore::crypto::{init, default_cache_key_path};
    use hcore::env as henv;
    use hcore::fs::{CACHE_ARTIFACT_PATH, CACHE_KEY_PATH, find_command};
    use hcore::os::process;
    use hcore::package::PackageIdent;

    use error::{Error, Result};
    use exec;
    use VERSION;

    const DOCKER_CMD: &'static str = "docker";
    const DOCKER_CMD_ENVVAR: &'static str = "HAB_DOCKER_BINARY";
    const DOCKER_IMAGE: &'static str = "habitat-docker-registry.bintray.io/studio";
    const DOCKER_IMAGE_ENVVAR: &'static str = "HAB_DOCKER_STUDIO_IMAGE";
    const DOCKER_OPTS_ENVVAR: &'static str = "HAB_DOCKER_OPTS";
    const DOCKER_SOCKET: &'static str = "/var/run/docker.sock";

    pub fn start(_ui: &mut UI, args: Vec<OsString>) -> Result<()> {
        if is_windows_studio(&args) {
            start_windows_studio(_ui, args)
        } else {
            start_docker_studio(_ui, args)
        }
    }

    pub fn start_windows_studio(ui: &mut UI, args: Vec<OsString>) -> Result<()> {
        let command = match henv::var(super::STUDIO_CMD_ENVVAR) {
            Ok(command) => PathBuf::from(command),
            Err(_) => {
                init();
                let version: Vec<&str> = VERSION.split("/").collect();
                let ident = PackageIdent::from_str(
                    &format!("{}/{}", super::STUDIO_PACKAGE_IDENT, version[0]),
                )?;
                exec::command_from_min_pkg(
                    ui,
                    super::STUDIO_CMD,
                    &ident,
                    &default_cache_key_path(None),
                    0,
                )?
            }
        };

        if let Some(cmd) = find_command(command.to_string_lossy().as_ref()) {
            process::become_command(cmd, args)?;
        } else {
            return Err(Error::ExecCommandNotFound(command));
        }
        Ok(())
    }

    pub fn start_docker_studio(_ui: &mut UI, args: Vec<OsString>) -> Result<()> {
        let docker_cmd = find_docker_cmd()?;

        if is_image_present(&docker_cmd) {
            debug!("Found Studio Docker image locally.");
        } else {
            debug!("Failed to find Studio Docker image locally.");
            pull_image(&docker_cmd)?;
        }

        let mut volumes = vec![
            format!("{}:/src", env::current_dir().unwrap().to_string_lossy()),
            format!(
                "{}:/{}",
                default_cache_key_path(None).to_string_lossy(),
                CACHE_KEY_PATH
            ),
        ];
        if let Ok(cache_artifact_path) = henv::var(super::ARTIFACT_PATH_ENVVAR) {
            volumes.push(format!("{}:/{}", cache_artifact_path, CACHE_ARTIFACT_PATH));
        }
        if Path::new(DOCKER_SOCKET).exists() || cfg!(target_os = "windows") {
            volumes.push(format!("{}:{}", DOCKER_SOCKET, DOCKER_SOCKET));
        }

        let env_vars = vec![
            "HAB_DEPOT_URL",
            "HAB_DEPOT_CHANNEL",
            "HAB_ORIGIN",
            "HAB_STUDIO_SUP",
            "HAB_UPDATE_STRATEGY_FREQUENCY_MS",
            "http_proxy",
            "https_proxy",
        ];

        check_mounts(&docker_cmd, volumes.iter())?;
        run_container(docker_cmd, args, volumes.iter(), env_vars.iter())
    }

    fn find_docker_cmd() -> Result<PathBuf> {
        let docker_cmd = henv::var(DOCKER_CMD_ENVVAR).unwrap_or(DOCKER_CMD.to_string());

        match find_command(&docker_cmd) {
            Some(docker_abs_path) => Ok(docker_abs_path),
            None => Err(Error::ExecCommandNotFound(docker_cmd.into())),
        }
    }

    fn is_image_present(docker_cmd: &Path) -> bool {
        let mut cmd = Command::new(docker_cmd);
        cmd.arg("images").arg(&image_identifier()).arg("-q");
        debug!("Running command: {:?}", cmd);
        let result = cmd.output().expect("Docker command failed to spawn");

        !String::from_utf8_lossy(&result.stdout).as_ref().is_empty()
    }

    fn pull_image(docker_cmd: &Path) -> Result<()> {
        let image = image_identifier();
        let mut cmd = Command::new(docker_cmd);
        cmd.arg("pull")
            .arg(&image)
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit());
        debug!("Running command: {:?}", cmd);
        let result = cmd.spawn()
            .expect("Docker command failed to spawn")
            .wait_with_output()
            .expect("Failed to wait on child process");

        if result.status.success() {
            debug!("Docker image '{}' is present locally.", &image);
        } else {
            debug!(
                "Pulling Docker image '{}' failed with exit code: {:?}",
                &image,
                result.status
            );

            let err_output = String::from_utf8_lossy(&result.stderr);

            if err_output.contains("image") && err_output.contains("not found") {
                return Err(Error::DockerImageNotFound(image_identifier().to_string()));
            } else if err_output.contains("Cannot connect to the Docker daemon") {
                return Err(Error::DockerDaemonDown);
            } else {
                return Err(Error::DockerNetworkDown(image_identifier().to_string()));
            }
        }

        Ok(())
    }

    /// Checks whether or not the volume mounts are working.
    ///
    /// We need to ensure that filesystem sharing has been enabled, otherwise the user will be
    /// greeted with a horrible error message that's difficult to make sense of. To mitigate this,
    /// we check the studio version. This will cause Docker to go through the mounting steps, so we
    /// can watch stderr for failure, but has the advantage of not requiring a TTY.
    fn check_mounts<I, S>(docker_cmd: &Path, volumes: I) -> Result<()>
    where
        I: IntoIterator<Item = S>,
        S: AsRef<OsStr>,
    {
        let mut volume_args: Vec<OsString> = Vec::new();
        for vol in volumes {
            volume_args.push("--volume".into());
            volume_args.push(vol.as_ref().into());
        }

        let version_output = Command::new(docker_cmd)
            .arg("run")
            .arg("--rm")
            .arg("--privileged")
            .args(volume_args)
            .arg(image_identifier())
            .arg("-V")
            .output()
            .expect("docker failed to start");

        let stderr = String::from_utf8(version_output.stderr).unwrap();
        if !stderr.is_empty() &&
            (stderr.as_str().contains("Mounts denied") ||
                 stderr.as_str().contains("drive is not shared"))
        {
            return Err(Error::DockerFileSharingNotEnabled);
        }
        Ok(())
    }

    fn run_container<I, J, S, T>(
        docker_cmd: PathBuf,
        args: Vec<OsString>,
        volumes: I,
        env_vars: J,
    ) -> Result<()>
    where
        I: IntoIterator<Item = S>,
        J: IntoIterator<Item = T>,
        S: AsRef<OsStr>,
        T: AsRef<str>,
    {
        let mut cmd_args: Vec<OsString> = vec![
            "run".into(),
            "--rm".into(),
            "--tty".into(),
            "--interactive".into(),
            "--privileged".into(),
        ];
        if let Ok(opts) = henv::var(DOCKER_OPTS_ENVVAR) {
            let opts = opts.split(" ")
                .map(|v| v.into())
                // Ensure we're not passing something like `--tty` again here.
                .filter(|v| !cmd_args.contains(v))
                .collect::<Vec<_>>();
            if !opts.is_empty() {
                debug!(
                    "Adding extra Docker options from {} = {:?}",
                    DOCKER_OPTS_ENVVAR,
                    opts
                );
                cmd_args.extend_from_slice(opts.as_slice());
            }
        }
        for var in env_vars {
            if let Ok(val) = henv::var(var.as_ref()) {
                debug!("Setting container env var: {:?}='{}'", var.as_ref(), val);
                cmd_args.push("--env".into());
                cmd_args.push(format!("{}={}", var.as_ref(), val).into());
            }
        }
        for vol in volumes {
            cmd_args.push("--volume".into());
            cmd_args.push(vol.as_ref().into());
        }
        cmd_args.push(image_identifier().into());
        cmd_args.extend_from_slice(args.as_slice());

        unset_proxy_env_vars();
        Ok(process::become_command(docker_cmd, cmd_args)?)
    }

    fn unset_proxy_env_vars() {
        for var in vec!["http_proxy", "https_proxy"] {
            if let Ok(_) = henv::var(var) {
                debug!("Unsetting process environment variable '{}'", var);
                env::remove_var(var);
            }
        }
    }

    /// Returns the Docker Studio image with tag for the desired version which corresponds to the
    /// same version (minus release) as this program.
    fn image_identifier() -> String {
        let version: Vec<&str> = VERSION.split("/").collect();
        henv::var(DOCKER_IMAGE_ENVVAR).unwrap_or(format!("{}:{}", DOCKER_IMAGE, version[0]))
    }

    fn is_windows_studio(args: &Vec<OsString>) -> bool {
        if cfg!(not(target_os = "windows")) {
            return false;
        }

        for arg in args.iter() {
            let str_arg = arg.to_string_lossy().to_lowercase();
            if str_arg == String::from("--windows") || str_arg == String::from("-w") {
                return true;
            }
        }

        return false;
    }

    #[cfg(test)]
    mod tests {
        use super::{image_identifier, DOCKER_IMAGE};
        use VERSION;

        #[test]
        fn retrieve_image_identifier() {
            assert_eq!(image_identifier(), format!("{}:{}", DOCKER_IMAGE, VERSION));
        }
    }
}
<|endoftext|>


<commit_before>//! Test rendering of primitive effects, through the Dispatch interface.

extern crate jagged_array;
extern crate libfriendship;
#[macro_use] extern crate ndarray;
extern crate url;

use std::sync::mpsc::{channel, Receiver, Sender};

use jagged_array::Jagged2Builder;
use ndarray::Array2;
use url::Url;

use libfriendship::{Dispatch, Client};
use libfriendship::dispatch::{OscRouteGraph, OscRenderer};
use libfriendship::render::RefRenderer;
use libfriendship::routing::{Edge, EdgeWeight, EffectId, NodeHandle};
use libfriendship::util::pack_f32;

struct MyClient {
    /// Where to send the rendered audio.
    tx: Sender<Array2<f32>>,
}
impl Client for MyClient {
    fn audio_rendered(&mut self, buffer: Array2<f32>, _idx: u64) {
        self.tx.send(buffer).unwrap();
    }
}

fn test_setup() -> (Dispatch<RefRenderer, MyClient>, Receiver<Array2<f32>>) {
    let (tx, rx) = channel();
    let dispatch = Dispatch::new(RefRenderer::default(), MyClient{ tx });
    (dispatch, rx)
}

/// Return the `EffectId` that universally represents `Delay` nodes.
fn delay_id() -> EffectId {
    EffectId::new("Delay".into(), None, vec![Url::parse("primitive:///Delay").unwrap()])
}

/// Return the `EffectId` that universally represents `F32Constant` nodes.
fn const_id() -> EffectId {
    EffectId::new("F32Constant".into(), None, vec![Url::parse("primitive:///F32Constant").unwrap()])
}

#[test]
fn render_passthrough() {
    let (mut dispatch, rx) = test_setup();

    // Add an edge from input to output
    dispatch.dispatch(OscRouteGraph::AddEdge((),
        (Edge::new_to_null(NodeHandle::toplevel(), EdgeWeight::new(0, 0)),)
    ).into()).unwrap();

    // Read some data from ch=0.
    let mut builder = Jagged2Builder::new();
    builder.extend(&[1f32, 2f32, 3f32, 4f32]);
    dispatch.dispatch(
        OscRenderer::RenderRange((), (0..4, 1, builder.into()))
    .into()).unwrap();
    let rendered = rx.recv().unwrap();
    assert_eq!(rendered, array![[1f32, 2f32, 3f32, 4f32]]);

    // Read some more data
    let mut builder = Jagged2Builder::new();
    builder.extend(&[0f32, 1f32, 2f32]);
    dispatch.dispatch(
        OscRenderer::RenderRange((), (4..8, 1, builder.into()))
    .into()).unwrap();
    let rendered = rx.recv().unwrap();
    // empty inputs are implicitly 0.
    assert_eq!(rendered, array![[0f32, 1f32, 2f32, 0f32]]);

    // Seek to zero and render more
    dispatch.dispatch(
        OscRenderer::RenderRange((), (0..4, 1, Default::default()))
    .into()).unwrap();
    // Seeking implicitly zeros the inputs
    let rendered = rx.recv().unwrap();
    assert_eq!(rendered, array![[0f32, 0f32, 0f32, 0f32]]);
}

<commit_msg>More ext input tests.<commit_after>//! Test rendering of primitive effects, through the Dispatch interface.

extern crate jagged_array;
extern crate libfriendship;
#[macro_use] extern crate ndarray;
extern crate url;

use std::sync::mpsc::{channel, Receiver, Sender};

use jagged_array::Jagged2Builder;
use ndarray::Array2;
use url::Url;

use libfriendship::{Dispatch, Client};
use libfriendship::dispatch::{OscRouteGraph, OscRenderer};
use libfriendship::render::RefRenderer;
use libfriendship::routing::{Edge, EdgeWeight, EffectId, NodeHandle};
use libfriendship::util::pack_f32;

struct MyClient {
    /// Where to send the rendered audio.
    tx: Sender<Array2<f32>>,
}
impl Client for MyClient {
    fn audio_rendered(&mut self, buffer: Array2<f32>, _idx: u64) {
        self.tx.send(buffer).unwrap();
    }
}

fn test_setup() -> (Dispatch<RefRenderer, MyClient>, Receiver<Array2<f32>>) {
    let (tx, rx) = channel();
    let dispatch = Dispatch::new(RefRenderer::default(), MyClient{ tx });
    (dispatch, rx)
}

/// Return the `EffectId` that universally represents `Delay` nodes.
fn delay_id() -> EffectId {
    EffectId::new("Delay".into(), None, vec![Url::parse("primitive:///Delay").unwrap()])
}

/// Return the `EffectId` that universally represents `F32Constant` nodes.
fn const_id() -> EffectId {
    EffectId::new("F32Constant".into(), None, vec![Url::parse("primitive:///F32Constant").unwrap()])
}

#[test]
fn render_passthrough() {
    let (mut dispatch, rx) = test_setup();

    // Add an edge from input to output
    dispatch.dispatch(OscRouteGraph::AddEdge((),
        (Edge::new_to_null(NodeHandle::toplevel(), EdgeWeight::new(0, 0)),)
    ).into()).unwrap();

    // Read some data from ch=0.
    let mut builder = Jagged2Builder::new();
    builder.extend(&[1f32, 2f32, 3f32, 4f32]);
    dispatch.dispatch(
        OscRenderer::RenderRange((), (0..4, 1, builder.into()))
    .into()).unwrap();
    let rendered = rx.recv().unwrap();
    assert_eq!(rendered, array![[1f32, 2f32, 3f32, 4f32]]);

    // Read some more data
    let mut builder = Jagged2Builder::new();
    builder.extend(&[0f32, 1f32, 2f32]);
    dispatch.dispatch(
        OscRenderer::RenderRange((), (4..8, 1, builder.into()))
    .into()).unwrap();
    let rendered = rx.recv().unwrap();
    // empty inputs are implicitly 0.
    assert_eq!(rendered, array![[0f32, 1f32, 2f32, 0f32]]);

    // Seek to zero and render more
    dispatch.dispatch(
        OscRenderer::RenderRange((), (0..4, 1, Default::default()))
    .into()).unwrap();
    // Seeking implicitly zeros the inputs
    let rendered = rx.recv().unwrap();
    assert_eq!(rendered, array![[0f32, 0f32, 0f32, 0f32]]);
}

#[test]
fn render_delay() {
    let (mut dispatch, rx) = test_setup();

    // Create delay node (id=1)
    let delay_hnd = NodeHandle::new(1);
    dispatch.dispatch(OscRouteGraph::AddNode( (), (delay_hnd, delay_id()) ).into()).unwrap();
    // Connect delay output to master output.
    dispatch.dispatch(OscRouteGraph::AddEdge((), (Edge::new_to_null(delay_hnd, EdgeWeight::new(0, 0)),)).into()).unwrap();

    // Add an edge from input to output
    dispatch.dispatch(OscRouteGraph::AddEdge((),
        (Edge::new_from_null(delay_hnd, EdgeWeight::new(0, 0)),)
    ).into()).unwrap();

    // Read some data from ch=0.
    let mut builder = Jagged2Builder::new();
    builder.extend(&[1f32, 2f32, 3f32, 4f32]);
    dispatch.dispatch(
        OscRenderer::RenderRange((), (0..4, 1, builder.into()))
    .into()).unwrap();
    let rendered = rx.recv().unwrap();
    assert_eq!(rendered, array![[1f32, 2f32, 3f32, 4f32]]);

    // Now delay by 1 frame.
    // Create Constant node (id=2)
    let const_hnd = NodeHandle::new(2);
    dispatch.dispatch(OscRouteGraph::AddNode( (), (const_hnd, const_id()) ).into()).unwrap();
    // Route constant output to delay input
    dispatch.dispatch(OscRouteGraph::AddEdge((), (Edge::new(const_hnd, delay_hnd, EdgeWeight::new(pack_f32(1f32), 1)),)).into()).unwrap();
    // Read some data from ch=0.
    let mut builder = Jagged2Builder::new();
    builder.extend(&[1f32, 2f32, 3f32, 4f32]);
    dispatch.dispatch(
        OscRenderer::RenderRange((), (4..8, 1, builder.into()))
    .into()).unwrap();
    let rendered = rx.recv().unwrap();
    assert_eq!(rendered, array![[4f32, 1f32, 2f32, 3f32]]);
}

<|endoftext|>


<commit_before>use eyre::Result;
use stencila_schema::*;

/// Encode a `Node` to plain, unstructured text
///
/// This is an an intentionally lossy encoding for when a
/// plain text encoding of a node is needed. The only structure
/// is adds is placing two newlines after each `BlockContent`
/// node. e.g. paragraphs, code blocks
pub fn encode(node: &Node) -> Result<String> {
    Ok(node.to_txt())
}

/// A trait to encode a `Node` as plain text
///
/// Made public for other modules in this crate.
/// Not implemented for all node types. Feel free
/// to add them!
pub trait ToTxt {
    fn to_txt(&self) -> String;
}

macro_rules! slice_to_txt {
    ($type:ty) => {
        impl ToTxt for $type {
            fn to_txt(&self) -> String {
                self.iter()
                    .map(|item| item.to_txt())
                    .collect::<Vec<String>>()
                    .concat()
            }
        }
    };
}
slice_to_txt!([Node]);
slice_to_txt!([InlineContent]);
slice_to_txt!([BlockContent]);

macro_rules! inline_content_to_txt {
    ($type:ty) => {
        impl ToTxt for $type {
            fn to_txt(&self) -> String {
                self.content.to_txt()
            }
        }
    };
}

inline_content_to_txt!(Delete);
inline_content_to_txt!(Emphasis);
inline_content_to_txt!(Link);
inline_content_to_txt!(NontextualAnnotation);
inline_content_to_txt!(Note);
inline_content_to_txt!(Quote);
inline_content_to_txt!(Strong);
inline_content_to_txt!(Subscript);
inline_content_to_txt!(Superscript);

macro_rules! block_content_to_txt {
    ($type:ty) => {
        impl ToTxt for $type {
            fn to_txt(&self) -> String {
                [&self.content.to_txt(), "\n\n"].concat()
            }
        }
    };
}

block_content_to_txt!(ClaimSimple);
block_content_to_txt!(Paragraph);
block_content_to_txt!(Heading);
block_content_to_txt!(QuoteBlock);

macro_rules! inline_text_to_txt {
    ($type:ty) => {
        impl ToTxt for $type {
            fn to_txt(&self) -> String {
                self.text.to_string()
            }
        }
    };
}

inline_text_to_txt!(CodeFragment);
inline_text_to_txt!(MathFragment);

macro_rules! block_text_to_txt {
    ($type:ty) => {
        impl ToTxt for $type {
            fn to_txt(&self) -> String {
                [&self.text.to_string(), "\n\n"].concat()
            }
        }
    };
}

block_text_to_txt!(CodeBlock);
block_text_to_txt!(MathBlock);

macro_rules! optional_content_to_txt {
    ($type:ty) => {
        impl ToTxt for $type {
            fn to_txt(&self) -> String {
                match &self.content {
                    Some(content) => content.to_txt(),
                    None => "".to_string(),
                }
            }
        }
    };
}

optional_content_to_txt!(Article);
optional_content_to_txt!(Cite);

/// Encode a `Node` to plain text
impl ToTxt for Node {
    fn to_txt(&self) -> String {
        match self {
            Node::Article(node) => node.to_txt(),
            Node::Boolean(node) => node.to_string(),
            Node::Cite(node) => node.to_txt(),
            Node::CodeBlock(node) => node.to_txt(),
            Node::CodeFragment(node) => node.to_txt(),
            Node::Delete(node) => node.to_txt(),
            Node::Emphasis(node) => node.to_txt(),
            Node::Heading(node) => node.to_txt(),
            Node::Integer(node) => node.to_string(),
            Node::Link(node) => node.to_txt(),
            Node::NontextualAnnotation(node) => node.to_txt(),
            Node::Note(node) => node.to_txt(),
            Node::Null => "null".to_string(),
            Node::Number(node) => node.to_string(),
            Node::Paragraph(node) => node.to_txt(),
            Node::Quote(node) => node.to_txt(),
            Node::QuoteBlock(node) => node.to_txt(),
            Node::String(node) => node.to_string(),
            Node::Strong(node) => node.to_txt(),
            Node::Subscript(node) => node.to_txt(),
            Node::Superscript(node) => node.to_txt(),
            _ => "".to_string(),
        }
    }
}

impl ToTxt for InlineContent {
    fn to_txt(&self) -> String {
        match self {
            InlineContent::Boolean(node) => node.to_string(),
            InlineContent::Cite(node) => node.to_txt(),
            InlineContent::CodeFragment(node) => node.to_txt(),
            InlineContent::Delete(node) => node.to_txt(),
            InlineContent::Emphasis(node) => node.to_txt(),
            InlineContent::Integer(node) => node.to_string(),
            InlineContent::Link(node) => node.to_txt(),
            InlineContent::NontextualAnnotation(node) => node.to_txt(),
            InlineContent::Note(node) => node.to_txt(),
            InlineContent::Null => "null".to_string(),
            InlineContent::Number(node) => node.to_string(),
            InlineContent::MathFragment(node) => node.to_txt(),
            InlineContent::Quote(node) => node.to_txt(),
            InlineContent::String(node) => node.to_string(),
            InlineContent::Strong(node) => node.to_txt(),
            InlineContent::Subscript(node) => node.to_txt(),
            InlineContent::Superscript(node) => node.to_txt(),
            _ => "".to_string(),
        }
    }
}

impl ToTxt for BlockContent {
    fn to_txt(&self) -> String {
        match self {
            BlockContent::Claim(node) => node.to_txt(),
            BlockContent::CodeBlock(node) => node.to_txt(),
            BlockContent::Heading(node) => node.to_txt(),
            BlockContent::MathBlock(node) => node.to_txt(),
            BlockContent::Paragraph(node) => node.to_txt(),
            BlockContent::QuoteBlock(node) => node.to_txt(),
            _ => "".to_string(),
        }
    }
}

impl ToTxt for ThingDescription {
    fn to_txt(&self) -> String {
        match self {
            ThingDescription::String(string) => string.to_string(),
            ThingDescription::VecInlineContent(inlines) => inlines.to_txt(),
            ThingDescription::VecBlockContent(blocks) => blocks.to_txt(),
        }
    }
}
<commit_msg>fix(Plain text): Trim whitespace<commit_after>use eyre::Result;
use stencila_schema::*;

/// Encode a `Node` to plain, unstructured text
///
/// This is an an intentionally lossy encoding for when a
/// plain text encoding of a node is needed. The only structure
/// is adds is placing two newlines after each `BlockContent`
/// node. e.g. paragraphs, code blocks
pub fn encode(node: &Node) -> Result<String> {
    Ok(node.to_txt().trim().to_string())
}

/// A trait to encode a `Node` as plain text
///
/// Made public for other modules in this crate.
/// Not implemented for all node types. Feel free
/// to add them!
pub trait ToTxt {
    fn to_txt(&self) -> String;
}

macro_rules! slice_to_txt {
    ($type:ty) => {
        impl ToTxt for $type {
            fn to_txt(&self) -> String {
                self.iter()
                    .map(|item| item.to_txt())
                    .collect::<Vec<String>>()
                    .concat()
            }
        }
    };
}
slice_to_txt!([Node]);
slice_to_txt!([InlineContent]);
slice_to_txt!([BlockContent]);

macro_rules! inline_content_to_txt {
    ($type:ty) => {
        impl ToTxt for $type {
            fn to_txt(&self) -> String {
                self.content.to_txt()
            }
        }
    };
}

inline_content_to_txt!(Delete);
inline_content_to_txt!(Emphasis);
inline_content_to_txt!(Link);
inline_content_to_txt!(NontextualAnnotation);
inline_content_to_txt!(Note);
inline_content_to_txt!(Quote);
inline_content_to_txt!(Strong);
inline_content_to_txt!(Subscript);
inline_content_to_txt!(Superscript);

macro_rules! block_content_to_txt {
    ($type:ty) => {
        impl ToTxt for $type {
            fn to_txt(&self) -> String {
                [&self.content.to_txt(), "\n\n"].concat()
            }
        }
    };
}

block_content_to_txt!(ClaimSimple);
block_content_to_txt!(Paragraph);
block_content_to_txt!(Heading);
block_content_to_txt!(QuoteBlock);

macro_rules! inline_text_to_txt {
    ($type:ty) => {
        impl ToTxt for $type {
            fn to_txt(&self) -> String {
                self.text.to_string()
            }
        }
    };
}

inline_text_to_txt!(CodeFragment);
inline_text_to_txt!(MathFragment);

macro_rules! block_text_to_txt {
    ($type:ty) => {
        impl ToTxt for $type {
            fn to_txt(&self) -> String {
                [&self.text.to_string(), "\n\n"].concat()
            }
        }
    };
}

block_text_to_txt!(CodeBlock);
block_text_to_txt!(MathBlock);

macro_rules! optional_content_to_txt {
    ($type:ty) => {
        impl ToTxt for $type {
            fn to_txt(&self) -> String {
                match &self.content {
                    Some(content) => content.to_txt(),
                    None => "".to_string(),
                }
            }
        }
    };
}

optional_content_to_txt!(Article);
optional_content_to_txt!(Cite);

/// Encode a `Node` to plain text
impl ToTxt for Node {
    fn to_txt(&self) -> String {
        match self {
            Node::Article(node) => node.to_txt(),
            Node::Boolean(node) => node.to_string(),
            Node::Cite(node) => node.to_txt(),
            Node::CodeBlock(node) => node.to_txt(),
            Node::CodeFragment(node) => node.to_txt(),
            Node::Delete(node) => node.to_txt(),
            Node::Emphasis(node) => node.to_txt(),
            Node::Heading(node) => node.to_txt(),
            Node::Integer(node) => node.to_string(),
            Node::Link(node) => node.to_txt(),
            Node::NontextualAnnotation(node) => node.to_txt(),
            Node::Note(node) => node.to_txt(),
            Node::Null => "null".to_string(),
            Node::Number(node) => node.to_string(),
            Node::Paragraph(node) => node.to_txt(),
            Node::Quote(node) => node.to_txt(),
            Node::QuoteBlock(node) => node.to_txt(),
            Node::String(node) => node.to_string(),
            Node::Strong(node) => node.to_txt(),
            Node::Subscript(node) => node.to_txt(),
            Node::Superscript(node) => node.to_txt(),
            _ => "".to_string(),
        }
    }
}

impl ToTxt for InlineContent {
    fn to_txt(&self) -> String {
        match self {
            InlineContent::Boolean(node) => node.to_string(),
            InlineContent::Cite(node) => node.to_txt(),
            InlineContent::CodeFragment(node) => node.to_txt(),
            InlineContent::Delete(node) => node.to_txt(),
            InlineContent::Emphasis(node) => node.to_txt(),
            InlineContent::Integer(node) => node.to_string(),
            InlineContent::Link(node) => node.to_txt(),
            InlineContent::NontextualAnnotation(node) => node.to_txt(),
            InlineContent::Note(node) => node.to_txt(),
            InlineContent::Null => "null".to_string(),
            InlineContent::Number(node) => node.to_string(),
            InlineContent::MathFragment(node) => node.to_txt(),
            InlineContent::Quote(node) => node.to_txt(),
            InlineContent::String(node) => node.to_string(),
            InlineContent::Strong(node) => node.to_txt(),
            InlineContent::Subscript(node) => node.to_txt(),
            InlineContent::Superscript(node) => node.to_txt(),
            _ => "".to_string(),
        }
    }
}

impl ToTxt for BlockContent {
    fn to_txt(&self) -> String {
        match self {
            BlockContent::Claim(node) => node.to_txt(),
            BlockContent::CodeBlock(node) => node.to_txt(),
            BlockContent::Heading(node) => node.to_txt(),
            BlockContent::MathBlock(node) => node.to_txt(),
            BlockContent::Paragraph(node) => node.to_txt(),
            BlockContent::QuoteBlock(node) => node.to_txt(),
            _ => "".to_string(),
        }
    }
}

impl ToTxt for ThingDescription {
    fn to_txt(&self) -> String {
        match self {
            ThingDescription::String(string) => string.to_string(),
            ThingDescription::VecInlineContent(inlines) => inlines.to_txt(),
            ThingDescription::VecBlockContent(blocks) => blocks.to_txt(),
        }
    }
}
<|endoftext|>


<commit_before>extern crate gcc;

use std::path::Path;
use std::process::Command;
use std::fs::File;
use std::io::{Read,Write};

fn main() {
    if !Path::new("jsonnet/.git").exists() {
        let _ = Command::new("git")
            .args(&["submodule", "update", "--init"])
            .status();
    }

    let dir = Path::new("jsonnet");

    let embedded = [
        "std",
    ];
    for f in &embedded {
        let output = dir.join("core").join(format!("{}.jsonnet.h", f));
        if output.exists() {
            continue;
        }
        let input = dir.join("stdlib").join(format!("{}.jsonnet", f));
        println!("embedding: {:?} -> {:?}", input, output);
        let in_f = File::open(input).unwrap();
        let mut out_f = File::create(&output).unwrap();
        for b in in_f.bytes() {
            write!(&mut out_f, "{},", b.unwrap()).unwrap();
        }
        writeln!(&mut out_f, "0").unwrap();
    }

    let jsonnet_core = [
        "desugarer.cpp",
        "formatter.cpp",
        "lexer.cpp",
        "libjsonnet.cpp",
        "parser.cpp",
        "pass.cpp",
        "static_analysis.cpp",
        "string_utils.cpp",
        "vm.cpp",
    ];

    let mut c = gcc::Config::new();
    c.cpp(true)
        .flag("-std=c++0x")
        .include(dir.join("include"))
        .include(dir.join("third_party/md5"));

    for f in &jsonnet_core {
        c.file(dir.join("core").join(f));
    }

    c.file(dir.join("third_party/md5/md5.cpp"));

    c.compile("libjsonnet.a")
}
<commit_msg>Rename deprecated `gcc::Config` -> `gcc::Build`<commit_after>extern crate gcc;

use std::path::Path;
use std::process::Command;
use std::fs::File;
use std::io::{Read,Write};

fn main() {
    if !Path::new("jsonnet/.git").exists() {
        let _ = Command::new("git")
            .args(&["submodule", "update", "--init"])
            .status();
    }

    let dir = Path::new("jsonnet");

    let embedded = [
        "std",
    ];
    for f in &embedded {
        let output = dir.join("core").join(format!("{}.jsonnet.h", f));
        if output.exists() {
            continue;
        }
        let input = dir.join("stdlib").join(format!("{}.jsonnet", f));
        println!("embedding: {:?} -> {:?}", input, output);
        let in_f = File::open(input).unwrap();
        let mut out_f = File::create(&output).unwrap();
        for b in in_f.bytes() {
            write!(&mut out_f, "{},", b.unwrap()).unwrap();
        }
        writeln!(&mut out_f, "0").unwrap();
    }

    let jsonnet_core = [
        "desugarer.cpp",
        "formatter.cpp",
        "lexer.cpp",
        "libjsonnet.cpp",
        "parser.cpp",
        "pass.cpp",
        "static_analysis.cpp",
        "string_utils.cpp",
        "vm.cpp",
    ];

    let mut c = gcc::Build::new();
    c.cpp(true)
        .flag("-std=c++0x")
        .include(dir.join("include"))
        .include(dir.join("third_party/md5"));

    for f in &jsonnet_core {
        c.file(dir.join("core").join(f));
    }

    c.file(dir.join("third_party/md5/md5.cpp"));

    c.compile("libjsonnet.a")
}
<|endoftext|>