<commit_before>//! Macros to define CSS properties.

pub trait Property<T> {
    fn inherits_automatically() -> bool;
    fn compute(&self, _: &T) -> Self;
}

/// Generates a property definition that simply parses strings to enum variants
/// or to a tuple struct of the given type.
///
/// For example, the SVG spec defines the `stroke-linejoin` property
/// to have possible values `miter | round | bevel | inherit`, with a default
/// of `miter`.  We can define the property like this:
///
/// ```ignore
/// make_property!(
/// StrokeLinejoin,
/// default: Miter,
///
/// "miter" => Miter,
/// "round" => Round,
/// "bevel" => Bevel,
/// );
/// ```
///
/// The macro will generate a `StrokeLinejoin` enum with the provided
/// variants.  It will generate an `impl Default for StrokeLinejoin`
/// with the provided `default:` value.  Finally, it will generate an
/// `impl Parse for StrokeLinejoin`, from `parsers::Parse`, with
/// `type Err = ValueErrorKind`.
#[macro_export]
macro_rules! make_property {
    ($computed_values_type: ty,
     $name: ident,
     default: $default: ident,
     inherits_automatically: $inherits_automatically: expr,
     identifiers:
     $($str_prop: expr => $variant: ident,)+
    ) => {
        #[derive(Debug, Copy, Clone, PartialEq)]
        pub enum $name {
            $($variant),+
        }

        impl_default!($name, $name::$default);
        impl_property!($computed_values_type, $name, $inherits_automatically);

        impl crate::parsers::Parse for $name {
            fn parse(parser: &mut ::cssparser::Parser<'_, '_>) -> Result<$name, crate::error::ValueErrorKind> {
                let loc = parser.current_source_location();

                parser
                    .expect_ident()
                    .and_then(|cow| match cow.as_ref() {
                        $($str_prop => Ok($name::$variant),)+

                            _ => Err(
                                loc.new_basic_unexpected_token_error(
                                    ::cssparser::Token::Ident(::cssparser::CowRcStr::from(
                                        cow.as_ref().to_string(),
                                    ))),
                            ),
                    })
                    .map_err(|_| {
                        crate::error::ValueErrorKind::parse_error("unexpected value")
                    })
            }
        }
    };

    ($computed_values_type: ty,
     $name: ident,
     default: $default: expr,
     inherits_automatically: $inherits_automatically: expr,
     newtype_parse: $type: ty,
    ) => {
        #[derive(Debug, Clone, PartialEq)]
        pub struct $name(pub $type);

        impl_default!($name, $name($default));
        impl_property!($computed_values_type, $name, $inherits_automatically);

        impl crate::parsers::Parse for $name {
            fn parse(parser: &mut ::cssparser::Parser<'_, '_>) -> Result<$name, crate::error::ValueErrorKind> {
                Ok($name(<$type as crate::parsers::Parse>::parse(parser)?))
            }
        }
    };

    ($computed_values_type: ty,
     $name: ident,
     default: $default: expr,
     newtype_parse: $type: ty,
     property_impl: { $prop: item }
    ) => {
        #[derive(Debug, Clone, PartialEq)]
        pub struct $name(pub $type);

        impl_default!($name, $name($default));

        $prop

        impl crate::parsers::Parse for $name {
            fn parse(parser: &mut ::cssparser::Parser<'_, '_>) -> Result<$name, crate::error::ValueErrorKind> {
                Ok($name(<$type as crate::parsers::Parse>::parse(parser)?))
            }
        }
    };

    ($computed_values_type: ty,
     $name: ident,
     default: $default: expr,
     newtype: $type: ty,
     property_impl: { $prop: item },
     parse_impl: { $parse: item }
    ) => {
        #[derive(Debug, Clone, PartialEq)]
        pub struct $name(pub $type);

        impl_default!($name, $name($default));

        $prop

        $parse
    };

    ($computed_values_type: ty,
     $name: ident,
     default: $default: expr,
     inherits_automatically: $inherits_automatically: expr,
     newtype: $type: ty,
     parse_impl: { $parse: item },
    ) => {
        #[derive(Debug, Clone, PartialEq)]
        pub struct $name(pub $type);

        impl_default!($name, $name($default));
        impl_property!($computed_values_type, $name, $inherits_automatically);

        $parse
    };

    ($computed_values_type: ty,
     $name: ident,
     inherits_automatically: $inherits_automatically: expr,
     fields: {
       $($field_name: ident : $field_type: ty, default: $field_default : expr,)+
     }
     parse_impl: { $parse: item }
    ) => {
        #[derive(Debug, Clone, PartialEq)]
        pub struct $name {
            $(pub $field_name: $field_type),+
        }

        impl_default!($name, $name { $($field_name: $field_default),+ });
        impl_property!($computed_values_type, $name, $inherits_automatically);

        $parse
    };
}

macro_rules! impl_default {
    ($name:ident, $default:expr) => {
        impl Default for $name {
            fn default() -> $name {
                $default
            }
        }
    };
}

macro_rules! impl_property {
    ($computed_values_type:ty, $name:ident, $inherits_automatically:expr) => {
        impl crate::property_macros::Property<$computed_values_type> for $name {
            fn inherits_automatically() -> bool {
                $inherits_automatically
            }

            fn compute(&self, _v: &$computed_values_type) -> Self {
                self.clone()
            }
        }
    };
}

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

    use crate::parsers::Parse;
    use cssparser::RGBA;

    #[test]
    fn check_generated_property() {
        make_property! {
            (),
            Foo,
            default: Def,
            inherits_automatically: true,

            identifiers:
            "def" => Def,
            "bar" => Bar,
            "baz" => Baz,
        }

        assert_eq!(<Foo as Default>::default(), Foo::Def);
        assert_eq!(<Foo as Property<()>>::inherits_automatically(), true);
        assert!(<Foo as Parse>::parse_str("blargh").is_err());
        assert_eq!(<Foo as Parse>::parse_str("bar"), Ok(Foo::Bar));
    }

    #[test]
    fn check_compute() {
        make_property! {
            RGBA,
            AddColor,
            default: RGBA::new(1, 1, 1, 1),
            newtype_parse: RGBA,
            property_impl: {
                impl Property<RGBA> for AddColor {
                    fn inherits_automatically() -> bool {
                        true
                    }

                    fn compute(&self, v: &RGBA) -> Self {
                        AddColor(RGBA::new(
                            self.0.red + v.red,
                            self.0.green + v.green,
                            self.0.blue + v.blue,
                            self.0.alpha + v.alpha
                        ))
                    }
                }
            }
        }

        let color = RGBA::new(1, 1, 1, 1);
        let a = <AddColor as Parse>::parse_str("#02030405").unwrap();
        let b = a.compute(&color);

        assert_eq!(b, AddColor(RGBA::new(3, 4, 5, 6)));
    }
}
<commit_msg>property_macros: use parse_identifiers! for the identifiers: case<commit_after>//! Macros to define CSS properties.

pub trait Property<T> {
    fn inherits_automatically() -> bool;
    fn compute(&self, _: &T) -> Self;
}

/// Generates a property definition that simply parses strings to enum variants
/// or to a tuple struct of the given type.
///
/// For example, the SVG spec defines the `stroke-linejoin` property
/// to have possible values `miter | round | bevel | inherit`, with a default
/// of `miter`.  We can define the property like this:
///
/// ```ignore
/// make_property!(
/// StrokeLinejoin,
/// default: Miter,
///
/// "miter" => Miter,
/// "round" => Round,
/// "bevel" => Bevel,
/// );
/// ```
///
/// The macro will generate a `StrokeLinejoin` enum with the provided
/// variants.  It will generate an `impl Default for StrokeLinejoin`
/// with the provided `default:` value.  Finally, it will generate an
/// `impl Parse for StrokeLinejoin`, from `parsers::Parse`, with
/// `type Err = ValueErrorKind`.
#[macro_export]
macro_rules! make_property {
    ($computed_values_type: ty,
     $name: ident,
     default: $default: ident,
     inherits_automatically: $inherits_automatically: expr,
     identifiers:
     $($str_prop: expr => $variant: ident,)+
    ) => {
        #[derive(Debug, Copy, Clone, PartialEq)]
        pub enum $name {
            $($variant),+
        }

        impl_default!($name, $name::$default);
        impl_property!($computed_values_type, $name, $inherits_automatically);

        impl crate::parsers::Parse for $name {
            fn parse(parser: &mut ::cssparser::Parser<'_, '_>) -> Result<$name, crate::error::ValueErrorKind> {
                parse_identifiers!(
                    parser,
                    $($str_prop => $name::$variant,)+
                ).map_err(|_e: crate::error::ParseError| crate::error::ValueErrorKind::parse_error("parse error"))
            }
        }
    };

    ($computed_values_type: ty,
     $name: ident,
     default: $default: expr,
     inherits_automatically: $inherits_automatically: expr,
     newtype_parse: $type: ty,
    ) => {
        #[derive(Debug, Clone, PartialEq)]
        pub struct $name(pub $type);

        impl_default!($name, $name($default));
        impl_property!($computed_values_type, $name, $inherits_automatically);

        impl crate::parsers::Parse for $name {
            fn parse(parser: &mut ::cssparser::Parser<'_, '_>) -> Result<$name, crate::error::ValueErrorKind> {
                Ok($name(<$type as crate::parsers::Parse>::parse(parser)?))
            }
        }
    };

    ($computed_values_type: ty,
     $name: ident,
     default: $default: expr,
     newtype_parse: $type: ty,
     property_impl: { $prop: item }
    ) => {
        #[derive(Debug, Clone, PartialEq)]
        pub struct $name(pub $type);

        impl_default!($name, $name($default));

        $prop

        impl crate::parsers::Parse for $name {
            fn parse(parser: &mut ::cssparser::Parser<'_, '_>) -> Result<$name, crate::error::ValueErrorKind> {
                Ok($name(<$type as crate::parsers::Parse>::parse(parser)?))
            }
        }
    };

    ($computed_values_type: ty,
     $name: ident,
     default: $default: expr,
     newtype: $type: ty,
     property_impl: { $prop: item },
     parse_impl: { $parse: item }
    ) => {
        #[derive(Debug, Clone, PartialEq)]
        pub struct $name(pub $type);

        impl_default!($name, $name($default));

        $prop

        $parse
    };

    ($computed_values_type: ty,
     $name: ident,
     default: $default: expr,
     inherits_automatically: $inherits_automatically: expr,
     newtype: $type: ty,
     parse_impl: { $parse: item },
    ) => {
        #[derive(Debug, Clone, PartialEq)]
        pub struct $name(pub $type);

        impl_default!($name, $name($default));
        impl_property!($computed_values_type, $name, $inherits_automatically);

        $parse
    };

    ($computed_values_type: ty,
     $name: ident,
     inherits_automatically: $inherits_automatically: expr,
     fields: {
       $($field_name: ident : $field_type: ty, default: $field_default : expr,)+
     }
     parse_impl: { $parse: item }
    ) => {
        #[derive(Debug, Clone, PartialEq)]
        pub struct $name {
            $(pub $field_name: $field_type),+
        }

        impl_default!($name, $name { $($field_name: $field_default),+ });
        impl_property!($computed_values_type, $name, $inherits_automatically);

        $parse
    };
}

macro_rules! impl_default {
    ($name:ident, $default:expr) => {
        impl Default for $name {
            fn default() -> $name {
                $default
            }
        }
    };
}

macro_rules! impl_property {
    ($computed_values_type:ty, $name:ident, $inherits_automatically:expr) => {
        impl crate::property_macros::Property<$computed_values_type> for $name {
            fn inherits_automatically() -> bool {
                $inherits_automatically
            }

            fn compute(&self, _v: &$computed_values_type) -> Self {
                self.clone()
            }
        }
    };
}

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

    use crate::parsers::Parse;
    use cssparser::RGBA;

    #[test]
    fn check_generated_property() {
        make_property! {
            (),
            Foo,
            default: Def,
            inherits_automatically: true,

            identifiers:
            "def" => Def,
            "bar" => Bar,
            "baz" => Baz,
        }

        assert_eq!(<Foo as Default>::default(), Foo::Def);
        assert_eq!(<Foo as Property<()>>::inherits_automatically(), true);
        assert!(<Foo as Parse>::parse_str("blargh").is_err());
        assert_eq!(<Foo as Parse>::parse_str("bar"), Ok(Foo::Bar));
    }

    #[test]
    fn check_compute() {
        make_property! {
            RGBA,
            AddColor,
            default: RGBA::new(1, 1, 1, 1),
            newtype_parse: RGBA,
            property_impl: {
                impl Property<RGBA> for AddColor {
                    fn inherits_automatically() -> bool {
                        true
                    }

                    fn compute(&self, v: &RGBA) -> Self {
                        AddColor(RGBA::new(
                            self.0.red + v.red,
                            self.0.green + v.green,
                            self.0.blue + v.blue,
                            self.0.alpha + v.alpha
                        ))
                    }
                }
            }
        }

        let color = RGBA::new(1, 1, 1, 1);
        let a = <AddColor as Parse>::parse_str("#02030405").unwrap();
        let b = a.compute(&color);

        assert_eq!(b, AddColor(RGBA::new(3, 4, 5, 6)));
    }
}
<|endoftext|>


<commit_before>
//! Repeat a stream multiple times.

use std::iter;

use event;
use modifier;
use transform;
use template;
use builder;

/// A stored stream representing an element if the parent stream does.
pub type MaybeElementTemplate<D> = modifier::Derived<template::TemplateStream, D>;

enum RepeatOrElseState<S, I, B, BE, SR>
where
    I: Iterator,
//    B: builder::BuildMutMapped<MaybeElementTemplate<S>, I::Item>,
    BE: builder::BuildOnce<S>,
{
    Start {
        stream: S,
        iter: I,
        builder: B,
        else_builder: BE,
    },
    Fallback {
        stream: BE::Stream,
    },
    Repeat {
        stream: SR,
    },
}

impl<S, I, B, BE, SR> RepeatOrElseState<S, I, B, BE, SR>
where
    S: event::Stream,
    SR: event::Stream,
    I: Iterator,
//    B: builder::BuildMutMapped<MaybeElementTemplate<S>, I::Item>,
    BE: builder::BuildOnce<S>,
{
    fn step<F>(self, stepper: F) -> modifier::StateResult<RepeatOrElseState<S, I, B, BE, SR>>
    where
        F: FnOnce(S, iter::Peekable, B) -> SR,
    {
        match self {
            RepeatOrElseState::Start { stream, iter, builder, else_builder } => {
                let mut iter = iter.peekable();
                if iter.peek().is_some() {
                    Ok(Some((event::noop(), Some(RepeatOrElseState::Repeat {
                        stream: stepper(stream, iter, builder),
                    }))))
                } else {
                    Ok(Some((event::noop(), Some(RepeatOrElseState::Fallback {
                        stream: transform::build_once(stream, else_builder),
                    }))))
                }
            },
            RepeatOrElseState::Fallback { stream } =>
                modifier::passthrough(stream, |stream| RepeatOrElseState::Fallback { stream }),
            RepeatOrElseState::Repeat { stream } =>
                modifier::passthrough(stream, |stream| RepeatOrElseState::Repeat { stream }),
        }
    }
}

/// Repeat a stream for each item of an iterator or run a fallback transformation.
pub struct RepeatOrElse<S, I, B, BE>
where
    I: Iterator,
    B: builder::BuildMutMapped<MaybeElementTemplate<S>, I::Item>,
    BE: builder::BuildOnce<S>,
{
    state: modifier::State<RepeatOrElseState<S, I, B, BE, Repeat<S, iter::Peekable, B>>>,
}

impl<S, I, B, BE> RepeatOrElse<S, I, B, BE>
where
    I: Iterator,
    B: builder::BuildMutMapped<MaybeElementTemplate<S>, I::Item>,
    BE: builder::BuildOnce<S>,
{
    pub(crate) fn new(
        stream: S,
        iter: I,
        builder: B,
        else_builder: BE,
    ) -> RepeatOrElse<S, I, B, BE> {
        RepeatOrElse {
            state: modifier::State::new(
                RepeatOrElseState::Start { stream, iter, builder, else_builder },
            ),
        }
    }
}

impl<S, I, B, BE> event::Stream for RepeatOrElse<S, I, B, BE>
where
    S: event::Stream,
    I: Iterator,
    B: builder::BuildMutMapped<MaybeElementTemplate<S>, I::Item>,
    BE: builder::BuildOnce<S>,
{
    fn next_event(&mut self) -> event::StreamResult {
        self.state.step(|state| state.step(
            |stream, iter, builder| Repeat::new(stream, iter, builder),
        ))
    }
}

/// Repeat a stream for each item of an iterator.
pub struct Repeat<S, I, B>
where
    I: Iterator,
    B: builder::BuildMutMapped<MaybeElementTemplate<S>, I::Item>,
{
    state: modifier::State<State<S, I, B, MaybeElementTemplate<S>>>,
}

impl<S, I, B> Repeat<S, I, B>
where
    I: Iterator,
    B: builder::BuildMutMapped<MaybeElementTemplate<S>, I::Item>,
{
    pub(crate) fn new(stream: S, iter: I, builder: B) -> Repeat<S, I, B> {
        Repeat {
            state: modifier::State::new(State::Collect { stream, iter, builder }),
        }
    }
}

impl<S, I, B> event::Stream for Repeat<S, I, B>
where
    S: event::Stream,
    I: Iterator,
    B: builder::BuildMutMapped<MaybeElementTemplate<S>, I::Item>,
{
    fn next_event(&mut self) -> event::StreamResult {
        self.state.step(|state| state.step(modifier::Derived::new))
    }
}

enum State<S, I, B, SB>
where
    I: Iterator,
    B: builder::BuildMutMapped<SB, I::Item>,
{
    Collect {
        stream: S,
        builder: B,
        iter: I,
    },
    Next {
        template: template::Template,
        builder: B,
        iter: I,
    },
    Emit {
        template: template::Template,
        stream: B::Stream,
        builder: B,
        iter: I,
    },
}

impl<S, I, B, SB> State<S, I, B, SB>
where
    I: Iterator,
    B: builder::BuildMutMapped<SB, I::Item>,
    S: event::Stream,
    SB: event::Stream,
{
    fn step<F>(self, stepper: F) -> modifier::StateResult<State<S, I, B, SB>>
    where
        F: FnOnce(template::TemplateStream) -> SB,
    {
        use event::{ Stream };
        match self {
            State::Collect { stream, builder, iter } => {
                let template = template::Template::from_stream(stream)?;
                Ok(Some((event::noop(), Some(State::Next { template, builder, iter }))))
            },
            State::Next { template, mut builder, mut iter } => {
                let item = match iter.next() {
                    Some(value) => value,
                    None => return Ok(None),
                };
                let stream = stepper(template.to_stream());
                let stream = transform::build_mut_mapped(stream, &mut builder, item);
                Ok(Some((event::noop(), Some(State::Emit { template, stream, builder, iter }))))
            },
            State::Emit { template, builder, iter, mut stream } => match stream.next_event()? {
                Some(event) => Ok(Some((
                    event,
                    Some(State::Emit { template, builder, iter, stream }),
                ))),
                None => Ok(Some((
                    event::noop(),
                    Some(State::Next { template, builder, iter }),
                ))),
            },
        }
    }
}

struct RepeatContentStream<S, I, B>
where
    I: Iterator,
    B: builder::BuildMutMapped<template::TemplateStream, I::Item>,
{
    state: modifier::State<State<S, I, B, template::TemplateStream>>,
}

impl<S, I, B> event::Stream for RepeatContentStream<S, I, B>
where
    S: event::Stream,
    I: Iterator,
    B: builder::BuildMutMapped<template::TemplateStream, I::Item>,
{
    fn next_event(&mut self) -> event::StreamResult {
        self.state.step(|state| state.step(|stream| stream))
    }
}

struct Builder {
    iter: I,
    builder: B,
}

impl<S, I, B> builder::BuildOnce<modifier::select::CurrentContent<S>> for Builder
where
    S: event::ElementStream,
    I: Iterator,
    B: builder::BuildMutMapped<template::TemplateStream, I::Item>,
{
    type Stream = RepeatContentStream<modifier::select::CurrentContent<S>, I, B>;

    fn build_once(self, stream: transform::Api<modifier::select::CurrentContent<S>>)
    -> transform::Api<Self::Stream> {
        transform::Api::pack(RepeatContentStream {
            state: modifier::State::new(State::Collect {
                stream: stream.unpack(),
                iter: self.iter,
                builder: self.builder,
            }),
        })
    }
}

/// Repeat the contents of the current element for each item of an iterator or run a
/// fallback transform.
pub struct RepeatContentOrElse<S, I, B, BE>
where
    S: event::ElementStream,
    I: Iterator,
    B: builder::BuildMutMapped<template::TemplateStream, I::Item>,
    BE: builder::BuildOnce<S>,
{
    state: modifier::State<
        RepeatOrElseState<S, I, B, BE, RepeatContent<S, iter::Peekable, B>>,
    >,
}

impl<S, I, B, BE> RepeatContentOrElse<S, I, B, BE>
where
    S: event::ElementStream,
    I: Iterator,
    B: builder::BuildMutMapped<template::TemplateStream, I::Item>,
    BE: builder::BuildOnce<S>,
{
    pub(crate) fn new(
        stream: S,
        iter: I,
        builder: B,
        else_builder: BE,
    ) -> RepeatContentOrElse<S, I, B, BE> {
        RepeatContentOrElse {
            state: modifier::State::new(
                RepeatOrElseState::Start { stream, iter, builder, else_builder },
            ),
        }
    }
}

impl<S, I, B, BE> event::Stream for RepeatContentOrElse<S, I, B, BE>
where
    S: event::ElementStream,
    I: Iterator,
    B: builder::BuildMutMapped<template::TemplateStream, I::Item>,
    BE: builder::BuildOnce<S>,
{
    fn next_event(&mut self) -> event::StreamResult {
        self.state.step(|state| state.step(
            |stream, iter, builder| RepeatContent::new(stream, iter, builder),
        ))
    }
}

/// Repeat the contents of the current element for each item of an iterator.
pub struct RepeatContent<S, I, B>
where
    S: event::ElementStream,
    I: Iterator,
    B: builder::BuildMutMapped<template::TemplateStream, I::Item>,
{
    stream: modifier::select::SelectContent<S, Builder>,
}

impl<S, I, B> RepeatContent<S, I, B>
where
    S: event::ElementStream,
    I: Iterator,
    B: builder::BuildMutMapped<template::TemplateStream, I::Item>,
{
    pub(crate) fn new(stream: S, iter: I, builder: B) -> RepeatContent<S, I, B> {
        RepeatContent {
            stream: modifier::select::SelectContent::new(stream, Builder { iter, builder }),
        }
    }
}

impl<S, I, B> event::Stream for RepeatContent<S, I, B>
where
    S: event::ElementStream,
    I: Iterator,
    B: builder::BuildMutMapped<template::TemplateStream, I::Item>,
{
    fn next_event(&mut self) -> event::StreamResult {
        self.stream.next_event()
    }
}

impl<S, I, B> event::ElementStream for RepeatContent<S, I, B>
where
    S: event::ElementStream,
    I: Iterator,
    B: builder::BuildMutMapped<template::TemplateStream, I::Item>,
{}

#[cfg(test)]
mod tests {

    test_group!(repeat_contents:
        "some integers" => transform_test!(
            "23<c></c>45",
            "23<c>3</c>4523<c>4</c>45",
            |html| html.select("b", |html| html.repeat_contents(3..5, |html, n| html
                .select("c", move |html| html.replace_contents(n))
            )),
        ),
        "empty iterator" => transform_test!(
            "23<c></c>45",
            "",
            |html| html.select("b", |html| html.repeat_contents(0..0, |html, n| html
                .select("c", move |html| html.replace_contents(n))
            )),
        ),
        "ensure element stream" => transform_test!(
            "<link>",
            "<link>",
            |html| html.select("b", |html| html
                .repeat_contents(0..0, |html, _n| html
                    .select("c", move |html| html)
                )
                .into_boxed_element()
            ),
        ),
    );

    test_group!(repeat:
        "some integers" => transform_test!(
            "23<c></c>45",
            "23<c>3</c>4523<c>4</c>45",
            |html| html.select("b", |html| html.repeat(3..5, |html, n| html
                .select("c", move |html| html.replace_contents(n))
            )),
        ),
        "empty iterator" => transform_test!(
            "23<c></c>45",
            "",
            |html| html.select("b", |html| html.repeat(0..0, |html, n| html
                .select("c", move |html| html.replace_contents(n))
            )),
        ),
        "ensure element stream" => transform_test!(
            "<link>",
            "<link>",
            |html| html.select("b", |html| html.repeat(0..0, |html, _n| html
                .into_boxed_element()
            )),
        ),
    );
}
<commit_msg>clearer inner repeat types with aliases<commit_after>
//! Repeat a stream multiple times.

use std::iter;

use event;
use modifier;
use transform;
use template;
use builder;

/// A stored stream representing an element if the parent stream does.
pub type MaybeElementTemplate<D> = modifier::Derived<template::TemplateStream, D>;

enum RepeatOrElseState<S, I, B, BE, SR>
where
    I: Iterator,
//    B: builder::BuildMutMapped<MaybeElementTemplate<S>, I::Item>,
    BE: builder::BuildOnce<S>,
{
    Start {
        stream: S,
        iter: I,
        builder: B,
        else_builder: BE,
    },
    Fallback {
        stream: BE::Stream,
    },
    Repeat {
        stream: SR,
    },
}

impl<S, I, B, BE, SR> RepeatOrElseState<S, I, B, BE, SR>
where
    S: event::Stream,
    SR: event::Stream,
    I: Iterator,
//    B: builder::BuildMutMapped<MaybeElementTemplate<S>, I::Item>,
    BE: builder::BuildOnce<S>,
{
    fn step<F>(self, stepper: F) -> modifier::StateResult<RepeatOrElseState<S, I, B, BE, SR>>
    where
        F: FnOnce(S, iter::Peekable, B) -> SR,
    {
        match self {
            RepeatOrElseState::Start { stream, iter, builder, else_builder } => {
                let mut iter = iter.peekable();
                if iter.peek().is_some() {
                    Ok(Some((event::noop(), Some(RepeatOrElseState::Repeat {
                        stream: stepper(stream, iter, builder),
                    }))))
                } else {
                    Ok(Some((event::noop(), Some(RepeatOrElseState::Fallback {
                        stream: transform::build_once(stream, else_builder),
                    }))))
                }
            },
            RepeatOrElseState::Fallback { stream } =>
                modifier::passthrough(stream, |stream| RepeatOrElseState::Fallback { stream }),
            RepeatOrElseState::Repeat { stream } =>
                modifier::passthrough(stream, |stream| RepeatOrElseState::Repeat { stream }),
        }
    }
}

type PeekableRepeatState<S, I, B, BE> = RepeatOrElseState<
    S, I, B, BE,
    Repeat<S, iter::Peekable, B>,
>;

/// Repeat a stream for each item of an iterator or run a fallback transformation.
pub struct RepeatOrElse<S, I, B, BE>
where
    I: Iterator,
    B: builder::BuildMutMapped<MaybeElementTemplate<S>, I::Item>,
    BE: builder::BuildOnce<S>,
{
    state: modifier::State<PeekableRepeatState<S, I, B, BE>>,
}

impl<S, I, B, BE> RepeatOrElse<S, I, B, BE>
where
    I: Iterator,
    B: builder::BuildMutMapped<MaybeElementTemplate<S>, I::Item>,
    BE: builder::BuildOnce<S>,
{
    pub(crate) fn new(
        stream: S,
        iter: I,
        builder: B,
        else_builder: BE,
    ) -> RepeatOrElse<S, I, B, BE> {
        RepeatOrElse {
            state: modifier::State::new(
                RepeatOrElseState::Start { stream, iter, builder, else_builder },
            ),
        }
    }
}

impl<S, I, B, BE> event::Stream for RepeatOrElse<S, I, B, BE>
where
    S: event::Stream,
    I: Iterator,
    B: builder::BuildMutMapped<MaybeElementTemplate<S>, I::Item>,
    BE: builder::BuildOnce<S>,
{
    fn next_event(&mut self) -> event::StreamResult {
        self.state.step(|state| state.step(
            |stream, iter, builder| Repeat::new(stream, iter, builder),
        ))
    }
}

/// Repeat a stream for each item of an iterator.
pub struct Repeat<S, I, B>
where
    I: Iterator,
    B: builder::BuildMutMapped<MaybeElementTemplate<S>, I::Item>,
{
    state: modifier::State<State<S, I, B, MaybeElementTemplate<S>>>,
}

impl<S, I, B> Repeat<S, I, B>
where
    I: Iterator,
    B: builder::BuildMutMapped<MaybeElementTemplate<S>, I::Item>,
{
    pub(crate) fn new(stream: S, iter: I, builder: B) -> Repeat<S, I, B> {
        Repeat {
            state: modifier::State::new(State::Collect { stream, iter, builder }),
        }
    }
}

impl<S, I, B> event::Stream for Repeat<S, I, B>
where
    S: event::Stream,
    I: Iterator,
    B: builder::BuildMutMapped<MaybeElementTemplate<S>, I::Item>,
{
    fn next_event(&mut self) -> event::StreamResult {
        self.state.step(|state| state.step(modifier::Derived::new))
    }
}

enum State<S, I, B, SB>
where
    I: Iterator,
    B: builder::BuildMutMapped<SB, I::Item>,
{
    Collect {
        stream: S,
        builder: B,
        iter: I,
    },
    Next {
        template: template::Template,
        builder: B,
        iter: I,
    },
    Emit {
        template: template::Template,
        stream: B::Stream,
        builder: B,
        iter: I,
    },
}

impl<S, I, B, SB> State<S, I, B, SB>
where
    I: Iterator,
    B: builder::BuildMutMapped<SB, I::Item>,
    S: event::Stream,
    SB: event::Stream,
{
    fn step<F>(self, stepper: F) -> modifier::StateResult<State<S, I, B, SB>>
    where
        F: FnOnce(template::TemplateStream) -> SB,
    {
        use event::{ Stream };
        match self {
            State::Collect { stream, builder, iter } => {
                let template = template::Template::from_stream(stream)?;
                Ok(Some((event::noop(), Some(State::Next { template, builder, iter }))))
            },
            State::Next { template, mut builder, mut iter } => {
                let item = match iter.next() {
                    Some(value) => value,
                    None => return Ok(None),
                };
                let stream = stepper(template.to_stream());
                let stream = transform::build_mut_mapped(stream, &mut builder, item);
                Ok(Some((event::noop(), Some(State::Emit { template, stream, builder, iter }))))
            },
            State::Emit { template, builder, iter, mut stream } => match stream.next_event()? {
                Some(event) => Ok(Some((
                    event,
                    Some(State::Emit { template, builder, iter, stream }),
                ))),
                None => Ok(Some((
                    event::noop(),
                    Some(State::Next { template, builder, iter }),
                ))),
            },
        }
    }
}

struct RepeatContentStream<S, I, B>
where
    I: Iterator,
    B: builder::BuildMutMapped<template::TemplateStream, I::Item>,
{
    state: modifier::State<State<S, I, B, template::TemplateStream>>,
}

impl<S, I, B> event::Stream for RepeatContentStream<S, I, B>
where
    S: event::Stream,
    I: Iterator,
    B: builder::BuildMutMapped<template::TemplateStream, I::Item>,
{
    fn next_event(&mut self) -> event::StreamResult {
        self.state.step(|state| state.step(|stream| stream))
    }
}

struct Builder {
    iter: I,
    builder: B,
}

impl<S, I, B> builder::BuildOnce<modifier::select::CurrentContent<S>> for Builder
where
    S: event::ElementStream,
    I: Iterator,
    B: builder::BuildMutMapped<template::TemplateStream, I::Item>,
{
    type Stream = RepeatContentStream<modifier::select::CurrentContent<S>, I, B>;

    fn build_once(self, stream: transform::Api<modifier::select::CurrentContent<S>>)
    -> transform::Api<Self::Stream> {
        transform::Api::pack(RepeatContentStream {
            state: modifier::State::new(State::Collect {
                stream: stream.unpack(),
                iter: self.iter,
                builder: self.builder,
            }),
        })
    }
}

type PeekableRepeatContentState<S, I, B, BE> = RepeatOrElseState<
    S, I, B, BE,
    RepeatContent<S, iter::Peekable, B>,
>;

/// Repeat the contents of the current element for each item of an iterator or run a
/// fallback transform.
pub struct RepeatContentOrElse<S, I, B, BE>
where
    S: event::ElementStream,
    I: Iterator,
    B: builder::BuildMutMapped<template::TemplateStream, I::Item>,
    BE: builder::BuildOnce<S>,
{
    state: modifier::State<PeekableRepeatContentState<S, I, B, BE>>,
}

impl<S, I, B, BE> RepeatContentOrElse<S, I, B, BE>
where
    S: event::ElementStream,
    I: Iterator,
    B: builder::BuildMutMapped<template::TemplateStream, I::Item>,
    BE: builder::BuildOnce<S>,
{
    pub(crate) fn new(
        stream: S,
        iter: I,
        builder: B,
        else_builder: BE,
    ) -> RepeatContentOrElse<S, I, B, BE> {
        RepeatContentOrElse {
            state: modifier::State::new(
                RepeatOrElseState::Start { stream, iter, builder, else_builder },
            ),
        }
    }
}

impl<S, I, B, BE> event::Stream for RepeatContentOrElse<S, I, B, BE>
where
    S: event::ElementStream,
    I: Iterator,
    B: builder::BuildMutMapped<template::TemplateStream, I::Item>,
    BE: builder::BuildOnce<S>,
{
    fn next_event(&mut self) -> event::StreamResult {
        self.state.step(|state| state.step(
            |stream, iter, builder| RepeatContent::new(stream, iter, builder),
        ))
    }
}

/// Repeat the contents of the current element for each item of an iterator.
pub struct RepeatContent<S, I, B>
where
    S: event::ElementStream,
    I: Iterator,
    B: builder::BuildMutMapped<template::TemplateStream, I::Item>,
{
    stream: modifier::select::SelectContent<S, Builder>,
}

impl<S, I, B> RepeatContent<S, I, B>
where
    S: event::ElementStream,
    I: Iterator,
    B: builder::BuildMutMapped<template::TemplateStream, I::Item>,
{
    pub(crate) fn new(stream: S, iter: I, builder: B) -> RepeatContent<S, I, B> {
        RepeatContent {
            stream: modifier::select::SelectContent::new(stream, Builder { iter, builder }),
        }
    }
}

impl<S, I, B> event::Stream for RepeatContent<S, I, B>
where
    S: event::ElementStream,
    I: Iterator,
    B: builder::BuildMutMapped<template::TemplateStream, I::Item>,
{
    fn next_event(&mut self) -> event::StreamResult {
        self.stream.next_event()
    }
}

impl<S, I, B> event::ElementStream for RepeatContent<S, I, B>
where
    S: event::ElementStream,
    I: Iterator,
    B: builder::BuildMutMapped<template::TemplateStream, I::Item>,
{}

#[cfg(test)]
mod tests {

    test_group!(repeat_contents:
        "some integers" => transform_test!(
            "23<c></c>45",
            "23<c>3</c>4523<c>4</c>45",
            |html| html.select("b", |html| html.repeat_contents(3..5, |html, n| html
                .select("c", move |html| html.replace_contents(n))
            )),
        ),
        "empty iterator" => transform_test!(
            "23<c></c>45",
            "",
            |html| html.select("b", |html| html.repeat_contents(0..0, |html, n| html
                .select("c", move |html| html.replace_contents(n))
            )),
        ),
        "ensure element stream" => transform_test!(
            "<link>",
            "<link>",
            |html| html.select("b", |html| html
                .repeat_contents(0..0, |html, _n| html
                    .select("c", move |html| html)
                )
                .into_boxed_element()
            ),
        ),
    );

    test_group!(repeat:
        "some integers" => transform_test!(
            "23<c></c>45",
            "23<c>3</c>4523<c>4</c>45",
            |html| html.select("b", |html| html.repeat(3..5, |html, n| html
                .select("c", move |html| html.replace_contents(n))
            )),
        ),
        "empty iterator" => transform_test!(
            "23<c></c>45",
            "",
            |html| html.select("b", |html| html.repeat(0..0, |html, n| html
                .select("c", move |html| html.replace_contents(n))
            )),
        ),
        "ensure element stream" => transform_test!(
            "<link>",
            "<link>",
            |html| html.select("b", |html| html.repeat(0..0, |html, _n| html
                .into_boxed_element()
            )),
        ),
    );
}
<|endoftext|>


<commit_before>extern crate async_dnssd;
extern crate futures;
extern crate tokio_core;

use async_dnssd::{
	Class,
	TimeoutTrait,
	Type,
};
use futures::{
	Future,
	Stream,
};
use std::{
	env,
	net::{
		IpAddr,
		Ipv4Addr,
		Ipv6Addr,
	},
	time::Duration,
};
use tokio_core::reactor::Core;

fn decode_address(a: &async_dnssd::QueryRecordResult) -> Option<IpAddr> {
	if a.rr_class == Class::IN {
		if a.rr_type == Type::A && a.rdata.len() == 4 {
			let mut octets = [0u8; 4];
			octets.clone_from_slice(&a.rdata);
			Some(IpAddr::V4(Ipv4Addr::from(octets)))
		} else if a.rr_type == Type::AAAA && a.rdata.len() == 16 {
			let mut octets = [0u8; 16];
			octets.clone_from_slice(&a.rdata);
			Some(IpAddr::V6(Ipv6Addr::from(octets)))
		} else {
			None
		}
	} else {
		None
	}
}

fn main() {
	let mut core = Core::new().unwrap();
	let handle = core.handle();

	let search_timeout = Duration::from_secs(10);
	let resolve_timeout = Duration::from_secs(3);
	let address_timeout = Duration::from_secs(3);

	// MetaQuery to list all services available.
	let list_all_services = "_services._dns-sd._udp";

	// Use `cargo run --example browse` to list all services broadcasting
	// or `cargo run --example browse -- _http._tcp` to resolve a service.
	let query = env::args()
		.nth(1)
		.unwrap_or_else(|| list_all_services.to_string());
	println!("Browse: {}", query);

	let listing = async_dnssd::browse(&query, &handle)
		.expect("failed browse")
		.timeout(search_timeout)
		.expect("failed timeout")
		.map_err(|e| e.into_io_error())
		.for_each(|service| {
			let added = service.flags.contains(async_dnssd::BrowsedFlags::ADD);
			if query == list_all_services {
				// resolving MetaQuery responses isn't useful (and fails
				// with "bad param")... we'd need to browse them

				let mut reg_type = service.reg_type.as_str();
				if reg_type.ends_with('.') {
					reg_type = &reg_type[..reg_type.len() - 1];
				}
				let reg_type = service.service_name.clone() + "." + reg_type;
				println!(
					"Service Type {}{:?}@{:?}\t\t[{:?}]",
					if added { '+' } else { '-' },
					reg_type,
					service.domain,
					service
				);

				return Ok(());
			}

			println!(
				"Service {}{:?}@{:?} (type {:?})\t\t[{:?}]",
				if added { '+' } else { '-' },
				service.service_name,
				service.domain,
				service.reg_type,
				service
			);

			if !added {
				// only resolve added services
				return Ok(());
			}

			let service_name_e = service.service_name.clone();

			let inner_handle = handle.clone();
			handle.spawn(
				match service.resolve(&handle) {
					Ok(r) => r,
					Err(e) => {
						println!("resolve failed: {:?}", e);
						return Ok(());
					},
				}
				.timeout(resolve_timeout)
				.expect("failed timeout")
				.map_err(|e| e.into_io_error())
				.for_each(move |r| {
					println!(
						"Resolved {:?}: {:?}:{} (txt {:?})\t\t[{:?}]",
						service.service_name,
						r.host_target,
						r.port,
						String::from_utf8_lossy(&r.txt),
						r
					);
					let host_target = r.host_target.clone();
					let host_target_e = r.host_target.clone();
					inner_handle.spawn(
						// Query IPv4
						async_dnssd::query_record(
							&r.host_target,
							Type::A,
							&inner_handle,
						)?
						.timeout(address_timeout)?
						.select(
							// Query IPv6 and merge the results
							async_dnssd::query_record(
								&r.host_target,
								Type::AAAA,
								&inner_handle,
							)?
							.timeout(address_timeout)?,
						)
						.map_err(|e| e.into_io_error())
						.for_each(move |a| {
							match decode_address(&a) {
								Some(addr) => println!(
									"Address for {}: {}\t\t[{:?}]",
									host_target, addr, a
								),
								None => println!(
									"Address for {}: <unknown>\t\t[{:?}]",
									host_target, a
								),
							}
							Ok(())
						})
						.or_else(move |e| {
							println!(
								"query_record {} failed: {}",
								host_target_e, e
							);
							Ok(())
						}),
					);
					Ok(())
				})
				.or_else(move |e| {
					println!("resolve {:?} failed: {}", service_name_e, e);
					Ok(())
				}),
			);

			Ok(())
		});
	core.run(listing).unwrap();
}
<commit_msg>use new features in browse example (parse TxtRecord, resolve hostnames)<commit_after>extern crate async_dnssd;
extern crate futures;
extern crate tokio_core;

use async_dnssd::{
	TimeoutTrait,
	TxtRecord,
};
use futures::{
	Future,
	Stream,
};
use std::{
	env,
	time::Duration,
};
use tokio_core::reactor::Core;

fn main() {
	let mut core = Core::new().unwrap();
	let handle = core.handle();

	let search_timeout = Duration::from_secs(10);
	let resolve_timeout = Duration::from_secs(3);
	let address_timeout = Duration::from_secs(3);

	// MetaQuery to list all services available.
	let list_all_services = "_services._dns-sd._udp";

	// Use `cargo run --example browse` to list all services broadcasting
	// or `cargo run --example browse -- _http._tcp` to resolve a service.
	let query = env::args()
		.nth(1)
		.unwrap_or_else(|| list_all_services.to_string());
	println!("Browse: {}", query);

	let listing = async_dnssd::browse(&query, &handle)
		.expect("failed browse")
		.timeout(search_timeout)
		.expect("failed timeout")
		.map_err(|e| e.into_io_error())
		.for_each(|service| {
			let added = service.flags.contains(async_dnssd::BrowsedFlags::ADD);
			if query == list_all_services {
				// resolving MetaQuery responses isn't useful (and fails
				// with "bad param")... we'd need to browse them

				let mut reg_type = service.reg_type.as_str();
				if reg_type.ends_with('.') {
					reg_type = &reg_type[..reg_type.len() - 1];
				}
				let reg_type = service.service_name.clone() + "." + reg_type;
				println!(
					"Service Type {}{:?}@{:?}\t\t[{:?}]",
					if added { '+' } else { '-' },
					reg_type,
					service.domain,
					service
				);

				return Ok(());
			}

			println!(
				"Service {}{:?}@{:?} (type {:?})\t\t[{:?}]",
				if added { '+' } else { '-' },
				service.service_name,
				service.domain,
				service.reg_type,
				service
			);

			if !added {
				// only resolve added services
				return Ok(());
			}

			let service_name_e = service.service_name.clone();

			let inner_handle = handle.clone();
			handle.spawn(
				match service.resolve(&handle) {
					Ok(r) => r,
					Err(e) => {
						println!("resolve failed: {:?}", e);
						return Ok(());
					},
				}
				.timeout(resolve_timeout)
				.expect("failed timeout")
				.map_err(|e| e.into_io_error())
				.for_each(move |r| {
					let txt = TxtRecord::parse(&r.txt).map(|rdata|
						rdata
						.iter()
						.map(|(key, value)| (
							String::from(String::from_utf8_lossy(key)),
							value.map(|value| String::from(String::from_utf8_lossy(value))),
						))
						.collect::<Vec<_>>()
					);
					println!(
						"Resolved {:?} on {:?}: {:?}:{} (txt {:?})\t\t[{:?}]",
						service.service_name,
						r.interface,
						r.host_target,
						r.port,
						txt,
						r
					);
					let fullname = r.fullname.clone();
					let host_target_e = r.host_target.clone();
					inner_handle.spawn(
						// Query IPv4 + IPv6
						r.resolve_socket_address(&inner_handle)?
						.timeout(address_timeout)?
						.map_err(|e| e.into_io_error())
						.for_each(move |addr| {
							println!(
								"Address for {}: {}",
								fullname, addr
							);
							Ok(())
						})
						.or_else(move |e| {
							println!(
								"query_record {} failed: {}",
								host_target_e, e
							);
							Ok(())
						}),
					);
					Ok(())
				})
				.or_else(move |e| {
					println!("resolve {:?} failed: {}", service_name_e, e);
					Ok(())
				}),
			);

			Ok(())
		});
	core.run(listing).unwrap();
}
<|endoftext|>


<commit_before>#![crate_id = "hashsum#1.0.0"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Arcterus <arcterus@mail.com>
 * (c) Vsevolod Velichko <torkvemada@sorokdva.net>
 * (c) Gil Cottle <gcottle@redtown.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

#![feature(macro_rules)]

extern crate regex;

extern crate crypto = "rust-crypto";
extern crate getopts;

use std::io::fs::File;
use std::io::stdio::stdin_raw;
use std::io::BufferedReader;
use regex::Regex;
use crypto::digest::Digest;
use crypto::md5::Md5;
use crypto::sha1::Sha1;
use crypto::sha2::{Sha224, Sha256, Sha384, Sha512};

#[path = "../common/util.rs"]
mod util;

static NAME: &'static str = "hashsum";
static VERSION: &'static str = "1.0.0";

fn is_custom_binary(program: &str) -> bool {
    match program {
        "md5sum" | "sha1sum"
            | "sha224sum" | "sha256sum"
            | "sha384sum" | "sha512sum" => true,
        _ => false
    }
}

fn get_algo_opts(program: &str) -> Vec<getopts::OptGroup> {
    if is_custom_binary(program) {
        Vec::new()
    } else {
        vec!(
            getopts::optflag("", "md5", "work with MD5"),
            getopts::optflag("", "sha1", "work with SHA1"),
            getopts::optflag("", "sha224", "work with SHA224"),
            getopts::optflag("", "sha256", "work with SHA256"),
            getopts::optflag("", "sha384", "work with SHA384"),
            getopts::optflag("", "sha512", "work with SHA512")
        )
    }
}

fn detect_algo(program: &str, matches: &getopts::Matches) -> (&str, Box<Digest>) {
    let mut alg: Option<Box<Digest>> = None;
    let mut name: &'static str = "";
    match program {
        "md5sum" => ("MD5", box Md5::new() as Box<Digest>),
        "sha1sum" => ("SHA1", box Sha1::new() as Box<Digest>),
        "sha224sum" => ("SHA224", box Sha224::new() as Box<Digest>),
        "sha256sum" => ("SHA256", box Sha256::new() as Box<Digest>),
        "sha384sum" => ("SHA384", box Sha384::new() as Box<Digest>),
        "sha512sum" => ("SHA512", box Sha512::new() as Box<Digest>),
        _ => {
            {
                let set_or_crash = |n: &'static str, val: Box<Digest>| -> () {
                    if alg.is_some() { crash!(1, "You cannot combine multiple hash algorithms!") };
                    name = n;
                    alg = Some(val);
                };
                if matches.opt_present("md5") { set_or_crash("MD5", box Md5::new()) };
                if matches.opt_present("sha1") { set_or_crash("SHA1", box Sha1::new()) };
                if matches.opt_present("sha224") { set_or_crash("SHA224", box Sha224::new()) };
                if matches.opt_present("sha256") { set_or_crash("SHA256", box Sha256::new()) };
                if matches.opt_present("sha384") { set_or_crash("SHA384", box Sha384::new()) };
                if matches.opt_present("sha512") { set_or_crash("SHA512", box Sha512::new()) };
            }
            if alg.is_none() { crash!(1, "You must specify hash algorithm!") };
            (name, alg.unwrap())
        }
    }
}

pub fn uumain(args: Vec<String>) -> int {
    let program = args.get(0).clone();
    let binary = Path::new(program.as_slice());
    let binary_name = binary.filename_str().unwrap();

    let mut opts: Vec<getopts::OptGroup> = vec!(
        getopts::optflag("b", "binary", "read in binary mode"),
        getopts::optflag("c", "check", "read hashsums from the FILEs and check them"),
        getopts::optflag("", "tag", "create a BSD-style checksum"),
        getopts::optflag("t", "text", "read in text mode (default)"),
        getopts::optflag("q", "quiet", "don't print OK for each successfully verified file"),
        getopts::optflag("s", "status", "don't output anything, status code shows success"),
        getopts::optflag("", "strict", "exit non-zero for improperly formatted checksum lines"),
        getopts::optflag("w", "warn", "warn about improperly formatted checksum lines"),
        getopts::optflag("h", "help", "display this help and exit"),
        getopts::optflag("V", "version", "output version information and exit"),
    );

    opts.push_all_move(get_algo_opts(binary_name.as_slice()));

    let matches = match getopts::getopts(args.tail(), opts.as_slice()) {
        Ok(m) => m,
        Err(f) => crash!(1, "{}", f)
    };

    if matches.opt_present("help") {
        usage(program.as_slice(), binary_name.as_slice(), opts.as_slice());
    } else if matches.opt_present("version") {
        version();
    } else {
        let (name, algo) = detect_algo(binary_name.as_slice(), &matches);

        let binary = matches.opt_present("binary");
        let check = matches.opt_present("check");
        let tag = matches.opt_present("tag");
        let status = matches.opt_present("status");
        let quiet = matches.opt_present("quiet") || status;
        let strict = matches.opt_present("strict");
        let warn = matches.opt_present("warn") && !status;
        let files = if matches.free.is_empty() {
            vec!("-".to_string())
        } else {
            matches.free
        };
        match hashsum(name, algo, files, binary, check, tag, status, quiet, strict, warn) {
            Ok(()) => return 0,
            Err(e) => return e
        }
    }

    0
}

fn version() {
    println!("{} v{}", NAME, VERSION);
}

fn usage(program: &str, binary_name: &str, opts: &[getopts::OptGroup]) {
    version();
    println!("");
    println!("Usage:");
    if is_custom_binary(binary_name) {
        println!("  {} [OPTION]... [FILE]...", program);
    } else {
        println!("  {} {{--md5|--sha1|--sha224|--sha256|--sha384|--sha512}} [OPTION]... [FILE]...", program);
    }
    println!("");
    print!("{}", getopts::usage("Compute and check message digests.", opts));
}

fn hashsum(algoname: &str, mut digest: Box<Digest>, files: Vec<String>, binary: bool, check: bool, tag: bool, status: bool, quiet: bool, strict: bool, warn: bool) -> Result<(), int> {
    let mut bad_format = 0u;
    let mut failed = 0u;
    let binary_marker = if binary {
        "*"
    } else {
        " "
    };
    for filename in files.iter() {
        let filename: &str = filename.as_slice();
        let mut file = BufferedReader::new(
            if filename == "-" {
                box stdin_raw() as Box<Reader>
            } else {
                box safe_unwrap!(File::open(&Path::new(filename))) as Box<Reader>
            }
        );
        if check {

            // Set up Regexes for line validation and parsing
            let bytes = digest.output_bits() / 4;
            let gnu_re = safe_unwrap!(
                Regex::new(
                    format!(
                        r"^(?P<digest>[a-fA-F0-9]{{{}}}) (?P<binary>[ \*])(?P<fileName>.*)",
                        bytes
                    ).as_slice()
                )
            );
            let bsd_re = safe_unwrap!(
                Regex::new(
                    format!(
                        r"^{algorithm} \((?P<fileName>.*)\) = (?P<digest>[a-fA-F0-9]{{{digest_size}}})",
                        algorithm = algoname,
                        digest_size = bytes
                    ).as_slice()
                )
            );

            let mut buffer = file;
            for (i, line) in buffer.lines().enumerate() {
                let line = safe_unwrap!(line);
                let (ck_filename, sum, binary_check) = match gnu_re.captures(line.as_slice()) {
                    Some(caps) => (caps.name("fileName"), caps.name("digest").to_ascii().to_lower(), caps.name("binary") == "*"),
                    None => match bsd_re.captures(line.as_slice()) {
                        Some(caps) => (caps.name("fileName"), caps.name("digest").to_ascii().to_lower(), true),
                        None => {
                            bad_format += 1;
                            if strict {
                                return Err(1);
                            }
                            if warn {
                                show_warning!("{}: {}: improperly formatted {} checksum line", filename, i + 1, algoname);
                            }
                            continue;
                        }
                    }
                };
                let real_sum = calc_sum(&mut digest, &mut safe_unwrap!(File::open(&Path::new(ck_filename))), binary_check)
                    .as_slice().to_ascii().to_lower();
                if sum.as_slice() == real_sum.as_slice() {
                    if !quiet {
                        println!("{}: OK", ck_filename);
                    }
                } else {
                    if !status {
                        println!("{}: FAILED", ck_filename);
                    }
                    failed += 1;
                }
            }
        } else {
            let sum = calc_sum(&mut digest, &mut file, binary);
            if tag {
                println!("{} ({}) = {}", algoname, filename, sum);
            } else {
                println!("{} {}{}", sum, binary_marker, filename);
            }
        }
    }
    if !status {
        if bad_format == 1 {
            show_warning!("{} line is improperly formatted", bad_format);
        } else if bad_format > 1 {
            show_warning!("{} lines are improperly formatted", bad_format);
        }
        if failed > 0 {
            show_warning!("{} computed checksum did NOT match", failed);
        }
    }

    Ok(())
}

fn calc_sum(digest: &mut Box<Digest>, file: &mut Reader, binary: bool) -> String {
    let data =
        if binary {
            (safe_unwrap!(file.read_to_end()))
        } else {
            (safe_unwrap!(file.read_to_str())).into_bytes()
        };
    digest.reset();
    digest.input(data.as_slice());
    digest.result_str()
}

<commit_msg>Use Less Memory and Fix Text Mode on Windows/UNIX<commit_after>#![crate_id = "hashsum#1.0.0"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Arcterus <arcterus@mail.com>
 * (c) Vsevolod Velichko <torkvemada@sorokdva.net>
 * (c) Gil Cottle <gcottle@redtown.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

#![feature(macro_rules)]

extern crate regex;

extern crate crypto = "rust-crypto";
extern crate getopts;

use std::io::fs::File;
use std::io::stdio::stdin_raw;
use std::io::BufferedReader;
use std::io::IoError;
use std::io::EndOfFile;
use regex::Regex;
use crypto::digest::Digest;
use crypto::md5::Md5;
use crypto::sha1::Sha1;
use crypto::sha2::{Sha224, Sha256, Sha384, Sha512};

#[path = "../common/util.rs"]
mod util;

static NAME: &'static str = "hashsum";
static VERSION: &'static str = "1.0.0";

fn is_custom_binary(program: &str) -> bool {
    match program {
        "md5sum" | "sha1sum"
            | "sha224sum" | "sha256sum"
            | "sha384sum" | "sha512sum" => true,
        _ => false
    }
}

fn get_algo_opts(program: &str) -> Vec<getopts::OptGroup> {
    if is_custom_binary(program) {
        Vec::new()
    } else {
        vec!(
            getopts::optflag("", "md5", "work with MD5"),
            getopts::optflag("", "sha1", "work with SHA1"),
            getopts::optflag("", "sha224", "work with SHA224"),
            getopts::optflag("", "sha256", "work with SHA256"),
            getopts::optflag("", "sha384", "work with SHA384"),
            getopts::optflag("", "sha512", "work with SHA512")
        )
    }
}

fn detect_algo(program: &str, matches: &getopts::Matches) -> (&str, Box<Digest>) {
    let mut alg: Option<Box<Digest>> = None;
    let mut name: &'static str = "";
    match program {
        "md5sum" => ("MD5", box Md5::new() as Box<Digest>),
        "sha1sum" => ("SHA1", box Sha1::new() as Box<Digest>),
        "sha224sum" => ("SHA224", box Sha224::new() as Box<Digest>),
        "sha256sum" => ("SHA256", box Sha256::new() as Box<Digest>),
        "sha384sum" => ("SHA384", box Sha384::new() as Box<Digest>),
        "sha512sum" => ("SHA512", box Sha512::new() as Box<Digest>),
        _ => {
            {
                let set_or_crash = |n: &'static str, val: Box<Digest>| -> () {
                    if alg.is_some() { crash!(1, "You cannot combine multiple hash algorithms!") };
                    name = n;
                    alg = Some(val);
                };
                if matches.opt_present("md5") { set_or_crash("MD5", box Md5::new()) };
                if matches.opt_present("sha1") { set_or_crash("SHA1", box Sha1::new()) };
                if matches.opt_present("sha224") { set_or_crash("SHA224", box Sha224::new()) };
                if matches.opt_present("sha256") { set_or_crash("SHA256", box Sha256::new()) };
                if matches.opt_present("sha384") { set_or_crash("SHA384", box Sha384::new()) };
                if matches.opt_present("sha512") { set_or_crash("SHA512", box Sha512::new()) };
            }
            if alg.is_none() { crash!(1, "You must specify hash algorithm!") };
            (name, alg.unwrap())
        }
    }
}

pub fn uumain(args: Vec<String>) -> int {
    let program = args.get(0).clone();
    let binary = Path::new(program.as_slice());
    let binary_name = binary.filename_str().unwrap();

    // Default binary in Windows, text mode otherwise
    let binary_flag_default = cfg!(windows);

    let mut opts: Vec<getopts::OptGroup> = vec!(
        getopts::optflag("b", "binary", format!("read in binary mode{}", if binary_flag_default { " (default)" } else { "" }).as_slice()),
        getopts::optflag("c", "check", "read hashsums from the FILEs and check them"),
        getopts::optflag("", "tag", "create a BSD-style checksum"),
        getopts::optflag("t", "text", format!("read in text mode{}", if binary_flag_default { "" } else { " (default)" }).as_slice()),
        getopts::optflag("q", "quiet", "don't print OK for each successfully verified file"),
        getopts::optflag("s", "status", "don't output anything, status code shows success"),
        getopts::optflag("", "strict", "exit non-zero for improperly formatted checksum lines"),
        getopts::optflag("w", "warn", "warn about improperly formatted checksum lines"),
        getopts::optflag("h", "help", "display this help and exit"),
        getopts::optflag("V", "version", "output version information and exit"),
    );

    opts.push_all_move(get_algo_opts(binary_name.as_slice()));

    let matches = match getopts::getopts(args.tail(), opts.as_slice()) {
        Ok(m) => m,
        Err(f) => crash!(1, "{}", f)
    };

    if matches.opt_present("help") {
        usage(program.as_slice(), binary_name.as_slice(), opts.as_slice());
    } else if matches.opt_present("version") {
        version();
    } else {
        let (name, algo) = detect_algo(binary_name.as_slice(), &matches);

        let binary_flag = matches.opt_present("binary");
        let text_flag = matches.opt_present("text");
        if binary_flag && text_flag {
            crash!(1, "cannot set binary and text mode at the same time");
        }
        let binary = if binary_flag { true } else if text_flag { false } else { binary_flag_default };
        let check = matches.opt_present("check");
        let tag = matches.opt_present("tag");
        let status = matches.opt_present("status");
        let quiet = matches.opt_present("quiet") || status;
        let strict = matches.opt_present("strict");
        let warn = matches.opt_present("warn") && !status;
        let files = if matches.free.is_empty() {
            vec!("-".to_string())
        } else {
            matches.free
        };
        match hashsum(name, algo, files, binary, check, tag, status, quiet, strict, warn) {
            Ok(()) => return 0,
            Err(e) => return e
        }
    }

    0
}

fn version() {
    println!("{} v{}", NAME, VERSION);
}

fn usage(program: &str, binary_name: &str, opts: &[getopts::OptGroup]) {
    version();
    println!("");
    println!("Usage:");
    if is_custom_binary(binary_name) {
        println!("  {} [OPTION]... [FILE]...", program);
    } else {
        println!("  {} {{--md5|--sha1|--sha224|--sha256|--sha384|--sha512}} [OPTION]... [FILE]...", program);
    }
    println!("");
    print!("{}", getopts::usage("Compute and check message digests.", opts));
}

fn hashsum(algoname: &str, mut digest: Box<Digest>, files: Vec<String>, binary: bool, check: bool, tag: bool, status: bool, quiet: bool, strict: bool, warn: bool) -> Result<(), int> {
    let mut bad_format = 0u;
    let mut failed = 0u;
    let binary_marker = if binary {
        "*"
    } else {
        " "
    };
    for filename in files.iter() {
        let filename: &str = filename.as_slice();
        let mut file = BufferedReader::new(
            if filename == "-" {
                box stdin_raw() as Box<Reader>
            } else {
                box safe_unwrap!(File::open(&Path::new(filename))) as Box<Reader>
            }
        );
        if check {

            // Set up Regexes for line validation and parsing
            let bytes = digest.output_bits() / 4;
            let gnu_re = safe_unwrap!(
                Regex::new(
                    format!(
                        r"^(?P<digest>[a-fA-F0-9]{{{}}}) (?P<binary>[ \*])(?P<fileName>.*)",
                        bytes
                    ).as_slice()
                )
            );
            let bsd_re = safe_unwrap!(
                Regex::new(
                    format!(
                        r"^{algorithm} \((?P<fileName>.*)\) = (?P<digest>[a-fA-F0-9]{{{digest_size}}})",
                        algorithm = algoname,
                        digest_size = bytes
                    ).as_slice()
                )
            );

            let mut buffer = file;
            for (i, line) in buffer.lines().enumerate() {
                let line = safe_unwrap!(line);
                let (ck_filename, sum, binary_check) = match gnu_re.captures(line.as_slice()) {
                    Some(caps) => (caps.name("fileName"), caps.name("digest").to_ascii().to_lower(), caps.name("binary") == "*"),
                    None => match bsd_re.captures(line.as_slice()) {
                        Some(caps) => (caps.name("fileName"), caps.name("digest").to_ascii().to_lower(), true),
                        None => {
                            bad_format += 1;
                            if strict {
                                return Err(1);
                            }
                            if warn {
                                show_warning!("{}: {}: improperly formatted {} checksum line", filename, i + 1, algoname);
                            }
                            continue;
                        }
                    }
                };
                let real_sum = safe_unwrap!(digest_reader(&mut digest, &mut safe_unwrap!(File::open(&Path::new(ck_filename))), binary_check))
                    .as_slice().to_ascii().to_lower();
                if sum.as_slice() == real_sum.as_slice() {
                    if !quiet {
                        println!("{}: OK", ck_filename);
                    }
                } else {
                    if !status {
                        println!("{}: FAILED", ck_filename);
                    }
                    failed += 1;
                }
            }
        } else {
            let sum = safe_unwrap!(digest_reader(&mut digest, &mut file, binary));
            if tag {
                println!("{} ({}) = {}", algoname, filename, sum);
            } else {
                println!("{} {}{}", sum, binary_marker, filename);
            }
        }
    }
    if !status {
        if bad_format == 1 {
            show_warning!("{} line is improperly formatted", bad_format);
        } else if bad_format > 1 {
            show_warning!("{} lines are improperly formatted", bad_format);
        }
        if failed > 0 {
            show_warning!("{} computed checksum did NOT match", failed);
        }
    }

    Ok(())
}

fn digest_reader(digest: &mut Box<Digest>, reader: &mut Reader, binary: bool) -> Result<String, IoError> {
    digest.reset();

    // Digest file, do not hold too much in memory at any given moment
    let windows = cfg!(windows);
    let mut buffer = [0, ..524288];
    let mut vec = Vec::with_capacity(524288);
    let mut looking_for_newline = false;
    loop {
        match reader.read(buffer) {
            Ok(0) => {},
            Ok(nread) => {
                if windows && !binary {
                    // Windows text mode returns '\n' when reading '\r\n'
                    for i in range(0, nread) {
                        if looking_for_newline {
                            if buffer[i] != ('\n' as u8) {
                                vec.push('\r' as u8);
                            }
                            if buffer[i] != ('\r' as u8) {
                                vec.push(buffer[i]);
                                looking_for_newline = false;
                            }
                        } else if buffer[i] != ('\r' as u8) {
                            vec.push(buffer[i]);
                        } else {
                            looking_for_newline = true;
                        }
                    }
                    digest.input(vec.as_slice());
                    vec.clear();
                } else {
                    digest.input(buffer.slice(0, nread));
                }
            },
            Err(e) => match e.kind {
                EndOfFile => {
                    break;
                },
                _ => {
                    return Err(e);
                }
            }
        }
    }
    if windows && looking_for_newline {
        vec.push('\r' as u8);
        digest.input(vec.as_slice());
    }

    Ok(digest.result_str())
}

<|endoftext|>


<commit_before>#![feature(test, plugin, decl_macro)]
#![plugin(rocket_codegen)]
extern crate rocket;
extern crate rocket_contrib;
extern crate rust_embed;

use std::path::PathBuf;
use std::io::Cursor;
use rocket::response;
use rocket::http::{ContentType, Status};
use rust_embed::*;

#[get("/")]
fn index<'r>() -> response::Result<'r> {
  let asset = embed!("examples/public".to_owned());
  asset("/index.html".to_owned()).map_or_else(
    || Err(Status::NotFound),
    |d| {
      response::Response::build()
        .header(ContentType::HTML)
        .sized_body(Cursor::new(d))
        .ok()
    },
  )
}

#[get("/dist/<file..>")]
fn dist<'r>(file: PathBuf) -> response::Result<'r> {
  let filename = file.display().to_string();
  let asset = embed!("examples/public/".to_owned());
  // let ext = Path::new(&filename).extension().and_then(OsStr::to_str).expect("Could not get file extension");
  // let content_type = ContentType::from_extension(ext).expect("Could not get file content type");
  asset(filename.clone()).map_or_else(
    || Err(Status::NotFound),
    |d| {
      response::Response::build()
        .header(ContentType::HTML)
        .sized_body(Cursor::new(d))
        .ok()
    },
  )
}

fn main() {
  rocket::ignite().mount("/", routes![index, dist]).launch();
}
<commit_msg>fix content type issues<commit_after>#![feature(test, plugin, decl_macro)]
#![plugin(rocket_codegen)]
extern crate rocket;
extern crate rocket_contrib;
extern crate rust_embed;

use std::path::PathBuf;
use std::ffi::OsStr;
use std::io::Cursor;
use rocket::response;
use rocket::http::{ContentType, Status};
use rust_embed::*;

#[get("/")]
fn index<'r>() -> response::Result<'r> {
  let asset = embed!("examples/public".to_owned());
  asset("/index.html".to_owned()).map_or_else(
    || Err(Status::NotFound),
    |d| {
      response::Response::build()
        .header(ContentType::HTML)
        .sized_body(Cursor::new(d))
        .ok()
    },
  )
}

#[get("/dist/<file..>")]
fn dist<'r>(file: PathBuf) -> response::Result<'r> {
  let filename = file.display().to_string();
  let asset = embed!("examples/public/".to_owned());
  let ext = file.as_path().extension().and_then(OsStr::to_str).expect("Could not get file extension");
  let content_type = ContentType::from_extension(ext).expect("Could not get file content type");
  asset(filename.clone()).map_or_else(
    || Err(Status::NotFound),
    |d| {
      response::Response::build()
        .header(content_type)
        .sized_body(Cursor::new(d))
        .ok()
    },
  )
}

fn main() {
  rocket::ignite().mount("/", routes![index, dist]).launch();
}
<|endoftext|>


<commit_before>extern crate rusttype;

use rusttype::{FontCollection, PixelsXY, point};
use std::io::Write;

fn main() {
    let font_data = include_bytes!("Gudea-Regular.ttf");
    let collection = FontCollection::from_bytes(font_data as &[u8]);
    let font = collection.into_font().unwrap(); // only succeeds if collection consists of one font
    {
        // Desired font pixel height
        let height: f32 = 20.1; // to get 80 chars across (fits most terminals); adjust as desired
        let pixel_height = height.ceil() as usize;

        // 2x scale in x direction to counter the aspect ratio of monospace characters.
        let scale = PixelsXY(height*2.0, height);

        let v_metrics = font.v_metrics(scale);
        let offset = point(0.0, v_metrics.ascent);

        // Glyphs to draw for "Hello!". Feel free to try other strings.
        let to_draw: Vec<_> = font.layout("Hello!", scale, offset).collect();

        // Find the most visually pleasing width to display
        let width = to_draw.iter().map(|g| g.h_metrics().advance_width)
            .fold(0.0, |x, y| x + y).ceil() as usize;

        println!("width: {}, height: {}", width, pixel_height);

        // Rasterise directly into ASCII art.
        let mut pixel_data = vec![b'@'; width * pixel_height];
        let mapping = b"@%#x+=:-. "; // The approximation of greyscale
        let mapping_scale = (mapping.len()-1) as f32;
        for g in to_draw {
            if let Some(bb) = g.pixel_bounding_box() {
                g.draw(|x, y, v| {
                    // v should be in the range 0.0 to 1.0
                    let i = (v*mapping_scale + 0.5) as usize;
                    // so something's wrong if you get $ in the output.
                    let c = mapping.get(i).cloned().unwrap_or(b'$');
                    let x = (x as i32 + bb.min.x) as usize;
                    let y = (y as i32 + bb.min.y) as usize;
                    pixel_data[(x + y * width)] = c;
                })
            }
        }

        // Print it out
        let stdout = ::std::io::stdout();
        let mut handle = stdout.lock();
        for j in 0..pixel_height {
            handle.write(&pixel_data[j*width..(j+1)*width]).unwrap();
            handle.write(b"\n").unwrap();
        }
    }
}
<commit_msg>Added more comments to the example. Improved variable names.<commit_after>extern crate rusttype;

use rusttype::{FontCollection, PixelsXY, point, PositionedGlyph};
use std::io::Write;

fn main() {
    let font_data = include_bytes!("Gudea-Regular.ttf");
    let collection = FontCollection::from_bytes(font_data as &[u8]);
    let font = collection.into_font().unwrap(); // only succeeds if collection consists of one font
    {
        // Desired font pixel height
        let height: f32 = 20.1; // to get 80 chars across (fits most terminals); adjust as desired
        let pixel_height = height.ceil() as usize;

        // 2x scale in x direction to counter the aspect ratio of monospace characters.
        let scale = PixelsXY(height*2.0, height);

        // The origin of a line of text is at the baseline (roughly where non-descending letters sit).
        // We don't want to clip the text, so we shift it down with an offset when laying it out.
        // v_metrics.ascent is the distance between the baseline and the highest edge of any glyph in
        // the font. That's enough to guarantee that there's no clipping.
        let v_metrics = font.v_metrics(scale);
        let offset = point(0.0, v_metrics.ascent);

        // Glyphs to draw for "Hello!". Feel free to try other strings.
        let glyphs: Vec<PositionedGlyph> = font.layout("Hello!", scale, offset).collect();

        // Find the most visually pleasing width to display
        let width = glyphs.iter().map(|g| g.h_metrics().advance_width)
            .fold(0.0, |x, y| x + y).ceil() as usize;

        println!("width: {}, height: {}", width, pixel_height);

        // Rasterise directly into ASCII art.
        let mut pixel_data = vec![b'@'; width * pixel_height];
        let mapping = b"@%#x+=:-. "; // The approximation of greyscale
        let mapping_scale = (mapping.len()-1) as f32;
        for g in glyphs {
            if let Some(bb) = g.pixel_bounding_box() {
                g.draw(|x, y, v| {
                    // v should be in the range 0.0 to 1.0
                    let i = (v*mapping_scale + 0.5) as usize;
                    // so something's wrong if you get $ in the output.
                    let c = mapping.get(i).cloned().unwrap_or(b'$');
                    let x = (x as i32 + bb.min.x) as usize;
                    let y = (y as i32 + bb.min.y) as usize;
                    pixel_data[(x + y * width)] = c;
                })
            }
        }

        // Print it out
        let stdout = ::std::io::stdout();
        let mut handle = stdout.lock();
        for j in 0..pixel_height {
            handle.write(&pixel_data[j*width..(j+1)*width]).unwrap();
            handle.write(b"\n").unwrap();
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate nom;
extern crate proc_macro;
extern crate quote;
extern crate syn;

use proc_macro::TokenStream;

mod generator;
mod parser;
mod path;

#[proc_macro_derive(Template, attributes(template))]
pub fn derive_template(input: TokenStream) -> TokenStream {
    let ast = syn::parse_derive_input(&input.to_string()).unwrap();
    match ast.body {
        syn::Body::Struct(ref data) => data,
        _ => panic!("#[derive(Template)] can only be used with structs"),
    };
    build_template(&ast).parse().unwrap()
}

/// Takes a `syn::DeriveInput` and generates source code for it
///
/// Reads the metadata from the `template()` attribute to get the template
/// metadata, then fetches the source from the filesystem. The source is
/// parsed, and the parse tree is fed to the code generator. Will print
/// the parse tree and/or generated source according to the `print` key's
/// value as passed to the `template()` attribute.
fn build_template(ast: &syn::DeriveInput) -> String {
    let meta = get_template_meta(ast);
    let path = path::find_template_from_path(&meta.path, None);
    let src = path::get_template_source(&path);
    let nodes = parser::parse(&src);
    if meta.print == "ast" || meta.print == "all" {
        println!("{:?}", nodes);
    }
    let code = generator::generate(ast, &meta.path, nodes);
    if meta.print == "code" || meta.print == "all" {
        println!("{}", code);
    }
    code
}

// Returns a `TemplateMeta` based on the `template()` attribute data found
// in the parsed struct or enum. Will panic if it does not find the required
// template path, or if the `print` key has an unexpected value.
fn get_template_meta(ast: &syn::DeriveInput) -> TemplateMeta {
    let attr = ast.attrs.iter().find(|a| a.name() == "template");
    if attr.is_none() {
        let msg = format!("'template' attribute not found on struct '{}'",
                          ast.ident.as_ref());
        panic!(msg);
    }

    let attr = attr.unwrap();
    let mut path = None;
    let mut print = "none".to_string();
    if let syn::MetaItem::List(_, ref inner) = attr.value {
        for nm_item in inner {
            if let syn::NestedMetaItem::MetaItem(ref item) = *nm_item {
                if let syn::MetaItem::NameValue(ref key, ref val) = *item {
                    match key.as_ref() {
                        "path" => if let syn::Lit::Str(ref s, _) = *val {
                            path = Some(s.clone());
                        } else {
                            panic!("template path must be string literal");
                        },
                        "print" => if let syn::Lit::Str(ref s, _) = *val {
                            print = s.clone();
                        } else {
                            panic!("print value must be string literal");
                        },
                        _ => { panic!("unsupported annotation key found") }
                    }
                }
            }
        }
    }
    if path.is_none() {
        panic!("template path not found in struct attributes");
    }
    TemplateMeta { path: path.unwrap(), print: print }
}

// Holds metadata for the template, based on the `template()` attribute.
struct TemplateMeta {
    path: String,
    print: String,
}
<commit_msg>Use enum for print option value<commit_after>#[macro_use]
extern crate nom;
extern crate proc_macro;
extern crate quote;
extern crate syn;

use proc_macro::TokenStream;

mod generator;
mod parser;
mod path;

#[proc_macro_derive(Template, attributes(template))]
pub fn derive_template(input: TokenStream) -> TokenStream {
    let ast = syn::parse_derive_input(&input.to_string()).unwrap();
    match ast.body {
        syn::Body::Struct(ref data) => data,
        _ => panic!("#[derive(Template)] can only be used with structs"),
    };
    build_template(&ast).parse().unwrap()
}

/// Takes a `syn::DeriveInput` and generates source code for it
///
/// Reads the metadata from the `template()` attribute to get the template
/// metadata, then fetches the source from the filesystem. The source is
/// parsed, and the parse tree is fed to the code generator. Will print
/// the parse tree and/or generated source according to the `print` key's
/// value as passed to the `template()` attribute.
fn build_template(ast: &syn::DeriveInput) -> String {
    let meta = get_template_meta(ast);
    let path = path::find_template_from_path(&meta.path, None);
    let src = path::get_template_source(&path);
    let nodes = parser::parse(&src);
    if meta.print == Print::Ast || meta.print == Print::All {
        println!("{:?}", nodes);
    }
    let code = generator::generate(ast, &meta.path, nodes);
    if meta.print == Print::Code || meta.print == Print::All {
        println!("{}", code);
    }
    code
}

// Returns a `TemplateMeta` based on the `template()` attribute data found
// in the parsed struct or enum. Will panic if it does not find the required
// template path, or if the `print` key has an unexpected value.
fn get_template_meta(ast: &syn::DeriveInput) -> TemplateMeta {
    let attr = ast.attrs.iter().find(|a| a.name() == "template");
    if attr.is_none() {
        let msg = format!("'template' attribute not found on struct '{}'",
                          ast.ident.as_ref());
        panic!(msg);
    }

    let attr = attr.unwrap();
    let mut path = None;
    let mut print = Print::None;
    if let syn::MetaItem::List(_, ref inner) = attr.value {
        for nm_item in inner {
            if let syn::NestedMetaItem::MetaItem(ref item) = *nm_item {
                if let syn::MetaItem::NameValue(ref key, ref val) = *item {
                    match key.as_ref() {
                        "path" => if let syn::Lit::Str(ref s, _) = *val {
                            path = Some(s.clone());
                        } else {
                            panic!("template path must be string literal");
                        },
                        "print" => if let syn::Lit::Str(ref s, _) = *val {
                            print = s.into();
                        } else {
                            panic!("print value must be string literal");
                        },
                        _ => { panic!("unsupported annotation key found") }
                    }
                }
            }
        }
    }
    if path.is_none() {
        panic!("template path not found in struct attributes");
    }
    TemplateMeta { path: path.unwrap(), print: print }
}

// Holds metadata for the template, based on the `template()` attribute.
struct TemplateMeta {
    path: String,
    print: Print,
}

#[derive(PartialEq)]
enum Print {
    All,
    Ast,
    Code,
    None,
}

impl<'a> From<&'a String> for Print {
    fn from(s: &'a String) -> Print {
        use Print::*;
        match s.as_ref() {
            "all" => All,
            "ast" => Ast,
            "code" => Code,
            "none" => None,
            v => panic!("invalid value for print option: {}", v),
        }
    }
}
<|endoftext|>


<commit_before>use render::Renderer;
use result;
use hyper::client;

pub struct InfluxDbRenderer {
    write_url: String,
}

impl InfluxDbRenderer {
    pub fn new(write_url: String) -> InfluxDbRenderer {
        InfluxDbRenderer { write_url: write_url }
    }
}

impl Renderer for InfluxDbRenderer {
    fn render(&mut self, result: result::RequestLogAnalyzerResult) -> () {
        let client = client::Client::new();
        client.post(&self.write_url)
            .body(&post_body(result))
            .send()
            .expect("Could not connect to InfluxDB host.");
    }
}

fn post_body(result: result::RequestLogAnalyzerResult) -> String {
    let mut timing_values = String::from("");
    let mut error_rate_values = String::from("");

    match result.timing {
        Some(timing) => {
            timing_values = format!("\
				time_max={} time_min={} time_avg={} time_median={} \
                                     time_90percent={}",
                                    timing.max,
                                    timing.min,
                                    timing.avg,
                                    timing.median,
                                    timing.percentile90);
        }
        None => warn!("No matching log lines in file."),
    }

    match result.error {
        Some(error) => {
            error_rate_values = format!("\
				client_error_4xx_rate={} server_error_5xx_rate={}",
                                        error.client_error_4xx,
                                        error.server_error_5xx);
        }
        None => warn!("No matching log lines in file."),
    }

    format!("request_log count={} {} {}",
            result.count,
            timing_values,
            error_rate_values)
}

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

    fn get_result_fixture() -> result::RequestLogAnalyzerResult {
        result::RequestLogAnalyzerResult {
            count: 3,
            timing: Some(analyzer::TimingResult {
                max: 100,
                min: 1,
                avg: 37,
                median: 10,
                percentile90: 100,
                count: 3,
            }),
            error: Some(analyzer::aggregated_error_rates::ErrorRatesResult {
                client_error_4xx: 0.1,
                server_error_5xx: 0.2,
            }),
        }
    }

    #[test]
    fn test_instantiate() {
        InfluxDbRenderer::new(String::from("http://example.com/write?db=testdb"));
    }

    #[test]
    fn test_post_body() {
        let result = post_body(get_result_fixture());

        assert!(result.starts_with("request_log "));

        assert!(result.contains("count=3"));
        assert!(result.contains("time_max=100"));
        assert!(result.contains("time_min=1"));
        assert!(result.contains("time_avg=37"));
        assert!(result.contains("time_median=10"));
        assert!(result.contains("time_90percent=100"));
        assert!(result.contains("client_error_4xx_rate=0.1"));
        assert!(result.contains("server_error_5xx_rate=0.2"));

    }

    #[test]
    fn test_post_body_empty() {
        let result = post_body(result::RequestLogAnalyzerResult {
            count: 0,
            timing: None,
            error: None,
        });

        assert!(result.starts_with("request_log "));

        assert!(result.contains("count=0"));

        // Don't include empty fields
        assert!(!result.contains("time_max="));
        assert!(!result.contains("time_min="));
        assert!(!result.contains("time_avg="));
        assert!(!result.contains("time_median="));
        assert!(!result.contains("time_90percent="));
        assert!(!result.contains("client_error_4xx_rate="));
        assert!(!result.contains("server_error_5xx_rate="));
    }
}
<commit_msg>Use correct InfluxDB write format<commit_after>use render::Renderer;
use result;
use hyper::client;

pub struct InfluxDbRenderer {
    write_url: String,
}

impl InfluxDbRenderer {
    pub fn new(write_url: String) -> InfluxDbRenderer {
        InfluxDbRenderer { write_url: write_url }
    }
}

impl Renderer for InfluxDbRenderer {
    fn render(&mut self, result: result::RequestLogAnalyzerResult) -> () {
        let client = client::Client::new();
        client.post(&self.write_url)
            .body(&post_body(result))
            .send()
            .expect("Could not connect to InfluxDB host.");
    }
}

fn post_body(result: result::RequestLogAnalyzerResult) -> String {
    let mut timing_values = String::from("");
    let mut error_rate_values = String::from("");

    match result.timing {
        Some(timing) => {
            timing_values = format!("\
				time_max={},time_min={},time_avg={},time_median={},\
                                     time_90percent={}",
                                    timing.max,
                                    timing.min,
                                    timing.avg,
                                    timing.median,
                                    timing.percentile90);
        }
        None => warn!("No matching log lines in file."),
    }

    match result.error {
        Some(error) => {
            error_rate_values = format!("\
				client_error_4xx_rate={},server_error_5xx_rate={}",
                                        error.client_error_4xx,
                                        error.server_error_5xx);
        }
        None => warn!("No matching log lines in file."),
    }

    format!("request_log count={},{},{}",
            result.count,
            timing_values,
            error_rate_values)
}

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

    fn get_result_fixture() -> result::RequestLogAnalyzerResult {
        result::RequestLogAnalyzerResult {
            count: 3,
            timing: Some(analyzer::TimingResult {
                max: 100,
                min: 1,
                avg: 37,
                median: 10,
                percentile90: 100,
                count: 3,
            }),
            error: Some(analyzer::aggregated_error_rates::ErrorRatesResult {
                client_error_4xx: 0.1,
                server_error_5xx: 0.2,
            }),
        }
    }

    #[test]
    fn test_instantiate() {
        InfluxDbRenderer::new(String::from("http://example.com/write?db=testdb"));
    }

    #[test]
    fn test_post_body() {
        let result = post_body(get_result_fixture());

        assert!(result.starts_with("request_log "));
        assert_eq!(result.matches(" ").count(), 1);

        assert!(result.contains("count=3"));
        assert!(result.contains("time_max=100"));
        assert!(result.contains("time_min=1"));
        assert!(result.contains("time_avg=37"));
        assert!(result.contains("time_median=10"));
        assert!(result.contains("time_90percent=100"));
        assert!(result.contains("client_error_4xx_rate=0.1"));
        assert!(result.contains("server_error_5xx_rate=0.2"));

    }

    #[test]
    fn test_post_body_empty() {
        let result = post_body(result::RequestLogAnalyzerResult {
            count: 0,
            timing: None,
            error: None,
        });

        assert!(result.starts_with("request_log "));

        assert!(result.contains("count=0"));

        // Don't include empty fields
        assert!(!result.contains("time_max="));
        assert!(!result.contains("time_min="));
        assert!(!result.contains("time_avg="));
        assert!(!result.contains("time_median="));
        assert!(!result.contains("time_90percent="));
        assert!(!result.contains("client_error_4xx_rate="));
        assert!(!result.contains("server_error_5xx_rate="));
    }
}
<|endoftext|>