<commit_before>use std::fmt::Debug;
use super::Matcher;

pub struct Contains<T>(pub T);

impl<T: Debug + PartialEq> Matcher<Vec<T>> for Contains<T> {
    fn matches(&self, lhs: &Vec<T>) -> bool {
        lhs.iter().any(|i| *i == self.0)
    }

    fn fail_msg(&self, lhs: &Vec<T>) -> String {
        format!("expected {:?} to contain {:?}", lhs, self.0)
    }

    fn negated_fail_msg(&self, lhs: &Vec<T>) -> String {
        format!("expected {:?} not to contain {:?}", lhs, self.0)
    }
}

impl Matcher<String> for Contains<char> {
    fn matches(&self, lhs: &String) -> bool {
        lhs.chars().any(|i| i == self.0)
    }

    fn fail_msg(&self, lhs: &String) -> String {
        format!("expected {:?} to contain {:?}", lhs, self.0)
    }

    fn negated_fail_msg(&self, lhs: &String) -> String {
        format!("expected {:?} not to contain {:?}", lhs, self.0)
    }
}

impl Matcher<String> for Contains<String> {
    fn matches(&self, lhs: &String) -> bool {
        lhs.contains(&*self.0)
    }

    fn fail_msg(&self, lhs: &String) -> String {
        format!("expected {:?} to contain {:?}", lhs, self.0)
    }

    fn negated_fail_msg(&self, lhs: &String) -> String {
        format!("expected {:?} not to contain {:?}", lhs, self.0)
    }
}

impl<'a> Matcher<String> for Contains<&'a str> {
    fn matches(&self, lhs: &String) -> bool {
        lhs.contains(self.0)
    }

    fn fail_msg(&self, lhs: &String) -> String {
        format!("expected {:?} to contain {:?}", lhs, self.0)
    }

    fn negated_fail_msg(&self, lhs: &String) -> String {
        format!("expected {:?} not to contain {:?}", lhs, self.0)
    }
}

impl<'a> Matcher<&'a str> for Contains<String> {
    fn matches(&self, lhs: &&'a str) -> bool {
        (*lhs).contains(&*self.0)
    }

    fn fail_msg(&self, lhs: &&'a str) -> String {
        format!("expected {:?} to contain {:?}", lhs, self.0)
    }

    fn negated_fail_msg(&self, lhs: &&'a str) -> String {
        format!("expected {:?} not to contain {:?}", lhs, self.0)
    }
}

impl<'a, 'b> Matcher<&'a str> for Contains<&'b str> {
    fn matches(&self, lhs: &&'a str) -> bool {
        let lhs = *lhs;
        let rhs = self.0;

        lhs.contains(rhs)
    }

    fn fail_msg(&self, lhs: &&'a str) -> String {
        format!("expected {:?} to contain {:?}", lhs, self.0)
    }

    fn negated_fail_msg(&self, lhs: &&'a str) -> String {
        format!("expected {:?} not to contain {:?}", lhs, self.0)
    }
}

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

    #[test]
    fn test_contains() {
        expect(vec![1, 2, 3]).to(contain(1));
    }

    #[test]
    #[should_panic(expected="expected [1, 2, 3] to contain 5")]
    fn test_not_contains_fails() {
        expect(vec![1, 2, 3]).to(contain(5));
    }

    #[test]
    fn test_contains_string_char() {
        expect("Hello, world!".to_string()).to(contain('H'));
    }

    #[test]
    #[should_panic(expected="expected \"fe fi fo fum\" to contain 'z'")]
    fn test_not_contains_char_fails() {
        expect("fe fi fo fum".to_string()).to(contain('z'));
    }

    #[test]
    fn test_contains_str_substring() {
        expect("Hello, world!").to(contain("Hello"));
    }

    #[test]
    #[should_panic(expected="expected \"Hello, world!\" to contain \"not-in-there\"")]
    fn test_not_contains_str_substring() {
        expect("Hello, world!").to(contain("not-in-there"));
    }

    #[test]
    fn test_contains_string_str_substring() {
        expect("Hello, world!".to_string()).to(contain("Hello"));
    }

    #[test]
    #[should_panic(expected="expected \"Hello, world!\" to contain \"not-in-there\"")]
    fn test_not_contains_string_str_substring() {
        expect("Hello, world!".to_string()).to(contain("not-in-there"));
    }

    #[test]
    fn test_contains_str_string_substring() {
        expect("Hello, world!").to(contain("Hello".to_string()));
    }

    #[test]
    #[should_panic(expected="expected \"Hello, world!\" to contain \"not-in-there\"")]
    fn test_not_contains_str_string_substring() {
        expect("Hello, world!").to(contain("not-in-there".to_string()));
    }

    #[test]
    fn test_contains_string_substring() {
        expect("Hello, world!".to_string()).to(contain("Hello".to_string()));
    }

    #[test]
    #[should_panic(expected="expected \"fe fi fo fum\" to contain \"substring\"")]
    fn test_not_contains_substring_fails() {
        expect("fe fi fo fum".to_string()).to(contain("substring".to_string()));
    }
}

<commit_msg>Refactor tests for contains<commit_after>use std::fmt::Debug;
use super::Matcher;

pub struct Contains<T>(pub T);

impl<T: Debug + PartialEq> Matcher<Vec<T>> for Contains<T> {
    fn matches(&self, lhs: &Vec<T>) -> bool {
        lhs.iter().any(|i| *i == self.0)
    }

    fn fail_msg(&self, lhs: &Vec<T>) -> String {
        format!("expected {:?} to contain {:?}", lhs, self.0)
    }

    fn negated_fail_msg(&self, lhs: &Vec<T>) -> String {
        format!("expected {:?} not to contain {:?}", lhs, self.0)
    }
}

impl Matcher<String> for Contains<char> {
    fn matches(&self, lhs: &String) -> bool {
        lhs.chars().any(|i| i == self.0)
    }

    fn fail_msg(&self, lhs: &String) -> String {
        format!("expected {:?} to contain {:?}", lhs, self.0)
    }

    fn negated_fail_msg(&self, lhs: &String) -> String {
        format!("expected {:?} not to contain {:?}", lhs, self.0)
    }
}

impl Matcher<String> for Contains<String> {
    fn matches(&self, lhs: &String) -> bool {
        lhs.contains(&*self.0)
    }

    fn fail_msg(&self, lhs: &String) -> String {
        format!("expected {:?} to contain {:?}", lhs, self.0)
    }

    fn negated_fail_msg(&self, lhs: &String) -> String {
        format!("expected {:?} not to contain {:?}", lhs, self.0)
    }
}

impl<'a> Matcher<String> for Contains<&'a str> {
    fn matches(&self, lhs: &String) -> bool {
        lhs.contains(self.0)
    }

    fn fail_msg(&self, lhs: &String) -> String {
        format!("expected {:?} to contain {:?}", lhs, self.0)
    }

    fn negated_fail_msg(&self, lhs: &String) -> String {
        format!("expected {:?} not to contain {:?}", lhs, self.0)
    }
}

impl<'a> Matcher<&'a str> for Contains<String> {
    fn matches(&self, lhs: &&'a str) -> bool {
        (*lhs).contains(&*self.0)
    }

    fn fail_msg(&self, lhs: &&'a str) -> String {
        format!("expected {:?} to contain {:?}", lhs, self.0)
    }

    fn negated_fail_msg(&self, lhs: &&'a str) -> String {
        format!("expected {:?} not to contain {:?}", lhs, self.0)
    }
}

impl<'a, 'b> Matcher<&'a str> for Contains<&'b str> {
    fn matches(&self, lhs: &&'a str) -> bool {
        let lhs = *lhs;
        let rhs = self.0;

        lhs.contains(rhs)
    }

    fn fail_msg(&self, lhs: &&'a str) -> String {
        format!("expected {:?} to contain {:?}", lhs, self.0)
    }

    fn negated_fail_msg(&self, lhs: &&'a str) -> String {
        format!("expected {:?} not to contain {:?}", lhs, self.0)
    }
}

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

        #[test]
        fn test_contains_with_vector_matches() {
            expect(vec![1, 2, 3]).to(contain(1));
        }

        #[test]
        #[should_panic(expected="expected [1, 2, 3] to contain 5")]
        fn test_contains_with_vector_fails_with_message() {
            expect(vec![1, 2, 3]).to(contain(5));
        }

        #[test]
        #[should_panic(expected="expected [1, 2, 3] not to contain 2")]
        fn test_negated_contains_with_vector_fails_with_message() {
            expect(vec![1, 2, 3]).to_not(contain(2));
        }
    }

    mod str_contains_char {
        use super::super::super::super::dsl::*;

        #[test]
        fn test_contains_char_in_str_matches() {
            expect("Hello, world!".to_string()).to(contain('H'));
        }

        #[test]
        #[should_panic(expected="expected \"fe fi fo fum\" to contain 'z'")]
        fn test_contains_char_in_str_fails_with_message() {
            expect("fe fi fo fum".to_string()).to(contain('z'));
        }

        #[test]
        #[should_panic(expected="expected \"fe fi fo fum\" not to contain 'f'")]
        fn test_negated_contains_char_in_str_fails_with_message() {
            expect("fe fi fo fum".to_string()).to_not(contain('f'));
        }
    }

    mod str_contains_str {
        use super::super::super::super::dsl::*;

        #[test]
        fn test_contains_substring_in_str_matches() {
            expect("Hello, world!").to(contain("Hello"));
        }

        #[test]
        #[should_panic(expected="expected \"Hello, world!\" to contain \"not-in-there\"")]
        fn test_contains_substring_in_str_fails_with_message() {
            expect("Hello, world!").to(contain("not-in-there"));
        }

        #[test]
        #[should_panic(expected="expected \"Hello, world!\" not to contain \"Hello\"")]
        fn test_negated_contains_substring_in_str_fails_with_message() {
            expect("Hello, world!").to_not(contain("Hello"));
        }
    }

    mod string_contains_str {
        use super::super::super::super::dsl::*;

        #[test]
        fn test_contains_substring_in_string_matches() {
            expect("Hello, world!".to_string()).to(contain("Hello"));
        }

        #[test]
        #[should_panic(expected="expected \"Hello, world!\" to contain \"not-in-there\"")]
        fn test_contains_substring_in_string_fails_with_message() {
            expect("Hello, world!".to_string()).to(contain("not-in-there"));
        }

        #[test]
        #[should_panic(expected="expected \"Hello, world!\" not to contain \"Hello\"")]
        fn test_negated_contains_substring_in_string_fails_with_message() {
            expect("Hello, world!".to_string()).to_not(contain("Hello"));
        }
    }

    mod str_contains_string {
        use super::super::super::super::dsl::*;

        #[test]
        fn test_contains_string_substring_in_str_matches() {
            expect("Hello, world!").to(contain("Hello".to_string()));
        }

        #[test]
        #[should_panic(expected="expected \"Hello, world!\" to contain \"not-in-there\"")]
        fn test_contains_string_substring_in_str_fails_with_message() {
            expect("Hello, world!").to(contain("not-in-there".to_string()));
        }

        #[test]
        #[should_panic(expected="expected \"Hello, world!\" not to contain \"Hello\"")]
        fn test_negated_contains_string_substring_in_str_fails_with_message() {
            expect("Hello, world!").to_not(contain("Hello".to_string()));
        }
    }

    mod string_contains_string {
        use super::super::super::super::dsl::*;

        #[test]
        fn test_contains_string_substring_in_string_matches() {
            expect("Hello, world!".to_string()).to(contain("Hello".to_string()));
        }

        #[test]
        #[should_panic(expected="expected \"fe fi fo fum\" to contain \"substring\"")]
        fn test_contains_string_substring_in_string_fails_with_message() {
            expect("fe fi fo fum".to_string()).to(contain("substring".to_string()));
        }

        #[test]
        #[should_panic(expected="expected \"fe fi fo fum\" not to contain \"fum\"")]
        fn test_negated_contains_string_substring_in_string_fails_with_message() {
            expect("fe fi fo fum".to_string()).to_not(contain("fum".to_string()));
        }
    }
}

<|endoftext|>


<commit_before>use std::slice::{Iter, IterMut};

static BLOCK_SEPARATOR: &str = ",";

/// Used to build a block (block | row), specifying additional details.
#[derive(Default)]
pub struct BlockBuilder {
    size: BlockSize,
    min_size: f64,
    max_size: f64,
}

impl BlockBuilder {
    /// Creates a new `BlockBuilder` with default values.
    pub fn new() -> Self {
        BlockBuilder::default()
    }

    /// Inserts a new size.
    pub fn size(mut self, size: BlockSize) -> Self {
        self.size = size;
        self
    }

    /// Inserts a new min size.
    pub fn min_size(mut self, min_size: f64) -> Self {
        self.min_size = min_size;
        self
    }

    /// Inserts a new max size.
    pub fn max_size(mut self, max_size: f64) -> Self {
        self.max_size = max_size;
        self
    }

    /// Builds the block.
    pub fn build(self) -> Block {
        Block {
            size: self.size,
            min_size: self.min_size,
            max_size: self.max_size,
            current_size: 0.0,
        }
    }
}

/// Used to define the block of the `Grid`.
#[derive(Copy, Clone, Debug, Default, PartialEq)]
pub struct Block {
    pub size: BlockSize,
    pub min_size: f64,
    pub max_size: f64,
    current_size: f64,
}

impl Block {
    /// Creates a new `BlockBuilder` object with default values.
    #[inline]
    pub fn create() -> BlockBuilder {
        BlockBuilder::new()
    }

    /// Gets the block size.
    pub fn size(&self) -> BlockSize {
        self.size
    }

    /// Gets the current size.
    pub fn current_size(&self) -> f64 {
        self.current_size
    }

    /// Sets the current size.
    pub fn set_current_size(&mut self, size: f64) {
        self.current_size = if self.min_size == 0.0 && self.max_size == 0.0 && size > 0.0 {
            size
        } else if size < self.min_size && self.min_size > 0.0 {
            self.min_size
        } else if size > self.max_size && self.max_size > 0.0 {
            self.max_size
        } else {
            size
        };
    }
}

impl From<&str> for Block {
    fn from(t: &str) -> Self {
        if let Ok(size) = t.parse::<f64>() {
            return Block::create().size(BlockSize::Size(size)).build();
        }
        match t {
            "Auto" | "auto" => Block::create().size(BlockSize::Auto).build(),
            _ => Block::create().size(BlockSize::Stretch).build(),
        }
    }
}

impl From<f64> for Block {
    fn from(t: f64) -> Self {
        Block::create().size(BlockSize::Size(t)).build()
    }
}

impl From<i32> for Block {
    fn from(t: i32) -> Self {
        Block::create().size(BlockSize::Size(t.into())).build()
    }
}

/// Used to define the size of a grid block.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum BlockSize {
    /// Block is measured by the largest child.
    Auto,

    /// Block expands to the rest available size.
    Stretch,

    /// Defines a fixed size for the block.
    Size(f64),
}

impl Default for BlockSize {
    fn default() -> Self {
        BlockSize::Stretch
    }
}

/// Used to build a blocks, specifying additional details.
#[derive(Default)]
pub struct BlocksBuilder {
    blocks: Vec<Block>,
}

/// Used to build a blocks, specifying additional details.
impl BlocksBuilder {
    /// Creates a new `BlocksBuilder` with default values.
    pub fn new() -> Self {
        BlocksBuilder::default()
    }

    /// Inserts a new block.
    pub fn push<C: Into<Block>>(mut self, block: C) -> Self {
        self.blocks.push(block.into());
        self
    }

    /// Inserts a new block.
    #[inline(always)]
    #[deprecated = "Use push instead"]
    pub fn block<C: Into<Block>>(self, block: C) -> Self {
        self.push(block)
    }

    /// Inserts a list of blocks.
    pub fn blocks<R: Into<Block> + Clone>(mut self, blocks: &[R]) -> Self {
        for block in blocks.to_vec() {
            self.blocks.push(block.into());
        }
        self
    }

    /// Inserts the given block as often as given.
    pub fn repeat<R: Into<Block> + Copy>(mut self, block: R, count: usize) -> Self {
        for _ in 0..count {
            self.blocks.push(block.into())
        }
        self
    }

    /// Builds the blocks.
    pub fn build(self) -> Blocks {
        Blocks(self.blocks)
    }
}

impl From<BlocksBuilder> for Blocks {
    fn from(builder: BlocksBuilder) -> Self {
        builder.build()
    }
}

/// Helper struct used inside of the blocks Property.
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Blocks(pub Vec<Block>);

/// Alias type for `Blocks` to don't break old api.
pub type Columns = Blocks;

/// Alias type for `Blocks` to don't break old api.
pub type Rows = Blocks;

impl Blocks {
    /// Creates a new `BlocksBuilder` object with default values.
    #[inline]
    pub fn create() -> BlocksBuilder {
        BlocksBuilder::new()
    }

    /// Returns the number of elements in the blocks list, also referred to as its 'length'.
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Is the block empty?
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    /// Returns a reference to an block.
    pub fn get(&self, block: usize) -> Option<&Block> {
        self.0.get(block)
    }

    /// Returns a mutable reference to an block.
    pub fn get_mut(&mut self, block: usize) -> Option<&mut Block> {
        self.0.get_mut(block)
    }

    /// Returns an iterator over the slice.
    pub fn iter(&self) -> Iter<Block> {
        self.0.iter()
    }

    /// Returns a mutable iterator over the slice.
    pub fn iter_mut(&mut self) -> IterMut<Block> {
        self.0.iter_mut()
    }
}

impl From<&str> for Blocks {
    fn from(blocks: &str) -> Self {
        let blocks = blocks.replace(" ", "");
        let mut block_builder = BlocksBuilder::new();
        for block in blocks.split(BLOCK_SEPARATOR) {
            block_builder = block_builder.push(Block::from(block));
        }

        block_builder.build()
    }
}

impl From<String> for Blocks {
    fn from(blocks: String) -> Self {
        Self::from(blocks.as_str())
    }
}

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

    const ERROR: f64 = f64::EPSILON;

    #[test]
    fn test_size() {
        let size = BlockSize::Size(64.0);

        let builder = BlockBuilder::new();
        let block = builder.size(size).build();

        assert_eq!(block.size, size);
    }

    #[test]
    fn test_min_size() {
        let min_size = 64.0;

        let builder = BlockBuilder::new();
        let block = builder.min_size(min_size).build();

        assert!((block.min_size - min_size).abs() < ERROR);
    }

    #[test]
    fn test_max_size() {
        let max_size = 64.0;

        let builder = BlockBuilder::new();
        let block = builder.max_size(max_size).build();

        assert!((block.max_size - max_size) < ERROR);
    }

    #[test]
    fn test_set_current_size() {
        let out_one_size = 10.0;
        let out_two_size = 66.0;
        let min_size = 14.0;
        let max_size = 64.0;

        let builder = BlockBuilder::new();
        let mut block = builder.min_size(min_size).max_size(max_size).build();

        block.set_current_size(out_one_size);
        assert!((block.current_size() - min_size).abs() < ERROR);

        block.set_current_size(out_two_size);
        assert!((block.current_size() - max_size).abs() < ERROR);
    }

    #[test]
    fn test_block() {
        let builder = BlocksBuilder::new();
        let blocks = builder.build();

        assert_eq!(blocks.len(), 0);

        let builder = BlocksBuilder::new();
        let blocks = builder
            .push(Block::create().build())
            .push(Block::create().build())
            .build();

        assert_eq!(blocks.len(), 2);
    }

    #[test]
    fn test_block_size_into() {
        let block: Block = "Auto".into();
        assert_eq!(block.size(), BlockSize::Auto);

        let block: Block = "auto".into();
        assert_eq!(block.size(), BlockSize::Auto);

        let block: Block = "Stretch".into();
        assert_eq!(block.size(), BlockSize::Stretch);

        let block: Block = "stretch".into();
        assert_eq!(block.size(), BlockSize::Stretch);

        let block: Block = "*".into();
        assert_eq!(block.size(), BlockSize::Stretch);

        let block: Block = "other".into();
        assert_eq!(block.size(), BlockSize::Stretch);

        let block: Block = 64.0.into();
        assert_eq!(block.size(), BlockSize::Size(64.0));
    }
}
<commit_msg>orbtk_core: `block` typo correction<commit_after>use std::slice::{Iter, IterMut};

static BLOCK_SEPARATOR: &str = ",";

/// Used to build a block (block | row), specifying additional details.
#[derive(Default)]
pub struct BlockBuilder {
    size: BlockSize,
    min_size: f64,
    max_size: f64,
}

impl BlockBuilder {
    /// Creates a new `BlockBuilder` with default values.
    pub fn new() -> Self {
        BlockBuilder::default()
    }

    /// Inserts a new size.
    pub fn size(mut self, size: BlockSize) -> Self {
        self.size = size;
        self
    }

    /// Inserts a new min size.
    pub fn min_size(mut self, min_size: f64) -> Self {
        self.min_size = min_size;
        self
    }

    /// Inserts a new max size.
    pub fn max_size(mut self, max_size: f64) -> Self {
        self.max_size = max_size;
        self
    }

    /// Builds the block.
    pub fn build(self) -> Block {
        Block {
            size: self.size,
            min_size: self.min_size,
            max_size: self.max_size,
            current_size: 0.0,
        }
    }
}

/// Used to define the block of the `Grid`.
#[derive(Copy, Clone, Debug, Default, PartialEq)]
pub struct Block {
    pub size: BlockSize,
    pub min_size: f64,
    pub max_size: f64,
    current_size: f64,
}

impl Block {
    /// Creates a new `BlockBuilder` object with default values.
    #[inline]
    pub fn create() -> BlockBuilder {
        BlockBuilder::new()
    }

    /// Gets the block size.
    pub fn size(&self) -> BlockSize {
        self.size
    }

    /// Gets the current size.
    pub fn current_size(&self) -> f64 {
        self.current_size
    }

    /// Sets the current size.
    pub fn set_current_size(&mut self, size: f64) {
        self.current_size = if self.min_size == 0.0 && self.max_size == 0.0 && size > 0.0 {
            size
        } else if size < self.min_size && self.min_size > 0.0 {
            self.min_size
        } else if size > self.max_size && self.max_size > 0.0 {
            self.max_size
        } else {
            size
        };
    }
}

impl From<&str> for Block {
    fn from(t: &str) -> Self {
        if let Ok(size) = t.parse::<f64>() {
            return Block::create().size(BlockSize::Size(size)).build();
        }
        match t {
            "Auto" | "auto" => Block::create().size(BlockSize::Auto).build(),
            _ => Block::create().size(BlockSize::Stretch).build(),
        }
    }
}

impl From<f64> for Block {
    fn from(t: f64) -> Self {
        Block::create().size(BlockSize::Size(t)).build()
    }
}

impl From<i32> for Block {
    fn from(t: i32) -> Self {
        Block::create().size(BlockSize::Size(t.into())).build()
    }
}

/// Used to define the size of a grid block.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum BlockSize {
    /// Block is measured by the largest child.
    Auto,

    /// Block expands to the rest available size.
    Stretch,

    /// Defines a fixed size for the block.
    Size(f64),
}

impl Default for BlockSize {
    fn default() -> Self {
        BlockSize::Stretch
    }
}

/// Used to build a blocks, specifying additional details.
#[derive(Default)]
pub struct BlocksBuilder {
    blocks: Vec<Block>,
}

/// Used to build a blocks, specifying additional details.
impl BlocksBuilder {
    /// Creates a new `BlocksBuilder` with default values.
    pub fn new() -> Self {
        BlocksBuilder::default()
    }

    /// Inserts a new block.
    pub fn push<C: Into<Block>>(mut self, block: C) -> Self {
        self.blocks.push(block.into());
        self
    }

    /// Inserts a new block.
    #[inline(always)]
    #[deprecated = "Use push instead"]
    pub fn block<C: Into<Block>>(self, block: C) -> Self {
        self.push(block)
    }

    /// Inserts a list of blocks.
    pub fn blocks<R: Into<Block> + Clone>(mut self, blocks: &[R]) -> Self {
        for block in blocks.to_vec() {
            self.blocks.push(block.into());
        }
        self
    }

    /// Inserts the given block as often as given.
    pub fn repeat<R: Into<Block> + Copy>(mut self, block: R, count: usize) -> Self {
        for _ in 0..count {
            self.blocks.push(block.into())
        }
        self
    }

    /// Builds the blocks.
    pub fn build(self) -> Blocks {
        Blocks(self.blocks)
    }
}

impl From<BlocksBuilder> for Blocks {
    fn from(builder: BlocksBuilder) -> Self {
        builder.build()
    }
}

/// Helper struct used inside of the blocks property.
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Blocks(pub Vec<Block>);

/// Alias type for `Blocks` to don't break old api.
pub type Columns = Blocks;

/// Alias type for `Blocks` to don't break old api.
pub type Rows = Blocks;

impl Blocks {
    /// Creates a new `BlocksBuilder` object with default values.
    #[inline]
    pub fn create() -> BlocksBuilder {
        BlocksBuilder::new()
    }

    /// Returns the number of elements in the blocks list, also referred to as its 'length'.
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Returns a boolean if the block is empty.
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    /// Returns a reference to a block.
    pub fn get(&self, block: usize) -> Option<&Block> {
        self.0.get(block)
    }

    /// Returns a mutable reference to a block.
    pub fn get_mut(&mut self, block: usize) -> Option<&mut Block> {
        self.0.get_mut(block)
    }

    /// Returns an iterator over the slice.
    pub fn iter(&self) -> Iter<Block> {
        self.0.iter()
    }

    /// Returns a mutable iterator over the slice.
    pub fn iter_mut(&mut self) -> IterMut<Block> {
        self.0.iter_mut()
    }
}

impl From<&str> for Blocks {
    fn from(blocks: &str) -> Self {
        let blocks = blocks.replace(" ", "");
        let mut block_builder = BlocksBuilder::new();
        for block in blocks.split(BLOCK_SEPARATOR) {
            block_builder = block_builder.push(Block::from(block));
        }

        block_builder.build()
    }
}

impl From<String> for Blocks {
    fn from(blocks: String) -> Self {
        Self::from(blocks.as_str())
    }
}

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

    const ERROR: f64 = f64::EPSILON;

    #[test]
    fn test_size() {
        let size = BlockSize::Size(64.0);

        let builder = BlockBuilder::new();
        let block = builder.size(size).build();

        assert_eq!(block.size, size);
    }

    #[test]
    fn test_min_size() {
        let min_size = 64.0;

        let builder = BlockBuilder::new();
        let block = builder.min_size(min_size).build();

        assert!((block.min_size - min_size).abs() < ERROR);
    }

    #[test]
    fn test_max_size() {
        let max_size = 64.0;

        let builder = BlockBuilder::new();
        let block = builder.max_size(max_size).build();

        assert!((block.max_size - max_size) < ERROR);
    }

    #[test]
    fn test_set_current_size() {
        let out_one_size = 10.0;
        let out_two_size = 66.0;
        let min_size = 14.0;
        let max_size = 64.0;

        let builder = BlockBuilder::new();
        let mut block = builder.min_size(min_size).max_size(max_size).build();

        block.set_current_size(out_one_size);
        assert!((block.current_size() - min_size).abs() < ERROR);

        block.set_current_size(out_two_size);
        assert!((block.current_size() - max_size).abs() < ERROR);
    }

    #[test]
    fn test_block() {
        let builder = BlocksBuilder::new();
        let blocks = builder.build();

        assert_eq!(blocks.len(), 0);

        let builder = BlocksBuilder::new();
        let blocks = builder
            .push(Block::create().build())
            .push(Block::create().build())
            .build();

        assert_eq!(blocks.len(), 2);
    }

    #[test]
    fn test_block_size_into() {
        let block: Block = "Auto".into();
        assert_eq!(block.size(), BlockSize::Auto);

        let block: Block = "auto".into();
        assert_eq!(block.size(), BlockSize::Auto);

        let block: Block = "Stretch".into();
        assert_eq!(block.size(), BlockSize::Stretch);

        let block: Block = "stretch".into();
        assert_eq!(block.size(), BlockSize::Stretch);

        let block: Block = "*".into();
        assert_eq!(block.size(), BlockSize::Stretch);

        let block: Block = "other".into();
        assert_eq!(block.size(), BlockSize::Stretch);

        let block: Block = 64.0.into();
        assert_eq!(block.size(), BlockSize::Size(64.0));
    }
}
<|endoftext|>


<commit_before>#[derive(Eq, PartialEq)]
enum Direction {
    North,
    East,
    South,
    West,
    Empty,
}

struct Table {
    w: i32,
    h: i32,
}

struct Robot {
    x: i32,
    y: i32,
    d: Direction,
    t: Table,
}

impl Default for Robot {
    fn default() -> Robot {
        let table = Table{w: 5, h: 5};

        Robot{x: 0, y: 0, d: Direction::Empty, t: table}
    }
}

impl Table {
    fn valid_pos(&self, x: i32, y: i32) -> bool {
        x >= 0 && x < self.w && y >= 0 && y < self.h
    }
}

impl Robot {
    fn is_placed(&self) -> bool {
        self.d == Direction::Empty
    }

    fn place(&mut self, x: i32, y: i32, d: Direction) {
        if self.t.valid_pos(x, y) {
            self.x = x;
            self.y = y;
            self.d = d;
        }
    }

    fn left(&mut self) {
    }

    fn right(&mut self) {
    }

    fn step(&mut self) {
    }

    fn report(&self) {
    }
}

fn main() {
    let mut robot: Robot = Default::default();

    robot.place(1, 2, Direction::North);
}
<commit_msg>Added left/right methods<commit_after>#[derive(Eq, PartialEq)]
enum Direction {
    North,
    East,
    South,
    West,
    Empty,
}

struct Table {
    w: i32,
    h: i32,
}

struct Robot {
    x: i32,
    y: i32,
    d: Direction,
    t: Table,
}

impl Default for Robot {
    fn default() -> Robot {
        let table = Table{w: 5, h: 5};

        Robot{x: 0, y: 0, d: Direction::Empty, t: table}
    }
}

impl Table {
    fn valid_pos(&self, x: i32, y: i32) -> bool {
        x >= 0 && x < self.w && y >= 0 && y < self.h
    }
}

impl Robot {
    fn is_placed(&self) -> bool {
        self.d == Direction::Empty
    }

    fn place(&mut self, x: i32, y: i32, d: Direction) {
        if self.t.valid_pos(x, y) {
            self.x = x;
            self.y = y;
            self.d = d;
        }
    }

    fn right(&mut self) {
        let d = match self.d {
            Direction::North => Direction::East,
            Direction::East => Direction::South,
            Direction::South => Direction::West,
            Direction::West => Direction::North,
            _ => Direction::Empty,
        };

        self.d = d;
    }

    fn left(&mut self) {
        let d = match self.d {
            Direction::North => Direction::West,
            Direction::West => Direction::South,
            Direction::South => Direction::East,
            Direction::East => Direction::North,
            _ => Direction::Empty,
        };

        self.d = d;
    }

    fn step(&mut self) {
    }

    fn report(&self) {
    }
}

fn main() {
    let mut robot: Robot = Default::default();

    robot.place(1, 2, Direction::North);
    robot.left();
}
<|endoftext|>


<commit_before>mod entry;
mod table;

const ENTRY_COUNT: usize = 512;

pub type PhysicalAddress = usize;
pub type VirtualAddress = usize;

pub struct Page {
    number: usize,
}
<commit_msg>Add function to translate virtual to physical address<commit_after>use memory::{PAGE_SIZE, Frame};

mod entry;
mod table;

const ENTRY_COUNT: usize = 512;

pub type PhysicalAddress = usize;
pub type VirtualAddress = usize;

pub struct Page {
    number: usize,
}

impl Page {
    fn containing_address(address: VirtualAddress) -> Page {
        assert!(address < 0x0000_8000_0000_0000 || address >= 0xffff_8000_0000_0000,
                "invalid address: 0x{:x}",
                address);
        Page { number: address / PAGE_SIZE }
    }

    fn start_address(&self) -> usize {
        self.number * PAGE_SIZE
    }

    fn p4_index(&self) -> usize {
        (self.number >> 27) & 0o777
    }
    fn p3_index(&self) -> usize {
        (self.number >> 18) & 0o777
    }
    fn p2_index(&self) -> usize {
        (self.number >> 9) & 0o777
    }
    fn p1_index(&self) -> usize {
        (self.number >> 0) & 0o777
    }
}

pub fn translate(virtual_address: VirtualAddress) -> Option<PhysicalAddress> {
    let offset = virtual_address % PAGE_SIZE;
    translate_page(Page::containing_address(virtual_address))
        .map(|frame| frame.number * PAGE_SIZE + offset)
}

fn translate_page(page: Page) -> Option<Frame> {
    use self::entry::HUGE_PAGE;

    let p3 = unsafe { &*table::P4 }.next_table(page.p4_index());

    let huge_page = || {
        p3.and_then(|p3| {
            let p3_entry = &p3[page.p3_index()];
            // 1GiB page?
            if let Some(start_frame) = p3_entry.pointed_frame() {
                if p3_entry.flags().contains(HUGE_PAGE) {
                    // address must be 1GiB aligned
                    assert!(start_frame.number % (ENTRY_COUNT * ENTRY_COUNT) == 0);
                    return Some(Frame {
                        number: start_frame.number + page.p2_index() * ENTRY_COUNT +
                                page.p1_index(),
                    });
                }
            }
            if let Some(p2) = p3.next_table(page.p3_index()) {
                let p2_entry = &p2[page.p2_index()];
                // 2MiB page?
                if let Some(start_frame) = p2_entry.pointed_frame() {
                    if p2_entry.flags().contains(HUGE_PAGE) {
                        // address must be 2MiB aligned
                        assert!(start_frame.number % ENTRY_COUNT == 0);
                        return Some(Frame { number: start_frame.number + page.p1_index() });
                    }
                }
            }
            None
        })
    };

    p3.and_then(|p3| p3.next_table(page.p3_index()))
      .and_then(|p2| p2.next_table(page.p2_index()))
      .and_then(|p1| p1[page.p1_index()].pointed_frame())
      .or_else(huge_page)
}
<|endoftext|>


<commit_before>use crate::ipc_protocol::{
    ServerConnection,
    ServerResponse,
    TurtleProp,
    TurtlePropValue,
    PenProp,
    PenPropValue,
};
use crate::renderer_client::ClientId;

use super::super::app::{TurtleId, TurtleDrawings};
use super::super::access_control::{AccessControl, RequiredData, RequiredTurtles};

pub(crate) async fn turtle_prop(
    conn: &ServerConnection,
    client_id: ClientId,
    app_control: &AccessControl,
    id: TurtleId,
    prop: TurtleProp,
) {
    let mut data = app_control.get(RequiredData {
        drawing: false,
        turtles: Some(RequiredTurtles::One(id)),
    }).await;
    let mut turtles = data.turtles_mut().await;

    let TurtleDrawings {state: turtle, ..} = turtles.one_mut();

    use TurtleProp::*;
    use PenProp::*;
    let value = match prop {
        Pen(IsEnabled) => TurtlePropValue::Pen(PenPropValue::IsEnabled(turtle.pen.is_enabled)),
        Pen(Thickness) => TurtlePropValue::Pen(PenPropValue::Thickness(turtle.pen.thickness)),
        Pen(Color) => TurtlePropValue::Pen(PenPropValue::Color(turtle.pen.color)),
        FillColor => TurtlePropValue::FillColor(turtle.fill_color),
        IsFilling => TurtlePropValue::IsFilling(turtle.is_filling),
        Position => TurtlePropValue::Position(turtle.position),
        PositionX => TurtlePropValue::PositionX(turtle.position.x),
        PositionY => TurtlePropValue::PositionY(turtle.position.y),
        Heading => TurtlePropValue::Heading(turtle.heading),
        Speed => TurtlePropValue::Speed(turtle.speed),
        IsVisible => TurtlePropValue::IsVisible(turtle.is_visible),
    };

    conn.send(client_id, ServerResponse::TurtleProp(id, value)).await
        .expect("unable to send response to IPC client");
}

pub(crate) async fn set_turtle_prop(
    app_control: &AccessControl,
    id: TurtleId,
    prop_value: TurtlePropValue,
) {
    let mut data = app_control.get(RequiredData {
        drawing: false,
        turtles: Some(RequiredTurtles::One(id)),
    }).await;
    let mut turtles = data.turtles_mut().await;

    let TurtleDrawings {state: turtle, current_fill_polygon, ..} = turtles.one_mut();

    use TurtlePropValue::*;
    use PenPropValue::*;
    match prop_value {
        Pen(IsEnabled(is_enabled)) => turtle.pen.is_enabled = is_enabled,
        Pen(Thickness(thickness)) => turtle.pen.thickness = thickness,
        Pen(Color(color)) => turtle.pen.color = color,
        FillColor(fill_color) => {
            turtle.fill_color = fill_color;
            //TODO: Update current fill polygon color if there is a fill polygon
        },
        IsFilling(_) => unreachable!("bug: should have used `BeginFill` and `EndFill` instead"),
        Position(_) |
        PositionX(_) |
        PositionY(_) => unreachable!("bug: should have used `MoveTo` instead"),
        Heading(heading) => unreachable!("bug: should have used `RotateInPlace` instead"),
        Speed(speed) => turtle.speed = speed,
        IsVisible(is_visible) => turtle.is_visible = is_visible,
    }
}
<commit_msg>Removed unused variable<commit_after>use crate::ipc_protocol::{
    ServerConnection,
    ServerResponse,
    TurtleProp,
    TurtlePropValue,
    PenProp,
    PenPropValue,
};
use crate::renderer_client::ClientId;

use super::super::app::{TurtleId, TurtleDrawings};
use super::super::access_control::{AccessControl, RequiredData, RequiredTurtles};

pub(crate) async fn turtle_prop(
    conn: &ServerConnection,
    client_id: ClientId,
    app_control: &AccessControl,
    id: TurtleId,
    prop: TurtleProp,
) {
    let mut data = app_control.get(RequiredData {
        drawing: false,
        turtles: Some(RequiredTurtles::One(id)),
    }).await;
    let mut turtles = data.turtles_mut().await;

    let TurtleDrawings {state: turtle, ..} = turtles.one_mut();

    use TurtleProp::*;
    use PenProp::*;
    let value = match prop {
        Pen(IsEnabled) => TurtlePropValue::Pen(PenPropValue::IsEnabled(turtle.pen.is_enabled)),
        Pen(Thickness) => TurtlePropValue::Pen(PenPropValue::Thickness(turtle.pen.thickness)),
        Pen(Color) => TurtlePropValue::Pen(PenPropValue::Color(turtle.pen.color)),
        FillColor => TurtlePropValue::FillColor(turtle.fill_color),
        IsFilling => TurtlePropValue::IsFilling(turtle.is_filling),
        Position => TurtlePropValue::Position(turtle.position),
        PositionX => TurtlePropValue::PositionX(turtle.position.x),
        PositionY => TurtlePropValue::PositionY(turtle.position.y),
        Heading => TurtlePropValue::Heading(turtle.heading),
        Speed => TurtlePropValue::Speed(turtle.speed),
        IsVisible => TurtlePropValue::IsVisible(turtle.is_visible),
    };

    conn.send(client_id, ServerResponse::TurtleProp(id, value)).await
        .expect("unable to send response to IPC client");
}

pub(crate) async fn set_turtle_prop(
    app_control: &AccessControl,
    id: TurtleId,
    prop_value: TurtlePropValue,
) {
    let mut data = app_control.get(RequiredData {
        drawing: false,
        turtles: Some(RequiredTurtles::One(id)),
    }).await;
    let mut turtles = data.turtles_mut().await;

    let TurtleDrawings {state: turtle, current_fill_polygon, ..} = turtles.one_mut();

    use TurtlePropValue::*;
    use PenPropValue::*;
    match prop_value {
        Pen(IsEnabled(is_enabled)) => turtle.pen.is_enabled = is_enabled,
        Pen(Thickness(thickness)) => turtle.pen.thickness = thickness,
        Pen(Color(color)) => turtle.pen.color = color,
        FillColor(fill_color) => {
            turtle.fill_color = fill_color;
            //TODO: Update current fill polygon color if there is a fill polygon
        },
        IsFilling(_) => unreachable!("bug: should have used `BeginFill` and `EndFill` instead"),
        Position(_) |
        PositionX(_) |
        PositionY(_) => unreachable!("bug: should have used `MoveTo` instead"),
        Heading(_) => unreachable!("bug: should have used `RotateInPlace` instead"),
        Speed(speed) => turtle.speed = speed,
        IsVisible(is_visible) => turtle.is_visible = is_visible,
    }
}
<|endoftext|>


<commit_before>use curve25519_dalek::constants::ED25519_BASEPOINT;
use curve25519_dalek::scalar::Scalar;
use sha256::sha256_compress;
use std::fmt;
use util::bs58_encode_check;

const PAYMENT_ADDRESS_PREFIX_LENGTH: usize = 2;
const SPENDING_KEY_PREFIX_LENGTH: usize = 2;
const INVIEWING_KEY_PREFIX_LENGTH: usize = 3;
pub const PAYMENT_ADDRESS_PREFIX: [u8; PAYMENT_ADDRESS_PREFIX_LENGTH] = [0x16, 0x9a];
pub const SPENDING_KEY_PREFIX: [u8; SPENDING_KEY_PREFIX_LENGTH] = [0xab, 0x36];
pub const INVIEWING_KEY_PREFIX: [u8; INVIEWING_KEY_PREFIX_LENGTH] = [0xa8, 0xab, 0xd3];

/// A Zcash spending key.
pub struct SpendingKey {
    a_sk: [u8; 32], // 252 bits; first 4 bits are always 0
}

/// A Zcash viewing key.
pub struct ViewingKey {
    pub sk_enc: [u8; 32],
    pub pk_enc: [u8; 32],
}

/// A Zcash payment address.
pub struct PaymentAddress {
    pub a_pk: [u8; 32],
    pub pk_enc: [u8; 32],
}

impl fmt::Display for SpendingKey {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut data = [0u8; SPENDING_KEY_PREFIX_LENGTH + 32];
        {
            let (prefix, rest) = data.split_at_mut(SPENDING_KEY_PREFIX_LENGTH);
            prefix.copy_from_slice(&SPENDING_KEY_PREFIX);
            rest.copy_from_slice(&self.a_sk);
        }

        f.write_str(bs58_encode_check(&data).as_str())
    }
}

impl fmt::Display for ViewingKey {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut data = [0u8; INVIEWING_KEY_PREFIX_LENGTH + 32 * 2];
        {
            let (prefix, rest) = data.split_at_mut(INVIEWING_KEY_PREFIX_LENGTH);
            prefix.copy_from_slice(&INVIEWING_KEY_PREFIX);
            rest[..32].copy_from_slice(&self.sk_enc);
            rest[32..].copy_from_slice(&self.pk_enc);
        }

        f.write_str(bs58_encode_check(&data).as_str())
    }
}

impl fmt::Display for PaymentAddress {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut data = [0u8; PAYMENT_ADDRESS_PREFIX_LENGTH + 32 * 2];
        {
            let (prefix, rest) = data.split_at_mut(PAYMENT_ADDRESS_PREFIX_LENGTH);
            prefix.copy_from_slice(&PAYMENT_ADDRESS_PREFIX);
            rest[..32].copy_from_slice(&self.a_pk);
            rest[32..].copy_from_slice(&self.pk_enc);
        }

        f.write_str(bs58_encode_check(&data).as_str())
    }
}

impl SpendingKey {
    /// Creates a new spending key instance for a given 252-bit *a_sk*.
    /// *a_sk* is represented by 32 bytes; the first four bits of the first byte are zeroed.
    pub fn new(a_sk_u256: [u8; 32]) -> SpendingKey {
        let mut a_sk_u252 = [0u8; 32];
        a_sk_u252.copy_from_slice(&a_sk_u256);
        a_sk_u252[0] &= 0x0f;

        SpendingKey { a_sk: a_sk_u252 }
    }

    /// Calculates the payment address for this spending key
    pub fn address(&self) -> PaymentAddress {
        let mut a_pk = [0u8; 32];
        pseudorandom_function_a_pk(&mut a_pk, &self.a_sk);

        PaymentAddress {
            a_pk: a_pk,
            pk_enc: self.viewing_key().pk_enc,
        }
    }

    /// Computes a viewing key for this spending key.
    pub fn viewing_key(&self) -> ViewingKey {
        let mut sk_enc = [0u8; 32];
        pseudorandom_function_sk_enc(&mut sk_enc, &self.a_sk);
        clamp_curve25519(&mut sk_enc);
        let pk = &Scalar(sk_enc) * &ED25519_BASEPOINT;
        let pk_enc = pk.compress_montgomery().unwrap().to_bytes();

        ViewingKey {
            sk_enc: sk_enc,
            pk_enc: pk_enc,
        }
    }
}

pub fn pseudorandom_function_a_pk(a_pk: &mut [u8; 32], a_sk: &[u8; 32]) {
    pseudorandom_function(a_pk, a_sk, 0)
}

pub fn pseudorandom_function_sk_enc(sk_enc: &mut [u8; 32], a_sk: &[u8; 32]) {
    pseudorandom_function(sk_enc, a_sk, 1)
}

fn pseudorandom_function(dst: &mut [u8; 32], input: &[u8; 32], t: u8) {
    let mut data = [0u8; 64];
    data[..32].copy_from_slice(input);
    data[0] = 0xc0 | (data[0] & 0x0f);
    data[32] = t;
    sha256_compress(dst, &data);
}

fn clamp_curve25519(key: &mut [u8; 32]) {
    key[0] &= 248;
    key[31] &= 127;
    key[31] |= 64;
}

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

    static ENCODED: [(&str, &str, &str); 5] = [
        (
            "SKxny894fJe2rmZjeuoE6GVfNkWoXfPp8337VrLLNWG56FfQtuS1",
            "zcbxovDeXGJJikZH5wQkcQvYx1gzsRt9mR5UnQir6NY8hhPHdgK7z7dE1vfa55Bq3JHJu7isfuWQGYrvMbLnud74z2vS4tS",
            "ZiVKaybZk1ocKMDtadmSWDfGjG4bg5qwRrKjGkF25LogWChMMoLLKvBu1wc4vD5h7Ho82EhsAHqziobbxEwiqaKQELQDuLTg4",
        ),
        (
            "SKxoo5QkFQgTbdc6EWRKyHPMdmtNDJhqudrAVhen9b4kjCwN6CeV",
            "zcRYvLiURno1LhXq95e8avXFcH2fKKToSFfhqaVKTy8mGH7i6SJbfuWcm4h9rEA6DvswrbxDhFGDQgpdDYV8zwUoHvwNvFX",
            "ZiVKVgrnKKboikjuwGtWACnB89CKN3DwCLp8o92LN5sh3xqVzX4y2YBy89CJs1gQEsQv3N9HEMc6ki1giTKCjcbytCWAENz7k",
        ),
        (
            "SKxsVGKsCESoVb3Gfm762psjRtGHmjmv7HVjHckud5MnESfktUuG",
            "zcWGguu2UPfNhh1ygWW9Joo3osvncsuehtz5ewvXd78vFDdnDCRNG6QeKSZpwZmYmkfEutPVf8HzCfBytqXWsEcF2iBAM1e",
            "ZiVKvnKuy7eRa9Ux7nT7QRQEPzt2wmvpqTpNsf3FHZVNf7utNA8TRdaKrwuceqYxen3GRQhh4Aq1q2PPJ8dyxPNxEhLN8kBsK",
        ),
        (
            "SKxp72QGQ2qtovHSoVnPp8jRFQpHBhG1xF8s27iRFjPXXkYMQUA6",
            "zcWZomPYMEjJ49S4UHcvTnhjYqogfdYJuEDMURDpbkrz94bkzdTdJEZKWkkpQ8nK62eyLkZCvLZDFtLC2Cq5BmEK3WCKGMN",
            "ZiVKUeb2TBbiDWz5WEhRo6keLjku9efg6Q3HNUidPUks24ZcsuQwy9wca2sVAg72S87LV1U3kvwf5FUqVpnddNCFh81cash76",
        ),
        (
            "SKxpmLdykLu3xxSXtw1EA7iLJnXu8hFh8hhmW1B2J2194ijh5CR4",
            "zcgjj3fJF59QGBufopx3F51jCjUpXbgEzec7YQT6jRt4Ebu5EV3AW4jHPN6ZdXhmygBvQDRJrXoZLa3Lkh5GqnsFUzt7Qok",
            "ZiVKvrKcmR53My1EmveLztCio6Yzwy3hYe2FvrqrtvDvisGHfWJ4BbG5oaivCmT8pLwr6EK3mN6GqXARUedEpQCyHrgiaTMPL",
        ),
    ];

    #[test]
    fn spending_keys_to_payment_addresses() {
        for &(spending_key_encoded, payment_address_encoded, _) in ENCODED.iter() {
            let mut a_sk = [0u8; 32];
            a_sk.copy_from_slice(
                &bs58::decode(spending_key_encoded).into_vec().unwrap()[2..34],
            );
            let spending_key = SpendingKey::new(a_sk);
            assert_eq!(spending_key_encoded, spending_key.to_string());
            let payment_address = spending_key.address();
            assert_eq!(payment_address_encoded, payment_address.to_string());
        }
    }

    #[test]
    fn spending_keys_to_viewing_keys() {
        for &(spending_key_encoded, _,viewing_key_encoded) in ENCODED.iter() {
            let mut a_sk = [0u8; 32];
            a_sk.copy_from_slice(
                &bs58::decode(spending_key_encoded).into_vec().unwrap()[2..34],
            );
            let spending_key = SpendingKey::new(a_sk);
            assert_eq!(spending_key_encoded, spending_key.to_string());
            let viewing_key = spending_key.viewing_key();
            assert_eq!(viewing_key_encoded, viewing_key.to_string());
        }
    }
}
<commit_msg>Fix the sequence of bytes when serializing byte sequences and incomporate the test string from https://github.com/FiloSottile/zcash-mini<commit_after>use curve25519_dalek::constants::ED25519_BASEPOINT;
use curve25519_dalek::scalar::Scalar;
use sha256::sha256_compress;
use std::fmt;
use util::bs58_encode_check;

const PAYMENT_ADDRESS_PREFIX_LENGTH: usize = 2;
const SPENDING_KEY_PREFIX_LENGTH: usize = 2;
const INVIEWING_KEY_PREFIX_LENGTH: usize = 3;
pub const PAYMENT_ADDRESS_PREFIX: [u8; PAYMENT_ADDRESS_PREFIX_LENGTH] = [0x16, 0x9a];
pub const SPENDING_KEY_PREFIX: [u8; SPENDING_KEY_PREFIX_LENGTH] = [0xab, 0x36];
pub const INVIEWING_KEY_PREFIX: [u8; INVIEWING_KEY_PREFIX_LENGTH] = [0xa8, 0xab, 0xd3];

/// A Zcash spending key.
pub struct SpendingKey {
    a_sk: [u8; 32], // 252 bits; first 4 bits are always 0
}

/// A Zcash viewing key.
pub struct ViewingKey {
    pub sk_enc: [u8; 32],
    pub pk_enc: [u8; 32],
}

/// A Zcash payment address.
pub struct PaymentAddress {
    pub a_pk: [u8; 32],
    pub pk_enc: [u8; 32],
}

impl fmt::Display for SpendingKey {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut data = [0u8; SPENDING_KEY_PREFIX_LENGTH + 32];
        {
            let (prefix, rest) = data.split_at_mut(SPENDING_KEY_PREFIX_LENGTH);
            prefix.copy_from_slice(&SPENDING_KEY_PREFIX);
            rest.copy_from_slice(&self.a_sk);
        }

        f.write_str(bs58_encode_check(&data).as_str())
    }
}

impl fmt::Display for ViewingKey {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut data = [0u8; INVIEWING_KEY_PREFIX_LENGTH + 32 * 2];
        {
            let (prefix, rest) = data.split_at_mut(INVIEWING_KEY_PREFIX_LENGTH);
            prefix.copy_from_slice(&INVIEWING_KEY_PREFIX);
            rest[..32].copy_from_slice(&self.pk_enc);
            rest[32..].copy_from_slice(&self.sk_enc);
        }

        f.write_str(bs58_encode_check(&data).as_str())
    }
}

impl fmt::Display for PaymentAddress {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut data = [0u8; PAYMENT_ADDRESS_PREFIX_LENGTH + 32 * 2];
        {
            let (prefix, rest) = data.split_at_mut(PAYMENT_ADDRESS_PREFIX_LENGTH);
            prefix.copy_from_slice(&PAYMENT_ADDRESS_PREFIX);
            rest[..32].copy_from_slice(&self.a_pk);
            rest[32..].copy_from_slice(&self.pk_enc);
        }

        f.write_str(bs58_encode_check(&data).as_str())
    }
}

impl SpendingKey {
    /// Creates a new spending key instance for a given 252-bit *a_sk*.
    /// *a_sk* is represented by 32 bytes; the first four bits of the first byte are zeroed.
    pub fn new(a_sk_u256: [u8; 32]) -> SpendingKey {
        let mut a_sk_u252 = [0u8; 32];
        a_sk_u252.copy_from_slice(&a_sk_u256);
        a_sk_u252[0] &= 0x0f;

        SpendingKey { a_sk: a_sk_u252 }
    }

    /// Calculates the payment address for this spending key
    pub fn address(&self) -> PaymentAddress {
        let mut a_pk = [0u8; 32];
        pseudorandom_function_a_pk(&mut a_pk, &self.a_sk);

        PaymentAddress {
            a_pk: a_pk,
            pk_enc: self.viewing_key().pk_enc,
        }
    }

    /// Computes a viewing key for this spending key.
    pub fn viewing_key(&self) -> ViewingKey {
        let mut sk_enc = [0u8; 32];
        pseudorandom_function_sk_enc(&mut sk_enc, &self.a_sk);
        clamp_curve25519(&mut sk_enc);
        let pk = &Scalar(sk_enc) * &ED25519_BASEPOINT;
        let pk_enc = pk.compress_montgomery().unwrap().to_bytes();

        ViewingKey {
            sk_enc: sk_enc,
            pk_enc: pk_enc,
        }
    }
}

pub fn pseudorandom_function_a_pk(a_pk: &mut [u8; 32], a_sk: &[u8; 32]) {
    pseudorandom_function(a_pk, a_sk, 0)
}

pub fn pseudorandom_function_sk_enc(sk_enc: &mut [u8; 32], a_sk: &[u8; 32]) {
    pseudorandom_function(sk_enc, a_sk, 1)
}

fn pseudorandom_function(dst: &mut [u8; 32], input: &[u8; 32], t: u8) {
    let mut data = [0u8; 64];
    data[..32].copy_from_slice(input);
    data[0] = 0xc0 | (data[0] & 0x0f);
    data[32] = t;
    sha256_compress(dst, &data);
}

fn clamp_curve25519(key: &mut [u8; 32]) {
    key[0] &= 248;
    key[31] &= 127;
    key[31] |= 64;
}

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

    static ENCODED: [(&str, &str, &str); 10] = [
	(
		"SKxt8pwrQipUL5KgZUcBAqyLj9R1YwMuRRR3ijGMCwCCqchmi8ut",
		"zcJLC7a3aRJohMNCVjSZQ8jFuofhAHJNAY4aX5soDkYfgNejzKnEZbucJmVibLWCwK8dyyfDhNhf3foXDDTouweC382LcX5",
		"ZiVKW6e4pHW9jRSa6v1BSYQY9FYeDpF2dS8h61DQJzGJe8WdyErzb57W4rALT235UKmEcu2wNnrWNc2V9mjNbqPHTDYpEXdqa",
	),
	(
		"SKxoo5QkFQgTbdc6EWRKyHPMdmtNDJhqudrAVhen9b4kjCwN6CeV",
		"zcRYvLiURno1LhXq95e8avXFcH2fKKToSFfhqaVKTy8mGH7i6SJbfuWcm4h9rEA6DvswrbxDhFGDQgpdDYV8zwUoHvwNvFX",
		"ZiVKkUCm1oqVfyN7e1TY1Xq6CPZgdG18CAirp1nAQWxWcDufxUUkmw11S1fRG23AVWv36rud1Fp3zDEUXEfLfbGyFU6XGTkKM",
	),
	(
		"SKxsVGKsCESoVb3Gfm762psjRtGHmjmv7HVjHckud5MnESfktUuG",
		"zcWGguu2UPfNhh1ygWW9Joo3osvncsuehtz5ewvXd78vFDdnDCRNG6QeKSZpwZmYmkfEutPVf8HzCfBytqXWsEcF2iBAM1e",
		"ZiVKhFPyGkDJjTXUuSi9yWBSnkJF8jLcnE3wSNLnexSgwGeoR8N7ZHRfoyyBMRiM1nbNVDPmcTQPYPBqmS1MDDh1B9fSTauDD",
	),
	(
		"SKxp72QGQ2qtovHSoVnPp8jRFQpHBhG1xF8s27iRFjPXXkYMQUA6",
		"zcWZomPYMEjJ49S4UHcvTnhjYqogfdYJuEDMURDpbkrz94bkzdTdJEZKWkkpQ8nK62eyLkZCvLZDFtLC2Cq5BmEK3WCKGMN",
		"ZiVKnYSZMncaac1SmjB9nfbY3xPdwwiJCVP9inpy59pxVoAWAjSCHTn4rcbQJznWajZALLiFHkKE9XCZQAwSuSqSVbPKtjr5v",
	),
	(
		"SKxpmLdykLu3xxSXtw1EA7iLJnXu8hFh8hhmW1B2J2194ijh5CR4",
		"zcgjj3fJF59QGBufopx3F51jCjUpXbgEzec7YQT6jRt4Ebu5EV3AW4jHPN6ZdXhmygBvQDRJrXoZLa3Lkh5GqnsFUzt7Qok",
		"ZiVKk7ZNUE2sjkNkmaJ5PwBEWm287XFZWeXvoAYcWacCtmuEpAGRDqSfvQstB6erm5bt8uptG3bSEjNipW63KRUv6TBbXrwk6"),
	(
		"SKxny894fJe2rmZjeuoE6GVfNkWoXfPp8337VrLLNWG56FfQtuS1",
		"zcbxovDeXGJJikZH5wQkcQvYx1gzsRt9mR5UnQir6NY8hhPHdgK7z7dE1vfa55Bq3JHJu7isfuWQGYrvMbLnud74z2vS4tS",
		"ZiVKZCknLZuWKZyyGoKDA4h2NRQeYkE5aLQFPPKAh7LYFjSKLhm4BAbk8ELyuwukmRxv29Cws8vaQNYLmq6unwvjZ1AgA5tZM",
	),
	(
		"SKxoo5QkFQgTbdc6EWRKyHPMdmtNDJhqudrAVhen9b4kjCwN6CeV",
		"zcRYvLiURno1LhXq95e8avXFcH2fKKToSFfhqaVKTy8mGH7i6SJbfuWcm4h9rEA6DvswrbxDhFGDQgpdDYV8zwUoHvwNvFX",
		"ZiVKkUCm1oqVfyN7e1TY1Xq6CPZgdG18CAirp1nAQWxWcDufxUUkmw11S1fRG23AVWv36rud1Fp3zDEUXEfLfbGyFU6XGTkKM",
	),
	(
		"SKxsVGKsCESoVb3Gfm762psjRtGHmjmv7HVjHckud5MnESfktUuG",
		"zcWGguu2UPfNhh1ygWW9Joo3osvncsuehtz5ewvXd78vFDdnDCRNG6QeKSZpwZmYmkfEutPVf8HzCfBytqXWsEcF2iBAM1e",
		"ZiVKhFPyGkDJjTXUuSi9yWBSnkJF8jLcnE3wSNLnexSgwGeoR8N7ZHRfoyyBMRiM1nbNVDPmcTQPYPBqmS1MDDh1B9fSTauDD",
	),
	(
		"SKxp72QGQ2qtovHSoVnPp8jRFQpHBhG1xF8s27iRFjPXXkYMQUA6",
		"zcWZomPYMEjJ49S4UHcvTnhjYqogfdYJuEDMURDpbkrz94bkzdTdJEZKWkkpQ8nK62eyLkZCvLZDFtLC2Cq5BmEK3WCKGMN",
		"ZiVKnYSZMncaac1SmjB9nfbY3xPdwwiJCVP9inpy59pxVoAWAjSCHTn4rcbQJznWajZALLiFHkKE9XCZQAwSuSqSVbPKtjr5v",
	),
	(
		"SKxpmLdykLu3xxSXtw1EA7iLJnXu8hFh8hhmW1B2J2194ijh5CR4",
		"zcgjj3fJF59QGBufopx3F51jCjUpXbgEzec7YQT6jRt4Ebu5EV3AW4jHPN6ZdXhmygBvQDRJrXoZLa3Lkh5GqnsFUzt7Qok",
		"ZiVKk7ZNUE2sjkNkmaJ5PwBEWm287XFZWeXvoAYcWacCtmuEpAGRDqSfvQstB6erm5bt8uptG3bSEjNipW63KRUv6TBbXrwk6",
	),
    ];

    #[test]
    fn spending_keys_to_payment_addresses() {
        for &(spending_key_encoded, payment_address_encoded, _) in ENCODED.iter() {
            let mut a_sk = [0u8; 32];
            a_sk.copy_from_slice(
                &bs58::decode(spending_key_encoded).into_vec().unwrap()[2..34],
            );
            let spending_key = SpendingKey::new(a_sk);
            assert_eq!(spending_key_encoded, spending_key.to_string());
            let payment_address = spending_key.address();
            assert_eq!(payment_address_encoded, payment_address.to_string());
        }
    }

    #[test]
    fn spending_keys_to_viewing_keys() {
        for &(spending_key_encoded, _,viewing_key_encoded) in ENCODED.iter() {
            let mut a_sk = [0u8; 32];
            a_sk.copy_from_slice(
                &bs58::decode(spending_key_encoded).into_vec().unwrap()[2..34],
            );
            let spending_key = SpendingKey::new(a_sk);
            assert_eq!(spending_key_encoded, spending_key.to_string());
            let viewing_key = spending_key.viewing_key();
            assert_eq!(viewing_key_encoded, viewing_key.to_string());
        }
    }
}
<|endoftext|>


<commit_before>use glium::{self, Surface};
use glium::glutin;
use glium::index::PrimitiveType;
use euclid::Point2D;
use vitral;

pub type Context = vitral::Context<usize, Vertex>;

pub type GliumTexture = glium::texture::CompressedSrgbTexture2d;

pub struct Backend {
    program: glium::Program,
    textures: Vec<GliumTexture>,
}

impl Backend {
    pub fn new(display: &glium::Display) -> Backend {
        let program = program!(
            display,
            150 => {
            vertex: "
                #version 150 core

                uniform mat4 matrix;

                in vec2 pos;
                in vec4 color;
                in vec2 tex_coord;

                out vec4 v_color;
                out vec2 v_tex_coord;

                void main() {
                    gl_Position = vec4(pos, 0.0, 1.0) * matrix;
                    v_color = color;
                    v_tex_coord = tex_coord;
                }
            ",

            fragment: "
                #version 150 core
                uniform sampler2D tex;
                in vec4 v_color;
                in vec2 v_tex_coord;
                out vec4 f_color;

                void main() {
                    f_color = v_color * texture(tex, v_tex_coord);
                }
            "})
                          .unwrap();

        Backend {
            program: program,
            textures: Vec::new(),
        }
    }

    pub fn make_texture(&mut self, display: &glium::Display, img: vitral::ImageBuffer) -> usize {
        let dim = (img.width(), img.height());
        let raw = glium::texture::RawImage2d::from_raw_rgba(img.into_raw(), dim);
        let tex = glium::texture::CompressedSrgbTexture2d::new(display, raw).unwrap();
        self.textures.push(tex);
        self.textures.len() - 1
    }

    fn process_events<V>(&self,
                         display: &glium::Display,
                         context: &mut vitral::Context<usize, V>)
                         -> bool
        where V: vitral::Vertex
    {
        // polling and handling the events received by the window
        for event in display.poll_events() {
            match event {
                glutin::Event::Closed => return false,
                glutin::Event::MouseMoved(x, y) => context.input_mouse_move(x, y),
                glutin::Event::MouseInput(state, button) => {
                    context.input_mouse_button(match button {
                                                   glutin::MouseButton::Left => {
                                                       vitral::MouseButton::Left
                                                   }
                                                   glutin::MouseButton::Right => {
                                                       vitral::MouseButton::Right
                                                   }
                                                   _ => vitral::MouseButton::Middle,
                                               },
                                               state == glutin::ElementState::Pressed)
                }
                glutin::Event::ReceivedCharacter(c) => context.input_char(c),
                glutin::Event::KeyboardInput(s, _, Some(vk)) => {
                    let is_down = s == glutin::ElementState::Pressed;
                    use glium::glutin::VirtualKeyCode::*;
                    if let Some(vk) = match vk {
                        Tab => Some(vitral::Keycode::Tab),
                        LShift | RShift => Some(vitral::Keycode::Shift),
                        LControl | RControl => Some(vitral::Keycode::Ctrl),
                        NumpadEnter | Return => Some(vitral::Keycode::Enter),
                        Back => Some(vitral::Keycode::Backspace),
                        Delete => Some(vitral::Keycode::Del),
                        Numpad8 | Up => Some(vitral::Keycode::Up),
                        Numpad2 | Down => Some(vitral::Keycode::Down),
                        Numpad4 | Left => Some(vitral::Keycode::Left),
                        Numpad6 | Right => Some(vitral::Keycode::Right),
                        _ => None,
                    } {
                        context.input_key_state(vk, is_down);
                    }
                }
                _ => (),
            }
        }

        true
    }

    pub fn update<V>(&self,
                     display: &glium::Display,
                     context: &mut vitral::Context<usize, V>)
                     -> bool
        where V: vitral::Vertex + glium::Vertex
    {
        let mut target = display.draw();
        target.clear_color(0.0, 0.0, 0.0, 0.0);
        let (w, h) = target.get_dimensions();

        for batch in context.end_frame() {
            // building the uniforms
            let uniforms = uniform! {
                matrix: [
                    [2.0 / w as f32, 0.0, 0.0, -1.0],
                    [0.0, -2.0 / h as f32, 0.0, 1.0],
                    [0.0, 0.0, 1.0, 0.0],
                    [0.0, 0.0, 0.0, 1.0f32]
                ],
                tex: glium::uniforms::Sampler::new(&self.textures[batch.texture])
                    .magnify_filter(glium::uniforms::MagnifySamplerFilter::Nearest),
            };

            let vertex_buffer = {
                glium::VertexBuffer::new(display, &batch.vertices).unwrap()
            };

            // building the index buffer
            let index_buffer = glium::IndexBuffer::new(display,
                                                       PrimitiveType::TrianglesList,
                                                       &batch.triangle_indices)
                                   .unwrap();

            let params = glium::draw_parameters::DrawParameters {
                scissor: batch.clip.map(|clip| {
                    glium::Rect {
                        left: clip.origin.x as u32,
                        bottom: h - (clip.origin.y + clip.size.height) as u32,
                        width: clip.size.width as u32,
                        height: clip.size.height as u32,
                    }
                }),
                blend: glium::Blend::alpha_blending(),
                ..Default::default()
            };

            target.draw(&vertex_buffer,
                        &index_buffer,
                        &self.program,
                        &uniforms,
                        &params)
                  .unwrap();
        }

        target.finish().unwrap();

        self.process_events(display, context)
    }
}

// XXX: An exact copy of Vitral vertex struct, just so that I can derive a
// Glium vertex implementatino for it.
#[derive(Copy, Clone)]
pub struct Vertex {
    pub pos: [f32; 2],
    pub color: [f32; 4],
    pub tex_coord: [f32; 2],
}
implement_vertex!(Vertex, pos, color, tex_coord);

impl vitral::Vertex for Vertex {
    fn new(pos: [f32; 2], color: [f32; 4], tex_coord: [f32; 2]) -> Self {
        Vertex {
            pos: pos,
            color: color,
            tex_coord: tex_coord,
        }
    }
}

/*
fn main() {
    // Construct Glium backend.
    let display = glutin::WindowBuilder::new()
                      .build_glium()
                      .unwrap();

    let mut backend = Backend::new(&display);

    // Construct Vitral context.
    let mut context: Context;
    let mut builder = vitral::Builder::new();
    let image = builder.add_image(&image::open(&Path::new("julia.png")).unwrap());
    context = builder.build(|img| backend.make_texture(&display, img));

    let font = context.default_font();

    let mut test_input = String::new();

    // Run the program.
    loop {
        context.begin_frame();

        context.draw_image(image, Point2D::new(100.0, 100.0), [1.0, 1.0, 1.0, 1.0]);

        if context.button("Hello, world") {
            println!("Click");
        }

        if context.button("Another button") {
            println!("Clack {}", test_input);
        }

        context.text_input(font,
                           Point2D::new(10.0, 120.0),
                           [0.8, 0.8, 0.8, 1.0],
                           &mut test_input);

        if !backend.update(&display, &mut context) {
            return;
        }
    }
}
*/
<commit_msg>Mucking with the shader<commit_after>use glium::{self, Surface};
use glium::glutin;
use glium::index::PrimitiveType;
use euclid::Point2D;
use vitral;

pub type Context = vitral::Context<usize, Vertex>;

pub type GliumTexture = glium::texture::CompressedSrgbTexture2d;

pub struct Backend {
    program: glium::Program,
    textures: Vec<GliumTexture>,
}

impl Backend {
    pub fn new(display: &glium::Display) -> Backend {
        let program = program!(
            display,
            150 => {
            vertex: "
                #version 150 core

                uniform mat4 matrix;

                in vec2 pos;
                in vec4 color;
                in vec2 tex_coord;

                out vec4 v_color;
                out vec2 v_tex_coord;

                void main() {
                    gl_Position = vec4(pos, 0.0, 1.0) * matrix;
                    v_color = color;
                    v_tex_coord = tex_coord;
                }
            ",

            fragment: "
                #version 150 core
                uniform sampler2D tex;
                in vec4 v_color;
                in vec2 v_tex_coord;
                out vec4 f_color;

                void main() {
                    vec4 tex_color = texture(tex, v_tex_coord);

                    // Discard fully transparent pixels to keep them from
                    // writing into the depth buffer.
                    if (tex_color.a == 0.0) discard;

                    f_color = v_color * tex_color;
                }
            "})
                          .unwrap();

        Backend {
            program: program,
            textures: Vec::new(),
        }
    }

    pub fn make_texture(&mut self, display: &glium::Display, img: vitral::ImageBuffer) -> usize {
        let dim = (img.width(), img.height());
        let raw = glium::texture::RawImage2d::from_raw_rgba(img.into_raw(), dim);
        let tex = glium::texture::CompressedSrgbTexture2d::new(display, raw).unwrap();
        self.textures.push(tex);
        self.textures.len() - 1
    }

    fn process_events<V>(&self,
                         display: &glium::Display,
                         context: &mut vitral::Context<usize, V>)
                         -> bool
        where V: vitral::Vertex
    {
        // polling and handling the events received by the window
        for event in display.poll_events() {
            match event {
                glutin::Event::Closed => return false,
                glutin::Event::MouseMoved(x, y) => context.input_mouse_move(x, y),
                glutin::Event::MouseInput(state, button) => {
                    context.input_mouse_button(match button {
                                                   glutin::MouseButton::Left => {
                                                       vitral::MouseButton::Left
                                                   }
                                                   glutin::MouseButton::Right => {
                                                       vitral::MouseButton::Right
                                                   }
                                                   _ => vitral::MouseButton::Middle,
                                               },
                                               state == glutin::ElementState::Pressed)
                }
                glutin::Event::ReceivedCharacter(c) => context.input_char(c),
                glutin::Event::KeyboardInput(s, _, Some(vk)) => {
                    let is_down = s == glutin::ElementState::Pressed;
                    use glium::glutin::VirtualKeyCode::*;
                    if let Some(vk) = match vk {
                        Tab => Some(vitral::Keycode::Tab),
                        LShift | RShift => Some(vitral::Keycode::Shift),
                        LControl | RControl => Some(vitral::Keycode::Ctrl),
                        NumpadEnter | Return => Some(vitral::Keycode::Enter),
                        Back => Some(vitral::Keycode::Backspace),
                        Delete => Some(vitral::Keycode::Del),
                        Numpad8 | Up => Some(vitral::Keycode::Up),
                        Numpad2 | Down => Some(vitral::Keycode::Down),
                        Numpad4 | Left => Some(vitral::Keycode::Left),
                        Numpad6 | Right => Some(vitral::Keycode::Right),
                        _ => None,
                    } {
                        context.input_key_state(vk, is_down);
                    }
                }
                _ => (),
            }
        }

        true
    }

    pub fn update<V>(&self,
                     display: &glium::Display,
                     context: &mut vitral::Context<usize, V>)
                     -> bool
        where V: vitral::Vertex + glium::Vertex
    {
        let mut target = display.draw();
        target.clear_color(0.0, 0.0, 0.0, 0.0);
        let (w, h) = target.get_dimensions();

        for batch in context.end_frame() {
            // building the uniforms
            let uniforms = uniform! {
                matrix: [
                    [2.0 / w as f32, 0.0, 0.0, -1.0],
                    [0.0, -2.0 / h as f32, 0.0, 1.0],
                    [0.0, 0.0, 1.0, 0.0],
                    [0.0, 0.0, 0.0, 1.0f32]
                ],
                tex: glium::uniforms::Sampler::new(&self.textures[batch.texture])
                    .magnify_filter(glium::uniforms::MagnifySamplerFilter::Nearest),
            };

            let vertex_buffer = {
                glium::VertexBuffer::new(display, &batch.vertices).unwrap()
            };

            // building the index buffer
            let index_buffer = glium::IndexBuffer::new(display,
                                                       PrimitiveType::TrianglesList,
                                                       &batch.triangle_indices)
                                   .unwrap();

            let params = glium::draw_parameters::DrawParameters {
                scissor: batch.clip.map(|clip| {
                    glium::Rect {
                        left: clip.origin.x as u32,
                        bottom: h - (clip.origin.y + clip.size.height) as u32,
                        width: clip.size.width as u32,
                        height: clip.size.height as u32,
                    }
                }),
                blend: glium::Blend::alpha_blending(),
                ..Default::default()
            };

            target.draw(&vertex_buffer,
                        &index_buffer,
                        &self.program,
                        &uniforms,
                        &params)
                  .unwrap();
        }

        target.finish().unwrap();

        self.process_events(display, context)
    }
}

// XXX: An exact copy of Vitral vertex struct, just so that I can derive a
// Glium vertex implementatino for it.
#[derive(Copy, Clone)]
pub struct Vertex {
    pub pos: [f32; 2],
    pub color: [f32; 4],
    pub tex_coord: [f32; 2],
}
implement_vertex!(Vertex, pos, color, tex_coord);

impl vitral::Vertex for Vertex {
    fn new(pos: [f32; 2], color: [f32; 4], tex_coord: [f32; 2]) -> Self {
        Vertex {
            pos: pos,
            color: color,
            tex_coord: tex_coord,
        }
    }
}

/*
fn main() {
    // Construct Glium backend.
    let display = glutin::WindowBuilder::new()
                      .build_glium()
                      .unwrap();

    let mut backend = Backend::new(&display);

    // Construct Vitral context.
    let mut context: Context;
    let mut builder = vitral::Builder::new();
    let image = builder.add_image(&image::open(&Path::new("julia.png")).unwrap());
    context = builder.build(|img| backend.make_texture(&display, img));

    let font = context.default_font();

    let mut test_input = String::new();

    // Run the program.
    loop {
        context.begin_frame();

        context.draw_image(image, Point2D::new(100.0, 100.0), [1.0, 1.0, 1.0, 1.0]);

        if context.button("Hello, world") {
            println!("Click");
        }

        if context.button("Another button") {
            println!("Clack {}", test_input);
        }

        context.text_input(font,
                           Point2D::new(10.0, 120.0),
                           [0.8, 0.8, 0.8, 1.0],
                           &mut test_input);

        if !backend.update(&display, &mut context) {
            return;
        }
    }
}
*/
<|endoftext|>


<commit_before>extern crate chrono;

use std::str;
use std::collections::HashMap;
use self::chrono::*;
use eew::*;

#[derive(Debug)]
pub enum JMAFormatParseError {
	TooShort,
	Split,
	InvalidPattern,
	InvalidSource,
	InvalidKind,
	InvalidIssueTime,
	InvalidOoccurrenceTime,
	InvalidId,
	InvalidStatus,
	InvalidNumber,
	UnknownEpicenter,
	InvalidLL,
	InvalidDepth,
	InvalidMagnitude,
	InvalidMaximumIntensity,
	InvalidEpicenterAccuracy,
	InvalidDepthAccuracy,
	InvalidMagnitudeAccuracy,
	InvalidEpicenterCategory,
	InvalidWarningStatus,
	InvalidIntensityChange,
	InvalidChangeReason,
	InvalidWaveStatus,
	InvalidEBI,
	PrematureEOS,
}

fn parse_datetime(datetime_text: &[u8]) -> Option<DateTime<UTC>>
{
	let jst: FixedOffset = FixedOffset::east(9 * 3600); // XXX: want to use const keyword...
	const DATETIME_FORMAT: &'static str = "%y%m%d%H%M%S";

	return str::from_utf8(&datetime_text).ok().and_then( |converted|
		jst.datetime_from_str(&converted, DATETIME_FORMAT).ok().map( |dt|
			dt.with_timezone(&UTC)
		)
	);
}

fn parse_number(number_text: &[u8]) -> Option<u32>
{
	str::from_utf8(&number_text).ok().and_then( |converted|
		converted.parse().ok()
	)
}

fn parse_intensity(intensity_text: &[u8]) -> Option<f32>
{
	match intensity_text {
		b"01" => Some(1.0),
		b"02" => Some(2.0),
		b"03" => Some(3.0),
		b"04" => Some(4.0),
		b"5-" => Some(4.75),
		b"5+" => Some(5.25),
		b"6-" => Some(5.75),
		b"6+" => Some(6.25),
		b"07" => Some(7.0),
		_ => None
	}
}

fn parse_arrival_time(arrival_text: &[u8], base: &DateTime<UTC>) -> Option<DateTime<UTC>>
{
	let jst: FixedOffset = FixedOffset::east(9 * 3600); // XXX: want to use const keyword...
	const TIME_FORMAT: &'static str = "%H%M%S";

	let adjust = |a_t: NaiveTime| {
		let base_t = base.with_timezone(&jst).time();
		let diff = a_t - base_t;
		if diff < Duration::seconds(0) {
			return base.checked_add(Duration::days(1) + diff);
		} else {
			return base.checked_add(diff);
		}
	};

	return str::from_utf8(&arrival_text).ok().and_then( |converted|
		NaiveTime::parse_from_str(&converted, TIME_FORMAT).ok().and_then( |t|
			adjust(t)
		)
	);
}

pub fn parse_jma_format(text: &[u8],
	epicenter_code_dict: &HashMap<[u8; 3], String>,
	area_code_dict: &HashMap<[u8; 3], String>) -> Result<EEW, JMAFormatParseError>
{
	if text.len() < 140 {
		return Err(JMAFormatParseError::TooShort);
	}

	let source = match &text[3..5] {
		b"03" => Source::Tokyo,
		b"04" => Source::Osaka,
		_ => return Err(JMAFormatParseError::InvalidSource)
	};

	let kind = match &text[6..8] {
		b"00" => Kind::Normal,
		b"01" => Kind::Drill,
		b"10" => Kind::Cancel,
		b"11" => Kind::DrillCancel,
		b"20" => Kind::Reference,
		b"30" => Kind::Test,
		_ => return Err(JMAFormatParseError::InvalidKind)
	};

	let issued_at = match parse_datetime(&text[9..21]) {
		Some(dt) => dt,
		None => return Err(JMAFormatParseError::InvalidIssueTime)
	};

	// TODO: accept split telegrams
	if &text[23..25] != b"11" {
		return Err(JMAFormatParseError::Split);
	}

	let occurred_at = match parse_datetime(&text[26..38]) {
		Some(dt) => dt,
		None => return Err(JMAFormatParseError::InvalidOoccurrenceTime)
	};

	let id = match str::from_utf8(&text[39..55]) {
		Ok(s) => s,
		Err(_) => return Err(JMAFormatParseError::InvalidId)
	};

	let status = match text[59] {
		b'0' => Status::Normal,
		b'6' => Status::Correction,
		b'7' => Status::CancelCorrection,
		b'8' => Status::LastWithCorrection,
		b'9' => Status::Last,
		b'/' => Status::Unknown,
		_ => return Err(JMAFormatParseError::InvalidStatus)
	};

	// we don't accept an EEW which has no telegram number
	let number = match parse_number(&text[60..62]) {
		Some(n) => n,
		None => return Err(JMAFormatParseError::InvalidNumber)
	};

	if &text[0..2] == b"39" {

		return Ok(EEW {
			source: source,
			kind: kind,
			issued_at: issued_at,
			occurred_at: occurred_at,
			id: id.to_string(),
			status: status,
			number: number,
			detail: EEWDetail::Cancel,
		});
	}

	let issue_pattern = match &text[0..2] {
		b"35" => IssuePattern::IntensityOnly,
		b"36" => IssuePattern::LowAccuracy,
		b"37" => IssuePattern::HighAccuracy,
		_ => return Err(JMAFormatParseError::InvalidPattern)
	};

	let epicenter_name = match epicenter_code_dict.get(&text[86..89]) {
		Some(s) => s.clone(),
		None => return Err(JMAFormatParseError::UnknownEpicenter)
	};

	let lat_value = match parse_number(&text[91..94]) {
		Some(v) => (v as f32) / 10.0,
		None => return Err(JMAFormatParseError::InvalidLL)
	};

	let lat = match text[90] {
		b'N' =>  lat_value,
		b'S' => -lat_value,
		_ => return Err(JMAFormatParseError::InvalidLL)
	};

	let lon_value = match parse_number(&text[96..100]) {
		Some(v) => (v as f32) / 10.0,
		None => return Err(JMAFormatParseError::InvalidLL)
	};

	let lon = match text[95] {
		b'E' =>  lon_value,
		b'W' => -lon_value,
		_ => return Err(JMAFormatParseError::InvalidLL)
	};

	let depth = match parse_number(&text[101..104]) {
		Some(v) => Some(v as f32),
		None => match &text[100..103] {
			b"///" => None,
			_ => return Err(JMAFormatParseError::InvalidDepth)
		}
	};

	let magnitude = match parse_number(&text[105..107]) {
		Some(v) => Some((v as f32) / 10.0),
		None => match &text[104..106] {
			b"///" => None,
			_ => return Err(JMAFormatParseError::InvalidMagnitude)
		}
	};

	let maximum_intensity = match parse_intensity(&text[108..110]) {
		Some(v) => Some(v),
		None => match &text[107..109] {
			b"//" => None,
			_ => return Err(JMAFormatParseError::InvalidMaximumIntensity)
		}
	};

	let epicenter_accuracy = match text[113] {
		b'1' => EpicenterAccuracy::Single,
		b'2' => EpicenterAccuracy::Territory,
		b'3' => EpicenterAccuracy::GridSearchLow,
		b'4' => EpicenterAccuracy::GridSearchHigh,
		b'5' => EpicenterAccuracy::NIEDLow,
		b'6' => EpicenterAccuracy::NIEDHigh,
		b'7' => EpicenterAccuracy::EPOSLow,
		b'8' => EpicenterAccuracy::EPOSHigh,
		b'/' => EpicenterAccuracy::Unknown,
		_ => return Err(JMAFormatParseError::InvalidEpicenterAccuracy)
	};

	let depth_accuracy = match text[114] {
		b'1' => DepthAccuracy::Single,
		b'2' => DepthAccuracy::Territory,
		b'3' => DepthAccuracy::GridSearchLow,
		b'4' => DepthAccuracy::GridSearchHigh,
		b'5' => DepthAccuracy::NIEDLow,
		b'6' => DepthAccuracy::NIEDHigh,
		b'7' => DepthAccuracy::EPOSLow,
		b'8' => DepthAccuracy::EPOSHigh,
		b'/' => DepthAccuracy::Unknown,
		_ => return Err(JMAFormatParseError::InvalidDepthAccuracy)
	};

	let magnitude_accuracy = match text[115] {
		b'2' => MagnitudeAccuracy::NIED,
		b'3' => MagnitudeAccuracy::PWave,
		b'4' => MagnitudeAccuracy::PSMixed,
		b'5' => MagnitudeAccuracy::SWave,
		b'6' => MagnitudeAccuracy::EPOS,
		b'8' => MagnitudeAccuracy::Level,
		b'/' => MagnitudeAccuracy::Unknown,
		_ => return Err(JMAFormatParseError::InvalidMagnitudeAccuracy)
	};

	let epicenter_caterogy = match text[121] {
		b'0' => EpicenterCategory::Land,
		b'1' => EpicenterCategory::Sea,
		b'/' => EpicenterCategory::Unknown,
		_ => return Err(JMAFormatParseError::InvalidEpicenterCategory)
	};

	let warning_status = match text[122] {
		b'0' => WarningStatus::Forecast,
		b'1' => WarningStatus::Alert,
		b'/' => WarningStatus::Unknown,
		_ => return Err(JMAFormatParseError::InvalidWarningStatus)
	};

	let intensity_change = match text[129] {
		b'0' => IntensityChange::Same,
		b'1' => IntensityChange::Up,
		b'2' => IntensityChange::Down,
		b'/' => IntensityChange::Unknown,
		_ => return Err(JMAFormatParseError::InvalidIntensityChange)
	};

	let change_reason = match text[130] {
		b'0' => ChangeReason::Nothing,
		b'1' => ChangeReason::Magnitude,
		b'2' => ChangeReason::Epicenter,
		b'3' => ChangeReason::Mixed,
		b'4' => ChangeReason::Depth,
		b'/' => ChangeReason::Unknown,
		_ => return Err(JMAFormatParseError::InvalidChangeReason)
	};

	let mut area_info = vec! {};

	if &text[135..138] == b"EBI" {

		let ebi_part_len = 20;
		let mut it = 138;

		while it + ebi_part_len < text.len() {

			let part = &text[it..(it+ebi_part_len)];

			let area_name = match area_code_dict.get(&part[1..4]) {
				Some(s) => s.clone(),
				None => return Err(JMAFormatParseError::InvalidEBI)
			};

			let left_intensity = match parse_intensity(&part[6..8]) {
				Some(v) => v,
				None => return Err(JMAFormatParseError::InvalidEBI)
			};

			let right_intensity = match parse_intensity(&part[8..10]) {
				Some(v) => Some(v),
				None => match &part[8..10] {
					b"//" => None,
					_ => return Err(JMAFormatParseError::InvalidEBI)
				}
			};

			let (minimum_intensity, maximum_intensity) = match right_intensity {
				Some(v) => (v, Some(left_intensity)),
				None => (left_intensity, None)
			};

			let reached_at = match parse_arrival_time(&part[11..17], &occurred_at) {
				Some(v) => Some(v),
				None => match &part[11..17] {
					b"//////" => None,
					_ => return Err(JMAFormatParseError::InvalidEBI)
				}
			};

			let local_warning_status = match part[18] {
				b'0' => WarningStatus::Forecast,
				b'1' => WarningStatus::Alert,
				b'/' => WarningStatus::Unknown,
				_ => return Err(JMAFormatParseError::InvalidEBI)
			};

			let wave_status = match part[19] {
				b'0' => WaveStatus::Unreached,
				b'1' => WaveStatus::Reached,
				b'/' => WaveStatus::Unknown,
				_ => return Err(JMAFormatParseError::InvalidEBI)
			};

			let area_eew = AreaEEW {
				area_name: area_name,
				minimum_intensity: minimum_intensity,
				maximum_intensity: maximum_intensity,
				reached_at: reached_at,
				warning_status: local_warning_status,
				wave_status: wave_status,
			};

			area_info.push(area_eew);

			it += ebi_part_len;
		}

		if it + 5 >= text.len() || &text[(it+1)..(it+6)] != b"9999=" {
			return Err(JMAFormatParseError::PrematureEOS);
		}
	}

	let detail = FullEEW {

		issue_pattern: issue_pattern,
		epicenter_name: epicenter_name,
		epicenter: (lat, lon),
		depth: depth,
		magnitude: magnitude,
		maximum_intensity: maximum_intensity,
		epicenter_accuracy: epicenter_accuracy,
		depth_accuracy: depth_accuracy,
		magnitude_accuracy: magnitude_accuracy,
		epicenter_caterogy: epicenter_caterogy,
		warning_status: warning_status,
		intensity_change: intensity_change,
		change_reason: change_reason,

		area_info: area_info
	};

	return Ok(EEW{
		source: source,
		kind: kind,
		issued_at: issued_at,
		occurred_at: occurred_at,
		id: id.to_string(),
		status: status,
		number: number,
		detail: EEWDetail::Full(detail),
	});
}
<commit_msg>Accept split telegrams when the code part isn't split<commit_after>extern crate chrono;

use std::str;
use std::collections::HashMap;
use self::chrono::*;
use eew::*;

#[derive(Debug)]
pub enum JMAFormatParseError {
	TooShort,
	Split,
	InvalidPattern,
	InvalidSource,
	InvalidKind,
	InvalidIssueTime,
	InvalidOoccurrenceTime,
	InvalidId,
	InvalidStatus,
	InvalidNumber,
	UnknownEpicenter,
	InvalidLL,
	InvalidDepth,
	InvalidMagnitude,
	InvalidMaximumIntensity,
	InvalidEpicenterAccuracy,
	InvalidDepthAccuracy,
	InvalidMagnitudeAccuracy,
	InvalidEpicenterCategory,
	InvalidWarningStatus,
	InvalidIntensityChange,
	InvalidChangeReason,
	InvalidWaveStatus,
	InvalidEBI,
	PrematureEOS,
}

fn parse_datetime(datetime_text: &[u8]) -> Option<DateTime<UTC>>
{
	let jst: FixedOffset = FixedOffset::east(9 * 3600); // XXX: want to use const keyword...
	const DATETIME_FORMAT: &'static str = "%y%m%d%H%M%S";

	return str::from_utf8(&datetime_text).ok().and_then( |converted|
		jst.datetime_from_str(&converted, DATETIME_FORMAT).ok().map( |dt|
			dt.with_timezone(&UTC)
		)
	);
}

fn parse_number(number_text: &[u8]) -> Option<u32>
{
	str::from_utf8(&number_text).ok().and_then( |converted|
		converted.parse().ok()
	)
}

fn parse_intensity(intensity_text: &[u8]) -> Option<f32>
{
	match intensity_text {
		b"01" => Some(1.0),
		b"02" => Some(2.0),
		b"03" => Some(3.0),
		b"04" => Some(4.0),
		b"5-" => Some(4.75),
		b"5+" => Some(5.25),
		b"6-" => Some(5.75),
		b"6+" => Some(6.25),
		b"07" => Some(7.0),
		_ => None
	}
}

fn parse_arrival_time(arrival_text: &[u8], base: &DateTime<UTC>) -> Option<DateTime<UTC>>
{
	let jst: FixedOffset = FixedOffset::east(9 * 3600); // XXX: want to use const keyword...
	const TIME_FORMAT: &'static str = "%H%M%S";

	let adjust = |a_t: NaiveTime| {
		let base_t = base.with_timezone(&jst).time();
		let diff = a_t - base_t;
		if diff < Duration::seconds(0) {
			return base.checked_add(Duration::days(1) + diff);
		} else {
			return base.checked_add(diff);
		}
	};

	return str::from_utf8(&arrival_text).ok().and_then( |converted|
		NaiveTime::parse_from_str(&converted, TIME_FORMAT).ok().and_then( |t|
			adjust(t)
		)
	);
}

pub fn parse_jma_format(text: &[u8],
	epicenter_code_dict: &HashMap<[u8; 3], String>,
	area_code_dict: &HashMap<[u8; 3], String>) -> Result<EEW, JMAFormatParseError>
{
	if text.len() < 140 {
		return Err(JMAFormatParseError::TooShort);
	}

	let source = match &text[3..5] {
		b"03" => Source::Tokyo,
		b"04" => Source::Osaka,
		_ => return Err(JMAFormatParseError::InvalidSource)
	};

	let kind = match &text[6..8] {
		b"00" => Kind::Normal,
		b"01" => Kind::Drill,
		b"10" => Kind::Cancel,
		b"11" => Kind::DrillCancel,
		b"20" => Kind::Reference,
		b"30" => Kind::Test,
		_ => return Err(JMAFormatParseError::InvalidKind)
	};

	let issued_at = match parse_datetime(&text[9..21]) {
		Some(dt) => dt,
		None => return Err(JMAFormatParseError::InvalidIssueTime)
	};

	// TODO: accept split telegrams
	if text[24] != b'1' {
		return Err(JMAFormatParseError::Split);
	}

	let occurred_at = match parse_datetime(&text[26..38]) {
		Some(dt) => dt,
		None => return Err(JMAFormatParseError::InvalidOoccurrenceTime)
	};

	let id = match str::from_utf8(&text[39..55]) {
		Ok(s) => s,
		Err(_) => return Err(JMAFormatParseError::InvalidId)
	};

	let status = match text[59] {
		b'0' => Status::Normal,
		b'6' => Status::Correction,
		b'7' => Status::CancelCorrection,
		b'8' => Status::LastWithCorrection,
		b'9' => Status::Last,
		b'/' => Status::Unknown,
		_ => return Err(JMAFormatParseError::InvalidStatus)
	};

	// we don't accept an EEW which has no telegram number
	let number = match parse_number(&text[60..62]) {
		Some(n) => n,
		None => return Err(JMAFormatParseError::InvalidNumber)
	};

	if &text[0..2] == b"39" {

		return Ok(EEW {
			source: source,
			kind: kind,
			issued_at: issued_at,
			occurred_at: occurred_at,
			id: id.to_string(),
			status: status,
			number: number,
			detail: EEWDetail::Cancel,
		});
	}

	let issue_pattern = match &text[0..2] {
		b"35" => IssuePattern::IntensityOnly,
		b"36" => IssuePattern::LowAccuracy,
		b"37" => IssuePattern::HighAccuracy,
		_ => return Err(JMAFormatParseError::InvalidPattern)
	};

	let epicenter_name = match epicenter_code_dict.get(&text[86..89]) {
		Some(s) => s.clone(),
		None => return Err(JMAFormatParseError::UnknownEpicenter)
	};

	let lat_value = match parse_number(&text[91..94]) {
		Some(v) => (v as f32) / 10.0,
		None => return Err(JMAFormatParseError::InvalidLL)
	};

	let lat = match text[90] {
		b'N' =>  lat_value,
		b'S' => -lat_value,
		_ => return Err(JMAFormatParseError::InvalidLL)
	};

	let lon_value = match parse_number(&text[96..100]) {
		Some(v) => (v as f32) / 10.0,
		None => return Err(JMAFormatParseError::InvalidLL)
	};

	let lon = match text[95] {
		b'E' =>  lon_value,
		b'W' => -lon_value,
		_ => return Err(JMAFormatParseError::InvalidLL)
	};

	let depth = match parse_number(&text[101..104]) {
		Some(v) => Some(v as f32),
		None => match &text[100..103] {
			b"///" => None,
			_ => return Err(JMAFormatParseError::InvalidDepth)
		}
	};

	let magnitude = match parse_number(&text[105..107]) {
		Some(v) => Some((v as f32) / 10.0),
		None => match &text[104..106] {
			b"///" => None,
			_ => return Err(JMAFormatParseError::InvalidMagnitude)
		}
	};

	let maximum_intensity = match parse_intensity(&text[108..110]) {
		Some(v) => Some(v),
		None => match &text[107..109] {
			b"//" => None,
			_ => return Err(JMAFormatParseError::InvalidMaximumIntensity)
		}
	};

	let epicenter_accuracy = match text[113] {
		b'1' => EpicenterAccuracy::Single,
		b'2' => EpicenterAccuracy::Territory,
		b'3' => EpicenterAccuracy::GridSearchLow,
		b'4' => EpicenterAccuracy::GridSearchHigh,
		b'5' => EpicenterAccuracy::NIEDLow,
		b'6' => EpicenterAccuracy::NIEDHigh,
		b'7' => EpicenterAccuracy::EPOSLow,
		b'8' => EpicenterAccuracy::EPOSHigh,
		b'/' => EpicenterAccuracy::Unknown,
		_ => return Err(JMAFormatParseError::InvalidEpicenterAccuracy)
	};

	let depth_accuracy = match text[114] {
		b'1' => DepthAccuracy::Single,
		b'2' => DepthAccuracy::Territory,
		b'3' => DepthAccuracy::GridSearchLow,
		b'4' => DepthAccuracy::GridSearchHigh,
		b'5' => DepthAccuracy::NIEDLow,
		b'6' => DepthAccuracy::NIEDHigh,
		b'7' => DepthAccuracy::EPOSLow,
		b'8' => DepthAccuracy::EPOSHigh,
		b'/' => DepthAccuracy::Unknown,
		_ => return Err(JMAFormatParseError::InvalidDepthAccuracy)
	};

	let magnitude_accuracy = match text[115] {
		b'2' => MagnitudeAccuracy::NIED,
		b'3' => MagnitudeAccuracy::PWave,
		b'4' => MagnitudeAccuracy::PSMixed,
		b'5' => MagnitudeAccuracy::SWave,
		b'6' => MagnitudeAccuracy::EPOS,
		b'8' => MagnitudeAccuracy::Level,
		b'/' => MagnitudeAccuracy::Unknown,
		_ => return Err(JMAFormatParseError::InvalidMagnitudeAccuracy)
	};

	let epicenter_caterogy = match text[121] {
		b'0' => EpicenterCategory::Land,
		b'1' => EpicenterCategory::Sea,
		b'/' => EpicenterCategory::Unknown,
		_ => return Err(JMAFormatParseError::InvalidEpicenterCategory)
	};

	let warning_status = match text[122] {
		b'0' => WarningStatus::Forecast,
		b'1' => WarningStatus::Alert,
		b'/' => WarningStatus::Unknown,
		_ => return Err(JMAFormatParseError::InvalidWarningStatus)
	};

	let intensity_change = match text[129] {
		b'0' => IntensityChange::Same,
		b'1' => IntensityChange::Up,
		b'2' => IntensityChange::Down,
		b'/' => IntensityChange::Unknown,
		_ => return Err(JMAFormatParseError::InvalidIntensityChange)
	};

	let change_reason = match text[130] {
		b'0' => ChangeReason::Nothing,
		b'1' => ChangeReason::Magnitude,
		b'2' => ChangeReason::Epicenter,
		b'3' => ChangeReason::Mixed,
		b'4' => ChangeReason::Depth,
		b'/' => ChangeReason::Unknown,
		_ => return Err(JMAFormatParseError::InvalidChangeReason)
	};

	let mut area_info = vec! {};

	if &text[135..138] == b"EBI" {

		let ebi_part_len = 20;
		let mut it = 138;

		while it + ebi_part_len < text.len() {

			if &text[(it+1)..(it+6)] == b"9999=" {
				break;
			}

			let part = &text[it..(it+ebi_part_len)];

			let area_name = match area_code_dict.get(&part[1..4]) {
				Some(s) => s.clone(),
				None => return Err(JMAFormatParseError::InvalidEBI)
			};

			let left_intensity = match parse_intensity(&part[6..8]) {
				Some(v) => v,
				None => return Err(JMAFormatParseError::InvalidEBI)
			};

			let right_intensity = match parse_intensity(&part[8..10]) {
				Some(v) => Some(v),
				None => match &part[8..10] {
					b"//" => None,
					_ => return Err(JMAFormatParseError::InvalidEBI)
				}
			};

			let (minimum_intensity, maximum_intensity) = match right_intensity {
				Some(v) => (v, Some(left_intensity)),
				None => (left_intensity, None)
			};

			let reached_at = match parse_arrival_time(&part[11..17], &occurred_at) {
				Some(v) => Some(v),
				None => match &part[11..17] {
					b"//////" => None,
					_ => return Err(JMAFormatParseError::InvalidEBI)
				}
			};

			let local_warning_status = match part[18] {
				b'0' => WarningStatus::Forecast,
				b'1' => WarningStatus::Alert,
				b'/' => WarningStatus::Unknown,
				_ => return Err(JMAFormatParseError::InvalidEBI)
			};

			let wave_status = match part[19] {
				b'0' => WaveStatus::Unreached,
				b'1' => WaveStatus::Reached,
				b'/' => WaveStatus::Unknown,
				_ => return Err(JMAFormatParseError::InvalidEBI)
			};

			let area_eew = AreaEEW {
				area_name: area_name,
				minimum_intensity: minimum_intensity,
				maximum_intensity: maximum_intensity,
				reached_at: reached_at,
				warning_status: local_warning_status,
				wave_status: wave_status,
			};

			area_info.push(area_eew);

			it += ebi_part_len;
		}

		if it + 5 >= text.len() || &text[(it+1)..(it+6)] != b"9999=" {
			return Err(JMAFormatParseError::PrematureEOS);
		}
	}

	let detail = FullEEW {

		issue_pattern: issue_pattern,
		epicenter_name: epicenter_name,
		epicenter: (lat, lon),
		depth: depth,
		magnitude: magnitude,
		maximum_intensity: maximum_intensity,
		epicenter_accuracy: epicenter_accuracy,
		depth_accuracy: depth_accuracy,
		magnitude_accuracy: magnitude_accuracy,
		epicenter_caterogy: epicenter_caterogy,
		warning_status: warning_status,
		intensity_change: intensity_change,
		change_reason: change_reason,

		area_info: area_info
	};

	return Ok(EEW{
		source: source,
		kind: kind,
		issued_at: issued_at,
		occurred_at: occurred_at,
		id: id.to_string(),
		status: status,
		number: number,
		detail: EEWDetail::Full(detail),
	});
}
<|endoftext|>