<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<%helpers:shorthand name="flex-flow" sub_properties="flex-direction flex-wrap" extra_prefixes="webkit"
                    spec="https://drafts.csswg.org/css-flexbox/#flex-flow-property">
    use properties::longhands::{flex_direction, flex_wrap};

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let mut direction = None;
        let mut wrap = None;
        loop {
            if direction.is_none() {
                if let Ok(value) = input.try(|input| flex_direction::parse(context, input)) {
                    direction = Some(value);
                    continue
                }
            }
            if wrap.is_none() {
                if let Ok(value) = input.try(|input| flex_wrap::parse(context, input)) {
                    wrap = Some(value);
                    continue
                }
            }
            break
        }

        if direction.is_none() && wrap.is_none() {
            return Err(())
        }
        Ok(Longhands {
            flex_direction: unwrap_or_initial!(flex_direction, direction),
            flex_wrap: unwrap_or_initial!(flex_wrap, wrap),
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            self.flex_direction.to_css(dest)?;
            dest.write_str(" ")?;
            self.flex_wrap.to_css(dest)
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="flex" sub_properties="flex-grow flex-shrink flex-basis" extra_prefixes="webkit"
                    spec="https://drafts.csswg.org/css-flexbox/#flex-property">
    use values::specified::Number;

    fn parse_flexibility(context: &ParserContext, input: &mut Parser)
                         -> Result<(Number, Option<Number>),()> {
        let grow = try!(Number::parse_non_negative(context, input));
        let shrink = input.try(|i| Number::parse_non_negative(context, i)).ok();
        Ok((grow, shrink))
    }

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let mut grow = None;
        let mut shrink = None;
        let mut basis = None;

        if input.try(|input| input.expect_ident_matching("none")).is_ok() {
            return Ok(Longhands {
                flex_grow: Number::new(0.0),
                flex_shrink: Number::new(0.0),
                flex_basis: longhands::flex_basis::SpecifiedValue::auto(),
            })
        }
        loop {
            if grow.is_none() {
                if let Ok((flex_grow, flex_shrink)) = input.try(|i| parse_flexibility(context, i)) {
                    grow = Some(flex_grow);
                    shrink = flex_shrink;
                    continue
                }
            }
            if basis.is_none() {
                if let Ok(value) = input.try(|input| longhands::flex_basis::parse(context, input)) {
                    basis = Some(value);
                    continue
                }
            }
            break
        }

        if grow.is_none() && basis.is_none() {
            return Err(())
        }
        Ok(Longhands {
            flex_grow: grow.unwrap_or(Number::new(1.0)),
            flex_shrink: shrink.unwrap_or(Number::new(1.0)),
            flex_basis: basis.unwrap_or(longhands::flex_basis::SpecifiedValue::zero()),
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            try!(self.flex_grow.to_css(dest));
            try!(dest.write_str(" "));

            try!(self.flex_shrink.to_css(dest));
            try!(dest.write_str(" "));

            self.flex_basis.to_css(dest)
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="grid-gap" sub_properties="grid-row-gap grid-column-gap"
                    spec="https://drafts.csswg.org/css-grid/#propdef-grid-gap"
                    products="gecko">
  use properties::longhands::{grid_row_gap, grid_column_gap};

  pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
      let row_gap = grid_row_gap::parse(context, input)?;
      let column_gap = input.try(|input| grid_column_gap::parse(context, input)).unwrap_or(row_gap.clone());

      Ok(Longhands {
        grid_row_gap: row_gap,
        grid_column_gap: column_gap,
      })
  }

  impl<'a> ToCss for LonghandsToSerialize<'a>  {
      fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
          if self.grid_row_gap == self.grid_column_gap {
            self.grid_row_gap.to_css(dest)
          } else {
            self.grid_row_gap.to_css(dest)?;
            dest.write_str(" ")?;
            self.grid_column_gap.to_css(dest)
          }
      }
  }

</%helpers:shorthand>

% for kind in ["row", "column"]:
<%helpers:shorthand name="grid-${kind}" sub_properties="grid-${kind}-start grid-${kind}-end"
                    spec="https://drafts.csswg.org/css-grid/#propdef-grid-${kind}"
                    products="gecko">
    use values::specified::GridLine;
    use parser::Parse;

    // NOTE: Since both the shorthands have the same code, we should (re-)use code from one to implement
    // the other. This might not be a big deal for now, but we should consider looking into this in the future
    // to limit the amount of code generated.
    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let start = input.try(|i| GridLine::parse(context, i))?;
        let end = if input.try(|i| i.expect_delim('/')).is_ok() {
            GridLine::parse(context, input)?
        } else {
            let mut line = GridLine::default();
            if start.integer.is_none() && !start.is_span {
                line.ident = start.ident.clone();       // ident from start value should be taken
            }

            line
        };

        Ok(Longhands {
            grid_${kind}_start: start,
            grid_${kind}_end: end,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a> {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            self.grid_${kind}_start.to_css(dest)?;
            dest.write_str(" / ")?;
            self.grid_${kind}_end.to_css(dest)
        }
    }
</%helpers:shorthand>
% endfor

<%helpers:shorthand name="grid-area"
                    sub_properties="grid-row-start grid-row-end grid-column-start grid-column-end"
                    spec="https://drafts.csswg.org/css-grid/#propdef-grid-area"
                    products="gecko">
    use values::specified::GridLine;
    use parser::Parse;

    // The code is the same as `grid-{row,column}` except that this can have four values at most.
    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        fn line_with_ident_from(other: &GridLine) -> GridLine {
            let mut this = GridLine::default();
            if other.integer.is_none() && !other.is_span {
                this.ident = other.ident.clone();
            }

            this
        }

        let row_start = input.try(|i| GridLine::parse(context, i))?;
        let (column_start, row_end, column_end) = if input.try(|i| i.expect_delim('/')).is_ok() {
            let column_start = GridLine::parse(context, input)?;
            let (row_end, column_end) = if input.try(|i| i.expect_delim('/')).is_ok() {
                let row_end = GridLine::parse(context, input)?;
                let column_end = if input.try(|i| i.expect_delim('/')).is_ok() {
                    GridLine::parse(context, input)?
                } else {        // grid-column-end has not been given
                    line_with_ident_from(&column_start)
                };

                (row_end, column_end)
            } else {        // grid-row-start and grid-column-start has been given
                let row_end = line_with_ident_from(&row_start);
                let column_end = line_with_ident_from(&column_start);
                (row_end, column_end)
            };

            (column_start, row_end, column_end)
        } else {        // only grid-row-start is given
            let line = line_with_ident_from(&row_start);
            (line.clone(), line.clone(), line)
        };

        Ok(Longhands {
            grid_row_start: row_start,
            grid_row_end: row_end,
            grid_column_start: column_start,
            grid_column_end: column_end,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a> {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            self.grid_row_start.to_css(dest)?;
            let values = [&self.grid_column_start, &self.grid_row_end, &self.grid_column_end];
            for value in &values {
                dest.write_str(" / ")?;
                value.to_css(dest)?;
            }

            Ok(())
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="place-content" sub_properties="align-content justify-content"
                    spec="https://drafts.csswg.org/css-align/#propdef-place-content"
                    products="gecko" disable_when_testing="True">
    use properties::longhands::align_content;
    use properties::longhands::justify_content;

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let align = align_content::parse(context, input)?;
        if align.has_extra_flags() {
            return Err(());
        }
        let justify = input.try(|input| justify_content::parse(context, input))
                           .unwrap_or(justify_content::SpecifiedValue::from(align));
        if justify.has_extra_flags() {
            return Err(());
        }

        Ok(Longhands {
            align_content: align,
            justify_content: justify,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a> {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if self.align_content == self.justify_content {
                self.align_content.to_css(dest)
            } else {
                self.justify_content.to_css(dest)?;
                dest.write_str(" ")?;
                self.justify_content.to_css(dest)
            }
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="place-self" sub_properties="align-self justify-self"
                    spec="https://drafts.csswg.org/css-align/#place-self-property"
                    products="gecko" disable_when_testing="True">
    use values::specified::align::AlignJustifySelf;
    use parser::Parse;

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let align = AlignJustifySelf::parse(context, input)?;
        if align.has_extra_flags() {
            return Err(());
        }
        let justify = input.try(|input| AlignJustifySelf::parse(context, input)).unwrap_or(align.clone());
        if justify.has_extra_flags() {
            return Err(());
        }

        Ok(Longhands {
            align_self: align,
            justify_self: justify,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a> {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if self.align_self == self.justify_self {
                self.align_self.to_css(dest)
            } else {
                self.align_self.to_css(dest)?;
                dest.write_str(" ")?;
                self.justify_self.to_css(dest)
            }
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="place-items" sub_properties="align-items justify-items"
                    spec="https://drafts.csswg.org/css-align/#place-items-property"
                    products="gecko" disable_when_testing="True">
    use values::specified::align::{AlignItems, JustifyItems};
    use parser::Parse;

    impl From<AlignItems> for JustifyItems {
        fn from(align: AlignItems) -> JustifyItems {
            JustifyItems(align.0)
        }
    }

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let align = AlignItems::parse(context, input)?;
        if align.has_extra_flags() {
            return Err(());
        }
        let justify = input.try(|input| JustifyItems::parse(context, input))
                           .unwrap_or(JustifyItems::from(align));
        if justify.has_extra_flags() {
            return Err(());
        }

        Ok(Longhands {
            align_items: align,
            justify_items: justify,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a> {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if self.align_items.0 == self.justify_items.0 {
                self.align_items.to_css(dest)
            } else {
                self.align_items.to_css(dest)?;
                dest.write_str(" ")?;
                self.justify_items.to_css(dest)
            }
        }
    }
</%helpers:shorthand>
<commit_msg>Fix serialisation of place-content<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<%helpers:shorthand name="flex-flow" sub_properties="flex-direction flex-wrap" extra_prefixes="webkit"
                    spec="https://drafts.csswg.org/css-flexbox/#flex-flow-property">
    use properties::longhands::{flex_direction, flex_wrap};

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let mut direction = None;
        let mut wrap = None;
        loop {
            if direction.is_none() {
                if let Ok(value) = input.try(|input| flex_direction::parse(context, input)) {
                    direction = Some(value);
                    continue
                }
            }
            if wrap.is_none() {
                if let Ok(value) = input.try(|input| flex_wrap::parse(context, input)) {
                    wrap = Some(value);
                    continue
                }
            }
            break
        }

        if direction.is_none() && wrap.is_none() {
            return Err(())
        }
        Ok(Longhands {
            flex_direction: unwrap_or_initial!(flex_direction, direction),
            flex_wrap: unwrap_or_initial!(flex_wrap, wrap),
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            self.flex_direction.to_css(dest)?;
            dest.write_str(" ")?;
            self.flex_wrap.to_css(dest)
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="flex" sub_properties="flex-grow flex-shrink flex-basis" extra_prefixes="webkit"
                    spec="https://drafts.csswg.org/css-flexbox/#flex-property">
    use values::specified::Number;

    fn parse_flexibility(context: &ParserContext, input: &mut Parser)
                         -> Result<(Number, Option<Number>),()> {
        let grow = try!(Number::parse_non_negative(context, input));
        let shrink = input.try(|i| Number::parse_non_negative(context, i)).ok();
        Ok((grow, shrink))
    }

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let mut grow = None;
        let mut shrink = None;
        let mut basis = None;

        if input.try(|input| input.expect_ident_matching("none")).is_ok() {
            return Ok(Longhands {
                flex_grow: Number::new(0.0),
                flex_shrink: Number::new(0.0),
                flex_basis: longhands::flex_basis::SpecifiedValue::auto(),
            })
        }
        loop {
            if grow.is_none() {
                if let Ok((flex_grow, flex_shrink)) = input.try(|i| parse_flexibility(context, i)) {
                    grow = Some(flex_grow);
                    shrink = flex_shrink;
                    continue
                }
            }
            if basis.is_none() {
                if let Ok(value) = input.try(|input| longhands::flex_basis::parse(context, input)) {
                    basis = Some(value);
                    continue
                }
            }
            break
        }

        if grow.is_none() && basis.is_none() {
            return Err(())
        }
        Ok(Longhands {
            flex_grow: grow.unwrap_or(Number::new(1.0)),
            flex_shrink: shrink.unwrap_or(Number::new(1.0)),
            flex_basis: basis.unwrap_or(longhands::flex_basis::SpecifiedValue::zero()),
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            try!(self.flex_grow.to_css(dest));
            try!(dest.write_str(" "));

            try!(self.flex_shrink.to_css(dest));
            try!(dest.write_str(" "));

            self.flex_basis.to_css(dest)
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="grid-gap" sub_properties="grid-row-gap grid-column-gap"
                    spec="https://drafts.csswg.org/css-grid/#propdef-grid-gap"
                    products="gecko">
  use properties::longhands::{grid_row_gap, grid_column_gap};

  pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
      let row_gap = grid_row_gap::parse(context, input)?;
      let column_gap = input.try(|input| grid_column_gap::parse(context, input)).unwrap_or(row_gap.clone());

      Ok(Longhands {
        grid_row_gap: row_gap,
        grid_column_gap: column_gap,
      })
  }

  impl<'a> ToCss for LonghandsToSerialize<'a>  {
      fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
          if self.grid_row_gap == self.grid_column_gap {
            self.grid_row_gap.to_css(dest)
          } else {
            self.grid_row_gap.to_css(dest)?;
            dest.write_str(" ")?;
            self.grid_column_gap.to_css(dest)
          }
      }
  }

</%helpers:shorthand>

% for kind in ["row", "column"]:
<%helpers:shorthand name="grid-${kind}" sub_properties="grid-${kind}-start grid-${kind}-end"
                    spec="https://drafts.csswg.org/css-grid/#propdef-grid-${kind}"
                    products="gecko">
    use values::specified::GridLine;
    use parser::Parse;

    // NOTE: Since both the shorthands have the same code, we should (re-)use code from one to implement
    // the other. This might not be a big deal for now, but we should consider looking into this in the future
    // to limit the amount of code generated.
    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let start = input.try(|i| GridLine::parse(context, i))?;
        let end = if input.try(|i| i.expect_delim('/')).is_ok() {
            GridLine::parse(context, input)?
        } else {
            let mut line = GridLine::default();
            if start.integer.is_none() && !start.is_span {
                line.ident = start.ident.clone();       // ident from start value should be taken
            }

            line
        };

        Ok(Longhands {
            grid_${kind}_start: start,
            grid_${kind}_end: end,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a> {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            self.grid_${kind}_start.to_css(dest)?;
            dest.write_str(" / ")?;
            self.grid_${kind}_end.to_css(dest)
        }
    }
</%helpers:shorthand>
% endfor

<%helpers:shorthand name="grid-area"
                    sub_properties="grid-row-start grid-row-end grid-column-start grid-column-end"
                    spec="https://drafts.csswg.org/css-grid/#propdef-grid-area"
                    products="gecko">
    use values::specified::GridLine;
    use parser::Parse;

    // The code is the same as `grid-{row,column}` except that this can have four values at most.
    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        fn line_with_ident_from(other: &GridLine) -> GridLine {
            let mut this = GridLine::default();
            if other.integer.is_none() && !other.is_span {
                this.ident = other.ident.clone();
            }

            this
        }

        let row_start = input.try(|i| GridLine::parse(context, i))?;
        let (column_start, row_end, column_end) = if input.try(|i| i.expect_delim('/')).is_ok() {
            let column_start = GridLine::parse(context, input)?;
            let (row_end, column_end) = if input.try(|i| i.expect_delim('/')).is_ok() {
                let row_end = GridLine::parse(context, input)?;
                let column_end = if input.try(|i| i.expect_delim('/')).is_ok() {
                    GridLine::parse(context, input)?
                } else {        // grid-column-end has not been given
                    line_with_ident_from(&column_start)
                };

                (row_end, column_end)
            } else {        // grid-row-start and grid-column-start has been given
                let row_end = line_with_ident_from(&row_start);
                let column_end = line_with_ident_from(&column_start);
                (row_end, column_end)
            };

            (column_start, row_end, column_end)
        } else {        // only grid-row-start is given
            let line = line_with_ident_from(&row_start);
            (line.clone(), line.clone(), line)
        };

        Ok(Longhands {
            grid_row_start: row_start,
            grid_row_end: row_end,
            grid_column_start: column_start,
            grid_column_end: column_end,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a> {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            self.grid_row_start.to_css(dest)?;
            let values = [&self.grid_column_start, &self.grid_row_end, &self.grid_column_end];
            for value in &values {
                dest.write_str(" / ")?;
                value.to_css(dest)?;
            }

            Ok(())
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="place-content" sub_properties="align-content justify-content"
                    spec="https://drafts.csswg.org/css-align/#propdef-place-content"
                    products="gecko" disable_when_testing="True">
    use properties::longhands::align_content;
    use properties::longhands::justify_content;

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let align = align_content::parse(context, input)?;
        if align.has_extra_flags() {
            return Err(());
        }
        let justify = input.try(|input| justify_content::parse(context, input))
                           .unwrap_or(justify_content::SpecifiedValue::from(align));
        if justify.has_extra_flags() {
            return Err(());
        }

        Ok(Longhands {
            align_content: align,
            justify_content: justify,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a> {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            self.align_content.to_css(dest)?;
            if self.align_content != self.justify_content {
                dest.write_str(" ")?;
                self.justify_content.to_css(dest)?;
            }
            Ok(())
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="place-self" sub_properties="align-self justify-self"
                    spec="https://drafts.csswg.org/css-align/#place-self-property"
                    products="gecko" disable_when_testing="True">
    use values::specified::align::AlignJustifySelf;
    use parser::Parse;

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let align = AlignJustifySelf::parse(context, input)?;
        if align.has_extra_flags() {
            return Err(());
        }
        let justify = input.try(|input| AlignJustifySelf::parse(context, input)).unwrap_or(align.clone());
        if justify.has_extra_flags() {
            return Err(());
        }

        Ok(Longhands {
            align_self: align,
            justify_self: justify,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a> {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if self.align_self == self.justify_self {
                self.align_self.to_css(dest)
            } else {
                self.align_self.to_css(dest)?;
                dest.write_str(" ")?;
                self.justify_self.to_css(dest)
            }
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="place-items" sub_properties="align-items justify-items"
                    spec="https://drafts.csswg.org/css-align/#place-items-property"
                    products="gecko" disable_when_testing="True">
    use values::specified::align::{AlignItems, JustifyItems};
    use parser::Parse;

    impl From<AlignItems> for JustifyItems {
        fn from(align: AlignItems) -> JustifyItems {
            JustifyItems(align.0)
        }
    }

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let align = AlignItems::parse(context, input)?;
        if align.has_extra_flags() {
            return Err(());
        }
        let justify = input.try(|input| JustifyItems::parse(context, input))
                           .unwrap_or(JustifyItems::from(align));
        if justify.has_extra_flags() {
            return Err(());
        }

        Ok(Longhands {
            align_items: align,
            justify_items: justify,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a> {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if self.align_items.0 == self.justify_items.0 {
                self.align_items.to_css(dest)
            } else {
                self.align_items.to_css(dest)?;
                dest.write_str(" ")?;
                self.justify_items.to_css(dest)
            }
        }
    }
</%helpers:shorthand>
<|endoftext|>


<commit_before>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io
use rustless;
use serde;
use serde_json;
use rustc_serialize::json;
use rustless::server::status;
use rustless::{Api, Nesting};
use valico::json_dsl;
use valico::common::error as valico_error;
use super::query;
use model::v1::*;
use model;

const MAX_LAT: f64 = 180f64;
const MIN_LAT: f64 = -180f64;

const MAX_LON: f64 = 90f64;
const MIN_LON: f64 = -90f64;

fn render<T>(mut client: rustless::Client,
             obj: T)
             -> Result<rustless::Client, rustless::ErrorResponse>
    where T: serde::Serialize
{
    client.set_json_content_type();
    client.text(serde_json::to_string(&obj).unwrap())
}

pub struct ApiEndPoint {
    pub es_cnx_string: String,
}

impl ApiEndPoint {
    pub fn root(&self) -> rustless::Api {
        Api::build(|api| {
            api.get("", |endpoint| {
                endpoint.handle(|client, _params| {
                    let desc = EndPoint { description: "autocomplete service".to_string() };
                    render(client, desc)
                })
            });

            api.error_formatter(|error, _media| {
                let err = if error.is::<rustless::errors::Validation>() {
                    let val_err = error.downcast::<rustless::errors::Validation>().unwrap();
                    // TODO better message, we shouldn't use {:?} but access the `path`
                    // and `detail` of all errrors in val_err.reason
                    CustomError {
                        short: "validation error".to_string(),
                        long: format!("invalid arguments {:?}", val_err.reason),
                    }
                } else {
                    CustomError {
                        short: "bad_request".to_string(),
                        long: format!("bad request, error: {}", error),
                    }
                };
                let mut resp = rustless::Response::from(status::StatusCode::BadRequest,
                                                        Box::new(serde_json::to_string(&err)
                                                            .unwrap()));
                resp.set_json_content_type();
                Some(resp)
            });
            api.mount(self.v1());
        })
    }

    fn v1(&self) -> rustless::Api {
        Api::build(|api| {
            api.mount(self.status());
            api.mount(self.autocomplete());
        })
    }

    fn status(&self) -> rustless::Api {
        Api::build(|api| {
            api.get("status", |endpoint| {
                let cnx = self.es_cnx_string.clone();
                endpoint.handle(move |client, _params| {
                    let status = Status {
                        version: env!("CARGO_PKG_VERSION").to_string(),
                        es: cnx.to_string(),
                        status: "good".to_string(),
                    };
                    render(client, status)
                })
            });
        })
    }

    fn autocomplete(&self) -> rustless::Api {
        Api::build(|api| {
            api.post("autocomplete", |endpoint| {
                endpoint.params(|params| {
                    params.opt_typed("q", json_dsl::string());
                    params.req("geometry", |geometry| {
                        geometry.coerce(json_dsl::object());
                        geometry.nest(|params| {
                            params.req("type", |geojson_type| {
                                geojson_type.coerce(json_dsl::string());
                                geojson_type.allow_values(&["Polygon".to_string()]);
                                geojson_type.reject_values(&["Polygon".to_string()]);
                            });
                        });
                        geometry.nest(|params| {
                            params.req("coordinates", |shape| {
                                shape.coerce(json_dsl::array());
                                shape.validate_with(|val, path| {
                                    check_coordinates(val, path, "Coordinates is invalid")
                                });
                            });
                        });
                    })
                });

                let cnx = self.es_cnx_string.clone();
                endpoint.handle(move |client, params| {
                    let q = match params.find("q") {
                            Some(val) => val.as_string().unwrap_or(""),
                            None => "",
                        }
                        .to_string();
                    let geometry = params.find_path(&["geometry"]).unwrap();
                    let coordinates =
                        geometry.find_path(&["coordinates"]).unwrap().as_array().unwrap();
                    let mut shape = Vec::new();
                    for ar in coordinates[0].as_array().unwrap() {
                        shape.push((ar[0].as_f64().unwrap(), ar[1].as_f64().unwrap()));
                    }
                    let model_autocomplete = query::autocomplete(q, None, &cnx, Some(shape));

                    let response = model::v1::AutocompleteResponse::from(model_autocomplete);
                    render(client, response)
                })
            });
            api.get("autocomplete", |endpoint| {
                endpoint.params(|params| {
                    params.req_typed("q", json_dsl::string());
                    params.opt("lon", |lon| {
                        lon.coerce(json_dsl::f64());
                        lon.validate_with(|val, path| {
                            check_bound(val, path, MIN_LON, MAX_LON, "lon is not a valid longitude")
                        });
                    });

                    params.opt("lat", |lat| {
                        lat.coerce(json_dsl::f64());
                        lat.validate_with(|val, path| {
                            check_bound(val, path, MIN_LAT, MAX_LAT, "lat is not a valid latitude")
                        });
                    });
                    params.validate_with(|val, path| {
                        // if we have a lat we should have a lon (and the opposite)
                        if let json::Json::Object(ref obj) = *val {
                            let has_lon = obj.get("lon").is_some();
                            let has_lat = obj.get("lat").is_some();
                            if has_lon ^ has_lat {
                                Err(vec![Box::new(json_dsl::errors::WrongValue {
                                             path: path.to_string(),
                                             detail: Some("you need to provide a lon AND a lat \
                                                           if you provide one of them"
                                                 .to_string()),
                                         })])
                            } else {
                                Ok(())
                            }
                        } else {
                            unreachable!("should never happen, already checked");
                        }
                    });
                });
                let cnx = self.es_cnx_string.clone();
                endpoint.handle(move |client, params| {
                    let q = params.find("q").unwrap().as_string().unwrap().to_string();
                    let lon = params.find("lon").and_then(|p| p.as_f64());
                    let lat = params.find("lat").and_then(|p| p.as_f64());
                    // we have already checked that if there is a lon, lat
                    // is not None so we can unwrap
                    let coord = lon.and_then(|lon| {
                        Some(model::Coord {
                            lon: lon,
                            lat: lat.unwrap(),
                        })
                    });
                    let model_autocomplete = query::autocomplete(q, coord, &cnx, None);

                    let response = model::v1::AutocompleteResponse::from(model_autocomplete);
                    render(client, response)
                })
            });
        })
    }
}

fn check_bound(val: &json::Json,
               path: &str,
               min: f64,
               max: f64,
               error_msg: &str)
               -> Result<(), valico_error::ValicoErrors> {
    if let json::Json::F64(lon) = *val {
        if min <= lon && lon <= max {
            Ok(())
        } else {
            Err(vec![Box::new(json_dsl::errors::WrongValue {
                         path: path.to_string(),
                         detail: Some(error_msg.to_string()),
                     })])
        }
    } else {
        unreachable!("should never happen, already checked");
    }
}

fn check_coordinates(val: &json::Json,
                     path: &str,
                     error_msg: &str)
                     -> Result<(), valico_error::ValicoErrors> {

    if !val.is_array() {
        return Err(vec![Box::new(json_dsl::errors::WrongType {
                            path: path.to_string(),
                            detail: error_msg.to_string(),
                        })]);
    }
    let array = val.as_array().unwrap();
    if array.is_empty() {
        return Err(vec![Box::new(json_dsl::errors::WrongValue {
                            path: path.to_string(),
                            detail: Some(error_msg.to_string()),
                        })]);
    }

    for arr0 in array {
        if !arr0.is_array() {
            return Err(vec![Box::new(json_dsl::errors::WrongType {
                                path: path.to_string(),
                                detail: error_msg.to_string(),
                            })]);
        }
        let arr1 = arr0.as_array().unwrap();
        if arr1.is_empty() {
            return Err(vec![Box::new(json_dsl::errors::WrongValue {
                                path: path.to_string(),
                                detail: Some(error_msg.to_string()),
                            })]);
        }
        for arr2 in arr1 {
            if !arr2.is_array() {
                return Err(vec![Box::new(json_dsl::errors::WrongType {
                                    path: path.to_string(),
                                    detail: error_msg.to_string(),
                                })]);
            }
            let lonlat = arr2.as_array().unwrap();
            if lonlat.len() != 2 {
                return Err(vec![Box::new(json_dsl::errors::WrongValue {
                                    path: path.to_string(),
                                    detail: Some(error_msg.to_string()),
                                })]);
            }

            if !(lonlat[0].is_f64() && lonlat[1].is_f64()) {
                return Err(vec![Box::new(json_dsl::errors::WrongType {
                                    path: path.to_string(),
                                    detail: error_msg.to_string(),
                                })]);
            }
            let lon = lonlat[0].as_f64().unwrap();
            let lat = lonlat[1].as_f64().unwrap();
            if !(MIN_LON <= lon && lon <= MAX_LON) {
                return Err(vec![Box::new(json_dsl::errors::WrongValue {
                                    path: path.to_string(),
                                    detail: Some(error_msg.to_string()),
                                })]);
            }
            if !(MIN_LAT <= lat && lat <= MAX_LAT) {
                return Err(vec![Box::new(json_dsl::errors::WrongValue {
                                    path: path.to_string(),
                                    detail: Some(error_msg.to_string()),
                                })]);
            }
        }
    }
    Ok(())
}
<commit_msg>correction<commit_after>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io
use rustless;
use serde;
use serde_json;
use rustc_serialize::json;
use rustless::server::status;
use rustless::{Api, Nesting};
use valico::json_dsl;
use valico::common::error as valico_error;
use super::query;
use model::v1::*;
use model;

const MAX_LAT: f64 = 180f64;
const MIN_LAT: f64 = -180f64;

const MAX_LON: f64 = 90f64;
const MIN_LON: f64 = -90f64;

fn render<T>(mut client: rustless::Client,
             obj: T)
             -> Result<rustless::Client, rustless::ErrorResponse>
    where T: serde::Serialize
{
    client.set_json_content_type();
    client.text(serde_json::to_string(&obj).unwrap())
}

pub struct ApiEndPoint {
    pub es_cnx_string: String,
}

impl ApiEndPoint {
    pub fn root(&self) -> rustless::Api {
        Api::build(|api| {
            api.get("", |endpoint| {
                endpoint.handle(|client, _params| {
                    let desc = EndPoint { description: "autocomplete service".to_string() };
                    render(client, desc)
                })
            });

            api.error_formatter(|error, _media| {
                let err = if error.is::<rustless::errors::Validation>() {
                    let val_err = error.downcast::<rustless::errors::Validation>().unwrap();
                    // TODO better message, we shouldn't use {:?} but access the `path`
                    // and `detail` of all errrors in val_err.reason
                    CustomError {
                        short: "validation error".to_string(),
                        long: format!("invalid arguments {:?}", val_err.reason),
                    }
                } else {
                    CustomError {
                        short: "bad_request".to_string(),
                        long: format!("bad request, error: {}", error),
                    }
                };
                let mut resp = rustless::Response::from(status::StatusCode::BadRequest,
                                                        Box::new(serde_json::to_string(&err)
                                                            .unwrap()));
                resp.set_json_content_type();
                Some(resp)
            });
            api.mount(self.v1());
        })
    }

    fn v1(&self) -> rustless::Api {
        Api::build(|api| {
            api.mount(self.status());
            api.mount(self.autocomplete());
        })
    }

    fn status(&self) -> rustless::Api {
        Api::build(|api| {
            api.get("status", |endpoint| {
                let cnx = self.es_cnx_string.clone();
                endpoint.handle(move |client, _params| {
                    let status = Status {
                        version: env!("CARGO_PKG_VERSION").to_string(),
                        es: cnx.to_string(),
                        status: "good".to_string(),
                    };
                    render(client, status)
                })
            });
        })
    }

    fn autocomplete(&self) -> rustless::Api {
        Api::build(|api| {
            api.post("autocomplete", |endpoint| {
                endpoint.params(|params| {
                    params.opt_typed("q", json_dsl::string());
                    params.req("geometry", |geometry| {
                        geometry.coerce(json_dsl::object());
                        geometry.nest(|params| {
                            params.req("type", |geojson_type| {
                                geojson_type.coerce(json_dsl::string());
                                geojson_type.allow_values(&["Polygon".to_string()]);
                            });
                        });
                        geometry.nest(|params| {
                            params.req("coordinates", |shape| {
                                shape.coerce(json_dsl::array());
                                shape.validate_with(|val, path| {
                                    check_coordinates(val, path, "Coordinates is invalid")
                                });
                            });
                        });
                    })
                });

                let cnx = self.es_cnx_string.clone();
                endpoint.handle(move |client, params| {
                    let q = match params.find("q") {
                            Some(val) => val.as_string().unwrap_or(""),
                            None => "",
                        }
                        .to_string();
                    let geometry = params.find_path(&["geometry"]).unwrap();
                    let coordinates =
                        geometry.find_path(&["coordinates"]).unwrap().as_array().unwrap();
                    let mut shape = Vec::new();
                    for ar in coordinates[0].as_array().unwrap() {
                        shape.push((ar[0].as_f64().unwrap(), ar[1].as_f64().unwrap()));
                    }
                    let model_autocomplete = query::autocomplete(q, None, &cnx, Some(shape));

                    let response = model::v1::AutocompleteResponse::from(model_autocomplete);
                    render(client, response)
                })
            });
            api.get("autocomplete", |endpoint| {
                endpoint.params(|params| {
                    params.req_typed("q", json_dsl::string());
                    params.opt("lon", |lon| {
                        lon.coerce(json_dsl::f64());
                        lon.validate_with(|val, path| {
                            check_bound(val, path, MIN_LON, MAX_LON, "lon is not a valid longitude")
                        });
                    });

                    params.opt("lat", |lat| {
                        lat.coerce(json_dsl::f64());
                        lat.validate_with(|val, path| {
                            check_bound(val, path, MIN_LAT, MAX_LAT, "lat is not a valid latitude")
                        });
                    });
                    params.validate_with(|val, path| {
                        // if we have a lat we should have a lon (and the opposite)
                        if let json::Json::Object(ref obj) = *val {
                            let has_lon = obj.get("lon").is_some();
                            let has_lat = obj.get("lat").is_some();
                            if has_lon ^ has_lat {
                                Err(vec![Box::new(json_dsl::errors::WrongValue {
                                             path: path.to_string(),
                                             detail: Some("you need to provide a lon AND a lat \
                                                           if you provide one of them"
                                                 .to_string()),
                                         })])
                            } else {
                                Ok(())
                            }
                        } else {
                            unreachable!("should never happen, already checked");
                        }
                    });
                });
                let cnx = self.es_cnx_string.clone();
                endpoint.handle(move |client, params| {
                    let q = params.find("q").unwrap().as_string().unwrap().to_string();
                    let lon = params.find("lon").and_then(|p| p.as_f64());
                    let lat = params.find("lat").and_then(|p| p.as_f64());
                    // we have already checked that if there is a lon, lat
                    // is not None so we can unwrap
                    let coord = lon.and_then(|lon| {
                        Some(model::Coord {
                            lon: lon,
                            lat: lat.unwrap(),
                        })
                    });
                    let model_autocomplete = query::autocomplete(q, coord, &cnx, None);

                    let response = model::v1::AutocompleteResponse::from(model_autocomplete);
                    render(client, response)
                })
            });
        })
    }
}

fn check_bound(val: &json::Json,
               path: &str,
               min: f64,
               max: f64,
               error_msg: &str)
               -> Result<(), valico_error::ValicoErrors> {
    if let json::Json::F64(lon) = *val {
        if min <= lon && lon <= max {
            Ok(())
        } else {
            Err(vec![Box::new(json_dsl::errors::WrongValue {
                         path: path.to_string(),
                         detail: Some(error_msg.to_string()),
                     })])
        }
    } else {
        unreachable!("should never happen, already checked");
    }
}

fn check_coordinates(val: &json::Json,
                     path: &str,
                     error_msg: &str)
                     -> Result<(), valico_error::ValicoErrors> {

    if !val.is_array() {
        return Err(vec![Box::new(json_dsl::errors::WrongType {
                            path: path.to_string(),
                            detail: error_msg.to_string(),
                        })]);
    }
    let array = val.as_array().unwrap();
    if array.is_empty() {
        return Err(vec![Box::new(json_dsl::errors::WrongValue {
                            path: path.to_string(),
                            detail: Some(error_msg.to_string()),
                        })]);
    }

    for arr0 in array {
        if !arr0.is_array() {
            return Err(vec![Box::new(json_dsl::errors::WrongType {
                                path: path.to_string(),
                                detail: error_msg.to_string(),
                            })]);
        }
        let arr1 = arr0.as_array().unwrap();
        if arr1.is_empty() {
            return Err(vec![Box::new(json_dsl::errors::WrongValue {
                                path: path.to_string(),
                                detail: Some(error_msg.to_string()),
                            })]);
        }
        for arr2 in arr1 {
            if !arr2.is_array() {
                return Err(vec![Box::new(json_dsl::errors::WrongType {
                                    path: path.to_string(),
                                    detail: error_msg.to_string(),
                                })]);
            }
            let lonlat = arr2.as_array().unwrap();
            if lonlat.len() != 2 {
                return Err(vec![Box::new(json_dsl::errors::WrongValue {
                                    path: path.to_string(),
                                    detail: Some(error_msg.to_string()),
                                })]);
            }

            if !(lonlat[0].is_f64() && lonlat[1].is_f64()) {
                return Err(vec![Box::new(json_dsl::errors::WrongType {
                                    path: path.to_string(),
                                    detail: error_msg.to_string(),
                                })]);
            }
            let lon = lonlat[0].as_f64().unwrap();
            let lat = lonlat[1].as_f64().unwrap();
            if !(MIN_LON <= lon && lon <= MAX_LON) {
                return Err(vec![Box::new(json_dsl::errors::WrongValue {
                                    path: path.to_string(),
                                    detail: Some(error_msg.to_string()),
                                })]);
            }
            if !(MIN_LAT <= lat && lat <= MAX_LAT) {
                return Err(vec![Box::new(json_dsl::errors::WrongValue {
                                    path: path.to_string(),
                                    detail: Some(error_msg.to_string()),
                                })]);
            }
        }
    }
    Ok(())
}
<|endoftext|>


<commit_before>//! Types for the *m.room.canonical_alias* event.

use ruma_events_macros::StateEventContent;
use ruma_identifiers::RoomAliasId;
use serde::{Deserialize, Serialize};

use crate::StateEvent;

/// Informs the room as to which alias is the canonical one.
pub type CanonicalAliasEvent = StateEvent<CanonicalAliasEventContent>;

/// The payload for `CanonicalAliasEvent`.
#[derive(Clone, Debug, Deserialize, Serialize, StateEventContent)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[ruma_event(type = "m.room.canonical_alias")]
pub struct CanonicalAliasEventContent {
    /// The canonical alias.
    ///
    /// Rooms with `alias: None` should be treated the same as a room
    /// with no canonical alias.
    #[serde(
        default,
        deserialize_with = "ruma_serde::empty_string_as_none",
        skip_serializing_if = "Option::is_none"
    )]
    pub alias: Option<RoomAliasId>,

    /// List of alternative aliases to the room.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub alt_aliases: Vec<RoomAliasId>,
}

impl CanonicalAliasEventContent {
    /// Creates an empty `CanonicalAliasEventContent`.
    pub fn new() -> Self {
        Self { alias: None, alt_aliases: Vec::new() }
    }
}

#[cfg(test)]
mod tests {
    use std::time::{Duration, UNIX_EPOCH};

    use ruma_identifiers::{event_id, room_alias_id, room_id, user_id};
    use ruma_serde::Raw;
    use serde_json::{from_value as from_json_value, json, to_value as to_json_value};

    use super::CanonicalAliasEventContent;
    use crate::{StateEvent, Unsigned};

    #[test]
    fn serialization_with_optional_fields_as_none() {
        let canonical_alias_event = StateEvent {
            content: CanonicalAliasEventContent {
                alias: Some(room_alias_id!("#somewhere:localhost")),
                alt_aliases: Vec::new(),
            },
            event_id: event_id!("$h29iv0s8:example.com"),
            origin_server_ts: UNIX_EPOCH + Duration::from_millis(1),
            prev_content: None,
            room_id: room_id!("!dummy:example.com"),
            sender: user_id!("@carl:example.com"),
            state_key: "".into(),
            unsigned: Unsigned::default(),
        };

        let actual = to_json_value(&canonical_alias_event).unwrap();
        let expected = json!({
            "content": {
                "alias": "#somewhere:localhost"
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "room_id": "!dummy:example.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.canonical_alias"
        });

        assert_eq!(actual, expected);
    }

    #[test]
    fn absent_field_as_none() {
        let json_data = json!({
            "content": {},
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "room_id": "!dummy:example.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.canonical_alias"
        });

        assert_eq!(
            from_json_value::<Raw<StateEvent<CanonicalAliasEventContent>>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap()
                .content
                .alias,
            None
        );
    }

    #[test]
    fn null_field_as_none() {
        let json_data = json!({
            "content": {
                "alias": null
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "room_id": "!dummy:example.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.canonical_alias"
        });
        assert_eq!(
            from_json_value::<Raw<StateEvent<CanonicalAliasEventContent>>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap()
                .content
                .alias,
            None
        );
    }

    #[test]
    fn empty_field_as_none() {
        let json_data = json!({
            "content": {
                "alias": ""
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "room_id": "!dummy:example.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.canonical_alias"
        });
        assert_eq!(
            from_json_value::<Raw<StateEvent<CanonicalAliasEventContent>>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap()
                .content
                .alias,
            None
        );
    }

    #[test]
    fn nonempty_field_as_some() {
        let alias = Some(room_alias_id!("#somewhere:localhost"));
        let json_data = json!({
            "content": {
                "alias": "#somewhere:localhost"
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "room_id": "!dummy:example.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.canonical_alias"
        });
        assert_eq!(
            from_json_value::<Raw<StateEvent<CanonicalAliasEventContent>>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap()
                .content
                .alias,
            alias
        );
    }
}
<commit_msg>events: Add missing Default implementation<commit_after>//! Types for the *m.room.canonical_alias* event.

use ruma_events_macros::StateEventContent;
use ruma_identifiers::RoomAliasId;
use serde::{Deserialize, Serialize};

use crate::StateEvent;

/// Informs the room as to which alias is the canonical one.
pub type CanonicalAliasEvent = StateEvent<CanonicalAliasEventContent>;

/// The payload for `CanonicalAliasEvent`.
#[derive(Clone, Debug, Default, Deserialize, Serialize, StateEventContent)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[ruma_event(type = "m.room.canonical_alias")]
pub struct CanonicalAliasEventContent {
    /// The canonical alias.
    ///
    /// Rooms with `alias: None` should be treated the same as a room
    /// with no canonical alias.
    #[serde(
        default,
        deserialize_with = "ruma_serde::empty_string_as_none",
        skip_serializing_if = "Option::is_none"
    )]
    pub alias: Option<RoomAliasId>,

    /// List of alternative aliases to the room.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub alt_aliases: Vec<RoomAliasId>,
}

impl CanonicalAliasEventContent {
    /// Creates an empty `CanonicalAliasEventContent`.
    pub fn new() -> Self {
        Self { alias: None, alt_aliases: Vec::new() }
    }
}

#[cfg(test)]
mod tests {
    use std::time::{Duration, UNIX_EPOCH};

    use ruma_identifiers::{event_id, room_alias_id, room_id, user_id};
    use ruma_serde::Raw;
    use serde_json::{from_value as from_json_value, json, to_value as to_json_value};

    use super::CanonicalAliasEventContent;
    use crate::{StateEvent, Unsigned};

    #[test]
    fn serialization_with_optional_fields_as_none() {
        let canonical_alias_event = StateEvent {
            content: CanonicalAliasEventContent {
                alias: Some(room_alias_id!("#somewhere:localhost")),
                alt_aliases: Vec::new(),
            },
            event_id: event_id!("$h29iv0s8:example.com"),
            origin_server_ts: UNIX_EPOCH + Duration::from_millis(1),
            prev_content: None,
            room_id: room_id!("!dummy:example.com"),
            sender: user_id!("@carl:example.com"),
            state_key: "".into(),
            unsigned: Unsigned::default(),
        };

        let actual = to_json_value(&canonical_alias_event).unwrap();
        let expected = json!({
            "content": {
                "alias": "#somewhere:localhost"
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "room_id": "!dummy:example.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.canonical_alias"
        });

        assert_eq!(actual, expected);
    }

    #[test]
    fn absent_field_as_none() {
        let json_data = json!({
            "content": {},
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "room_id": "!dummy:example.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.canonical_alias"
        });

        assert_eq!(
            from_json_value::<Raw<StateEvent<CanonicalAliasEventContent>>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap()
                .content
                .alias,
            None
        );
    }

    #[test]
    fn null_field_as_none() {
        let json_data = json!({
            "content": {
                "alias": null
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "room_id": "!dummy:example.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.canonical_alias"
        });
        assert_eq!(
            from_json_value::<Raw<StateEvent<CanonicalAliasEventContent>>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap()
                .content
                .alias,
            None
        );
    }

    #[test]
    fn empty_field_as_none() {
        let json_data = json!({
            "content": {
                "alias": ""
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "room_id": "!dummy:example.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.canonical_alias"
        });
        assert_eq!(
            from_json_value::<Raw<StateEvent<CanonicalAliasEventContent>>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap()
                .content
                .alias,
            None
        );
    }

    #[test]
    fn nonempty_field_as_some() {
        let alias = Some(room_alias_id!("#somewhere:localhost"));
        let json_data = json!({
            "content": {
                "alias": "#somewhere:localhost"
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "room_id": "!dummy:example.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.canonical_alias"
        });
        assert_eq!(
            from_json_value::<Raw<StateEvent<CanonicalAliasEventContent>>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap()
                .content
                .alias,
            alias
        );
    }
}
<|endoftext|>


<commit_before>use std::cell::Cell;

use cairo::{self, MatrixTrait};

use crate::attributes::Attribute;
use crate::bbox::BoundingBox;
use crate::coord_units::CoordUnits;
use crate::drawing_ctx::DrawingCtx;
use crate::error::RenderingError;
use crate::node::{NodeResult, NodeTrait, RsvgNode};
use crate::parsers::ParseValue;
use crate::property_bag::PropertyBag;

coord_units!(ClipPathUnits, CoordUnits::UserSpaceOnUse);

pub struct NodeClipPath {
    units: Cell<ClipPathUnits>,
}

impl NodeClipPath {
    pub fn new() -> NodeClipPath {
        NodeClipPath {
            units: Cell::new(ClipPathUnits::default()),
        }
    }

    pub fn get_units(&self) -> ClipPathUnits {
        self.units.get()
    }

    pub fn to_cairo_context(
        &self,
        node: &RsvgNode,
        draw_ctx: &mut DrawingCtx,
        bbox: &BoundingBox,
    ) -> Result<(), RenderingError> {
        let cascaded = node.get_cascaded_values();

        let clip_units = self.units.get();

        draw_ctx.with_saved_matrix(&mut |dc| {
            let cr = dc.get_cairo_context();

            if clip_units == ClipPathUnits(CoordUnits::ObjectBoundingBox) {
                if bbox.rect.is_none() {
                    // The node being clipped is empty / doesn't have a
                    // bounding box, so there's nothing to clip!
                    return Ok(());
                }

                let rect = bbox.rect.as_ref().unwrap();

                cr.transform(cairo::Matrix::new(
                    rect.width,
                    0.0,
                    0.0,
                    rect.height,
                    rect.x,
                    rect.y,
                ))
            }

            // here we don't push a layer because we are clipping
            let res = node.draw_children(&cascaded, dc, true);

            cr.clip();
            res
        })
    }
}

impl NodeTrait for NodeClipPath {
    fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
        for (attr, value) in pbag.iter() {
            match attr {
                Attribute::ClipPathUnits => self.units.set(attr.parse(value)?),
                _ => (),
            }
        }

        Ok(())
    }
}
<commit_msg>NodeClipPath::to_cairo_context(): Move the short-circuit if there's no bounding box to the beginning<commit_after>use std::cell::Cell;

use cairo::{self, MatrixTrait};

use crate::attributes::Attribute;
use crate::bbox::BoundingBox;
use crate::coord_units::CoordUnits;
use crate::drawing_ctx::DrawingCtx;
use crate::error::RenderingError;
use crate::node::{NodeResult, NodeTrait, RsvgNode};
use crate::parsers::ParseValue;
use crate::property_bag::PropertyBag;

coord_units!(ClipPathUnits, CoordUnits::UserSpaceOnUse);

pub struct NodeClipPath {
    units: Cell<ClipPathUnits>,
}

impl NodeClipPath {
    pub fn new() -> NodeClipPath {
        NodeClipPath {
            units: Cell::new(ClipPathUnits::default()),
        }
    }

    pub fn get_units(&self) -> ClipPathUnits {
        self.units.get()
    }

    pub fn to_cairo_context(
        &self,
        node: &RsvgNode,
        draw_ctx: &mut DrawingCtx,
        bbox: &BoundingBox,
    ) -> Result<(), RenderingError> {
        let clip_units = self.units.get();

        if clip_units == ClipPathUnits(CoordUnits::ObjectBoundingBox) && bbox.rect.is_none() {
            // The node being clipped is empty / doesn't have a
            // bounding box, so there's nothing to clip!
            return Ok(());
        }

        let cascaded = node.get_cascaded_values();

        draw_ctx.with_saved_matrix(&mut |dc| {
            let cr = dc.get_cairo_context();

            if clip_units == ClipPathUnits(CoordUnits::ObjectBoundingBox) {
                let rect = bbox.rect.as_ref().unwrap();

                cr.transform(cairo::Matrix::new(
                    rect.width,
                    0.0,
                    0.0,
                    rect.height,
                    rect.x,
                    rect.y,
                ))
            }

            // here we don't push a layer because we are clipping
            let res = node.draw_children(&cascaded, dc, true);

            cr.clip();
            res
        })
    }
}

impl NodeTrait for NodeClipPath {
    fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
        for (attr, value) in pbag.iter() {
            match attr {
                Attribute::ClipPathUnits => self.units.set(attr.parse(value)?),
                _ => (),
            }
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>extern crate base64;
extern crate serde;

extern crate base64_serde;

use base64::STANDARD;
use base64_serde::base64_serde_type;
use serde::{Deserialize, Serialize};

use std::collections::HashMap;

base64_serde_type!(Base64Standard, STANDARD);

/// https://tools.ietf.org/html/bcp47
#[derive(Serialize, Deserialize, Clone)]
pub struct LanguageTag(String);

/// https://tools.ietf.org/html/rfc7231#section-3.1.1.1
#[derive(Serialize, Deserialize, Clone)]
pub struct MediaType(String);

#[derive(Serialize, Deserialize, Clone)]
pub struct VariantAttributes {
    language: Option<LanguageTag>,
}

#[derive(Serialize, Deserialize, Clone)]
pub struct FileName(String);

#[derive(Serialize, Deserialize, Clone)]
pub struct TextVariant {
    attributes: VariantAttributes,
    value: String,
}

#[derive(Serialize, Deserialize, Clone)]
pub struct FileVariant {
    attributes: VariantAttributes,
    name: Option<FileName>,
    r#type: Option<MediaType>,
    #[serde(with = "Base64Standard")]
    content: Vec<u8>,
}

#[derive(Serialize, Deserialize, Clone)]
pub struct Problem {
    title: Vec<TextVariant>,
    statement: Vec<FileVariant>,
    attachments: HashMap<String, FileVariant>,
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn it_works() {
        println!(
            "{}",
            serde_json::to_string(&Problem {
                title: vec![TextVariant {
                    attributes: VariantAttributes {
                        language: Some(LanguageTag("en-US".into())),
                    },
                    value: "Title".into(),
                }],
                statement: vec![FileVariant {
                    attributes: VariantAttributes {
                        language: Some(LanguageTag("en-US".into())),
                    },
                    name: Some(FileName("english.pdf".into())),
                    r#type: Some(MediaType("application/pdf".into())),
                    content: vec![],
                }],
                attachments: {
                    let mut map = HashMap::new();
                    map.insert(
                        "skeleton".into(),
                        FileVariant {
                            attributes: VariantAttributes { language: None },
                            name: Some(FileName("skeleton.cpp".into())),
                            r#type: None,
                            content: vec![0],
                        },
                    );
                    map
                },
            })
            .unwrap()
        );
    }
}
<commit_msg>ppf: add some type safety<commit_after>extern crate base64;
extern crate serde;

extern crate base64_serde;

use base64::STANDARD;
use base64_serde::base64_serde_type;
use serde::{Deserialize, Serialize};

use std::collections::HashMap;

base64_serde_type!(Base64Standard, STANDARD);

/// https://tools.ietf.org/html/bcp47
#[derive(Serialize, Deserialize, Clone)]
pub struct LanguageTag(String);

impl From<&str> for LanguageTag {
    fn from(s: &str) -> LanguageTag {
        // TODO: validation
        LanguageTag(s.to_owned())
    }
}

/// https://tools.ietf.org/html/rfc7231#section-3.1.1.1
#[derive(Serialize, Deserialize, Clone)]
pub struct MediaType(String);

impl From<&str> for MediaType {
    fn from(s: &str) -> MediaType {
        // TODO: validation
        MediaType(s.to_owned())
    }
}

#[derive(Serialize, Deserialize, Clone, Default)]
pub struct VariantAttributes(HashMap<String, String>);

pub struct VariantAttributesBuilder(HashMap<String, String>);

impl VariantAttributes {
    pub fn builder() -> VariantAttributesBuilder {
        VariantAttributesBuilder(HashMap::new())
    }
}

impl VariantAttributesBuilder {
    pub fn language(mut self, tag: LanguageTag) -> VariantAttributesBuilder {
        self.0.insert("language".into(), tag.0);
        self
    }

    pub fn build(self) -> VariantAttributes {
        VariantAttributes(self.0)
    }
}

#[derive(Serialize, Deserialize, Clone)]
pub struct FileName(String);

#[derive(Serialize, Deserialize, Clone)]
pub struct TextVariant {
    attributes: VariantAttributes,
    value: String,
}

#[derive(Serialize, Deserialize, Clone)]
pub struct FileVariant {
    attributes: VariantAttributes,
    name: Option<FileName>,
    r#type: Option<MediaType>,
    #[serde(with = "Base64Standard")]
    content: Vec<u8>,
}

#[derive(Serialize, Deserialize, Clone)]
pub struct Problem {
    title: Vec<TextVariant>,
    statement: Vec<FileVariant>,
    attachments: HashMap<String, FileVariant>,
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn it_works() {
        println!(
            "{}",
            serde_json::to_string(&Problem {
                title: vec![TextVariant {
                    attributes: VariantAttributes::builder()
                        .language("en-US".into())
                        .build(),
                    value: "Title".into(),
                }],
                statement: vec![FileVariant {
                    attributes: VariantAttributes::builder()
                        .language("en-US".into())
                        .build(),
                    name: Some(FileName("english.pdf".into())),
                    r#type: Some(MediaType::from("application/pdf")),
                    content: vec![],
                }],
                attachments: {
                    let mut map = HashMap::new();
                    map.insert(
                        "skeleton".into(),
                        FileVariant {
                            attributes: VariantAttributes::default(),
                            name: Some(FileName("skeleton.cpp".into())),
                            r#type: None,
                            content: vec![0],
                        },
                    );
                    map
                },
            })
            .unwrap()
        );
    }
}
<|endoftext|>


<commit_before>extern crate memmap;
extern crate object;

use std::{env, fs, process};

use object::{Object, SectionKind, Symbol, SymbolKind};

fn main() {
    let arg_len = env::args().len();
    if arg_len <= 1 {
        eprintln!("Usage: {} <file> ...", env::args().next().unwrap());
        process::exit(1);
    }

    for file_path in env::args().skip(1) {
        if arg_len > 2 {
            println!();
            println!("{}:", file_path);
        }

        let file = match fs::File::open(&file_path) {
            Ok(file) => file,
            Err(err) => {
                println!("Failed to open file '{}': {}", file_path, err,);
                continue;
            }
        };
        let file = match unsafe { memmap::Mmap::map(&file) } {
            Ok(mmap) => mmap,
            Err(err) => {
                println!("Failed to map file '{}': {}", file_path, err,);
                continue;
            }
        };
        let file = match object::File::parse(&*file) {
            Ok(file) => file,
            Err(err) => {
                println!("Failed to parse file '{}': {}", file_path, err);
                continue;
            }
        };

        println!("Debugging symbols:");
        for symbol in file.symbols() {
            print_symbol(&symbol);
        }
        println!();

        println!("Dynamic symbols:");
        for symbol in file.dynamic_symbols() {
            print_symbol(&symbol);
        }
    }
}

fn print_symbol(symbol: &Symbol) {
    match symbol.kind() {
        SymbolKind::Section | SymbolKind::File => return,
        _ => {}
    }
    let kind = match symbol.section_kind() {
        Some(SectionKind::Unknown) => '?',
        Some(SectionKind::Text) => {
            if symbol.is_global() {
                'T'
            } else {
                't'
            }
        }
        Some(SectionKind::Data) => {
            if symbol.is_global() {
                'D'
            } else {
                'd'
            }
        }
        Some(SectionKind::ReadOnlyData) => {
            if symbol.is_global() {
                'R'
            } else {
                'r'
            }
        }
        Some(SectionKind::UninitializedData) => {
            if symbol.is_global() {
                'B'
            } else {
                'b'
            }
        }
        Some(SectionKind::Other) => {
            if symbol.is_global() {
                'S'
            } else {
                's'
            }
        }
        None => 'U',
    };
    if symbol.is_undefined() {
        print!("{:16} ", "");
    } else {
        print!("{:016x} ", symbol.address());
    }
    println!(
        "{:016x} {} {}",
        symbol.size(),
        kind,
        symbol.name().unwrap_or("<unknown>"),
    );
}
<commit_msg>Modernize nm examples<commit_after>extern crate memmap;
extern crate object;

use std::{env, fs, process};

use object::{Object, SectionKind, Symbol, SymbolKind};

fn main() {
    let arg_len = env::args().len();
    if arg_len <= 1 {
        eprintln!("Usage: {} <file> ...", env::args().next().unwrap());
        process::exit(1);
    }

    for file_path in env::args().skip(1) {
        if arg_len > 2 {
            println!();
            println!("{}:", file_path);
        }

        let file = match fs::File::open(&file_path) {
            Ok(file) => file,
            Err(err) => {
                println!("Failed to open file '{}': {}", file_path, err,);
                continue;
            }
        };
        let file = match unsafe { memmap::Mmap::map(&file) } {
            Ok(mmap) => mmap,
            Err(err) => {
                println!("Failed to map file '{}': {}", file_path, err,);
                continue;
            }
        };
        let file = match object::File::parse(&*file) {
            Ok(file) => file,
            Err(err) => {
                println!("Failed to parse file '{}': {}", file_path, err);
                continue;
            }
        };

        println!("Debugging symbols:");
        for symbol in file.symbols() {
            print_symbol(&symbol);
        }
        println!();

        println!("Dynamic symbols:");
        for symbol in file.dynamic_symbols() {
            print_symbol(&symbol);
        }
    }
}

fn print_symbol(symbol: &Symbol) {
    if let SymbolKind::Section | SymbolKind::File = symbol.kind() {
        return;
    }

    let mut kind = match symbol.section_kind() {
        Some(SectionKind::Unknown) => '?',
        Some(SectionKind::Text) => 't',
        Some(SectionKind::Data) => 'd',
        Some(SectionKind::ReadOnlyData) => 'r',
        Some(SectionKind::UninitializedData) => 'b',
        Some(SectionKind::Other) => 's',
        None => 'U',
    };

    if symbol.is_global() {
        kind = kind.to_ascii_uppercase();
    }

    if symbol.is_undefined() {
        print!("{:16} ", "");
    } else {
        print!("{:016x} ", symbol.address());
    }
    println!(
        "{:016x} {} {}",
        symbol.size(),
        kind,
        symbol.name().unwrap_or("<unknown>"),
    );
}
<|endoftext|>


<commit_before>/* 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()));
    }
}
<commit_msg>Replace HashMap with vector of 2-tuples<commit_after>/* AES ECB cut and paste
 *
 * Dmitry Vasiliev <dima@hlabs.org>
 */

extern crate aes_lib;

use std::rand::random;

use aes_lib::{AES_BLOCK_SIZE, decrypt_aes_ecb, encrypt_aes_ecb};

struct Profile {
    key: Vec<u8>,
}

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 map = [("email".into_string(), email.to_string()),
                   ("uid".into_string(), "10".into_string()),
                   ("role".into_string(), "user".into_string())].to_vec();
        let encoded = encode_kv(map);
        encrypt_aes_ecb(encoded.as_slice(), self.key.as_slice())
    }

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

fn random_bytes(len: uint) -> Vec<u8> {
    Vec::from_fn(len, |_| random::<u8>())
}

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

fn encode_kv(map: Vec<(String, String)>) -> Vec<u8> {
    let mut bytes = Vec::new();
    for item in map.iter() {
        bytes.push_all(item.ref0().clone().into_bytes().as_slice());
        bytes.push(b'=');
        bytes.push_all(item.ref1().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 super::{Profile, parse_kv, encode_kv};

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

    #[test]
    fn test_profile() {
        let profile = Profile::new();
        let email = "bob@microsoft.com";
        let encrypted = profile.profile_for(email.as_slice());
        assert_eq!(profile.decrypt(encrypted.as_slice()),
                   [("email".into_string(), email.into_string()),
                    ("uid".into_string(), "10".into_string()),
                    ("role".into_string(), "user".into_string())].to_vec());
    }
}
<|endoftext|>


<commit_before>use crate::direction::Direction;
use crate::event::{AnyCb, Event, EventResult};
use crate::view::{scroll, ScrollStrategy, Selector, View};
use crate::{Printer, Rect, Vec2, With};

/// Wraps a view in a scrollable area.
pub struct ScrollView<V> {
    /// The wrapped view.
    inner: V,

    core: scroll::Core,
}

impl<V> scroll::Scroller for ScrollView<V>
where
    V: View,
{
    fn get_scroller(&self) -> &scroll::Core {
        &self.core
    }
    fn get_scroller_mut(&mut self) -> &mut scroll::Core {
        &mut self.core
    }
}

impl<V> ScrollView<V>
where
    V: View,
{
    /// Creates a new ScrollView around `view`.
    pub fn new(inner: V) -> Self {
        ScrollView {
            inner,
            core: scroll::Core::new(),
        }
    }

    /// Returns the viewport in the inner content.
    pub fn content_viewport(&self) -> Rect {
        self.core.content_viewport()
    }

    /// Defines the way scrolling is adjusted on content or size change.
    ///
    /// The scroll strategy defines how the scrolling position is adjusted
    /// when the size of the view or the content change.
    ///
    /// It is reset to `ScrollStrategy::KeepRow` whenever the user scrolls
    /// manually.
    pub fn set_scroll_strategy(&mut self, strategy: ScrollStrategy) {
        self.core.set_scroll_strategy(strategy);
    }

    /// Defines the way scrolling is adjusted on content or size change.
    ///
    /// Chainable variant.
    pub fn scroll_strategy(self, strategy: ScrollStrategy) -> Self {
        self.with(|s| s.set_scroll_strategy(strategy))
    }

    /// Control whether scroll bars are visibile.
    ///
    /// Defaults to `true`.
    pub fn set_show_scrollbars(&mut self, show_scrollbars: bool) {
        self.core.set_show_scrollbars(show_scrollbars);
    }

    /// Control whether scroll bars are visibile.
    ///
    /// Chainable variant
    pub fn show_scrollbars(self, show_scrollbars: bool) -> Self {
        self.with(|s| s.set_show_scrollbars(show_scrollbars))
    }

    /// Sets the scroll offset to the given value
    pub fn set_offset<S>(&mut self, offset: S)
    where
        S: Into<Vec2>,
    {
        self.core.set_offset(offset);
    }

    /// Controls whether this view can scroll vertically.
    ///
    /// Defaults to `true`.
    pub fn set_scroll_y(&mut self, enabled: bool) {
        self.core.set_scroll_y(enabled);
    }

    /// Controls whether this view can scroll horizontally.
    ///
    /// Defaults to `false`.
    pub fn set_scroll_x(&mut self, enabled: bool) {
        self.core.set_scroll_x(enabled);
    }

    /// Controls whether this view can scroll vertically.
    ///
    /// Defaults to `true`.
    ///
    /// Chainable variant.
    pub fn scroll_y(self, enabled: bool) -> Self {
        self.with(|s| s.set_scroll_y(enabled))
    }

    /// Controls whether this view can scroll horizontally.
    ///
    /// Defaults to `false`.
    ///
    /// Chainable variant.
    pub fn scroll_x(self, enabled: bool) -> Self {
        self.with(|s| s.set_scroll_x(enabled))
    }

    /// Programmatically scroll to the top of the view.
    pub fn scroll_to_top(&mut self) {
        self.core.scroll_to_top();
    }

    /// Programmatically scroll to the bottom of the view.
    pub fn scroll_to_bottom(&mut self) {
        self.core.scroll_to_bottom();
    }

    /// Programmatically scroll to the leftmost side of the view.
    pub fn scroll_to_left(&mut self) {
        self.core.scroll_to_left();
    }

    /// Programmatically scroll to the rightmost side of the view.
    pub fn scroll_to_right(&mut self) {
        self.core.scroll_to_right();
    }

    /// Returns the wrapped view.
    pub fn into_inner(self) -> V {
        self.inner
    }

    inner_getters!(self.inner: V);
}

impl<V> View for ScrollView<V>
where
    V: View,
{
    fn draw(&self, printer: &Printer<'_, '_>) {
        scroll::draw(self, printer, |s, p| s.inner.draw(p));
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        scroll::on_event(
            self,
            event,
            |s, e| s.inner.on_event(e),
            |s, si| s.inner.important_area(si),
        )
    }

    fn layout(&mut self, size: Vec2) {
        scroll::layout(
            self,
            size,
            self.inner.needs_relayout(),
            |s, si| s.inner.layout(si),
            |s, c| s.inner.required_size(c),
        );
    }

    fn needs_relayout(&self) -> bool {
        self.core.needs_relayout() || self.inner.needs_relayout()
    }

    fn required_size(&mut self, constraint: Vec2) -> Vec2 {
        scroll::required_size(
            self,
            constraint,
            self.inner.needs_relayout(),
            |s, c| s.inner.required_size(c),
        )
    }

    fn call_on_any<'a>(&mut self, selector: &Selector<'_>, cb: AnyCb<'a>) {
        self.inner.call_on_any(selector, cb)
    }

    fn focus_view(&mut self, selector: &Selector<'_>) -> Result<(), ()> {
        self.inner.focus_view(selector)
    }

    fn take_focus(&mut self, source: Direction) -> bool {
        self.inner.take_focus(source) || self.core.is_scrolling().any()
    }

    fn important_area(&self, size: Vec2) -> Rect {
        scroll::important_area(self, size, |s, si| s.inner.important_area(si))
    }
}
<commit_msg>Add ScrollView::{inner_size, is_at_{bottom, top, left, right}}<commit_after>use crate::direction::Direction;
use crate::event::{AnyCb, Event, EventResult};
use crate::view::{scroll, ScrollStrategy, Selector, View};
use crate::{Printer, Rect, Vec2, With};

/// Wraps a view in a scrollable area.
pub struct ScrollView<V> {
    /// The wrapped view.
    inner: V,

    core: scroll::Core,
}

impl<V> scroll::Scroller for ScrollView<V>
where
    V: View,
{
    fn get_scroller(&self) -> &scroll::Core {
        &self.core
    }
    fn get_scroller_mut(&mut self) -> &mut scroll::Core {
        &mut self.core
    }
}

impl<V> ScrollView<V>
where
    V: View,
{
    /// Creates a new ScrollView around `view`.
    pub fn new(inner: V) -> Self {
        ScrollView {
            inner,
            core: scroll::Core::new(),
        }
    }

    /// Returns the viewport in the inner content.
    pub fn content_viewport(&self) -> Rect {
        self.core.content_viewport()
    }

    /// Returns the size of the content view, as it was on the last layout
    /// phase.
    ///
    /// This is only the size the content _thinks_ it has, and may be larger
    /// than the actual size used by this `ScrollView`.
    pub fn inner_size(&self) -> Vec2 {
        self.core.inner_size()
    }

    /// Returns `true` if the top row of the content is in view.
    pub fn is_at_top(&self) -> bool {
        self.content_viewport().top() == 0
    }

    /// Returns `true` if the bottom row of the content is in view.
    pub fn is_at_bottom(&self) -> bool {
        // The viewport indicates which row is in view.
        // So the bottom row will be (height - 1)
        (1 + self.content_viewport().bottom()) >= self.inner_size().y
    }

    /// Return `true` if the left-most column of the content is in view.
    pub fn is_at_left_edge(&self) -> bool {
        self.content_viewport().left() == 0
    }

    /// Return `true` if the right-most column of the content is in view.
    pub fn is_at_right_edge(&self) -> bool {
        // The viewport indicates which row is in view.
        // So the right-most column will be (width - 1)
        (1 + self.content_viewport().right()) >= self.inner_size().x
    }

    /// Defines the way scrolling is adjusted on content or size change.
    ///
    /// The scroll strategy defines how the scrolling position is adjusted
    /// when the size of the view or the content change.
    ///
    /// It is reset to `ScrollStrategy::KeepRow` whenever the user scrolls
    /// manually.
    pub fn set_scroll_strategy(&mut self, strategy: ScrollStrategy) {
        self.core.set_scroll_strategy(strategy);
    }

    /// Defines the way scrolling is adjusted on content or size change.
    ///
    /// Chainable variant.
    pub fn scroll_strategy(self, strategy: ScrollStrategy) -> Self {
        self.with(|s| s.set_scroll_strategy(strategy))
    }

    /// Control whether scroll bars are visibile.
    ///
    /// Defaults to `true`.
    pub fn set_show_scrollbars(&mut self, show_scrollbars: bool) {
        self.core.set_show_scrollbars(show_scrollbars);
    }

    /// Control whether scroll bars are visibile.
    ///
    /// Chainable variant
    pub fn show_scrollbars(self, show_scrollbars: bool) -> Self {
        self.with(|s| s.set_show_scrollbars(show_scrollbars))
    }

    /// Sets the scroll offset to the given value
    pub fn set_offset<S>(&mut self, offset: S)
    where
        S: Into<Vec2>,
    {
        self.core.set_offset(offset);
    }

    /// Controls whether this view can scroll vertically.
    ///
    /// Defaults to `true`.
    pub fn set_scroll_y(&mut self, enabled: bool) {
        self.core.set_scroll_y(enabled);
    }

    /// Controls whether this view can scroll horizontally.
    ///
    /// Defaults to `false`.
    pub fn set_scroll_x(&mut self, enabled: bool) {
        self.core.set_scroll_x(enabled);
    }

    /// Controls whether this view can scroll vertically.
    ///
    /// Defaults to `true`.
    ///
    /// Chainable variant.
    pub fn scroll_y(self, enabled: bool) -> Self {
        self.with(|s| s.set_scroll_y(enabled))
    }

    /// Controls whether this view can scroll horizontally.
    ///
    /// Defaults to `false`.
    ///
    /// Chainable variant.
    pub fn scroll_x(self, enabled: bool) -> Self {
        self.with(|s| s.set_scroll_x(enabled))
    }

    /// Programmatically scroll to the top of the view.
    pub fn scroll_to_top(&mut self) {
        self.core.scroll_to_top();
    }

    /// Programmatically scroll to the bottom of the view.
    pub fn scroll_to_bottom(&mut self) {
        self.core.scroll_to_bottom();
    }

    /// Programmatically scroll to the leftmost side of the view.
    pub fn scroll_to_left(&mut self) {
        self.core.scroll_to_left();
    }

    /// Programmatically scroll to the rightmost side of the view.
    pub fn scroll_to_right(&mut self) {
        self.core.scroll_to_right();
    }

    /// Returns the wrapped view.
    pub fn into_inner(self) -> V {
        self.inner
    }

    inner_getters!(self.inner: V);
}

impl<V> View for ScrollView<V>
where
    V: View,
{
    fn draw(&self, printer: &Printer<'_, '_>) {
        scroll::draw(self, printer, |s, p| s.inner.draw(p));
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        scroll::on_event(
            self,
            event,
            |s, e| s.inner.on_event(e),
            |s, si| s.inner.important_area(si),
        )
    }

    fn layout(&mut self, size: Vec2) {
        scroll::layout(
            self,
            size,
            self.inner.needs_relayout(),
            |s, si| s.inner.layout(si),
            |s, c| s.inner.required_size(c),
        );
    }

    fn needs_relayout(&self) -> bool {
        self.core.needs_relayout() || self.inner.needs_relayout()
    }

    fn required_size(&mut self, constraint: Vec2) -> Vec2 {
        scroll::required_size(
            self,
            constraint,
            self.inner.needs_relayout(),
            |s, c| s.inner.required_size(c),
        )
    }

    fn call_on_any<'a>(&mut self, selector: &Selector<'_>, cb: AnyCb<'a>) {
        self.inner.call_on_any(selector, cb)
    }

    fn focus_view(&mut self, selector: &Selector<'_>) -> Result<(), ()> {
        self.inner.focus_view(selector)
    }

    fn take_focus(&mut self, source: Direction) -> bool {
        self.inner.take_focus(source) || self.core.is_scrolling().any()
    }

    fn important_area(&self, size: Vec2) -> Rect {
        scroll::important_area(self, size, |s, si| s.inner.important_area(si))
    }
}
<|endoftext|>