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

//! Type and subtype declarations

#![allow(unused_variables)]
#![allow(unused_imports)]

use hir::prelude::*;
use hir::{EnumLit, ExprContext, Range2};
use term::{self, TermContext};
use ty2::{AnyType, EnumBasetype, EnumVariant, IntegerBasetype, IntegerRange, UniversalIntegerType};

/// A type declaration.
///
/// See IEEE 1076-2008 section 6.2.
#[derive(Debug)]
pub struct TypeDecl2<'t> {
    span: Span,
    name: Spanned<Name>,
    data: Spanned<TypeData<'t>>,
}

/// The meat of a type declaration.
#[derive(Debug)]
enum TypeData<'t> {
    /// An incomplete type declaration.
    Incomplete,
    /// An enumeration type.
    Enum(Spanned<Vec<EnumLit>>),
    /// An integer or floating point type.
    Range(Spanned<Range2<'t>>),
    // /// A physical type.
    // Physical(Spanned<Range2<'t>>, PhysicalUnits),
}

impl<'t> TypeDecl2<'t> {
    /// Return the declared type.
    ///
    /// This function maps the type declaration data to an actual `Type`.
    pub fn declared_type<C>(&self, ctx: C) -> Result<&'t Type>
    where
        C: ExprContext<'t> + Copy,
    {
        match self.data.value {
            TypeData::Incomplete => {
                ctx.emit(
                    DiagBuilder2::error(format!("type `{}` is incomplete", self.name.value))
                        .span(self.span)
                        .add_note(format!(
                            "Provide a declaration of the form `type {} is ...` somewhere.",
                            self.name.value
                        )),
                );
                Err(())
            }
            TypeData::Enum(ref literals) => {
                let ty = EnumBasetype::new(literals.value.iter().map(|x| match *x {
                    EnumLit::Ident(x) => EnumVariant::Ident(x.value),
                    EnumLit::Char(x) => EnumVariant::Char(x.value),
                }));
                Ok(ctx.alloc_owned(ty.into_owned()))
            }
            TypeData::Range(ref range) => {
                let (dir, lb, rb) = range.value.constant_value(ctx)?;
                assert_eq!(lb.ty(), rb.ty());
                let ty = lb.ty();
                match ty.as_any() {
                    AnyType::Integer(ty) => {
                        let lb = lb.as_any().unwrap_integer();
                        let rb = rb.as_any().unwrap_integer();
                        let ty = IntegerBasetype::new(IntegerRange::with_left_right(
                            dir,
                            lb.value().clone(),
                            rb.value().clone(),
                        ));
                        Ok(ctx.alloc_owned(ty.into_owned()))
                    }
                    AnyType::Floating(ty) => unimplemented!(),
                    _ => {
                        ctx.emit(
                            DiagBuilder2::error("bounds must be of integer or floating-point type")
                                .span(range.span)
                                .add_note(format!("bounds are of type {}", ty)),
                        );
                        Err(())
                    }
                }
            }
        }
    }
}

impl<'t> FromAst<'t> for TypeDecl2<'t> {
    type AllocInput = &'t ast::TypeDecl;
    type LatentInput = Self::AllocInput;
    type Context = AllocContext<'t>;
    type Latent = &'t Slot<'t, Self>;

    fn alloc_slot(ast: Self::AllocInput, context: Self::Context) -> Result<Self::Latent> {
        let slot = context.alloc(Slot::new(ast, context));
        // TODO: Make the definition weak such that an actual type definition
        // may override it.
        context.define(ast.name.map_into(), Def2::Type(slot))?;
        Ok(slot)
    }

    fn from_ast(ast: Self::LatentInput, context: Self::Context) -> Result<Self> {
        let data = match ast.data {
            Some(ref data) => {
                Spanned::new(unpack_type_data(&data.value, ast.name, context)?, data.span)
            }
            None => Spanned::new(TypeData::Incomplete, ast.span),
        };
        Ok(TypeDecl2 {
            span: ast.span,
            name: ast.name,
            data: data,
        })
    }
}

impl<'t> Node<'t> for TypeDecl2<'t> {
    fn span(&self) -> Span {
        self.span
    }

    fn desc_kind(&self) -> String {
        "type declaration".into()
    }

    fn desc_name(&self) -> String {
        format!("type declaration `{}`", self.name.value)
    }

    fn accept(&'t self, visitor: &mut Visitor<'t>) {
        visitor.visit_type_decl(self);
    }

    fn walk(&'t self, visitor: &mut Visitor<'t>) {
        visitor.visit_name(self.name);
    }
}

impl<'t> Decl2<'t> for TypeDecl2<'t> {
    fn name(&self) -> Spanned<ResolvableName> {
        self.name.map_into()
    }
}

/// Map an AST type data to the corresponding HIR type data.
fn unpack_type_data<'t>(
    data: &ast::TypeData,
    type_name: Spanned<Name>,
    context: AllocContext<'t>,
) -> Result<TypeData<'t>> {
    match *data {
        ast::EnumType(ref paren_elems) => {
            let literals = paren_elems
                .value
                .iter()
                .map(|elem| match paren_elem_to_enum_literal(elem) {
                    Some(x) => Ok(x),
                    None => {
                        context.emit(
                            DiagBuilder2::error(format!(
                                "`{}` is not an enumeration literal",
                                elem.span.extract()
                            )).span(elem.span)
                                .add_note("expected an identifier or character literal"),
                        );
                        Err(())
                    }
                })
                .collect::<Vec<Result<_>>>()
                .into_iter()
                .collect::<Result<Vec<_>>>()?;
            Ok(TypeData::Enum(Spanned::new(literals, paren_elems.span)))
        }
        ast::RangeType(ref range_expr, ref units) => {
            let termctx = TermContext::new2(context);
            let range_expr = termctx.termify_expr(range_expr)?;
            let range = term::term_to_range(range_expr, context)?;
            if units.is_some() {
                panic!("units not yet implemented");
            }
            Ok(TypeData::Range(range))
        }
        _ => unimplemented!(
            "type `{}` unsupported type data {:#?}",
            type_name.value,
            data
        ),
    }
}

/// Unpack a parenthesis element as an enumeration literal.
fn paren_elem_to_enum_literal(elem: &ast::ParenElem) -> Option<EnumLit> {
    if !elem.choices.value.is_empty() {
        None
    } else {
        match elem.expr.data {
            ast::NameExpr(ast::CompoundName {
                primary: ast::PrimaryName { kind, span, .. },
                ref parts,
                ..
            }) if parts.is_empty() =>
            {
                match kind {
                    ast::PrimaryNameKind::Ident(n) => Some(EnumLit::Ident(Spanned::new(n, span))),
                    ast::PrimaryNameKind::Char(c) => Some(EnumLit::Char(Spanned::new(c, span))),
                    _ => None,
                }
            }
            _ => None,
        }
    }
}
<commit_msg>vhdl: improve error message for range type bounds<commit_after>// Copyright (c) 2018 Fabian Schuiki

//! Type and subtype declarations

#![allow(unused_variables)]
#![allow(unused_imports)]

use hir::prelude::*;
use hir::{EnumLit, ExprContext, Range2};
use term::{self, TermContext};
use ty2::{AnyType, EnumBasetype, EnumVariant, IntegerBasetype, IntegerRange, UniversalIntegerType};

/// A type declaration.
///
/// See IEEE 1076-2008 section 6.2.
#[derive(Debug)]
pub struct TypeDecl2<'t> {
    span: Span,
    name: Spanned<Name>,
    data: Spanned<TypeData<'t>>,
}

/// The meat of a type declaration.
#[derive(Debug)]
enum TypeData<'t> {
    /// An incomplete type declaration.
    Incomplete,
    /// An enumeration type.
    Enum(Spanned<Vec<EnumLit>>),
    /// An integer or floating point type.
    Range(Spanned<Range2<'t>>),
    // /// A physical type.
    // Physical(Spanned<Range2<'t>>, PhysicalUnits),
}

impl<'t> TypeDecl2<'t> {
    /// Return the declared type.
    ///
    /// This function maps the type declaration data to an actual `Type`.
    pub fn declared_type<C>(&self, ctx: C) -> Result<&'t Type>
    where
        C: ExprContext<'t> + Copy,
    {
        match self.data.value {
            TypeData::Incomplete => {
                ctx.emit(
                    DiagBuilder2::error(format!("type `{}` is incomplete", self.name.value))
                        .span(self.span)
                        .add_note(format!(
                            "Provide a declaration of the form `type {} is ...` somewhere.",
                            self.name.value
                        )),
                );
                Err(())
            }
            TypeData::Enum(ref literals) => {
                let ty = EnumBasetype::new(literals.value.iter().map(|x| match *x {
                    EnumLit::Ident(x) => EnumVariant::Ident(x.value),
                    EnumLit::Char(x) => EnumVariant::Char(x.value),
                }));
                Ok(ctx.alloc_owned(ty.into_owned()))
            }
            TypeData::Range(ref range) => {
                let (dir, lb, rb) = range.value.constant_value(ctx)?;
                assert_eq!(lb.ty(), rb.ty());
                let ty = lb.ty();
                match ty.as_any() {
                    AnyType::Integer(ty) => {
                        let lb = lb.as_any().unwrap_integer();
                        let rb = rb.as_any().unwrap_integer();
                        let ty = IntegerBasetype::new(IntegerRange::with_left_right(
                            dir,
                            lb.value().clone(),
                            rb.value().clone(),
                        ));
                        Ok(ctx.alloc_owned(ty.into_owned()))
                    }
                    AnyType::Floating(ty) => unimplemented!(),
                    _ => {
                        ctx.emit(
                            DiagBuilder2::error(format!(
                                "bounds of type `{}` must be of integer or floating-point type",
                                self.name.value
                            )).span(range.span)
                                .add_note(format!("bounds are of type {}", ty)),
                        );
                        Err(())
                    }
                }
            }
        }
    }
}

impl<'t> FromAst<'t> for TypeDecl2<'t> {
    type AllocInput = &'t ast::TypeDecl;
    type LatentInput = Self::AllocInput;
    type Context = AllocContext<'t>;
    type Latent = &'t Slot<'t, Self>;

    fn alloc_slot(ast: Self::AllocInput, context: Self::Context) -> Result<Self::Latent> {
        let slot = context.alloc(Slot::new(ast, context));
        // TODO: Make the definition weak such that an actual type definition
        // may override it.
        context.define(ast.name.map_into(), Def2::Type(slot))?;
        Ok(slot)
    }

    fn from_ast(ast: Self::LatentInput, context: Self::Context) -> Result<Self> {
        let data = match ast.data {
            Some(ref data) => {
                Spanned::new(unpack_type_data(&data.value, ast.name, context)?, data.span)
            }
            None => Spanned::new(TypeData::Incomplete, ast.span),
        };
        Ok(TypeDecl2 {
            span: ast.span,
            name: ast.name,
            data: data,
        })
    }
}

impl<'t> Node<'t> for TypeDecl2<'t> {
    fn span(&self) -> Span {
        self.span
    }

    fn desc_kind(&self) -> String {
        "type declaration".into()
    }

    fn desc_name(&self) -> String {
        format!("type declaration `{}`", self.name.value)
    }

    fn accept(&'t self, visitor: &mut Visitor<'t>) {
        visitor.visit_type_decl(self);
    }

    fn walk(&'t self, visitor: &mut Visitor<'t>) {
        visitor.visit_name(self.name);
    }
}

impl<'t> Decl2<'t> for TypeDecl2<'t> {
    fn name(&self) -> Spanned<ResolvableName> {
        self.name.map_into()
    }
}

/// Map an AST type data to the corresponding HIR type data.
fn unpack_type_data<'t>(
    data: &ast::TypeData,
    type_name: Spanned<Name>,
    context: AllocContext<'t>,
) -> Result<TypeData<'t>> {
    match *data {
        ast::EnumType(ref paren_elems) => {
            let literals = paren_elems
                .value
                .iter()
                .map(|elem| match paren_elem_to_enum_literal(elem) {
                    Some(x) => Ok(x),
                    None => {
                        context.emit(
                            DiagBuilder2::error(format!(
                                "`{}` is not an enumeration literal",
                                elem.span.extract()
                            )).span(elem.span)
                                .add_note("expected an identifier or character literal"),
                        );
                        Err(())
                    }
                })
                .collect::<Vec<Result<_>>>()
                .into_iter()
                .collect::<Result<Vec<_>>>()?;
            Ok(TypeData::Enum(Spanned::new(literals, paren_elems.span)))
        }
        ast::RangeType(ref range_expr, ref units) => {
            let termctx = TermContext::new2(context);
            let range_expr = termctx.termify_expr(range_expr)?;
            let range = term::term_to_range(range_expr, context)?;
            if units.is_some() {
                panic!("units not yet implemented");
            }
            Ok(TypeData::Range(range))
        }
        _ => unimplemented!(
            "type `{}` unsupported type data {:#?}",
            type_name.value,
            data
        ),
    }
}

/// Unpack a parenthesis element as an enumeration literal.
fn paren_elem_to_enum_literal(elem: &ast::ParenElem) -> Option<EnumLit> {
    if !elem.choices.value.is_empty() {
        None
    } else {
        match elem.expr.data {
            ast::NameExpr(ast::CompoundName {
                primary: ast::PrimaryName { kind, span, .. },
                ref parts,
                ..
            }) if parts.is_empty() =>
            {
                match kind {
                    ast::PrimaryNameKind::Ident(n) => Some(EnumLit::Ident(Spanned::new(n, span))),
                    ast::PrimaryNameKind::Char(c) => Some(EnumLit::Char(Spanned::new(c, span))),
                    _ => None,
                }
            }
            _ => None,
        }
    }
}
<|endoftext|>


<commit_before>use hyper::Client;
use super::helper::KinesisHelper;
use utils::screen::Screen;
use utils::screen::Status;
use rusoto::{
    DefaultCredentialsProvider,
    Region
};

enum State {
    Root,
    End,
    StreamList(Vec<String>),
    ShardList(String, Vec<String>),
    RecordList(String),
    Record(String, String),
}

pub fn run(credential_provider: DefaultCredentialsProvider, client: Client, region: Region) {

    let kinesis_helper = KinesisHelper::new(client, credential_provider, region);
    let mut screen: Screen = Screen::new();
    let mut state: State = State::Root;

    loop {
        state = match state {
            State::Root                           => root(&screen, &kinesis_helper),
            State::StreamList(streams)            => stream_list(&screen, &kinesis_helper, streams),
            State::ShardList(stream_name, shards) => shared_list(&screen, &kinesis_helper, stream_name, shards),
            State::RecordList(shard_iterator)     => record_list(&screen, &kinesis_helper, shard_iterator),
            State::Record(shard_iterator, record) => record(&screen, &kinesis_helper, shard_iterator, record),
            State::End => {
                break;
            }
        };
    }
}

fn root(screen: &mut Screen, kinesis_helper: &KinesisHelper) -> State {

    let commands = vec!["l".to_string(), "q".to_string()];
    screen.update_screen("🍓  Kinesis", &commands);

    match screen.select_line() {
        Status::Error | Status::Quit => State::End,
        Status::Selected(ref c) if c == "l" => {
            match kinesis_helper.list_streams() {
                Ok(streams) => State::StreamList(streams),
                Err(e) =>  State::Root
            }
        }
        _ => State::Root
    }
}

fn stream_list(screen: &mut Screen, kinesis_helper: &KinesisHelper, streams: Vec<String>) -> State {

    screen.update_screen("🍓  Kinesis > Streams", &streams);

    match screen.select_line() {
        Status::Error | Status::Quit => State::End,
        Status::Selected(stream_name) => {
            match kinesis_helper.describe_shards(&stream_name) {
                Ok(shards) => State::ShardList(stream_name.to_string(), shards),
                Err(e) => State::Root,
            }
        }
        _ => State::Root
    }
}

fn shared_list(screen: &mut Screen, kinesis_helper: &KinesisHelper, stream_name: &str, shards: Vec<String>) -> State {

    screen.update_screen("🍓  Kinesis > Streams > Shards", &shards);

    match screen.select_line() {
        Status::Error | Status::Quit => State::End,
        Status::Selected(shard_id) => {
            match kinesis_helper.get_shard_iterator(&stream_name, &shard_id) {
                Ok(shard_iterator) => State::RecordList(shard_iterator),
                Err(e) => State::Root,
            }
        }
        _ => State::Root
    }

}

fn record_list(screen: &mut Screen, kinesis_helper: &KinesisHelper, shard_iterator: String) -> State {

    match kinesis_helper.get_records(&shard_iterator) {

        Ok((results, iterator)) => {

            screen.update_screen("🍓  Kinesis > Streams > Shards > Records",
                                 &kinesis_helper.decode_records(&results));

            match screen.select_line() {
                Status::Error | Status::Quit => State::End,
                Status::Selected(ref c) if c == "n" => {
                    match iterator {
                        Some(iterator) => State::RecordList(iterator),
                        None => State::Root
                    }
                },
                Status::Selected(ref record) => State::Record(&shard_iterator, record.to_string()),
                _ => State::Root
            }
        },
        Err(e) => State::Root,
    }
}

fn record(screen: &mut Screen, kinesis_helper: &KinesisHelper, shard_iterator: String, record: String) -> State {
    screen.update_screen("🍓  Kinesis > Streams > Shards > Records > Record", &vec!(String::new()));

    match screen.render(&kinesis_helper.format_record(&record)) {
        Status::Error | Status::Quit => State::End,
        Status::Escaped  => State::RecordList(shard_iterator),
        _ => State::Root
    }
}
<commit_msg>Use trait<commit_after>use hyper::Client;
use super::helper::KinesisHelper;
use utils::screen::Screen;
use utils::screen::Status;
use rusoto::{
    DefaultCredentialsProvider,
    Region
};

enum State {
    Root,
    End,
    StreamList(Vec<String>),
    ShardList(String, Vec<String>),
    RecordList(String),
    Record(String, String),
}

trait Controller {
    fn run(&self, credential_provider: DefaultCredentialsProvider, client: Client, region: Region) -> ()
}

struct KinesisController {
    kinesis_helper: KinesisHelper,
    screen: Screen,
}

impl KinesisController {

    pub fn new() -> KinesisController {

    }

    fn root() -> State {

    }
}

pub fn run(credential_provider: DefaultCredentialsProvider, client: Client, region: Region) {

    let kinesis_helper = KinesisHelper::new(client, credential_provider, region);
    let mut screen: Screen = Screen::new();
    let mut state: State = State::Root;

    loop {
        state = match state {
            State::Root                           => root(&screen, &kinesis_helper),
            State::StreamList(streams)            => stream_list(&screen, &kinesis_helper, streams),
            State::ShardList(stream_name, shards) => shared_list(&screen, &kinesis_helper, stream_name, shards),
            State::RecordList(shard_iterator)     => record_list(&screen, &kinesis_helper, shard_iterator),
            State::Record(shard_iterator, record) => record(&screen, &kinesis_helper, shard_iterator, record),
            State::End => {
                break;
            }
        };
    }
}

fn root(screen: &mut Screen, kinesis_helper: &KinesisHelper) -> State {

    let commands = vec!["l".to_string(), "q".to_string()];
    screen.update_screen("🍓  Kinesis", &commands);

    match screen.select_line() {
        Status::Error | Status::Quit => State::End,
        Status::Selected(ref c) if c == "l" => {
            match kinesis_helper.list_streams() {
                Ok(streams) => State::StreamList(streams),
                Err(e) =>  State::Root
            }
        }
        _ => State::Root
    }
}

fn stream_list(screen: &mut Screen, kinesis_helper: &KinesisHelper, streams: Vec<String>) -> State {

    screen.update_screen("🍓  Kinesis > Streams", &streams);

    match screen.select_line() {
        Status::Error | Status::Quit => State::End,
        Status::Selected(stream_name) => {
            match kinesis_helper.describe_shards(&stream_name) {
                Ok(shards) => State::ShardList(stream_name.to_string(), shards),
                Err(e) => State::Root,
            }
        }
        _ => State::Root
    }
}

fn shared_list(screen: &mut Screen, kinesis_helper: &KinesisHelper, stream_name: &str, shards: Vec<String>) -> State {

    screen.update_screen("🍓  Kinesis > Streams > Shards", &shards);

    match screen.select_line() {
        Status::Error | Status::Quit => State::End,
        Status::Selected(shard_id) => {
            match kinesis_helper.get_shard_iterator(&stream_name, &shard_id) {
                Ok(shard_iterator) => State::RecordList(shard_iterator),
                Err(e) => State::Root,
            }
        }
        _ => State::Root
    }

}

fn record_list(screen: &mut Screen, kinesis_helper: &KinesisHelper, shard_iterator: String) -> State {

    match kinesis_helper.get_records(&shard_iterator) {

        Ok((results, iterator)) => {

            screen.update_screen("🍓  Kinesis > Streams > Shards > Records",
                                 &kinesis_helper.decode_records(&results));

            match screen.select_line() {
                Status::Error | Status::Quit => State::End,
                Status::Selected(ref c) if c == "n" => {
                    match iterator {
                        Some(iterator) => State::RecordList(iterator),
                        None => State::Root
                    }
                },
                Status::Selected(ref record) => State::Record(&shard_iterator, record.to_string()),
                _ => State::Root
            }
        },
        Err(e) => State::Root,
    }
}

fn record(screen: &mut Screen, kinesis_helper: &KinesisHelper, shard_iterator: String, record: String) -> State {
    screen.update_screen("🍓  Kinesis > Streams > Shards > Records > Record", &vec!(String::new()));

    match screen.render(&kinesis_helper.format_record(&record)) {
        Status::Error | Status::Quit => State::End,
        Status::Escaped  => State::RecordList(shard_iterator),
        _ => State::Root
    }
}
<|endoftext|>


<commit_before>//! This module implements minimal run-time feature detection for x86.
//!
//! The features are detected using the `detect_features` function below.
//! This function uses the CPUID instruction to read the feature flags from the
//! CPU and encodes them in an `usize` where each bit position represents
//! whether a feature is available (bit is set) or unavaiable (bit is cleared).
//!
//! The enum `Feature` is used to map bit positions to feature names, and the
//! the `__crate::detect::check_for!` macro is used to map string literals (e.g.,
//! "avx") to these bit positions (e.g., `Feature::avx`).
//!
//! The run-time feature detection is performed by the
//! `__crate::detect::check_for(Feature) -> bool` function. On its first call,
//! this functions queries the CPU for the available features and stores them
//! in a global `AtomicUsize` variable. The query is performed by just checking
//! whether the feature bit in this global variable is set or cleared.

features! {
    @TARGET: x86;
    @MACRO_NAME: is_x86_feature_detected;
    @MACRO_ATTRS:
    /// A macro to test at *runtime* whether a CPU feature is available on
    /// x86/x86-64 platforms.
    ///
    /// This macro is provided in the standard library and will detect at runtime
    /// whether the specified CPU feature is detected. This does **not** resolve at
    /// compile time unless the specified feature is already enabled for the entire
    /// crate. Runtime detection currently relies mostly on the `cpuid` instruction.
    ///
    /// This macro only takes one argument which is a string literal of the feature
    /// being tested for. The feature names supported are the lowercase versions of
    /// the ones defined by Intel in [their documentation][docs].
    ///
    /// ## Supported arguments
    ///
    /// This macro supports the same names that `#[target_feature]` supports. Unlike
    /// `#[target_feature]`, however, this macro does not support names separated
    /// with a comma. Instead testing for multiple features must be done through
    /// separate macro invocations for now.
    ///
    /// Supported arguments are:
    ///
    /// * `"aes"`
    /// * `"pclmulqdq"`
    /// * `"rdrand"`
    /// * `"rdseed"`
    /// * `"tsc"`
    /// * `"mmx"`
    /// * `"sse"`
    /// * `"sse2"`
    /// * `"sse3"`
    /// * `"ssse3"`
    /// * `"sse4.1"`
    /// * `"sse4.2"`
    /// * `"sse4a"`
    /// * `"sha"`
    /// * `"avx"`
    /// * `"avx2"`
    /// * `"avx512f"`
    /// * `"avx512cd"`
    /// * `"avx512er"`
    /// * `"avx512pf"`
    /// * `"avx512bw"`
    /// * `"avx512dq"`
    /// * `"avx512vl"`
    /// * `"avx512ifma"`
    /// * `"avx512vbmi"`
    /// * `"avx512vpopcntdq"`
    /// * `"fma"`
    /// * `"bmi1"`
    /// * `"bmi2"`
    /// * `"abm"`
    /// * `"lzcnt"`
    /// * `"tbm"`
    /// * `"popcnt"`
    /// * `"fxsr"`
    /// * `"xsave"`
    /// * `"xsaveopt"`
    /// * `"xsaves"`
    /// * `"xsavec"`
    ///
    /// [docs]: https://software.intel.com/sites/landingpage/IntrinsicsGuide
    #[stable(feature = "simd_x86", since = "1.27.0")]
    @BIND_FEATURE_NAME: "abm"; "lzcnt"; // abm is a synonym for lzcnt
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] aes: "aes";
    /// AES (Advanced Encryption Standard New Instructions AES-NI)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] pclmulqdq: "pclmulqdq";
    /// CLMUL (Carry-less Multiplication)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")]  rdrand: "rdrand";
    /// RDRAND
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] rdseed: "rdseed";
    /// RDSEED
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] tsc: "tsc";
    /// TSC (Time Stamp Counter)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] mmx: "mmx";
    /// MMX (MultiMedia eXtensions)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse: "sse";
    /// SSE (Streaming SIMD Extensions)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse2: "sse2";
    /// SSE2 (Streaming SIMD Extensions 2)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse3: "sse3";
    /// SSE3 (Streaming SIMD Extensions 3)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] ssse3: "ssse3";
    /// SSSE3 (Supplemental Streaming SIMD Extensions 3)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse4_1: "sse4.1";
    /// SSE4.1 (Streaming SIMD Extensions 4.1)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse4_2: "sse4.2";
    /// SSE4.2 (Streaming SIMD Extensions 4.2)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] sse4a: "sse4a";
    /// SSE4a (Streaming SIMD Extensions 4a)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sha: "sha";
    /// SHA
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx: "avx";
    /// AVX (Advanced Vector Extensions)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx2: "avx2";
    /// AVX2 (Advanced Vector Extensions 2)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512f: "avx512f" ;
    /// AVX-512 F (Foundation)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512cd: "avx512cd" ;
    /// AVX-512 CD (Conflict Detection Instructions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512er: "avx512er";
    /// AVX-512 ER (Expo nential and Reciprocal Instructions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512pf: "avx512pf";
    /// AVX-512 PF (Prefetch Instructions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512bw: "avx512bw";
    /// AVX-512 BW (Byte and Word Instructions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512dq: "avx512dq";
    /// AVX-512 DQ (Doubleword and Quadword)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512vl: "avx512vl";
    /// AVX-512 VL (Vector Length Extensions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512ifma: "avx512ifma";
    /// AVX-512 IFMA (Integer Fused Multiply Add)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512vbmi: "avx512vbmi";
    /// AVX-512 VBMI (Vector Byte Manipulation Instructions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512vpopcntdq: "avx512vpopcntdq";
    /// AVX-512 VPOPCNTDQ (Vector Population Count Doubleword and
    /// Quadword)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512vbmi2: "avx512vbmi2";
    /// AVX-512 VBMI2 (Additional byte, word, dword and qword capabilities)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512gfni: "avx512gfni";
    /// AVX-512 GFNI (Galois Field New Instruction)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512vaes: "avx512vaes";
    /// AVX-512 VAES (Vector AES instruction)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512vpclmulqdq: "avx512vpclmulqdq";
    /// AVX-512 VPCLMULQDQ (Vector PCLMULQDQ instructions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512vnni: "avx512vnni";
    /// AVX-512 VNNI (Vector Neural Network Instructions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512bitalg: "avx512bitalg";
    /// AVX-512 BITALG (Support for VPOPCNT[B,W] and VPSHUFBITQMB)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512bf16: "avx512bf16";
    /// AVX-512 BF16 (BFLOAT16 instructions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512vp2intersect: "avx512vp2intersect";
    /// AVX-512 P2INTERSECT
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] f16c: "f16c";
    /// F16C (Conversions between IEEE-754 `binary16` and `binary32` formats)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] fma: "fma";
    /// FMA (Fused Multiply Add)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] bmi1: "bmi1" ;
    /// BMI1 (Bit Manipulation Instructions 1)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] bmi2: "bmi2" ;
    /// BMI2 (Bit Manipulation Instructions 2)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] lzcnt: "lzcnt";
    /// ABM (Advanced Bit Manipulation) / LZCNT (Leading Zero Count)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] tbm: "tbm";
    /// TBM (Trailing Bit Manipulation)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] popcnt: "popcnt";
    /// POPCNT (Population Count)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] fxsr: "fxsr";
    /// FXSR (Floating-point context fast save and restor)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] xsave: "xsave";
    /// XSAVE (Save Processor Extended States)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] xsaveopt: "xsaveopt";
    /// XSAVEOPT (Save Processor Extended States Optimized)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] xsaves: "xsaves";
    /// XSAVES (Save Processor Extended States Supervisor)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] xsavec: "xsavec";
    /// XSAVEC (Save Processor Extended States Compacted)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] cmpxchg16b: "cmpxchg16b";
    /// CMPXCH16B (16-byte compare-and-swap instruction)
    @FEATURE: #[stable(feature = "simd_x86_adx", since = "1.33.0")] adx: "adx";
    /// ADX, Intel ADX (Multi-Precision Add-Carry Instruction Extensions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] rtm: "rtm";
    /// RTM, Intel (Restricted Transactional Memory)
}
<commit_msg>re-stabilize the AVX-512 features that were stabilized in Rust 1.27.0 (#842)<commit_after>//! This module implements minimal run-time feature detection for x86.
//!
//! The features are detected using the `detect_features` function below.
//! This function uses the CPUID instruction to read the feature flags from the
//! CPU and encodes them in an `usize` where each bit position represents
//! whether a feature is available (bit is set) or unavaiable (bit is cleared).
//!
//! The enum `Feature` is used to map bit positions to feature names, and the
//! the `__crate::detect::check_for!` macro is used to map string literals (e.g.,
//! "avx") to these bit positions (e.g., `Feature::avx`).
//!
//! The run-time feature detection is performed by the
//! `__crate::detect::check_for(Feature) -> bool` function. On its first call,
//! this functions queries the CPU for the available features and stores them
//! in a global `AtomicUsize` variable. The query is performed by just checking
//! whether the feature bit in this global variable is set or cleared.

features! {
    @TARGET: x86;
    @MACRO_NAME: is_x86_feature_detected;
    @MACRO_ATTRS:
    /// A macro to test at *runtime* whether a CPU feature is available on
    /// x86/x86-64 platforms.
    ///
    /// This macro is provided in the standard library and will detect at runtime
    /// whether the specified CPU feature is detected. This does **not** resolve at
    /// compile time unless the specified feature is already enabled for the entire
    /// crate. Runtime detection currently relies mostly on the `cpuid` instruction.
    ///
    /// This macro only takes one argument which is a string literal of the feature
    /// being tested for. The feature names supported are the lowercase versions of
    /// the ones defined by Intel in [their documentation][docs].
    ///
    /// ## Supported arguments
    ///
    /// This macro supports the same names that `#[target_feature]` supports. Unlike
    /// `#[target_feature]`, however, this macro does not support names separated
    /// with a comma. Instead testing for multiple features must be done through
    /// separate macro invocations for now.
    ///
    /// Supported arguments are:
    ///
    /// * `"aes"`
    /// * `"pclmulqdq"`
    /// * `"rdrand"`
    /// * `"rdseed"`
    /// * `"tsc"`
    /// * `"mmx"`
    /// * `"sse"`
    /// * `"sse2"`
    /// * `"sse3"`
    /// * `"ssse3"`
    /// * `"sse4.1"`
    /// * `"sse4.2"`
    /// * `"sse4a"`
    /// * `"sha"`
    /// * `"avx"`
    /// * `"avx2"`
    /// * `"avx512f"`
    /// * `"avx512cd"`
    /// * `"avx512er"`
    /// * `"avx512pf"`
    /// * `"avx512bw"`
    /// * `"avx512dq"`
    /// * `"avx512vl"`
    /// * `"avx512ifma"`
    /// * `"avx512vbmi"`
    /// * `"avx512vpopcntdq"`
    /// * `"fma"`
    /// * `"bmi1"`
    /// * `"bmi2"`
    /// * `"abm"`
    /// * `"lzcnt"`
    /// * `"tbm"`
    /// * `"popcnt"`
    /// * `"fxsr"`
    /// * `"xsave"`
    /// * `"xsaveopt"`
    /// * `"xsaves"`
    /// * `"xsavec"`
    ///
    /// [docs]: https://software.intel.com/sites/landingpage/IntrinsicsGuide
    #[stable(feature = "simd_x86", since = "1.27.0")]
    @BIND_FEATURE_NAME: "abm"; "lzcnt"; // abm is a synonym for lzcnt
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] aes: "aes";
    /// AES (Advanced Encryption Standard New Instructions AES-NI)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] pclmulqdq: "pclmulqdq";
    /// CLMUL (Carry-less Multiplication)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")]  rdrand: "rdrand";
    /// RDRAND
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] rdseed: "rdseed";
    /// RDSEED
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] tsc: "tsc";
    /// TSC (Time Stamp Counter)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] mmx: "mmx";
    /// MMX (MultiMedia eXtensions)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse: "sse";
    /// SSE (Streaming SIMD Extensions)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse2: "sse2";
    /// SSE2 (Streaming SIMD Extensions 2)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse3: "sse3";
    /// SSE3 (Streaming SIMD Extensions 3)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] ssse3: "ssse3";
    /// SSSE3 (Supplemental Streaming SIMD Extensions 3)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse4_1: "sse4.1";
    /// SSE4.1 (Streaming SIMD Extensions 4.1)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse4_2: "sse4.2";
    /// SSE4.2 (Streaming SIMD Extensions 4.2)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse4a: "sse4a";
    /// SSE4a (Streaming SIMD Extensions 4a)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sha: "sha";
    /// SHA
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx: "avx";
    /// AVX (Advanced Vector Extensions)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx2: "avx2";
    /// AVX2 (Advanced Vector Extensions 2)
    // Detection for the AVX-512 features below was accidentally stabilized in
    // Rust 1.27.0, even though the corresponding intrinsics are still unstable
    // or unimplemeted. There are stable callers who rely on detection support,
    // e.g. to call AVX-512 C code via FFI.
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512f: "avx512f" ;
    /// AVX-512 F (Foundation)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512cd: "avx512cd" ;
    /// AVX-512 CD (Conflict Detection Instructions)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512er: "avx512er";
    /// AVX-512 ER (Expo nential and Reciprocal Instructions)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512pf: "avx512pf";
    /// AVX-512 PF (Prefetch Instructions)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512bw: "avx512bw";
    /// AVX-512 BW (Byte and Word Instructions)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512dq: "avx512dq";
    /// AVX-512 DQ (Doubleword and Quadword)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512vl: "avx512vl";
    /// AVX-512 VL (Vector Length Extensions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512ifma: "avx512ifma";
    /// AVX-512 IFMA (Integer Fused Multiply Add)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512vbmi: "avx512vbmi";
    /// AVX-512 VBMI (Vector Byte Manipulation Instructions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512vpopcntdq: "avx512vpopcntdq";
    /// AVX-512 VPOPCNTDQ (Vector Population Count Doubleword and
    /// Quadword)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512vbmi2: "avx512vbmi2";
    /// AVX-512 VBMI2 (Additional byte, word, dword and qword capabilities)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512gfni: "avx512gfni";
    /// AVX-512 GFNI (Galois Field New Instruction)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512vaes: "avx512vaes";
    /// AVX-512 VAES (Vector AES instruction)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512vpclmulqdq: "avx512vpclmulqdq";
    /// AVX-512 VPCLMULQDQ (Vector PCLMULQDQ instructions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512vnni: "avx512vnni";
    /// AVX-512 VNNI (Vector Neural Network Instructions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512bitalg: "avx512bitalg";
    /// AVX-512 BITALG (Support for VPOPCNT[B,W] and VPSHUFBITQMB)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512bf16: "avx512bf16";
    /// AVX-512 BF16 (BFLOAT16 instructions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] avx512vp2intersect: "avx512vp2intersect";
    /// AVX-512 P2INTERSECT
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] f16c: "f16c";
    /// F16C (Conversions between IEEE-754 `binary16` and `binary32` formats)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] fma: "fma";
    /// FMA (Fused Multiply Add)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] bmi1: "bmi1" ;
    /// BMI1 (Bit Manipulation Instructions 1)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] bmi2: "bmi2" ;
    /// BMI2 (Bit Manipulation Instructions 2)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] lzcnt: "lzcnt";
    /// ABM (Advanced Bit Manipulation) / LZCNT (Leading Zero Count)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] tbm: "tbm";
    /// TBM (Trailing Bit Manipulation)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] popcnt: "popcnt";
    /// POPCNT (Population Count)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] fxsr: "fxsr";
    /// FXSR (Floating-point context fast save and restor)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] xsave: "xsave";
    /// XSAVE (Save Processor Extended States)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] xsaveopt: "xsaveopt";
    /// XSAVEOPT (Save Processor Extended States Optimized)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] xsaves: "xsaves";
    /// XSAVES (Save Processor Extended States Supervisor)
    @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] xsavec: "xsavec";
    /// XSAVEC (Save Processor Extended States Compacted)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] cmpxchg16b: "cmpxchg16b";
    /// CMPXCH16B (16-byte compare-and-swap instruction)
    @FEATURE: #[stable(feature = "simd_x86_adx", since = "1.33.0")] adx: "adx";
    /// ADX, Intel ADX (Multi-Precision Add-Carry Instruction Extensions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] rtm: "rtm";
    /// RTM, Intel (Restricted Transactional Memory)
}
<|endoftext|>


<commit_before>/* AES ECB cut and paste
 *
 * Dmitry Vasiliev <dima@hlabs.org>
 */

use std::collections::HashMap;

fn parse_kv(string: &[u8]) -> HashMap<String, String> {
    let kv = string.split(|&c| c == b'&');
    let mut pairs = kv.map(|kv| kv.split(|&c| c == b'='));
    let mut map = HashMap::new();
    for mut it in pairs {
        let pair: Vec<&[u8]> = it.collect();
        map.insert(String::from_utf8(pair[0].to_vec()).unwrap(),
                   String::from_utf8(pair[1].to_vec()).unwrap());
    }
    map
}

fn encode_kv(map: HashMap<String, String>) -> Vec<u8> {
    let mut bytes = Vec::new();
    for (k, v) in map.iter() {
        bytes.push_all(k.clone().into_bytes().as_slice());
        bytes.push(b'=');
        bytes.push_all(v.clone().into_bytes().as_slice());
        bytes.push(b'&');
    }
    bytes.pop();
    bytes
}

#[cfg(not(test))]
fn main() {
    let obj = parse_kv(b"foo=bar&baz=qux&zap=zazzle");
    println!("Obj: {}", obj);
    let s = encode_kv(obj);
    println!("String: {}", String::from_utf8_lossy(s.as_slice()));
}

/*
 * Tests
 */
#[cfg(test)]
mod test {
    use std::collections::HashMap;
    use super::{parse_kv, encode_kv};

    fn split_encoded(string: &[u8]) -> Vec<&[u8]> {
        let mut pairs: Vec<&[u8]> = string.split(|&c| c == b'&').collect();
        pairs.sort();
        pairs
    }

    #[test]
    fn test_parse_encode_kv() {
        let mut map: HashMap<String, String> = HashMap::new();
        let encoded: Vec<u8> = b"foo=bar&baz=qux&zap=zazzle".to_vec();
        map.insert("foo".into_string(), "bar".into_string());
        map.insert("baz".into_string(), "qux".into_string());
        map.insert("zap".into_string(), "zazzle".into_string());
        assert_eq!(map, parse_kv(encoded.as_slice()));
        assert_eq!(split_encoded(encode_kv(map).as_slice()),
                   split_encoded(encoded.as_slice()));
    }
}
<commit_msg>Add profiles with AES encryption<commit_after>/* AES ECB cut and paste
 *
 * Dmitry Vasiliev <dima@hlabs.org>
 */

extern crate aes_lib;

#[cfg(not(test))]
use std::rand::random;
use std::collections::HashMap;

#[cfg(not(test))]
use aes_lib::{AES_BLOCK_SIZE, decrypt_aes_ecb, encrypt_aes_ecb};

#[cfg(not(test))]
struct Profile {
    key: Vec<u8>,
}

#[cfg(not(test))]
impl Profile {
    fn new() -> Profile {
        let key = random_bytes(AES_BLOCK_SIZE);
        Profile{key: key}
    }

    fn profile_for(&self, email: &str) -> Vec<u8> {
        if email.contains("&") || email.contains("=") {
            fail!("Invalid character in email: {}", email);
        }
        let mut map = HashMap::with_capacity(3);
        map.insert("email".into_string(), email.to_string());
        map.insert("uid".into_string(), "10".into_string());
        map.insert("role".into_string(), "user".into_string());
        let encoded = encode_kv(map);
        encrypt_aes_ecb(encoded.as_slice(), self.key.as_slice())
    }

    fn decrypt(&self, encrypted: &[u8]) -> HashMap<String, String> {
        let encoded = decrypt_aes_ecb(encrypted, self.key.as_slice());
        parse_kv(encoded.as_slice())
    }
}

#[cfg(not(test))]
fn random_bytes(len: uint) -> Vec<u8> {
    Vec::from_fn(len, |_| random::<u8>())
}

fn parse_kv(string: &[u8]) -> HashMap<String, String> {
    let kv = string.split(|&c| c == b'&');
    let mut pairs = kv.map(|kv| kv.split(|&c| c == b'='));
    let mut map = HashMap::new();
    for mut it in pairs {
        let pair: Vec<&[u8]> = it.collect();
        map.insert(String::from_utf8(pair[0].to_vec()).unwrap(),
                   String::from_utf8(pair[1].to_vec()).unwrap());
    }
    map
}

fn encode_kv(map: HashMap<String, String>) -> Vec<u8> {
    let mut bytes = Vec::new();
    for (k, v) in map.iter() {
        bytes.push_all(k.clone().into_bytes().as_slice());
        bytes.push(b'=');
        bytes.push_all(v.clone().into_bytes().as_slice());
        bytes.push(b'&');
    }
    bytes.pop();
    bytes
}

#[cfg(not(test))]
fn main() {
    let profile = Profile::new();
    let encrypted = profile.profile_for("bob@microsoft.com".as_slice());
    println!("Decrypted: {}", profile.decrypt(encrypted.as_slice()));
}

/*
 * Tests
 */
#[cfg(test)]
mod test {
    use std::collections::HashMap;
    use super::{parse_kv, encode_kv};

    fn split_encoded(string: &[u8]) -> Vec<&[u8]> {
        let mut pairs: Vec<&[u8]> = string.split(|&c| c == b'&').collect();
        pairs.sort();
        pairs
    }

    #[test]
    fn test_parse_encode_kv() {
        let mut map: HashMap<String, String> = HashMap::new();
        let encoded: Vec<u8> = b"foo=bar&baz=qux&zap=zazzle".to_vec();
        map.insert("foo".into_string(), "bar".into_string());
        map.insert("baz".into_string(), "qux".into_string());
        map.insert("zap".into_string(), "zazzle".into_string());
        assert_eq!(map, parse_kv(encoded.as_slice()));
        assert_eq!(split_encoded(encode_kv(map).as_slice()),
                   split_encoded(encoded.as_slice()));
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::Read;

use nom::{alphanumeric};

#[derive(PartialEq, Debug)]
enum FortranTokenType {
    Comment(String),            // Simgle line comment with !
    Itentifier(String),         // distance, particle_id
}

#[derive(PartialEq, Debug)]
struct Token {
    line_number: u32,
    column: u32,
    token: FortranTokenType
}

named!(parse_comment<&str, Option<FortranTokenType>>, ws!(opt!(
    do_parse!(
        tag!("!") >>
        content: take_while!(call!(|c| c != '\n')) >>
        (FortranTokenType::Comment(content.to_string()))
    ))));

named!(parse_comment_line<&str, FortranTokenType>, ws!(
    do_parse!(
        tag!("!") >>
        content: take_while!(call!(|c| c != '\n')) >>
        (FortranTokenType::Comment(content.to_string()))
    )));

named!(parse_many_comment_lines<&str, Vec<FortranTokenType> >, many0!(parse_comment_line));

// fn parse_identifier(input: &str) -> IResult<&str, String> {
//     if input.len() == 0 {
//         IResult::Incomplete(Needed::Unknown)
//     } else {
//         if !input[0].is_alphabetic() {
//             IResult::Error
//         } else {
//             IResult::Done
//         }
//     }
// }

named!(parse_identifier<&str, String>, do_parse!(
    values: many1!(alt!(alphanumeric | tag!("_"))) >>
    (values.join(""))
));

named!(parse_program<&str, Vec<FortranTokenType> >, do_parse!(
    ws!(tag!("program")) >>
    ws!(parse_identifier) >>
    // parse_use_statements >>
    // parse_implicit_statements >>
    // variable_decls: parse_variable_declarations >>
    // statements: parse_statements >>
    // parse_many_comment_lines >>
    ws!(tag!("end")) >>
    ws!(tag!("program")) >>
    ws!(parse_identifier) >>
    parse_comment >>
    parse_many_comment_lines >>
    //(variable_decls.extend(&statements))

    parse_comment >>
    (Vec::new())
));

named!(parse_module<&str, Vec<FortranTokenType> >, do_parse!(
    ws!(tag!("program")) >>
    ws!(parse_identifier) >>
    parse_comment >>
    // TODO: add function and subroutine
    ws!(tag!("end")) >>
    ws!(tag!("program")) >>
    ws!(parse_identifier) >>
    parse_comment >>
    parse_many_comment_lines >>
    (Vec::new())
));

named!(parse_code<&str, Vec<FortranTokenType> >, do_parse!(
    parse_many_comment_lines >>
    actual_code: alt!(parse_program | parse_module) >>
    (actual_code)
));

pub fn process_language_fortran(file_name: &str) {
    println!("processing fortran source file: '{}'", file_name);

    let mut buffer = String::new();

    let mut f = File::open(file_name).expect("unable to open file");

    f.read_to_string(&mut buffer).expect("unable to read to string");

    let result = parse_code(&buffer);
    println!("result: {:?}", result);

}
<commit_msg>add test cases<commit_after>use std::fs::File;
use std::io::Read;

use nom::{alphanumeric, IResult};
use nom;

#[derive(PartialEq, Debug)]
enum FortranTokenType {
    Comment(String),            // Simgle line comment with !
    Itentifier(String),         // distance, particle_id
}

#[derive(PartialEq, Debug)]
struct Token {
    line_number: u32,
    column: u32,
    token: FortranTokenType
}

fn easy_nom_error<'a>(error_code: u32) -> nom::IResult<&'a str, Option<FortranTokenType>> {
    IResult::Error(nom::Err::Code(nom::ErrorKind::Custom(error_code)))
}

fn parse_comment(input: &str) -> IResult<&str, Option<FortranTokenType>> {
    if input.len() == 0 {
        return IResult::Done(&input, None)
    } else {
        for (i, c) in input.chars().enumerate() {
            match c {
                '!' => return IResult::Done(&input[0..i], Some(FortranTokenType::Comment(input[i+1..].to_owned()))),
                ' ' | '\t' => continue,
                _ => return easy_nom_error(1)
            }
        }
    }
    IResult::Done(&input, None)
}

named!(parse_comment_line<&str, FortranTokenType>, do_parse!(
    tag!("!") >>
    content: take_while!(call!(|c| c != '\n')) >>
    (FortranTokenType::Comment(content.to_string()))
));

named!(parse_many_comment_lines<&str, Vec<FortranTokenType> >, many0!(parse_comment_line));

// fn parse_identifier(input: &str) -> IResult<&str, String> {
//     if input.len() == 0 {
//         IResult::Incomplete(Needed::Unknown)
//     } else {
//         if !input[0].is_alphabetic() {
//             IResult::Error
//         } else {
//             IResult::Done
//         }
//     }
// }

named!(parse_identifier<&str, String>, do_parse!(
    values: many1!(alt!(alphanumeric | tag!("_"))) >>
    (values.join(""))
));

named!(parse_program<&str, Vec<FortranTokenType> >, do_parse!(
    ws!(tag!("program")) >>
    ws!(parse_identifier) >>
    // parse_use_statements >>
    // parse_implicit_statements >>
    // variable_decls: parse_variable_declarations >>
    // statements: parse_statements >>
    // parse_many_comment_lines >>
    ws!(tag!("end")) >>
    ws!(tag!("program")) >>
    ws!(parse_identifier) >>
    parse_comment >>
    parse_many_comment_lines >>
    //(variable_decls.extend(&statements))

    parse_comment >>
    (Vec::new())
));

named!(parse_module<&str, Vec<FortranTokenType> >, do_parse!(
    ws!(tag!("program")) >>
    ws!(parse_identifier) >>
    parse_comment >>
    // TODO: add function and subroutine
    ws!(tag!("end")) >>
    ws!(tag!("program")) >>
    ws!(parse_identifier) >>
    parse_comment >>
    parse_many_comment_lines >>
    (Vec::new())
));

named!(parse_code<&str, Vec<FortranTokenType> >, do_parse!(
    parse_many_comment_lines >>
    actual_code: alt!(parse_program | parse_module) >>
    (actual_code)
));

pub fn process_language_fortran(file_name: &str) {
    println!("processing fortran source file: '{}'", file_name);

    let mut buffer = String::new();

    let mut f = File::open(file_name).expect("unable to open file");

    f.read_to_string(&mut buffer).expect("unable to read to string");

    let result = parse_code(&buffer);
    println!("result: {:?}", result);

}

#[cfg(test)]
mod test {
    use nom::{IResult};

    use super::{FortranTokenType, easy_nom_error, parse_comment, parse_comment_line,
        parse_many_comment_lines, parse_identifier, parse_program, parse_module, parse_code};

    #[test]
    fn parse_comment1() {
        let input = "";
        let expected_output = IResult::Done("", None);

        let result = parse_comment(input);

        assert_eq!(result, expected_output);
    }

    #[test]
    fn parse_comment2() {
        let input = " ";
        let expected_output = IResult::Done(" ", None);

        let result = parse_comment(input);

        assert_eq!(result, expected_output);
    }

    #[test]
    fn parse_comment3() {
        let input = "!";
        let expected_output = IResult::Done("", Some(FortranTokenType::Comment("".to_owned())));

        let result = parse_comment(input);

        assert_eq!(result, expected_output);
    }

    #[test]
    fn parse_comment4() {
        let input = " !";
        let expected_output = IResult::Done(" ", Some(FortranTokenType::Comment("".to_owned())));

        let result = parse_comment(input);

        assert_eq!(result, expected_output);
    }

    #[test]
    fn parse_comment5() {
        let input = " ! ";
        let expected_output = IResult::Done(" ", Some(FortranTokenType::Comment(" ".to_owned())));

        let result = parse_comment(input);

        assert_eq!(result, expected_output);
    }

    #[test]
    fn parse_comment6() {
        let input = "!Test";
        let expected_output = IResult::Done("", Some(FortranTokenType::Comment("Test".to_owned())));

        let result = parse_comment(input);

        assert_eq!(result, expected_output);
    }

    #[test]
    fn parse_comment7() {
        let input = "! Test";
        let expected_output = IResult::Done("", Some(FortranTokenType::Comment(" Test".to_owned())));

        let result = parse_comment(input);

        assert_eq!(result, expected_output);
    }

    #[test]
    fn parse_comment8() {
        let input = " ! Test";
        let expected_output = IResult::Done(" ", Some(FortranTokenType::Comment(" Test".to_owned())));

        let result = parse_comment(input);

        assert_eq!(result, expected_output);
    }

    #[test]
    fn parse_comment9() {
        let input = "x";
        let expected_output = easy_nom_error(1);

        let result = parse_comment(input);

        assert_eq!(result, expected_output);
    }

    #[test]
    fn parse_comment10() {
        let input = " x";
        let expected_output = easy_nom_error(1);

        let result = parse_comment(input);

        assert_eq!(result, expected_output);
    }

    #[test]
    fn parse_comment11() {
        let input = " x ";
        let expected_output = easy_nom_error(1);

        let result = parse_comment(input);

        assert_eq!(result, expected_output);
    }

    #[test]
    fn parse_comment12() {
        let input = " x!";
        let expected_output = easy_nom_error(1);

        let result = parse_comment(input);

        assert_eq!(result, expected_output);
    }

    #[test]
    fn parse_comment13() {
        let input = " x !";
        let expected_output = easy_nom_error(1);

        let result = parse_comment(input);

        assert_eq!(result, expected_output);
    }

    #[test]
    fn parse_comment14() {
        let input = " x ! ";
        let expected_output = easy_nom_error(1);

        let result = parse_comment(input);

        assert_eq!(result, expected_output);
    }

    #[test]
    fn parse_comment15() {
        let input = " x !Test";
        let expected_output = easy_nom_error(1);

        let result = parse_comment(input);

        assert_eq!(result, expected_output);
    }

    #[test]
    fn parse_comment16() {
        let input = " x ! Test";
        let expected_output = easy_nom_error(1);

        let result = parse_comment(input);

        assert_eq!(result, expected_output);
    }

}
<|endoftext|>


<commit_before>// Copyright 2020 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).

use std::collections::HashMap;
use std::fs::OpenOptions;
use std::io;
use std::net::Ipv4Addr;
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use std::path::PathBuf;
use std::sync::Arc;

use async_latch::AsyncLatch;
use bytes::Bytes;
use futures::channel::oneshot;
use futures::{future, sink, stream, FutureExt, SinkExt, StreamExt, TryStreamExt};
use log::{debug, info};
use nails::execution::{
  self, child_channel, sink_for, stream_for, ChildInput, ChildOutput, ExitCode,
};
use nails::Nail;
use task_executor::Executor;
use tokio::fs::File;
use tokio::net::TcpListener;
use tokio::sync::{Notify, RwLock};

pub struct Server {
  exit_sender: oneshot::Sender<()>,
  exited_receiver: oneshot::Receiver<Result<(), String>>,
  port: u16,
}

impl Server {
  ///
  /// Spawn the server on a background Task.
  ///
  /// The port provided here may be `0` in order to request a random port. A caller can use
  /// `Server.port()` to determine what port was actually selected.
  ///
  pub async fn new(
    executor: Executor,
    port_requested: u16,
    runner: impl RawFdRunner + 'static,
  ) -> Result<Server, String> {
    let listener = TcpListener::bind((Ipv4Addr::new(127, 0, 0, 1), port_requested))
      .await
      .map_err(|e| format!("Could not bind to port {}: {:?}", port_requested, e))?;
    let port_actual = listener
      .local_addr()
      .map_err(|e| format!("No local address for listener: {:?}", e))?
      .port();

    // NB: The C client requires noisy_stdin (see the `nails` crate for more info), but neither
    // `nails` nor the pants python client do.
    let config = nails::Config::default().noisy_stdin(false);
    let nail = RawFdNail {
      executor: executor.clone(),
      runner: Arc::new(runner),
    };

    let (exited_sender, exited_receiver) = oneshot::channel();
    let (exit_sender, exit_receiver) = oneshot::channel();

    let _join = executor.spawn(Self::serve(
      executor.clone(),
      config,
      nail,
      exit_receiver,
      exited_sender,
      listener,
    ));

    Ok(Server {
      exit_sender,
      exited_receiver,
      port: port_actual,
    })
  }

  ///
  /// The main loop of the server. Public for testing.
  ///
  pub(crate) async fn serve(
    executor: Executor,
    config: nails::Config,
    nail: impl Nail,
    should_exit: oneshot::Receiver<()>,
    exited: oneshot::Sender<Result<(), String>>,
    listener: TcpListener,
  ) {
    let exit_result = Self::accept_loop(executor, config, nail, should_exit, listener).await;
    info!("Server exiting with {:?}", exit_result);
    let _ = exited.send(exit_result);
  }

  async fn accept_loop(
    executor: Executor,
    config: nails::Config,
    nail: impl Nail,
    mut should_exit: oneshot::Receiver<()>,
    listener: TcpListener,
  ) -> Result<(), String> {
    // While connections are ongoing, they acquire `read`; before shutting down, the server
    // acquires `write`.
    let ongoing_connections = Arc::new(RwLock::new(()));

    let result = loop {
      let tcp_stream = match future::select(listener.accept().boxed(), should_exit).await {
        future::Either::Left((Ok((tcp_stream, _addr)), s_e)) => {
          // Got a connection.
          should_exit = s_e;
          tcp_stream
        }
        future::Either::Left((Err(e), _)) => {
          break Err(format!("Server failed to accept connections: {}", e));
        }
        future::Either::Right((_, _)) => {
          break Ok(());
        }
      };

      debug!("Accepted connection: {:?}", tcp_stream);

      // There is a slightly delicate dance here: we wait for a connection to have acquired the
      // ongoing connections lock before proceeding to the next iteration of the loop. This
      // prevents us from observing an empty lock and exiting before the connection has actually
      // acquired it. Unfortunately we cannot acquire the lock in this thread and then send the
      // guard to the other thread due to its lifetime bounds.
      let connection_started = Arc::new(Notify::new());
      let _join = executor.spawn({
        let config = config.clone();
        let nail = nail.clone();
        let connection_started = connection_started.clone();
        let ongoing_connections = ongoing_connections.clone();
        async move {
          let ongoing_connection_guard = ongoing_connections.read().await;
          connection_started.notify_one();
          let result = nails::server::handle_connection(config, nail, tcp_stream).await;
          std::mem::drop(ongoing_connection_guard);
          result
        }
      });
      connection_started.notified().await;
    };

    // Before exiting, acquire write access on the ongoing_connections lock to prove that all
    // connections have completed.
    debug!("Server waiting for connections to complete...");
    let _ = ongoing_connections.write().await;
    debug!("All connections completed.");
    result
  }

  ///
  /// The port that the server is listening on.
  ///
  pub fn port(&self) -> u16 {
    self.port
  }

  ///
  /// Returns a Future that will shut down the server by:
  /// 1. stopping accepting new connections
  /// 2. waiting for all ongoing connections to have completed
  ///
  pub async fn shutdown(self) -> Result<(), String> {
    // If we fail to send the exit signal, it's because the task is already shut down.
    let _ = self.exit_sender.send(());
    self
      .exited_receiver
      .await
      .map_err(|_| "Server exited uncleanly.".to_owned())?
  }
}

pub struct RawFdExecution {
  pub cmd: execution::Command,
  pub cancelled: AsyncLatch,
  pub stdin_fd: RawFd,
  pub stdout_fd: RawFd,
  pub stderr_fd: RawFd,
}

///
/// Implementations of this trait should _not_ close the input file handles, and should let the
/// caller do so instead.
///
pub trait RawFdRunner: Fn(RawFdExecution) -> ExitCode + Send + Sync {}

impl<T: Fn(RawFdExecution) -> ExitCode + Send + Sync> RawFdRunner for T {}

///
/// A Nail implementation that proxies stdio to file handles that can be consumed by the given
/// callback function.
///
/// If any of stdio is a tty (detected via environment variables special cased in our nailgun
/// client), we ignore the input fds and open new fds directly to the tty path, which happens to
/// be addressable as a file in OSX and Linux. In that case, there is no middle-man, and we
/// ignore the fds opened by the nailgun server for data sent via the protocol.
///
#[derive(Clone)]
struct RawFdNail {
  executor: Executor,
  runner: Arc<dyn RawFdRunner>,
}

impl Nail for RawFdNail {
  fn spawn(&self, cmd: execution::Command) -> Result<nails::server::Child, io::Error> {
    let env = cmd.env.iter().cloned().collect::<HashMap<_, _>>();

    // Handle stdin.
    let (stdin_handle, stdin_sink) = Self::input(Self::ttypath_from_env(&env, 0))?;
    let maybe_stdin_write = if let Some(mut stdin_sink) = stdin_sink {
      let (stdin_write, stdin_read) = child_channel::<ChildInput>();
      // Spawn a task that will propagate the input stream.
      let _join = self.executor.spawn(async move {
        let mut input_stream = stdin_read.map(|child_input| match child_input {
          ChildInput::Stdin(bytes) => Ok(bytes),
        });
        let _ = stdin_sink.send_all(&mut input_stream).await;
      });
      Some(stdin_write)
    } else {
      // Stdin will be handled directly by the TTY.
      None
    };

    // And stdout/stderr.
    let (stdout_stream, stdout_handle) = Self::output(Self::ttypath_from_env(&env, 1))?;
    let (stderr_stream, stderr_handle) = Self::output(Self::ttypath_from_env(&env, 2))?;

    // Set up a cancellation token that is triggered on client shutdown.
    let cancelled = AsyncLatch::new();
    let shutdown = {
      let cancelled = cancelled.clone();
      async move {
        cancelled.trigger();
      }
    };

    // Spawn the underlying function as a blocking task, and capture its exit code to append to the
    // output stream.
    let nail = self.clone();
    let exit_code = self
      .executor
      .spawn_blocking(move || {
        // NB: This closure captures the stdio handles, and will drop/close them when it completes.
        (nail.runner)(RawFdExecution {
          cmd,
          cancelled,
          stdin_fd: stdin_handle.as_raw_fd(),
          stdout_fd: stdout_handle.as_raw_fd(),
          stderr_fd: stderr_handle.as_raw_fd(),
        })
      })
      .boxed();

    // Select a single stdout/stderr stream.
    let stdout_stream = stdout_stream.map_ok(ChildOutput::Stdout);
    let stderr_stream = stderr_stream.map_ok(ChildOutput::Stderr);
    let output_stream = stream::select(stdout_stream, stderr_stream).boxed();

    Ok(nails::server::Child::new(
      output_stream,
      maybe_stdin_write,
      exit_code,
      Some(shutdown.boxed()),
    ))
  }
}

impl RawFdNail {
  ///
  /// Returns a tuple of a readable file handle and an optional sink for nails to send stdin to.
  ///
  /// In the case of a TTY, the file handle will point directly to the TTY, and no stdin data will
  /// flow over the protocol. Otherwise, it will be backed by a new anonymous pipe, and data should
  /// be copied to the returned Sink.
  ///
  fn input(
    tty_path: Option<PathBuf>,
  ) -> Result<(Box<dyn AsRawFd + Send>, Option<impl sink::Sink<Bytes>>), io::Error> {
    if let Some(tty) = Self::try_open_tty(tty_path, OpenOptions::new().read(true)) {
      Ok((Box::new(tty), None))
    } else {
      let (stdin_reader, stdin_writer) = os_pipe::pipe()?;
      let write_handle =
        File::from_std(unsafe { std::fs::File::from_raw_fd(stdin_writer.into_raw_fd()) });
      Ok((Box::new(stdin_reader), Some(sink_for(write_handle))))
    }
  }

  ///
  /// Returns a tuple of a possibly empty Stream for nails to read data from, and a writable file handle.
  ///
  /// See `Self::input` and the struct's rustdoc for more info on the TTY case.
  ///
  #[allow(clippy::type_complexity)]
  fn output(
    tty_path: Option<PathBuf>,
  ) -> Result<
    (
      stream::BoxStream<'static, Result<Bytes, io::Error>>,
      Box<dyn AsRawFd + Send>,
    ),
    io::Error,
  > {
    if let Some(tty) = Self::try_open_tty(tty_path, OpenOptions::new().write(true).create(false)) {
      Ok((stream::empty().boxed(), Box::new(tty)))
    } else {
      let (stdin_reader, stdin_writer) = os_pipe::pipe()?;
      let read_handle =
        File::from_std(unsafe { std::fs::File::from_raw_fd(stdin_reader.into_raw_fd()) });
      Ok((stream_for(read_handle).boxed(), Box::new(stdin_writer)))
    }
  }

  ///
  /// Attempt to open the given TTY-path, logging any errors.
  ///
  fn try_open_tty(tty_path: Option<PathBuf>, open_options: &OpenOptions) -> Option<std::fs::File> {
    let tty_path = tty_path?;
    open_options
      .open(&tty_path)
      .map_err(|e| {
        log::debug!(
          "Failed to open TTY at {}: {:?}, falling back to socket access.",
          tty_path.display(),
          e
        );
      })
      .ok()
  }

  ///
  /// Corresponds to `ttynames_to_env` in `nailgun_protocol.py`. See this struct's rustdocs.
  ///
  fn ttypath_from_env(env: &HashMap<String, String>, fd_number: usize) -> Option<PathBuf> {
    env
      .get(&format!("NAILGUN_TTY_PATH_{}", fd_number))
      .map(PathBuf::from)
  }
}
<commit_msg>Fix pantsd TTY handling. (#16038)<commit_after>// Copyright 2020 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).

use std::collections::HashMap;
use std::fs::OpenOptions;
use std::io;
use std::net::Ipv4Addr;
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use std::path::PathBuf;
use std::sync::Arc;

use async_latch::AsyncLatch;
use bytes::Bytes;
use futures::channel::oneshot;
use futures::{future, sink, stream, FutureExt, SinkExt, StreamExt, TryStreamExt};
use log::{debug, info};
use nails::execution::{
  self, child_channel, sink_for, stream_for, ChildInput, ChildOutput, ExitCode,
};
use nails::Nail;
use task_executor::Executor;
use tokio::fs::File;
use tokio::net::TcpListener;
use tokio::sync::{Notify, RwLock};

pub struct Server {
  exit_sender: oneshot::Sender<()>,
  exited_receiver: oneshot::Receiver<Result<(), String>>,
  port: u16,
}

impl Server {
  ///
  /// Spawn the server on a background Task.
  ///
  /// The port provided here may be `0` in order to request a random port. A caller can use
  /// `Server.port()` to determine what port was actually selected.
  ///
  pub async fn new(
    executor: Executor,
    port_requested: u16,
    runner: impl RawFdRunner + 'static,
  ) -> Result<Server, String> {
    let listener = TcpListener::bind((Ipv4Addr::new(127, 0, 0, 1), port_requested))
      .await
      .map_err(|e| format!("Could not bind to port {}: {:?}", port_requested, e))?;
    let port_actual = listener
      .local_addr()
      .map_err(|e| format!("No local address for listener: {:?}", e))?
      .port();

    // NB: The C client requires noisy_stdin (see the `nails` crate for more info), but neither
    // `nails` nor the pants python client do.
    let config = nails::Config::default().noisy_stdin(false);
    let nail = RawFdNail {
      executor: executor.clone(),
      runner: Arc::new(runner),
    };

    let (exited_sender, exited_receiver) = oneshot::channel();
    let (exit_sender, exit_receiver) = oneshot::channel();

    let _join = executor.spawn(Self::serve(
      executor.clone(),
      config,
      nail,
      exit_receiver,
      exited_sender,
      listener,
    ));

    Ok(Server {
      exit_sender,
      exited_receiver,
      port: port_actual,
    })
  }

  ///
  /// The main loop of the server. Public for testing.
  ///
  pub(crate) async fn serve(
    executor: Executor,
    config: nails::Config,
    nail: impl Nail,
    should_exit: oneshot::Receiver<()>,
    exited: oneshot::Sender<Result<(), String>>,
    listener: TcpListener,
  ) {
    let exit_result = Self::accept_loop(executor, config, nail, should_exit, listener).await;
    info!("Server exiting with {:?}", exit_result);
    let _ = exited.send(exit_result);
  }

  async fn accept_loop(
    executor: Executor,
    config: nails::Config,
    nail: impl Nail,
    mut should_exit: oneshot::Receiver<()>,
    listener: TcpListener,
  ) -> Result<(), String> {
    // While connections are ongoing, they acquire `read`; before shutting down, the server
    // acquires `write`.
    let ongoing_connections = Arc::new(RwLock::new(()));

    let result = loop {
      let tcp_stream = match future::select(listener.accept().boxed(), should_exit).await {
        future::Either::Left((Ok((tcp_stream, _addr)), s_e)) => {
          // Got a connection.
          should_exit = s_e;
          tcp_stream
        }
        future::Either::Left((Err(e), _)) => {
          break Err(format!("Server failed to accept connections: {}", e));
        }
        future::Either::Right((_, _)) => {
          break Ok(());
        }
      };

      debug!("Accepted connection: {:?}", tcp_stream);

      // There is a slightly delicate dance here: we wait for a connection to have acquired the
      // ongoing connections lock before proceeding to the next iteration of the loop. This
      // prevents us from observing an empty lock and exiting before the connection has actually
      // acquired it. Unfortunately we cannot acquire the lock in this thread and then send the
      // guard to the other thread due to its lifetime bounds.
      let connection_started = Arc::new(Notify::new());
      let _join = executor.spawn({
        let config = config.clone();
        let nail = nail.clone();
        let connection_started = connection_started.clone();
        let ongoing_connections = ongoing_connections.clone();
        async move {
          let ongoing_connection_guard = ongoing_connections.read().await;
          connection_started.notify_one();
          let result = nails::server::handle_connection(config, nail, tcp_stream).await;
          std::mem::drop(ongoing_connection_guard);
          result
        }
      });
      connection_started.notified().await;
    };

    // Before exiting, acquire write access on the ongoing_connections lock to prove that all
    // connections have completed.
    debug!("Server waiting for connections to complete...");
    let _ = ongoing_connections.write().await;
    debug!("All connections completed.");
    result
  }

  ///
  /// The port that the server is listening on.
  ///
  pub fn port(&self) -> u16 {
    self.port
  }

  ///
  /// Returns a Future that will shut down the server by:
  /// 1. stopping accepting new connections
  /// 2. waiting for all ongoing connections to have completed
  ///
  pub async fn shutdown(self) -> Result<(), String> {
    // If we fail to send the exit signal, it's because the task is already shut down.
    let _ = self.exit_sender.send(());
    self
      .exited_receiver
      .await
      .map_err(|_| "Server exited uncleanly.".to_owned())?
  }
}

pub struct RawFdExecution {
  pub cmd: execution::Command,
  pub cancelled: AsyncLatch,
  pub stdin_fd: RawFd,
  pub stdout_fd: RawFd,
  pub stderr_fd: RawFd,
}

///
/// Implementations of this trait should _not_ close the input file handles, and should let the
/// caller do so instead.
///
pub trait RawFdRunner: Fn(RawFdExecution) -> ExitCode + Send + Sync {}

impl<T: Fn(RawFdExecution) -> ExitCode + Send + Sync> RawFdRunner for T {}

///
/// A Nail implementation that proxies stdio to file handles that can be consumed by the given
/// callback function.
///
/// If any of stdio is a tty (detected via environment variables special cased in our nailgun
/// client), we ignore the input fds and open new fds directly to the tty path, which happens to
/// be addressable as a file in OSX and Linux. In that case, there is no middle-man, and we
/// ignore the fds opened by the nailgun server for data sent via the protocol.
///
#[derive(Clone)]
struct RawFdNail {
  executor: Executor,
  runner: Arc<dyn RawFdRunner>,
}

impl Nail for RawFdNail {
  fn spawn(&self, cmd: execution::Command) -> Result<nails::server::Child, io::Error> {
    let env = cmd.env.iter().cloned().collect::<HashMap<_, _>>();

    // Handle stdin.
    let (stdin_handle, stdin_sink) = Self::input(Self::ttypath_from_env(&env, 0))?;
    let maybe_stdin_write = if let Some(mut stdin_sink) = stdin_sink {
      let (stdin_write, stdin_read) = child_channel::<ChildInput>();
      // Spawn a task that will propagate the input stream.
      let _join = self.executor.spawn(async move {
        let mut input_stream = stdin_read.map(|child_input| match child_input {
          ChildInput::Stdin(bytes) => Ok(bytes),
        });
        let _ = stdin_sink.send_all(&mut input_stream).await;
      });
      Some(stdin_write)
    } else {
      // Stdin will be handled directly by the TTY.
      None
    };

    // And stdout/stderr.
    let (stdout_stream, stdout_handle) = Self::output(Self::ttypath_from_env(&env, 1), false)?;
    // N.B.: POSIX demands stderr is opened read-write and some programs, like pagers, rely on this.
    // See: https://pubs.opengroup.org/onlinepubs/9699919799/functions/stdin.html
    let (stderr_stream, stderr_handle) = Self::output(Self::ttypath_from_env(&env, 2), true)?;

    // Set up a cancellation token that is triggered on client shutdown.
    let cancelled = AsyncLatch::new();
    let shutdown = {
      let cancelled = cancelled.clone();
      async move {
        cancelled.trigger();
      }
    };

    // Spawn the underlying function as a blocking task, and capture its exit code to append to the
    // output stream.
    let nail = self.clone();
    let exit_code = self
      .executor
      .spawn_blocking(move || {
        // NB: This closure captures the stdio handles, and will drop/close them when it completes.
        (nail.runner)(RawFdExecution {
          cmd,
          cancelled,
          stdin_fd: stdin_handle.as_raw_fd(),
          stdout_fd: stdout_handle.as_raw_fd(),
          stderr_fd: stderr_handle.as_raw_fd(),
        })
      })
      .boxed();

    // Select a single stdout/stderr stream.
    let stdout_stream = stdout_stream.map_ok(ChildOutput::Stdout);
    let stderr_stream = stderr_stream.map_ok(ChildOutput::Stderr);
    let output_stream = stream::select(stdout_stream, stderr_stream).boxed();

    Ok(nails::server::Child::new(
      output_stream,
      maybe_stdin_write,
      exit_code,
      Some(shutdown.boxed()),
    ))
  }
}

impl RawFdNail {
  ///
  /// Returns a tuple of a readable file handle and an optional sink for nails to send stdin to.
  ///
  /// In the case of a TTY, the file handle will point directly to the TTY, and no stdin data will
  /// flow over the protocol. Otherwise, it will be backed by a new anonymous pipe, and data should
  /// be copied to the returned Sink.
  ///
  fn input(
    tty_path: Option<PathBuf>,
  ) -> Result<(Box<dyn AsRawFd + Send>, Option<impl sink::Sink<Bytes>>), io::Error> {
    if let Some(tty) = Self::try_open_tty(tty_path, OpenOptions::new().read(true)) {
      Ok((Box::new(tty), None))
    } else {
      let (stdin_reader, stdin_writer) = os_pipe::pipe()?;
      let write_handle =
        File::from_std(unsafe { std::fs::File::from_raw_fd(stdin_writer.into_raw_fd()) });
      Ok((Box::new(stdin_reader), Some(sink_for(write_handle))))
    }
  }

  ///
  /// Returns a tuple of a possibly empty Stream for nails to read data from, and a writable file handle.
  ///
  /// See `Self::input` and the struct's rustdoc for more info on the TTY case.
  ///
  #[allow(clippy::type_complexity)]
  fn output(
    tty_path: Option<PathBuf>,
    read_write: bool,
  ) -> Result<
    (
      stream::BoxStream<'static, Result<Bytes, io::Error>>,
      Box<dyn AsRawFd + Send>,
    ),
    io::Error,
  > {
    if let Some(tty) = Self::try_open_tty(
      tty_path,
      OpenOptions::new()
        .read(read_write)
        .write(true)
        .create(false),
    ) {
      Ok((stream::empty().boxed(), Box::new(tty)))
    } else {
      let (stdin_reader, stdin_writer) = os_pipe::pipe()?;
      let read_handle =
        File::from_std(unsafe { std::fs::File::from_raw_fd(stdin_reader.into_raw_fd()) });
      Ok((stream_for(read_handle).boxed(), Box::new(stdin_writer)))
    }
  }

  ///
  /// Attempt to open the given TTY-path, logging any errors.
  ///
  fn try_open_tty(tty_path: Option<PathBuf>, open_options: &OpenOptions) -> Option<std::fs::File> {
    let tty_path = tty_path?;
    open_options
      .open(&tty_path)
      .map_err(|e| {
        log::debug!(
          "Failed to open TTY at {}: {:?}, falling back to socket access.",
          tty_path.display(),
          e
        );
      })
      .ok()
  }

  ///
  /// Corresponds to `ttynames_to_env` in `nailgun_protocol.py`. See this struct's rustdocs.
  ///
  fn ttypath_from_env(env: &HashMap<String, String>, fd_number: usize) -> Option<PathBuf> {
    env
      .get(&format!("NAILGUN_TTY_PATH_{}", fd_number))
      .map(PathBuf::from)
  }
}
<|endoftext|>


<commit_before>/*!

Rust MIR: a lowered representation of Rust. Also: an experiment!

*/

#![feature(nll)]
#![feature(in_band_lifetimes)]
#![feature(inner_deref)]
#![feature(bool_to_option)]
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(crate_visibility_modifier)]
#![feature(core_intrinsics)]
#![feature(decl_macro)]
#![feature(drain_filter)]
#![feature(exhaustive_patterns)]
#![feature(iter_order_by)]
#![feature(never_type)]
#![feature(specialization)]
#![feature(try_trait)]
#![feature(unicode_internals)]
#![feature(slice_concat_ext)]
#![feature(trusted_len)]
#![feature(try_blocks)]
#![feature(associated_type_bounds)]
#![feature(range_is_empty)]
#![feature(stmt_expr_attributes)]
#![feature(trait_alias)]
#![recursion_limit = "256"]

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

mod borrow_check;
pub mod const_eval;
pub mod dataflow;
pub mod interpret;
pub mod monomorphize;
mod shim;
pub mod transform;
pub mod util;

use rustc::ty::query::Providers;

pub fn provide(providers: &mut Providers<'_>) {
    borrow_check::provide(providers);
    const_eval::provide(providers);
    shim::provide(providers);
    transform::provide(providers);
    monomorphize::partitioning::provide(providers);
    providers.const_eval_validated = const_eval::const_eval_validated_provider;
    providers.const_eval_raw = const_eval::const_eval_raw_provider;
    providers.const_caller_location = const_eval::const_caller_location;
    providers.const_field = |tcx, param_env_and_value| {
        let (param_env, (value, field)) = param_env_and_value.into_parts();
        const_eval::const_field(tcx, param_env, None, field, value)
    };
    providers.destructure_const = |tcx, param_env_and_value| {
        let (param_env, value) = param_env_and_value.into_parts();
        const_eval::destructure_const(tcx, param_env, value)
    };
}
<commit_msg>Remove unused feature gates from librustc_mir<commit_after>/*!

Rust MIR: a lowered representation of Rust. Also: an experiment!

*/

#![feature(nll)]
#![feature(in_band_lifetimes)]
#![feature(bool_to_option)]
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(crate_visibility_modifier)]
#![feature(drain_filter)]
#![feature(exhaustive_patterns)]
#![feature(iter_order_by)]
#![feature(never_type)]
#![feature(specialization)]
#![feature(trusted_len)]
#![feature(try_blocks)]
#![feature(associated_type_bounds)]
#![feature(range_is_empty)]
#![feature(stmt_expr_attributes)]
#![feature(trait_alias)]
#![recursion_limit = "256"]

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

mod borrow_check;
pub mod const_eval;
pub mod dataflow;
pub mod interpret;
pub mod monomorphize;
mod shim;
pub mod transform;
pub mod util;

use rustc::ty::query::Providers;

pub fn provide(providers: &mut Providers<'_>) {
    borrow_check::provide(providers);
    const_eval::provide(providers);
    shim::provide(providers);
    transform::provide(providers);
    monomorphize::partitioning::provide(providers);
    providers.const_eval_validated = const_eval::const_eval_validated_provider;
    providers.const_eval_raw = const_eval::const_eval_raw_provider;
    providers.const_caller_location = const_eval::const_caller_location;
    providers.const_field = |tcx, param_env_and_value| {
        let (param_env, (value, field)) = param_env_and_value.into_parts();
        const_eval::const_field(tcx, param_env, None, field, value)
    };
    providers.destructure_const = |tcx, param_env_and_value| {
        let (param_env, value) = param_env_and_value.into_parts();
        const_eval::destructure_const(tcx, param_env, value)
    };
}
<|endoftext|>


<commit_before>use form_urlencoded::Serializer as Encoder;
use matches::assert_matches;
use ruma_serde::urlencoded;
use serde::Deserialize;

#[derive(Deserialize, Debug, PartialEq)]
struct NewType<T>(T);

#[test]
fn deserialize_newtype_i32() {
    let result = vec![("field".to_owned(), NewType(11))];

    assert_eq!(urlencoded::from_str("field=11"), Ok(result));
}

#[test]
fn deserialize_bytes() {
    let result = vec![("first".to_owned(), 23), ("last".to_owned(), 42)];

    assert_eq!(urlencoded::from_bytes(b"first=23&last=42"), Ok(result));
}

#[test]
fn deserialize_str() {
    let result = vec![("first".to_owned(), 23), ("last".to_owned(), 42)];

    assert_eq!(urlencoded::from_str("first=23&last=42"), Ok(result));
}

#[test]
fn deserialize_borrowed_str() {
    let result = vec![("first", 23), ("last", 42)];

    assert_eq!(urlencoded::from_str("first=23&last=42"), Ok(result));
}

#[test]
fn deserialize_reader() {
    let result = vec![("first".to_owned(), 23), ("last".to_owned(), 42)];

    assert_eq!(urlencoded::from_reader(b"first=23&last=42" as &[_]), Ok(result));
}

#[test]
fn deserialize_option() {
    let result = vec![("first".to_owned(), Some(23)), ("last".to_owned(), Some(42))];
    assert_eq!(urlencoded::from_str("first=23&last=42"), Ok(result));
}

#[test]
fn deserialize_unit() {
    assert_eq!(urlencoded::from_str(""), Ok(()));
    assert_eq!(urlencoded::from_str("&"), Ok(()));
    assert_eq!(urlencoded::from_str("&&"), Ok(()));
    assert!(urlencoded::from_str::<()>("first=23").is_err());
}

#[derive(Deserialize, Debug, PartialEq, Eq)]
enum X {
    A,
    B,
    C,
}

#[test]
fn deserialize_unit_enum() {
    let result: Vec<(String, X)> = urlencoded::from_str("one=A&two=B&three=C").unwrap();

    assert_eq!(result.len(), 3);
    assert!(result.contains(&("one".to_owned(), X::A)));
    assert!(result.contains(&("two".to_owned(), X::B)));
    assert!(result.contains(&("three".to_owned(), X::C)));
}

#[test]
fn deserialize_unit_type() {
    assert_eq!(urlencoded::from_str(""), Ok(()));
}

#[derive(Clone, Copy, Debug, PartialEq, Deserialize)]
struct Params<'a> {
    a: usize,
    b: &'a str,
    c: Option<u8>,
}

#[test]
fn deserialize_struct() {
    let de = Params { a: 10, b: "Hello", c: None };
    assert_eq!(urlencoded::from_str("a=10&b=Hello"), Ok(de));
    assert_eq!(urlencoded::from_str("b=Hello&a=10"), Ok(de));
    assert_eq!(urlencoded::from_str("a=10&b=Hello&d=1&d=2"), Ok(de));
}

#[test]
fn deserialize_list_of_str() {
    // TODO: It would make sense to support this.
    assert_matches!(
        urlencoded::from_str::<Vec<(&str, &str)>>("a=a&a=b"),
        Err(error) if error.to_string().contains("unsupported")
    );

    assert_eq!(urlencoded::from_str("a=a&a=b"), Ok(vec![("a", vec!["a", "b"])]))
}

#[test]
fn deserialize_multiple_lists() {
    #[derive(Debug, PartialEq, Deserialize)]
    struct Lists {
        xs: Vec<bool>,
        ys: Vec<u32>,
    }

    assert_eq!(
        urlencoded::from_str("xs=true&xs=false&ys=3&ys=2&ys=1"),
        Ok(Lists { xs: vec![true, false], ys: vec![3, 2, 1] })
    );

    assert_eq!(
        urlencoded::from_str("ys=3&xs=true&ys=2&xs=false&ys=1"),
        Ok(Lists { xs: vec![true, false], ys: vec![3, 2, 1] })
    );
}

#[test]
fn deserialize_with_serde_attributes() {
    use std::time::{Duration, SystemTime, UNIX_EPOCH};

    #[derive(Debug, PartialEq, Deserialize)]
    struct FieldsWithAttributes {
        #[serde(default)]
        xs: Vec<bool>,
        #[serde(default)]
        def: Option<u8>,
        #[serde(
            default,
            deserialize_with = "ruma_serde::time::opt_ms_since_unix_epoch::deserialize"
        )]
        time: Option<SystemTime>,
        #[serde(default)]
        flag: bool,
    }

    assert_eq!(
        urlencoded::from_str("xs=true&xs=false&def=3&time=1&flag=true"),
        Ok(FieldsWithAttributes {
            xs: vec![true, false],
            def: Some(3),
            time: Some(UNIX_EPOCH + Duration::from_millis(1)),
            flag: true,
        })
    );

    assert_eq!(
        urlencoded::from_str(""),
        Ok(FieldsWithAttributes { xs: vec![], def: None, time: None, flag: false })
    );
}

#[test]
fn deserialize_nested_list() {
    assert!(urlencoded::from_str::<Vec<(&str, Vec<Vec<bool>>)>>("a=b").is_err());
}

#[test]
fn deserialize_list_of_option() {
    assert_eq!(
        urlencoded::from_str("list=10&list=100"),
        Ok(vec![("list", vec![Some(10), Some(100)])])
    );
}

#[test]
fn deserialize_list_of_newtype() {
    assert_eq!(urlencoded::from_str("list=test"), Ok(vec![("list", vec![NewType("test")])]));
}

#[test]
fn deserialize_list_of_enum() {
    assert_eq!(
        urlencoded::from_str("item=A&item=B&item=C"),
        Ok(vec![("item", vec![X::A, X::B, X::C])])
    );
}

#[derive(Debug, Deserialize, PartialEq)]
struct Wrapper<T> {
    item: T,
}

#[derive(Debug, PartialEq, Deserialize)]
struct NewStruct<'a> {
    #[serde(borrow)]
    list: Vec<&'a str>,
}

#[derive(Debug, PartialEq, Deserialize)]
struct Struct<'a> {
    #[serde(borrow)]
    list: Vec<Option<&'a str>>,
}

#[derive(Debug, PartialEq, Deserialize)]
struct NumList {
    list: Vec<u8>,
}

#[derive(Debug, PartialEq, Deserialize)]
struct ListStruct {
    list: Vec<NewType<usize>>,
}

#[test]
fn deserialize_newstruct() {
    let de = NewStruct { list: vec!["hello", "world"] };
    assert_eq!(urlencoded::from_str("list=hello&list=world"), Ok(de));
}

#[test]
fn deserialize_numlist() {
    let de = NumList { list: vec![1, 2, 3, 4] };
    assert_eq!(urlencoded::from_str("list=1&list=2&list=3&list=4"), Ok(de));
}

#[derive(Debug, Deserialize, PartialEq)]
struct Nested<T> {
    item: T,
}

#[derive(Debug, Deserialize, PartialEq)]
struct Inner<'a> {
    c: &'a str,
    a: usize,
    b: &'a str,
}

#[derive(Debug, Deserialize, PartialEq)]
struct InnerList<T> {
    list: Vec<T>,
}

#[test]
#[ignore]
fn deserialize_nested_struct() {
    let mut encoder = Encoder::new(String::new());

    let nested = Nested { item: Inner { c: "hello", a: 10, b: "bye" } };
    assert_eq!(
        urlencoded::from_str(
            &encoder.append_pair("item", r#"{"c":"hello","a":10,"b":"bye"}"#).finish(),
        ),
        Ok(nested)
    );
}

#[test]
#[ignore]
fn deserialize_nested_struct_with_list() {
    let mut encoder = Encoder::new(String::new());

    let nested = Nested { item: InnerList { list: vec![1, 2, 3] } };

    assert_eq!(
        urlencoded::from_str(&encoder.append_pair("item", r#"{"list":[1,2,3]}"#).finish()),
        Ok(nested)
    );
}

#[test]
#[ignore]
fn deserialize_nested_list_option() {
    let mut encoder = Encoder::new(String::new());

    let nested = Nested { item: InnerList { list: vec![Some(1), Some(2), None] } };
    assert_eq!(
        urlencoded::from_str(&encoder.append_pair("item", r#"{"list":[1,2,null]}"#).finish()),
        Ok(nested)
    );
}
<commit_msg>serde: Fix url_deserialize test<commit_after>use form_urlencoded::Serializer as Encoder;
use matches::assert_matches;
use ruma_serde::urlencoded;
use serde::Deserialize;

#[derive(Deserialize, Debug, PartialEq)]
struct NewType<T>(T);

#[test]
fn deserialize_newtype_i32() {
    let result = vec![("field".to_owned(), NewType(11))];

    assert_eq!(urlencoded::from_str("field=11"), Ok(result));
}

#[test]
fn deserialize_bytes() {
    let result = vec![("first".to_owned(), 23), ("last".to_owned(), 42)];

    assert_eq!(urlencoded::from_bytes(b"first=23&last=42"), Ok(result));
}

#[test]
fn deserialize_str() {
    let result = vec![("first".to_owned(), 23), ("last".to_owned(), 42)];

    assert_eq!(urlencoded::from_str("first=23&last=42"), Ok(result));
}

#[test]
fn deserialize_borrowed_str() {
    let result = vec![("first", 23), ("last", 42)];

    assert_eq!(urlencoded::from_str("first=23&last=42"), Ok(result));
}

#[test]
fn deserialize_reader() {
    let result = vec![("first".to_owned(), 23), ("last".to_owned(), 42)];

    assert_eq!(urlencoded::from_reader(b"first=23&last=42" as &[_]), Ok(result));
}

#[test]
fn deserialize_option() {
    let result = vec![("first".to_owned(), Some(23)), ("last".to_owned(), Some(42))];
    assert_eq!(urlencoded::from_str("first=23&last=42"), Ok(result));
}

#[test]
fn deserialize_unit() {
    assert_eq!(urlencoded::from_str(""), Ok(()));
    assert_eq!(urlencoded::from_str("&"), Ok(()));
    assert_eq!(urlencoded::from_str("&&"), Ok(()));
    assert!(urlencoded::from_str::<()>("first=23").is_err());
}

#[derive(Deserialize, Debug, PartialEq, Eq)]
enum X {
    A,
    B,
    C,
}

#[test]
fn deserialize_unit_enum() {
    let result: Vec<(String, X)> = urlencoded::from_str("one=A&two=B&three=C").unwrap();

    assert_eq!(result.len(), 3);
    assert!(result.contains(&("one".to_owned(), X::A)));
    assert!(result.contains(&("two".to_owned(), X::B)));
    assert!(result.contains(&("three".to_owned(), X::C)));
}

#[test]
fn deserialize_unit_type() {
    assert_eq!(urlencoded::from_str(""), Ok(()));
}

#[derive(Clone, Copy, Debug, PartialEq, Deserialize)]
struct Params<'a> {
    a: usize,
    b: &'a str,
    c: Option<u8>,
}

#[test]
fn deserialize_struct() {
    let de = Params { a: 10, b: "Hello", c: None };
    assert_eq!(urlencoded::from_str("a=10&b=Hello"), Ok(de));
    assert_eq!(urlencoded::from_str("b=Hello&a=10"), Ok(de));
    assert_eq!(urlencoded::from_str("a=10&b=Hello&d=1&d=2"), Ok(de));
}

#[test]
fn deserialize_list_of_str() {
    // TODO: It would make sense to support this.
    assert_matches!(
        urlencoded::from_str::<Vec<(&str, &str)>>("a=a&a=b"),
        Err(error) if error.to_string().contains("unsupported")
    );

    assert_eq!(urlencoded::from_str("a=a&a=b"), Ok(vec![("a", vec!["a", "b"])]))
}

#[test]
fn deserialize_multiple_lists() {
    #[derive(Debug, PartialEq, Deserialize)]
    struct Lists {
        xs: Vec<bool>,
        ys: Vec<u32>,
    }

    assert_eq!(
        urlencoded::from_str("xs=true&xs=false&ys=3&ys=2&ys=1"),
        Ok(Lists { xs: vec![true, false], ys: vec![3, 2, 1] })
    );

    assert_eq!(
        urlencoded::from_str("ys=3&xs=true&ys=2&xs=false&ys=1"),
        Ok(Lists { xs: vec![true, false], ys: vec![3, 2, 1] })
    );
}

#[test]
fn deserialize_with_serde_attributes() {
    #[derive(Debug, PartialEq, Deserialize)]
    struct FieldsWithAttributes {
        #[serde(default)]
        xs: Vec<bool>,
        #[serde(default)]
        def: Option<u8>,
        #[serde(default, deserialize_with = "ruma_serde::empty_string_as_none")]
        str: Option<String>,
        #[serde(default)]
        flag: bool,
    }

    assert_eq!(
        urlencoded::from_str("xs=true&xs=false&def=3&str=&flag=true"),
        Ok(FieldsWithAttributes { xs: vec![true, false], def: Some(3), str: None, flag: true })
    );

    assert_eq!(
        urlencoded::from_str(""),
        Ok(FieldsWithAttributes { xs: vec![], def: None, str: None, flag: false })
    );
}

#[test]
fn deserialize_nested_list() {
    assert!(urlencoded::from_str::<Vec<(&str, Vec<Vec<bool>>)>>("a=b").is_err());
}

#[test]
fn deserialize_list_of_option() {
    assert_eq!(
        urlencoded::from_str("list=10&list=100"),
        Ok(vec![("list", vec![Some(10), Some(100)])])
    );
}

#[test]
fn deserialize_list_of_newtype() {
    assert_eq!(urlencoded::from_str("list=test"), Ok(vec![("list", vec![NewType("test")])]));
}

#[test]
fn deserialize_list_of_enum() {
    assert_eq!(
        urlencoded::from_str("item=A&item=B&item=C"),
        Ok(vec![("item", vec![X::A, X::B, X::C])])
    );
}

#[derive(Debug, Deserialize, PartialEq)]
struct Wrapper<T> {
    item: T,
}

#[derive(Debug, PartialEq, Deserialize)]
struct NewStruct<'a> {
    #[serde(borrow)]
    list: Vec<&'a str>,
}

#[derive(Debug, PartialEq, Deserialize)]
struct Struct<'a> {
    #[serde(borrow)]
    list: Vec<Option<&'a str>>,
}

#[derive(Debug, PartialEq, Deserialize)]
struct NumList {
    list: Vec<u8>,
}

#[derive(Debug, PartialEq, Deserialize)]
struct ListStruct {
    list: Vec<NewType<usize>>,
}

#[test]
fn deserialize_newstruct() {
    let de = NewStruct { list: vec!["hello", "world"] };
    assert_eq!(urlencoded::from_str("list=hello&list=world"), Ok(de));
}

#[test]
fn deserialize_numlist() {
    let de = NumList { list: vec![1, 2, 3, 4] };
    assert_eq!(urlencoded::from_str("list=1&list=2&list=3&list=4"), Ok(de));
}

#[derive(Debug, Deserialize, PartialEq)]
struct Nested<T> {
    item: T,
}

#[derive(Debug, Deserialize, PartialEq)]
struct Inner<'a> {
    c: &'a str,
    a: usize,
    b: &'a str,
}

#[derive(Debug, Deserialize, PartialEq)]
struct InnerList<T> {
    list: Vec<T>,
}

#[test]
#[ignore]
fn deserialize_nested_struct() {
    let mut encoder = Encoder::new(String::new());

    let nested = Nested { item: Inner { c: "hello", a: 10, b: "bye" } };
    assert_eq!(
        urlencoded::from_str(
            &encoder.append_pair("item", r#"{"c":"hello","a":10,"b":"bye"}"#).finish(),
        ),
        Ok(nested)
    );
}

#[test]
#[ignore]
fn deserialize_nested_struct_with_list() {
    let mut encoder = Encoder::new(String::new());

    let nested = Nested { item: InnerList { list: vec![1, 2, 3] } };

    assert_eq!(
        urlencoded::from_str(&encoder.append_pair("item", r#"{"list":[1,2,3]}"#).finish()),
        Ok(nested)
    );
}

#[test]
#[ignore]
fn deserialize_nested_list_option() {
    let mut encoder = Encoder::new(String::new());

    let nested = Nested { item: InnerList { list: vec![Some(1), Some(2), None] } };
    assert_eq!(
        urlencoded::from_str(&encoder.append_pair("item", r#"{"list":[1,2,null]}"#).finish()),
        Ok(nested)
    );
}
<|endoftext|>