<commit_before>use std::iter;
use std::fmt;
use std::cmp::Ordering;
use super::{ST, OrderedST};

pub struct Node<K, V> {
    key: K,
    val: V,
    left:  Option<Box<Node<K, V>>>,
    right: Option<Box<Node<K, V>>>
}

impl<K, V> Node<K, V> {
    pub fn new(key: K, val: V) -> Node<K, V> {
        Node {
            key: key,
            val: val,
            left: None,
            right: None
        }
    }

    fn size(&self) -> usize {
        let mut ret = 1;
        if self.left.is_some() {
            ret += self.left.as_ref().unwrap().size()
        }
        if self.right.is_some() {
            ret += self.right.as_ref().unwrap().size()
        }
        ret
    }
}

impl<K: fmt::Debug, V: fmt::Debug> Node<K, V> {
    fn dump(&self, depth: usize, f: &mut fmt::Formatter) {
        if depth == 0 {
            writeln!(f, "{:?}[{:?}]", self.key, self.val).unwrap();
        } else {
            writeln!(f, "{}+--{:?}[{:?}]", iter::repeat("|  ").take(depth-1).collect::<Vec<&str>>().concat(),
                     self.key, self.val).unwrap();
        }
        if self.left.is_some() {
            self.left.as_ref().unwrap().dump(depth + 1, f);
        }
        if self.right.is_some() {
            self.right.as_ref().unwrap().dump(depth + 1, f);
        }
    }
}

fn put<K: Ord, V>(x: Option<Box<Node<K,V>>>, key: K, val: V) -> Option<Box<Node<K,V>>> {
    let mut x = x;
    if x.is_none() {
        return Some(Box::new(Node::new(key, val)));
    }
    let cmp = key.cmp(&x.as_ref().unwrap().key);
    match cmp {
        Ordering::Less => {
            let left = x.as_mut().unwrap().left.take();
            x.as_mut().unwrap().left = put(left, key, val)
        },
        Ordering::Greater => {
            let right = x.as_mut().unwrap().right.take();
            x.as_mut().unwrap().right = put(right, key, val)
        },
        Ordering::Equal => {
            x.as_mut().unwrap().val = val
        }
    }
    x
}

pub struct BST<K, V> {
    root: Option<Box<Node<K, V>>>
}

impl<K: Ord, V> ST<K, V> for BST<K, V> {
    fn new() -> BST<K, V> {
        BST { root: None }
    }

    fn get(&self, key: &K) -> Option<&V> {
        let mut x = self.root.as_ref();
        while x.is_some() {
            match key.cmp(&x.unwrap().key) {
                Ordering::Less => {
                    x = x.unwrap().left.as_ref();
                },
                Ordering::Greater => {
                    x = x.unwrap().right.as_ref();
                },
                Ordering::Equal  => {
                    return Some(&x.unwrap().val)
                }
            }
        }
        None
    }

    fn put(&mut self, key: K, val: V) {
        let root = self.root.take();
        self.root = put(root, key, val);
    }

    fn delete(&mut self, key: &K) {
        unimplemented!()
    }

    fn is_empty(&self) -> bool {
        self.root.is_none()
    }

    /// number of key-value pairs in the table
    fn size(&self) -> usize {
        if self.is_empty() {
            0
        } else {
            self.root.as_ref().unwrap().size()
        }
    }
}

fn floor<'a, K: Ord, V>(x: Option<&'a Box<Node<K,V>>>, key: &K) -> Option<&'a Node<K,V>> {
    if x.is_none() {
        return None;
    }

    match key.cmp(&x.unwrap().key) {
        Ordering::Equal => {
            return Some(&(**x.unwrap()));
        },
        Ordering::Less => {
            return floor(x.unwrap().left.as_ref(), key);
        },
        _ => (),
    }

    let t = floor(x.unwrap().right.as_ref(), key);
    if t.is_some() {
        return t
    } else {
        return Some(x.unwrap())
    }
}

impl<K: Ord, V> OrderedST<K, V> for BST<K, V> {
    /// smallest key
    fn min(&self) -> Option<&K> {
        unimplemented!()
    }

    /// largest key
    fn max(&self) -> Option<&K> {
        unimplemented!()
    }

    /// largest key less than or equal to key
    fn floor(&self, key: &K) -> Option<&K> {
        let x = floor(self.root.as_ref(), key);
        if x.is_none() {
            None
        } else {
            Some(&x.unwrap().key)
        }
    }

    /// smallest key greater than or equal to key
    fn ceiling(&self, key: &K) -> Option<&K> {
        unimplemented!()
    }

    /// number of keys less than key
    fn rank(&self, key: &K) -> usize {
        fn rank_helper<'a, K: Ord, V>(x: Option<&'a Box<Node<K,V>>>, key: &K) -> usize {
            if x.is_none() {
                return 0;
            }

            match key.cmp(&x.unwrap().key) {
                Ordering::Less => {
                    rank_helper(x.unwrap().left.as_ref(), key)
                },
                Ordering::Greater => {
                    1 + x.as_ref().unwrap().left.as_ref().map(|ref n| n.size()).unwrap_or(0) +
                        rank_helper(x.unwrap().right.as_ref(), key)
                }
                Ordering::Equal => {
                    x.as_ref().unwrap().left.as_ref().map(|ref n| n.size()).unwrap_or(0)
                }
            }
        }

        rank_helper(self.root.as_ref(), key)
    }

    /// key of rank k
    fn select(&self, k: usize) -> Option<&K> {
        unimplemented!()
    }

    /// delete smallest key
    fn delete_min(&mut self) {
        unimplemented!()
    }

    /// delete largest key
    fn delete_max(&mut self) {
        unimplemented!()
    }
}

impl<K: fmt::Debug, V: fmt::Debug> fmt::Debug for BST<K, V> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.root.is_none() {
            write!(f, "<empty tree>")
        } else {
            self.root.as_ref().unwrap().dump(0, f);
            Ok(())
        }
    }
}

#[test]
fn test_binary_search_tree() {
    let mut t = BST::<char, usize>::new();
    for (i, c) in "SEARCHEXAMP".chars().enumerate() {
        t.put(c, i);
    }

    println!("{:?}", t);
    assert_eq!(t.get(&'E'),  Some(&6));
    assert_eq!(t.floor(&'O'), Some(&'M'));
    assert_eq!(t.size(), 9);
    assert_eq!(t.rank(&'E'), 2);
    assert_eq!(t.rank(&'M'), 4);
}
<commit_msg>symbol_tables/binary_search_tree: impl keys() iter for BST, inorder traversal<commit_after>use std::iter;
use std::fmt;
use std::cmp::Ordering;
use super::{ST, OrderedST};
// FIXME: out implementation can't be used. :(
// use super::super::stacks_and_queues::Queue;
// use super::super::stacks_and_queues::resizing_array_queue::ResizingArrayQueue;

pub struct Node<K, V> {
    key: K,
    val: V,
    left:  Option<Box<Node<K, V>>>,
    right: Option<Box<Node<K, V>>>
}

impl<K, V> Node<K, V> {
    pub fn new(key: K, val: V) -> Node<K, V> {
        Node {
            key: key,
            val: val,
            left: None,
            right: None
        }
    }

    fn size(&self) -> usize {
        let mut ret = 1;
        if self.left.is_some() {
            ret += self.left.as_ref().unwrap().size()
        }
        if self.right.is_some() {
            ret += self.right.as_ref().unwrap().size()
        }
        ret
    }
}

impl<K: fmt::Debug, V: fmt::Debug> Node<K, V> {
    fn dump(&self, depth: usize, f: &mut fmt::Formatter) {
        if depth == 0 {
            writeln!(f, "{:?}[{:?}]", self.key, self.val).unwrap();
        } else {
            writeln!(f, "{}+--{:?}[{:?}]", iter::repeat("|  ").take(depth-1).collect::<Vec<&str>>().concat(),
                     self.key, self.val).unwrap();
        }
        if self.left.is_some() {
            self.left.as_ref().unwrap().dump(depth + 1, f);
        }
        if self.right.is_some() {
            self.right.as_ref().unwrap().dump(depth + 1, f);
        }
    }
}

fn put<K: Ord, V>(x: Option<Box<Node<K,V>>>, key: K, val: V) -> Option<Box<Node<K,V>>> {
    let mut x = x;
    if x.is_none() {
        return Some(Box::new(Node::new(key, val)));
    }
    let cmp = key.cmp(&x.as_ref().unwrap().key);
    match cmp {
        Ordering::Less => {
            let left = x.as_mut().unwrap().left.take();
            x.as_mut().unwrap().left = put(left, key, val)
        },
        Ordering::Greater => {
            let right = x.as_mut().unwrap().right.take();
            x.as_mut().unwrap().right = put(right, key, val)
        },
        Ordering::Equal => {
            x.as_mut().unwrap().val = val
        }
    }
    x
}

pub struct BST<K, V> {
    root: Option<Box<Node<K, V>>>
}

impl<K: Ord, V> ST<K, V> for BST<K, V> {
    fn new() -> BST<K, V> {
        BST { root: None }
    }

    fn get(&self, key: &K) -> Option<&V> {
        let mut x = self.root.as_ref();
        while x.is_some() {
            match key.cmp(&x.unwrap().key) {
                Ordering::Less => {
                    x = x.unwrap().left.as_ref();
                },
                Ordering::Greater => {
                    x = x.unwrap().right.as_ref();
                },
                Ordering::Equal  => {
                    return Some(&x.unwrap().val)
                }
            }
        }
        None
    }

    fn put(&mut self, key: K, val: V) {
        let root = self.root.take();
        self.root = put(root, key, val);
    }

    fn delete(&mut self, key: &K) {
        unimplemented!()
    }

    fn is_empty(&self) -> bool {
        self.root.is_none()
    }

    /// number of key-value pairs in the table
    fn size(&self) -> usize {
        if self.is_empty() {
            0
        } else {
            self.root.as_ref().unwrap().size()
        }
    }
}

fn floor<'a, K: Ord, V>(x: Option<&'a Box<Node<K,V>>>, key: &K) -> Option<&'a Node<K,V>> {
    if x.is_none() {
        return None;
    }

    match key.cmp(&x.unwrap().key) {
        Ordering::Equal => {
            return Some(&(**x.unwrap()));
        },
        Ordering::Less => {
            return floor(x.unwrap().left.as_ref(), key);
        },
        _ => (),
    }

    let t = floor(x.unwrap().right.as_ref(), key);
    if t.is_some() {
        return t
    } else {
        return Some(x.unwrap())
    }
}

impl<K: Ord, V> OrderedST<K, V> for BST<K, V> {
    /// smallest key
    fn min(&self) -> Option<&K> {
        unimplemented!()
    }

    /// largest key
    fn max(&self) -> Option<&K> {
        unimplemented!()
    }

    /// largest key less than or equal to key
    fn floor(&self, key: &K) -> Option<&K> {
        let x = floor(self.root.as_ref(), key);
        if x.is_none() {
            None
        } else {
            Some(&x.unwrap().key)
        }
    }

    /// smallest key greater than or equal to key
    fn ceiling(&self, key: &K) -> Option<&K> {
        unimplemented!()
    }

    /// number of keys less than key
    fn rank(&self, key: &K) -> usize {
        fn rank_helper<'a, K: Ord, V>(x: Option<&'a Box<Node<K,V>>>, key: &K) -> usize {
            if x.is_none() {
                return 0;
            }

            match key.cmp(&x.unwrap().key) {
                Ordering::Less => {
                    rank_helper(x.unwrap().left.as_ref(), key)
                },
                Ordering::Greater => {
                    1 + x.as_ref().unwrap().left.as_ref().map(|ref n| n.size()).unwrap_or(0) +
                        rank_helper(x.unwrap().right.as_ref(), key)
                }
                Ordering::Equal => {
                    x.as_ref().unwrap().left.as_ref().map(|ref n| n.size()).unwrap_or(0)
                }
            }
        }

        rank_helper(self.root.as_ref(), key)
    }

    /// key of rank k
    fn select(&self, k: usize) -> Option<&K> {
        unimplemented!()
    }

    /// delete smallest key
    fn delete_min(&mut self) {
        unimplemented!()
    }

    /// delete largest key
    fn delete_max(&mut self) {
        unimplemented!()
    }
}

impl<K: Ord, V> BST<K, V> {
    pub fn keys<'a>(&'a self) -> ::std::vec::IntoIter<&'a K> {
        let mut queue: Vec<&'a K> = Vec::new();
        fn inorder<'a, K, V>(x: Option<&'a Box<Node<K,V>>>, queue: &mut Vec<&'a K>) {
            if x.is_none() {
                return;
            }
            inorder(x.unwrap().left.as_ref(), queue);
            queue.push(&x.unwrap().key);
            inorder(x.unwrap().right.as_ref(), queue);
        };
        inorder(self.root.as_ref(), &mut queue);
        queue.into_iter()
    }
}

impl<K: fmt::Debug, V: fmt::Debug> fmt::Debug for BST<K, V> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.root.is_none() {
            write!(f, "<empty tree>")
        } else {
            self.root.as_ref().unwrap().dump(0, f);
            Ok(())
        }
    }
}

#[test]
fn test_binary_search_tree() {
    use std::iter::FromIterator;

    let mut t = BST::<char, usize>::new();
    for (i, c) in "SEARCHEXAMP".chars().enumerate() {
        t.put(c, i);
    }

    println!("{:?}", t);
    assert_eq!(t.get(&'E'),  Some(&6));
    assert_eq!(t.floor(&'O'), Some(&'M'));
    assert_eq!(t.size(), 9);
    assert_eq!(t.rank(&'E'), 2);
    assert_eq!(t.rank(&'M'), 4);
    // inorder visite
    assert_eq!(String::from_iter(t.keys().map(|&c| c)), "ACEHMPRSX");
}
<|endoftext|>


<commit_before>pub(crate) enum Slashes {
    Unix,
    Windows,
}

impl Slashes {
    pub fn here() -> Slashes {
        if cfg!(windows) {
            Slashes::Windows
        } else if cfg!(unix) {
            Slashes::Unix
        } else {
            panic!("Unknown operating system")
        }
    }

    fn slashes(&self) -> &'static [char] {
        match self {
            Slashes::Unix => &['/'],
            Slashes::Windows => &['/', '\\'],
        }
    }

    fn _is_slash(&self, c: char) -> bool {
        self.slashes().contains(&c)
    }

    pub fn norm_path(&self, path: &str) -> String {
        match self {
            Slashes::Unix => path.to_owned(),
            Slashes::Windows => path.replace('\\', "/"),
        }
    }

    fn remove_dot_slash<'a>(&self, path: &'a str) -> &'a str {
        if path == "." {
            ""
        } else if path.starts_with(".") {
            let mut temp_path = &path[1..];
            let mut at_least_one_slash = false;
            while temp_path.starts_with(self.slashes()) {
                temp_path = &temp_path[1..];
                at_least_one_slash = true;
            }
            if at_least_one_slash {
                temp_path
            } else {
                path
            }
        } else {
            path
        }
    }

    /// Remove leading ./ from path
    pub fn remove_dot_slashes<'a>(&self, mut path: &'a str) -> &'a str {
        loop {
            let new_path = self.remove_dot_slash(path);
            if new_path == path {
                return new_path;
            }
            path = new_path;
        }
    }
}
<commit_msg>Fix compilation for ancient rust<commit_after>pub(crate) enum Slashes {
    Unix,
    Windows,
}

impl Slashes {
    pub fn here() -> Slashes {
        if cfg!(windows) {
            Slashes::Windows
        } else if cfg!(unix) {
            Slashes::Unix
        } else {
            panic!("Unknown operating system")
        }
    }

    fn slashes(&self) -> &'static [char] {
        match self {
            &Slashes::Unix => &['/'],
            &Slashes::Windows => &['/', '\\'],
        }
    }

    fn _is_slash(&self, c: char) -> bool {
        self.slashes().contains(&c)
    }

    pub fn norm_path(&self, path: &str) -> String {
        match self {
            &Slashes::Unix => path.to_owned(),
            &Slashes::Windows => path.replace('\\', "/"),
        }
    }

    fn remove_dot_slash<'a>(&self, path: &'a str) -> &'a str {
        if path == "." {
            ""
        } else if path.starts_with(".") {
            let mut temp_path = &path[1..];
            let mut at_least_one_slash = false;
            while temp_path.starts_with(self.slashes()) {
                temp_path = &temp_path[1..];
                at_least_one_slash = true;
            }
            if at_least_one_slash {
                temp_path
            } else {
                path
            }
        } else {
            path
        }
    }

    /// Remove leading ./ from path
    pub fn remove_dot_slashes<'a>(&self, mut path: &'a str) -> &'a str {
        loop {
            let new_path = self.remove_dot_slash(path);
            if new_path == path {
                return new_path;
            }
            path = new_path;
        }
    }
}
<|endoftext|>


<commit_before>use types::*;
use types::flags::*;
use types::generation::*;
use types::parsing::*;

use cookie_factory::GenError;

pub const NAME:          &'static str   = "{{protocol.name}}";
pub const MAJOR_VERSION: ShortShortUInt = {{protocol.major_version}};
pub const MINOR_VERSION: ShortShortUInt = {{protocol.minor_version}};
pub const REVISION:      ShortShortUInt = {{protocol.revision}};
pub const PORT:          LongUInt       = {{protocol.port}};
pub const COPYRIGHT:     &'static str   = r#"{{copyright}}"#;

{{#each protocol.constants as |constant| ~}}
pub const {{sanitize_name constant.name}}: {{constant.type}} = {{constant.value}};
{{/each ~}}

pub enum AMQPError {
    Soft(AMQPSoftError),
    Hard(AMQPHardError),
}

impl AMQPError {
    pub fn get_id(&self) -> ShortUInt {
        match *self {
            AMQPError::Soft(ref s) => s.get_id(),
            AMQPError::Hard(ref h) => h.get_id(),
        }
    }

    pub fn from_id(id: ShortUInt) -> Option<AMQPError> {
        AMQPSoftError::from_id(id).map(|e| AMQPError::Soft(e)).or_else(|| AMQPHardError::from_id(id).map(|e| AMQPError::Hard(e)))
    }
}

pub enum AMQPSoftError {
    {{#each protocol.soft_errors as |constant| ~}}
    {{camel constant.name}},
    {{/each ~}}
}

impl AMQPSoftError {
    pub fn get_id(&self) -> ShortUInt {
        match *self {
            {{#each protocol.soft_errors as |constant| ~}}
            AMQPSoftError::{{camel constant.name}} => {{constant.value}},
            {{/each ~}}
        }
    }

    pub fn from_id(id: ShortUInt) -> Option<AMQPSoftError> {
        match id {
            {{#each protocol.soft_errors as |constant| ~}}
            {{constant.value}} => Some(AMQPSoftError::{{camel constant.name}}),
            {{/each ~}}
            _                  => None,
        }
    }
}

pub enum AMQPHardError {
    {{#each protocol.hard_errors as |constant| ~}}
    {{camel constant.name}},
    {{/each ~}}
}

impl AMQPHardError {
    pub fn get_id(&self) -> ShortUInt {
        match *self {
            {{#each protocol.hard_errors as |constant| ~}}
            AMQPHardError::{{camel constant.name}} => {{constant.value}},
            {{/each ~}}
        }
    }

    pub fn from_id(id: ShortUInt) -> Option<AMQPHardError> {
        match id {
            {{#each protocol.hard_errors as |constant| ~}}
            {{constant.value}} => Some(AMQPHardError::{{camel constant.name}}),
            {{/each ~}}
            _                  => None,
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum AMQPClass {
    {{#each protocol.classes as |class| ~}}
    {{camel class.name}}({{snake class.name}}::Method),
    {{/each ~}}
}

{{#each protocol.classes as |class|}}
pub mod {{snake class.name}} {
    use super::*;

    /* FIXME: simplify and get rid of the Option/Some when nom supports trailing | */
    named!(pub parse_{{snake class.name}}<{{snake class.name}}::Method>, map!(switch!(map!(parse_short_uint, Some),
        {{#each class.methods as |method| ~}}
        Some({{method.id}}) => map!(call!(parse_{{snake method.name}}), |m| Some(Method::{{camel method.name}}(m))) |
        {{/each ~}}
        None                => value!(None)
    ), |c: Option<{{snake class.name}}::Method>| c.expect("We can't get there as we mapped to Some, only there to get a parser after the trailing |")));

    pub fn gen_{{snake class.name}}<'a>(input:(&'a mut [u8], usize), method: &Method) -> Result<(&'a mut [u8], usize), GenError> {
        match *method {
            {{#each class.methods as |method| ~}}
            Method::{{camel method.name}}(ref {{snake method.name}}) => {
                do_gen!(input,
                    gen_short_uint(&{{class.id}}) >>
                    gen_{{snake method.name}}({{snake method.name}})
                )
            },
            {{/each ~}}
        }
    }

    #[derive(Clone, Debug, PartialEq)]
    pub enum Method {
        {{#each class.methods as |method| ~}}
        {{camel method.name}}({{camel method.name}}),
        {{/each ~}}
    }

    {{#each class.methods as |method|}}
    #[derive(Clone, Debug, PartialEq)]
    pub struct {{camel method.name}} {
        {{#each_argument method.arguments as |argument| ~}}
        {{#if argument_is_value ~}}
        pub {{snake argument.name}}: {{argument.type}},
        {{else}}
        {{#each_flag argument as |flag| ~}}
        pub {{snake flag.name}}: Boolean,
        {{/each_flag ~}}
        {{/if ~}}
        {{/each_argument ~}}
    }

    named!(pub parse_{{snake method.name}}<{{camel method.name}}>, do_parse!(
        {{#each_argument method.arguments as |argument| ~}}
        {{#if argument_is_value ~}}
        {{snake argument.name}}: parse_{{snake_type argument.type}} >>
        {{else}}
        /* FIXME: support multiple flags structs? */
        flags: apply!(parse_flags, &vec![
            {{#each_flag argument as |flag| ~}}
            "{{flag.name}}",
            {{/each_flag ~}}
        ]) >>
        {{/if ~}}
        {{/each_argument ~}}
        ({{camel method.name}} {
            {{#each_argument method.arguments as |argument| ~}}
            {{#if argument_is_value ~}}
            {{snake argument.name}}: {{snake argument.name}},
            {{else}}
            {{#each_flag argument as |flag| ~}}
            {{snake flag.name}}: flags.get_flag("{{snake flag.name}}").unwrap_or({{flag.default_value}}),
            {{/each_flag ~}}
            {{/if ~}}
            {{/each_argument ~}}
        })
    ));

    pub fn gen_{{snake method.name}}<'a>(input:(&'a mut [u8], usize), {{#if method.has_arguments ~}}method{{else}}_{{/if ~}}: &{{camel method.name}}) -> Result<(&'a mut [u8],usize), GenError> {
        {{#if method.has_flags ~}}
        /* FIXME: support multiple flags structs? */
        let mut flags = AMQPFlags::new();
        {{#each_argument method.arguments as |argument| ~}}
        {{#unless argument_is_value ~}}
        {{#each_flag argument as |flag| ~}}
        flags.add_flag("{{snake flag.name}}".to_string(), method.{{snake flag.name}});
        {{/each_flag ~}}
        {{/unless ~}}
        {{/each_argument ~}}
        {{/if ~}}
        do_gen!(input,
            gen_short_uint(&{{method.id}})
            {{#each_argument method.arguments as |argument| ~}}
            {{#if argument_is_value ~}}
            >> gen_{{snake_type argument.type}}(&method.{{snake argument.name}})
            {{else}}
            >> gen_flags(&flags)
            {{/if ~}}
            {{/each_argument ~}}
        )
    }
    {{/each ~}}
}
{{/each ~}}
<commit_msg>protocol: implement class generation<commit_after>use types::*;
use types::flags::*;
use types::generation::*;
use types::parsing::*;

use cookie_factory::GenError;

pub const NAME:          &'static str   = "{{protocol.name}}";
pub const MAJOR_VERSION: ShortShortUInt = {{protocol.major_version}};
pub const MINOR_VERSION: ShortShortUInt = {{protocol.minor_version}};
pub const REVISION:      ShortShortUInt = {{protocol.revision}};
pub const PORT:          LongUInt       = {{protocol.port}};
pub const COPYRIGHT:     &'static str   = r#"{{copyright}}"#;

{{#each protocol.constants as |constant| ~}}
pub const {{sanitize_name constant.name}}: {{constant.type}} = {{constant.value}};
{{/each ~}}

pub enum AMQPError {
    Soft(AMQPSoftError),
    Hard(AMQPHardError),
}

impl AMQPError {
    pub fn get_id(&self) -> ShortUInt {
        match *self {
            AMQPError::Soft(ref s) => s.get_id(),
            AMQPError::Hard(ref h) => h.get_id(),
        }
    }

    pub fn from_id(id: ShortUInt) -> Option<AMQPError> {
        AMQPSoftError::from_id(id).map(|e| AMQPError::Soft(e)).or_else(|| AMQPHardError::from_id(id).map(|e| AMQPError::Hard(e)))
    }
}

pub enum AMQPSoftError {
    {{#each protocol.soft_errors as |constant| ~}}
    {{camel constant.name}},
    {{/each ~}}
}

impl AMQPSoftError {
    pub fn get_id(&self) -> ShortUInt {
        match *self {
            {{#each protocol.soft_errors as |constant| ~}}
            AMQPSoftError::{{camel constant.name}} => {{constant.value}},
            {{/each ~}}
        }
    }

    pub fn from_id(id: ShortUInt) -> Option<AMQPSoftError> {
        match id {
            {{#each protocol.soft_errors as |constant| ~}}
            {{constant.value}} => Some(AMQPSoftError::{{camel constant.name}}),
            {{/each ~}}
            _                  => None,
        }
    }
}

pub enum AMQPHardError {
    {{#each protocol.hard_errors as |constant| ~}}
    {{camel constant.name}},
    {{/each ~}}
}

impl AMQPHardError {
    pub fn get_id(&self) -> ShortUInt {
        match *self {
            {{#each protocol.hard_errors as |constant| ~}}
            AMQPHardError::{{camel constant.name}} => {{constant.value}},
            {{/each ~}}
        }
    }

    pub fn from_id(id: ShortUInt) -> Option<AMQPHardError> {
        match id {
            {{#each protocol.hard_errors as |constant| ~}}
            {{constant.value}} => Some(AMQPHardError::{{camel constant.name}}),
            {{/each ~}}
            _                  => None,
        }
    }
}

pub fn gen_class<'a>(input: (&'a mut [u8], usize), class: &AMQPClass) -> Result<(&'a mut [u8], usize), GenError> {
    match *class {
        {{#each protocol.classes as |class| ~}}
        AMQPClass::{{camel class.name}}(ref {{snake class.name}}) => {{snake class.name}}::gen_{{snake class.name}}(input, {{snake class.name}}),
        {{/each ~}}
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum AMQPClass {
    {{#each protocol.classes as |class| ~}}
    {{camel class.name}}({{snake class.name}}::Method),
    {{/each ~}}
}

{{#each protocol.classes as |class|}}
pub mod {{snake class.name}} {
    use super::*;

    /* FIXME: simplify and get rid of the Option/Some when nom supports trailing | */
    named!(pub parse_{{snake class.name}}<{{snake class.name}}::Method>, map!(switch!(map!(parse_short_uint, Some),
        {{#each class.methods as |method| ~}}
        Some({{method.id}}) => map!(call!(parse_{{snake method.name}}), |m| Some(Method::{{camel method.name}}(m))) |
        {{/each ~}}
        None                => value!(None)
    ), |c: Option<{{snake class.name}}::Method>| c.expect("We can't get there as we mapped to Some, only there to get a parser after the trailing |")));

    pub fn gen_{{snake class.name}}<'a>(input: (&'a mut [u8], usize), method: &Method) -> Result<(&'a mut [u8], usize), GenError> {
        match *method {
            {{#each class.methods as |method| ~}}
            Method::{{camel method.name}}(ref {{snake method.name}}) => {
                do_gen!(input,
                    gen_short_uint(&{{class.id}}) >>
                    gen_{{snake method.name}}({{snake method.name}})
                )
            },
            {{/each ~}}
        }
    }

    #[derive(Clone, Debug, PartialEq)]
    pub enum Method {
        {{#each class.methods as |method| ~}}
        {{camel method.name}}({{camel method.name}}),
        {{/each ~}}
    }

    {{#each class.methods as |method|}}
    #[derive(Clone, Debug, PartialEq)]
    pub struct {{camel method.name}} {
        {{#each_argument method.arguments as |argument| ~}}
        {{#if argument_is_value ~}}
        pub {{snake argument.name}}: {{argument.type}},
        {{else}}
        {{#each_flag argument as |flag| ~}}
        pub {{snake flag.name}}: Boolean,
        {{/each_flag ~}}
        {{/if ~}}
        {{/each_argument ~}}
    }

    named!(pub parse_{{snake method.name}}<{{camel method.name}}>, do_parse!(
        {{#each_argument method.arguments as |argument| ~}}
        {{#if argument_is_value ~}}
        {{snake argument.name}}: parse_{{snake_type argument.type}} >>
        {{else}}
        /* FIXME: support multiple flags structs? */
        flags: apply!(parse_flags, &vec![
            {{#each_flag argument as |flag| ~}}
            "{{flag.name}}",
            {{/each_flag ~}}
        ]) >>
        {{/if ~}}
        {{/each_argument ~}}
        ({{camel method.name}} {
            {{#each_argument method.arguments as |argument| ~}}
            {{#if argument_is_value ~}}
            {{snake argument.name}}: {{snake argument.name}},
            {{else}}
            {{#each_flag argument as |flag| ~}}
            {{snake flag.name}}: flags.get_flag("{{snake flag.name}}").unwrap_or({{flag.default_value}}),
            {{/each_flag ~}}
            {{/if ~}}
            {{/each_argument ~}}
        })
    ));

    pub fn gen_{{snake method.name}}<'a>(input: (&'a mut [u8], usize), {{#if method.has_arguments ~}}method{{else}}_{{/if ~}}: &{{camel method.name}}) -> Result<(&'a mut [u8],usize), GenError> {
        {{#if method.has_flags ~}}
        /* FIXME: support multiple flags structs? */
        let mut flags = AMQPFlags::new();
        {{#each_argument method.arguments as |argument| ~}}
        {{#unless argument_is_value ~}}
        {{#each_flag argument as |flag| ~}}
        flags.add_flag("{{snake flag.name}}".to_string(), method.{{snake flag.name}});
        {{/each_flag ~}}
        {{/unless ~}}
        {{/each_argument ~}}
        {{/if ~}}
        do_gen!(input,
            gen_short_uint(&{{method.id}})
            {{#each_argument method.arguments as |argument| ~}}
            {{#if argument_is_value ~}}
            >> gen_{{snake_type argument.type}}(&method.{{snake argument.name}})
            {{else}}
            >> gen_flags(&flags)
            {{/if ~}}
            {{/each_argument ~}}
        )
    }
    {{/each ~}}
}
{{/each ~}}
<|endoftext|>


<commit_before>#[link(name = "word_count", vers = "1.0")];
#[crate_type = "lib"];

use std::hashmap::HashMap;

mod word_count;

fn check_word_count(s: &str, pairs: &[(&str, uint)]) {
    let mut m: HashMap<~str, uint> = word_count::word_count(s);
    for &(k, v) in pairs.iter() {
        assert_eq!((k, m.pop(&k.to_owned()).unwrap()), (k, v));
    }
    assert_eq!(m.iter().to_owned_vec(), ~[]);
}

#[test]
#[ignore]
fn test_no_words() {
    check_word_count("", []);
    check_word_count("-!@#$ $#() @@@", []);
}

#[test]
#[ignore]
fn test_count_one_word() {
    check_word_count("word", [("word", 1)]);
}

#[test]
#[ignore]
fn test_count_one_of_each() {
    check_word_count(
        "one of each",
        [("one", 1),
         ("of", 1),
         ("each", 1)]);
}

#[test]
#[ignore]
fn test_count_multiple_occurrences() {
    check_word_count(
        "one fish two fish red fish blue fish",
        [("one", 1),
         ("fish", 4),
         ("two", 1),
         ("red", 1),
         ("blue", 1)]);
}

#[test]
#[ignore]
fn test_ignore_punctuation() {
    check_word_count(
        "car : carpet as java : javascript!!&@$%^&",
        [("car", 1),
         ("carpet", 1),
         ("as", 1),
         ("java", 1),
         ("javascript", 1)]);
}

#[test]
#[ignore]
fn test_include_numbers() {
    check_word_count(
        "testing, 1, 2 testing",
        [("testing", 2),
         ("1", 1),
         ("2", 1)]);
}

#[test]
#[ignore]
fn test_normalize_case() {
    check_word_count(
        "go Go GO",
        [("go", 3)]);
}

#[test]
#[ignore]
fn test_prefix_punctuation() {
    check_word_count(
        "!%%#testing, 1, 2 testing",
        [("testing", 2),
         ("1", 1),
         ("2", 1)]);
}

#[test]
#[ignore]
fn test_symbols_are_separators() {
    check_word_count(
        "hey,my_spacebar_is_broken.",
        [("hey", 1),
         ("my", 1),
         ("spacebar", 1),
         ("is", 1),
         ("broken", 1)]);
}
<commit_msg>rust - add comments to explain why word-count_test looks the way it does<commit_after>#[link(name = "word_count", vers = "1.0")];
#[crate_type = "lib"];

use std::hashmap::HashMap;

mod word_count;

fn check_word_count(s: &str, pairs: &[(&str, uint)]) {
    // The reason for the awkward code in here is to ensure that the failure
    // message for assert_eq! is as informative as possible. A simpler
    // solution would simply check the length of the map, and then
    // check for the presence and value of each key in the given pairs vector.
    let mut m: HashMap<~str, uint> = word_count::word_count(s);
    for &(k, v) in pairs.iter() {
        assert_eq!((k, m.pop(&k.to_owned()).unwrap()), (k, v));
    }
    // may fail with a message that clearly shows all extra pairs in the map
    assert_eq!(m.iter().to_owned_vec(), ~[]);
}

#[test]
#[ignore]
fn test_no_words() {
    check_word_count("", []);
    check_word_count("-!@#$ $#() @@@", []);
}

#[test]
#[ignore]
fn test_count_one_word() {
    check_word_count("word", [("word", 1)]);
}

#[test]
#[ignore]
fn test_count_one_of_each() {
    check_word_count(
        "one of each",
        [("one", 1),
         ("of", 1),
         ("each", 1)]);
}

#[test]
#[ignore]
fn test_count_multiple_occurrences() {
    check_word_count(
        "one fish two fish red fish blue fish",
        [("one", 1),
         ("fish", 4),
         ("two", 1),
         ("red", 1),
         ("blue", 1)]);
}

#[test]
#[ignore]
fn test_ignore_punctuation() {
    check_word_count(
        "car : carpet as java : javascript!!&@$%^&",
        [("car", 1),
         ("carpet", 1),
         ("as", 1),
         ("java", 1),
         ("javascript", 1)]);
}

#[test]
#[ignore]
fn test_include_numbers() {
    check_word_count(
        "testing, 1, 2 testing",
        [("testing", 2),
         ("1", 1),
         ("2", 1)]);
}

#[test]
#[ignore]
fn test_normalize_case() {
    check_word_count(
        "go Go GO",
        [("go", 3)]);
}

#[test]
#[ignore]
fn test_prefix_punctuation() {
    check_word_count(
        "!%%#testing, 1, 2 testing",
        [("testing", 2),
         ("1", 1),
         ("2", 1)]);
}

#[test]
#[ignore]
fn test_symbols_are_separators() {
    check_word_count(
        "hey,my_spacebar_is_broken.",
        [("hey", 1),
         ("my", 1),
         ("spacebar", 1),
         ("is", 1),
         ("broken", 1)]);
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::{BufReader};
use std::path::PathBuf;

use cgmath::{Matrix4, Deg, Rad, Vector4, SquareMatrix, Vector3};
use soto::task::{task_log};
use soto::Error;
use sotolib_fbx::{RawFbx, id_name, friendly_name, ObjectTreeNode};
use sotolib_fbx::animation::{Animation};
use sotolib_fbx::simple::{SimpleFbx, ObjectType, ModelProperties};
use sotolib_smd::{Smd, SmdVertex, SmdTriangle, SmdAnimationFrameBone, SmdBone};

pub fn create_reference_smd(fbx: &PathBuf) -> Result<Smd, Error> {
    // Read in the fbx we got told to convert
    let file = BufReader::new(File::open(&fbx).unwrap());
    let fbx = SimpleFbx::from_raw(&RawFbx::parse(file).unwrap()).unwrap();
    let fbx_tree = ObjectTreeNode::from_simple(&fbx);

    // Go over all FBX root nodes and turn them into SMD data
    let mut smd = Smd::new();
    process_fbx_node(&fbx_tree, &Matrix4::identity(), Vector3::new(0.0, 0.0, 0.0), &mut smd, None)?;

    Ok(smd)
}

pub fn create_animation_smd(_ref_smd: &Smd, fbx: &PathBuf) -> Result<Smd, Error> {
    // Read in the fbx we got told to convert
    let file = BufReader::new(File::open(&fbx).unwrap());
    let mut fbx = SimpleFbx::from_raw(&RawFbx::parse(file).unwrap()).unwrap();

    // Read in the animation data itself
    let animation = Animation::from_simple(&fbx).unwrap();

    // Finally, turn the animation data into bone positions in the SMD
    let smd = Smd::new();
    for frame in 0..animation.frame_count(&fbx) {
        // First transform the FBX for this frame
        animation.transform_fbx_to_frame(&mut fbx, frame);

        // Now go over all models
        /*for model in fbx.models() {
            // For this model, look up the matching BoneId in the reference SMD
            if let Some(bone) = ref_smd.id_of_bone(&id_name(&model.name)) {
                // Now add an entry in the animation SMD for that bone at this frame
                // TODO
            }
        }*/
    }

    Ok(smd)
}

fn process_fbx_node(
    fbx_node: &ObjectTreeNode, matrix: &Matrix4<f32>, pivot: Vector3<f32>,
    mut smd: &mut Smd, current_bone: Option<&SmdBone>,
) -> Result<(), Error> {
    // Perform node type specific information
    match fbx_node.object.class {
        ObjectType::Geometry(ref geometry) => {
            // Add triangles to parent node
            let tris = geometry.triangles();
            for tri in tris {
                // Turn the vertices in this triangle to SMD vertices
                let mut smd_verts: [SmdVertex; 3] = Default::default();
                for (i, vert) in tri.iter().enumerate() {
                    // Multiply the vectors that need to be multiplied
                    let pos = matrix * Vector4::new(vert.0[0], vert.0[1], vert.0[2], 1.0);
                    let norm = matrix * Vector4::new(vert.1[0], vert.1[1], vert.1[2], 0.0);

                    smd_verts[i] = SmdVertex {
                        parent_bone: current_bone.unwrap().id, // This is overwritten by links
                        position: pos.truncate().into(),
                        normal: norm.truncate().into(),
                        uv: vert.2,
                        links: vec!(
                            /*Not needed, we aren't using weights anyways so this done by parent_bone
                            SmdLink {
                                bone: bone_id,
                                weight: 1.0,
                            }*/
                        )
                    };
                }

                // Add the actual SMD triangle
                smd.triangles.push(SmdTriangle {
                    material: "layl_test_texture".into(),
                    vertices: smd_verts,
                });
            }
        },
        ObjectType::Model(ref _model) => {
            task_log(format!("Adding model \"{}\" to SMD data", friendly_name(&fbx_node.object.name)));
            let properties = ModelProperties::from_generic(&fbx_node.object.properties);

            // Create a new transformation matrix
            let rot_pivot: Vector3<_> = properties.rotation_pivot.into();
            let rot_pivot_mat = Matrix4::from_translation(rot_pivot);

            let rotation =
                Matrix4::from_angle_z(Deg(properties.rotation[2])) *
                Matrix4::from_angle_y(Deg(properties.rotation[1])) *
                Matrix4::from_angle_x(Deg(properties.rotation[0]));

            let local_matrix_for_vertices =
                Matrix4::from_translation(properties.translation.into()) *
                rot_pivot_mat *
                rotation *
                rot_pivot_mat.invert().unwrap() *
                Matrix4::from_nonuniform_scale(
                    properties.scale[0],
                    properties.scale[1],
                    properties.scale[2]
                );

            // Create a new bone and set the transformations
            let new_bone = smd.new_bone(
                &id_name(&fbx_node.object.name).unwrap(),
                current_bone.map(|b| b.id)
            )
                .ok_or_else(|| Error::Task(format!(
                    "Bone \"{}\" exists multiple times in the FBX",
                    &fbx_node.object.name
                )))?
                .clone(); // Clone needed to avoid a borrow since we need to mut borrow SMD later
            let first_frame = SmdAnimationFrameBone {
                // This needs to be derived from the matrix to get the right location
                translation: (
                    Matrix4::from_translation(-pivot) *
                    local_matrix_for_vertices *
                    Vector4::new(rot_pivot.x, rot_pivot.y, rot_pivot.z, 1.0)
                ).truncate().into(),
                rotation: [
                    Rad::from(Deg(properties.rotation[0])).0,
                    Rad::from(Deg(properties.rotation[1])).0,
                    Rad::from(Deg(properties.rotation[2])).0,
                ],
            };
            smd.set_animation(0, new_bone.id, first_frame);

            // Make new matrices for children
            let matrix = matrix * local_matrix_for_vertices;
            let pivot = rot_pivot;

            // Make sure the child nodes will receive this new bone
            for node in &fbx_node.nodes {
                process_fbx_node(node, &matrix, pivot, smd, Some(&new_bone))?;
            }
        },
        _ => {
            // Just go straight to the children
            for node in &fbx_node.nodes {
                process_fbx_node(node, matrix, pivot, smd, current_bone)?;
            }
        }
    }

    Ok(())
}
<commit_msg>Minor refactoring<commit_after>use std::fs::File;
use std::io::{BufReader};
use std::path::PathBuf;

use cgmath::{Matrix4, Deg, Rad, Vector4, SquareMatrix, Vector3};
use soto::task::{task_log};
use soto::Error;
use sotolib_fbx::{RawFbx, id_name, friendly_name, ObjectTreeNode};
use sotolib_fbx::animation::{Animation};
use sotolib_fbx::simple::{SimpleFbx, ObjectType, ModelProperties, Geometry};
use sotolib_smd::{Smd, SmdVertex, SmdTriangle, SmdAnimationFrameBone, SmdBone};

pub fn create_reference_smd(fbx: &PathBuf) -> Result<Smd, Error> {
    // Read in the fbx we got told to convert
    let file = BufReader::new(File::open(&fbx).unwrap());
    let fbx = SimpleFbx::from_raw(&RawFbx::parse(file).unwrap()).unwrap();
    let fbx_tree = ObjectTreeNode::from_simple(&fbx);

    // Go over all FBX root nodes and turn them into SMD data
    let mut smd = Smd::new();
    process_fbx_node(&fbx_tree, &mut smd, &Matrix4::identity(), Vector3::new(0.0, 0.0, 0.0), None)?;

    Ok(smd)
}

pub fn create_animation_smd(_ref_smd: &Smd, fbx: &PathBuf) -> Result<Smd, Error> {
    // Read in the fbx we got told to convert
    let file = BufReader::new(File::open(&fbx).unwrap());
    let mut fbx = SimpleFbx::from_raw(&RawFbx::parse(file).unwrap()).unwrap();

    // Read in the animation data itself
    let animation = Animation::from_simple(&fbx).unwrap();

    // Finally, turn the animation data into bone positions in the SMD
    let smd = Smd::new();
    for frame in 0..animation.frame_count(&fbx) {
        // First transform the FBX for this frame
        animation.transform_fbx_to_frame(&mut fbx, frame);

        // Now go over all models
        /*for model in fbx.models() {
            // For this model, look up the matching BoneId in the reference SMD
            if let Some(bone) = ref_smd.id_of_bone(&id_name(&model.name)) {
                // Now add an entry in the animation SMD for that bone at this frame
                // TODO
            }
        }*/
    }

    Ok(smd)
}

fn process_fbx_node(
    fbx_node: &ObjectTreeNode, mut smd: &mut Smd,
    matrix: &Matrix4<f32>, pivot: Vector3<f32>,
    current_bone: Option<&SmdBone>,
) -> Result<(), Error> {
    // Perform node type specific information
    match fbx_node.object.class {
        ObjectType::Geometry(ref geometry) =>
            process_geometry(smd, geometry, matrix, current_bone.unwrap()),
        ObjectType::Model(ref _model) =>
            process_model(fbx_node, smd, matrix, pivot, current_bone)?,
        _ => {
            // Just go straight to the children
            for node in &fbx_node.nodes {
                process_fbx_node(node, smd, matrix, pivot, current_bone)?;
            }
        }
    }

    Ok(())
}

fn process_geometry(smd: &mut Smd, geometry: &Geometry, matrix: &Matrix4<f32>, current_bone: &SmdBone) {
    // Add triangles to parent node
    let tris = geometry.triangles();
    for tri in tris {
        // Turn the vertices in this triangle to SMD vertices
        let mut smd_verts: [SmdVertex; 3] = Default::default();
        for (i, vert) in tri.iter().enumerate() {
            // Multiply the vectors that need to be multiplied
            let pos = matrix * Vector4::new(vert.0[0], vert.0[1], vert.0[2], 1.0);
            let norm = matrix * Vector4::new(vert.1[0], vert.1[1], vert.1[2], 0.0);

            smd_verts[i] = SmdVertex {
                parent_bone: current_bone.id, // This is overwritten by links
                position: pos.truncate().into(),
                normal: norm.truncate().into(),
                uv: vert.2,
                links: vec!(
                    /*Not needed, we aren't using weights anyways so this done by parent_bone
                    SmdLink {
                        bone: bone_id,
                        weight: 1.0,
                    }*/
                )
            };
        }

        // Add the actual SMD triangle
        smd.triangles.push(SmdTriangle {
            material: "layl_test_texture".into(),
            vertices: smd_verts,
        });
    }
}

fn process_model(
    fbx_node: &ObjectTreeNode, smd: &mut Smd,
    matrix: &Matrix4<f32>, pivot: Vector3<f32>,
    current_bone: Option<&SmdBone>
) -> Result<(), Error> {
    task_log(format!("Adding model \"{}\" to SMD data", friendly_name(&fbx_node.object.name)));
    let properties = ModelProperties::from_generic(&fbx_node.object.properties);

    // Create a new transformation matrix
    let (vertices_matrix, rot_pivot) = local_matrices(&properties);

    // Create a new bone and set the transformations
    let new_bone = smd.new_bone(
        &id_name(&fbx_node.object.name).unwrap(),
        current_bone.map(|b| b.id)
    )
        .ok_or_else(|| Error::Task(format!(
            "Bone \"{}\" exists multiple times in the FBX",
            &fbx_node.object.name
        )))?
        .clone(); // Clone needed to avoid a borrow since we need to mut borrow SMD later
    let first_frame = SmdAnimationFrameBone {
        // This needs to be derived from the matrix to get the right location
        translation: (
            Matrix4::from_translation(-pivot) *
            vertices_matrix *
            Vector4::new(rot_pivot.x, rot_pivot.y, rot_pivot.z, 1.0)
        ).truncate().into(),
        // This can just be directly copied over
        rotation: [
            Rad::from(Deg(properties.rotation[0])).0,
            Rad::from(Deg(properties.rotation[1])).0,
            Rad::from(Deg(properties.rotation[2])).0,
        ],
    };
    smd.set_animation(0, new_bone.id, first_frame);

    // Make new matrices for children
    let matrix = matrix * vertices_matrix;
    let pivot = rot_pivot;

    // Make sure the child nodes will receive this new bone
    for node in &fbx_node.nodes {
        process_fbx_node(node, smd, &matrix, pivot, Some(&new_bone))?;
    }

    Ok(())
}

/// Returns (vertices_matrix, rot_pivot)
fn local_matrices(properties: &ModelProperties) -> (Matrix4<f32>, Vector3<f32>) {
    let rot_pivot: Vector3<_> = properties.rotation_pivot.into();
    let rot_pivot_mat = Matrix4::from_translation(rot_pivot);

    let rotation =
        Matrix4::from_angle_z(Deg(properties.rotation[2])) *
        Matrix4::from_angle_y(Deg(properties.rotation[1])) *
        Matrix4::from_angle_x(Deg(properties.rotation[0]));

    let local_matrix_for_vertices =
        Matrix4::from_translation(properties.translation.into()) *
        rot_pivot_mat *
        rotation *
        rot_pivot_mat.invert().unwrap() *
        Matrix4::from_nonuniform_scale(
            properties.scale[0],
            properties.scale[1],
            properties.scale[2]
        );

    (local_matrix_for_vertices, rot_pivot)
}
<|endoftext|>


<commit_before>//! Builder for the shell-style stock tokeniser.
#![experimental]

use builders::types::{
    StockEscapeMap,
    StockQuoteMap,
    StockTokeniser
};
use escape_scheme::LiteralEscape;
use tokeniser::{ Tokeniser, IgnoreEscapes, ParseEscapes };

/// Creates a Tokeniser that provides shell-style quoting.
///
/// This recognises pairs of " and ' as delineating words, and parses
/// anything following a \ as its literal value.  Anything in single quotes
/// is returned verbatim.
///
/// # Return value
///
/// A Tokeniser with shell-style quoting.
///
/// # Example
///
/// ```rust
/// use russet::shell_style_tokeniser;
///
/// let tok = shell_style_tokeniser();
/// let tok2 = tok.add_line("word1 word\\ 2 \"word\\ 3\" 'word\\ \"4\"'");
/// assert_eq!(tok2.into_strings(), Ok(vec!("word1".into_string(),
///                                         "word 2".into_string(),
///                                         "word 3".into_string(),
///                                         "word\\ \"4\"".into_string())));
/// ```
#[experimental]
pub fn shell_style_tokeniser() -> StockTokeniser {
    let quote_map: StockQuoteMap =
        vec![ ( '\"', ( '\"', ParseEscapes ) ),
              ( '\'', ( '\'', IgnoreEscapes ) ) ].move_iter().collect();
    let escape_map: StockEscapeMap =
        vec![ ( '\\', LiteralEscape ) ].move_iter().collect();
    Tokeniser::new(quote_map, escape_map)
}
<commit_msg>Add shell-style tokeniser tests.<commit_after>//! Builder for the shell-style stock tokeniser.
#![experimental]

use builders::types::{
    StockEscapeMap,
    StockQuoteMap,
    StockTokeniser
};
use escape_scheme::LiteralEscape;
use tokeniser::{ Tokeniser, IgnoreEscapes, ParseEscapes };

/// Creates a Tokeniser that provides shell-style quoting.
///
/// This recognises pairs of " and ' as delineating words, and parses
/// anything following a \ as its literal value.  Anything in single quotes
/// is returned verbatim.
///
/// # Return value
///
/// A Tokeniser with shell-style quoting.
///
/// # Example
///
/// ```rust
/// use russet::shell_style_tokeniser;
///
/// let tok = shell_style_tokeniser();
/// let tok2 = tok.add_line("word1 word\\ 2 \"word\\ 3\" 'word\\ \"4\"'");
/// assert_eq!(tok2.into_strings(), Ok(vec!("word1".into_string(),
///                                         "word 2".into_string(),
///                                         "word 3".into_string(),
///                                         "word\\ \"4\"".into_string())));
/// ```
#[experimental]
pub fn shell_style_tokeniser() -> StockTokeniser {
    let quote_map: StockQuoteMap =
        vec![ ( '\"', ( '\"', ParseEscapes ) ),
              ( '\'', ( '\'', IgnoreEscapes ) ) ].move_iter().collect();
    let escape_map: StockEscapeMap =
        vec![ ( '\\', LiteralEscape ) ].move_iter().collect();
    Tokeniser::new(quote_map, escape_map)
}

#[cfg(test)]
mod test {
    use super::shell_style_tokeniser;
    use line::LineTokeniser;
    use tokeniser::{ UnmatchedQuote, UnfinishedEscape };

    #[test]
    fn shell_style_unmatched_single_quote() {
        assert_eq!(shell_style_tokeniser.line("\'abcde"),
                   Err(UnmatchedQuote));
    }

    #[test]
    fn shell_style_unmatched_double_quote() {
        assert_eq!(shell_style_tokeniser.line("\"abcde"),
                   Err(UnmatchedQuote));
    }

    #[test]
    fn shell_style_unfinished_escape() {
        assert_eq!(shell_style_tokeniser.line("zxcvbn m\\"),
                   Err(UnfinishedEscape));
    }

    #[test]
    fn shell_style_empty_string() {
        assert_eq!(shell_style_tokeniser.line(""), Ok(vec![]));
    }

    #[test]
    fn shell_style_leading_whitespace() {
        let rhs = vec![ "abc".into_string(), "def".into_string() ];
        assert_eq!(shell_style_tokeniser.line("     abc def"), Ok(rhs));
    }

    #[test]
    fn shell_style_trailing_whitespace() {
        let rhs = vec![ "ghi".into_string(), "jkl".into_string() ];
        assert_eq!(shell_style_tokeniser.line("ghi jkl     \n"), Ok(rhs));
    }

    #[test]
    fn shell_style_enqueue_command_double_quotes() {
        let lhs =
            "enqueue file \"C:\\\\Users\\\\Test\\\\Artist - Title.mp3\" 1";
        let rhs = vec![ "enqueue".into_string(),
                        "file".into_string(),
                        "C:\\Users\\Test\\Artist - Title.mp3".into_string(),
                        "1".into_string() ];
        assert_eq!(shell_style_tokeniser.line(lhs), Ok(rhs));
    }

    #[test]
    fn shell_style_enqueue_command_single_quotes() {
        let lhs =
            "enqueue file \'C:\\Users\\Test\\Artist - Title.mp3\' 1";
        let rhs = vec![ "enqueue".into_string(),
                        "file".into_string(),
                        "C:\\Users\\Test\\Artist - Title.mp3".into_string(),
                        "1".into_string() ];
        assert_eq!(shell_style_tokeniser.line(lhs), Ok(rhs));
    }

    #[test]
    fn shell_style_escaped_newline() {
        // Unquoted backslash-escape
        assert_eq!(shell_style_tokeniser.line("abc\\\nde"),
                   Ok(vec![ "abc\nde".into_string() ]));
        // Double-quoted backslash-escape
        assert_eq!(shell_style_tokeniser.line("\"abc\\\nde\""),
                   Ok(vec![ "abc\nde".into_string() ]));
        // Double-quoted implicit-escape
        assert_eq!(shell_style_tokeniser.line("\"abc\nde\""),
                   Ok(vec![ "abc\nde".into_string() ]));
        // Single-quoted implicit-escape
        assert_eq!(shell_style_tokeniser.line("\'abc\nde\'"),
                   Ok(vec![ "abc\nde".into_string() ]));
        // But single-quoted backslash-escape shouldn't work
        assert_eq!(shell_style_tokeniser.line("\'abc\\\nde\'"),
                   Ok(vec![ "abc\\\nde".into_string() ]));
    }
}
<|endoftext|>


<commit_before>use std::io::Write;

use inflector::Inflector;

use botocore::{Operation, Shape, ShapeType};
use ::Service;
use super::{GenerateProtocol, error_type_name, FileWriter, IoResult, rest_response_parser,
            rest_request_generator, generate_field_name};

pub struct RestJsonGenerator;

impl GenerateProtocol for RestJsonGenerator {
    fn generate_method_signatures(&self, writer: &mut FileWriter, service: &Service) -> IoResult {
        for (operation_name, operation) in service.operations().iter() {
            let input_type = operation.input_shape();
            let output_type = operation.output_shape_or("()");

            // Retrieve the `Shape` for the input for this operation.
            let input_shape = &service.get_shape(input_type).unwrap();

            writeln!(writer,
                     "
                {documentation}
                {method_signature} -> \
                      RusotoFuture<{output_type}, {error_type}>;
                ",
                     documentation = generate_documentation(operation).unwrap_or_else(|| "".to_owned()),
                     method_signature = generate_method_signature(operation, input_shape),
                     error_type = error_type_name(service, operation_name),
                     output_type = output_type)?
        }
        Ok(())
    }

    fn generate_method_impls(&self, writer: &mut FileWriter, service: &Service) -> IoResult {
        for (operation_name, operation) in service.operations().iter() {
            let input_type = operation.input_shape();
            let output_type = operation.output_shape_or("()");

            // Retrieve the `Shape` for the input for this operation.
            let input_shape = &service.get_shape(input_type).unwrap();

            let (request_uri, _) = rest_request_generator::parse_query_string(&operation.http
                .request_uri);

            writeln!(writer,"
                {documentation}
                {method_signature} -> RusotoFuture<{output_type}, {error_type}> {{
                    {request_uri_formatter}

                    let mut request = SignedRequest::new(\"{http_method}\", \"{endpoint_prefix}\", &self.region, &request_uri);
                    {default_headers}
                    {set_headers}
                    {modify_endpoint_prefix}
                    {load_payload}
                    {load_headers}
                    {load_params}

                    self.client.sign_and_dispatch(request, |response| {{
                        if {status_check} {{
                            Box::new(response.buffer().from_err().map(|response| {{
                                {parse_body}
                                {parse_headers}
                                {parse_status_code}
                                result
                            }}))
                        }} else {{
                            Box::new(response.buffer().from_err().and_then(|response| {{
                                Err({error_type}::from_response(response))
                            }}))
                        }}
                    }})
                }}
                ",
                documentation = generate_documentation(operation).unwrap_or_else(|| "".to_owned()),
                method_signature = generate_method_signature(operation, input_shape),
                endpoint_prefix = service.signing_name(),
                modify_endpoint_prefix = generate_endpoint_modification(service).unwrap_or_else(|| "".to_owned()),
                http_method = operation.http.method,
                error_type = error_type_name(service, operation_name),
                status_check = http_code_expected(operation.http.response_code),
                parse_body = generate_body_parser(operation, service),
                parse_status_code = generate_status_code_parser(operation, service),
                output_type = output_type,
                load_headers = rest_request_generator::generate_headers(service, operation).unwrap_or_else(|| "".to_string()),
                parse_headers = rest_response_parser::generate_response_headers_parser(service, operation)
                    .unwrap_or_else(|| "".to_owned()),
                request_uri_formatter = rest_request_generator::generate_uri_formatter(
                    &request_uri,
                    service,
                    operation
                ).unwrap_or_else(|| "".to_string()),
                load_payload = generate_payload(service, input_shape).unwrap_or_else(|| "".to_string()),
                load_params = rest_request_generator::generate_params_loading_string(service, operation).unwrap_or_else(|| "".to_string()),
                default_headers = generate_default_headers(service).unwrap_or_else(|| "".to_string()),
                set_headers = generate_headers(service).unwrap_or_else(|| "".to_string()),
            )?
        }
        Ok(())
    }

    fn generate_prelude(&self, writer: &mut FileWriter, service: &Service) -> IoResult {
        writeln!(writer, "use serde_json;")?;

        // avoid unused imports when building services that don't use params
        if service_has_query_parameters(service) {
            writeln!(writer, "use rusoto_core::param::{{Params, ServiceParams}};")?;
        }

        writeln!(writer,
                 "use rusoto_core::signature::SignedRequest;
                  use serde_json::from_slice;
                  use serde_json::Value as SerdeJsonValue;")

    }

    fn serialize_trait(&self) -> Option<&'static str> {
        Some("Serialize")
    }

    fn deserialize_trait(&self) -> Option<&'static str> {
        Some("Deserialize")
    }

    fn timestamp_type(&self) -> &'static str {
        "f64"
    }
}

fn http_code_expected(code: Option<i32>) -> String {
    match code {
        Some(actual_code) => format!("response.status.as_u16() == {}", actual_code),
        // Some service definitions such as elastictranscoder don't specify
        // the response code, we'll assume 2xx is okay:
        None => "response.status.is_success()".to_string(),
    }
}

// Glacier needs a special header added, not included in botocore definition.  See
// http://docs.aws.amazon.com/amazonglacier/latest/dev/api-common-request-headers.html
fn generate_headers(service: &Service) -> Option<String> {
    if service.full_name() == "Amazon Glacier" {
        return Some("request.add_header(\"x-amz-glacier-version\", \"2012-06-01\");".to_string());
    }
    None
}

// SageMaker Runtime allows to overwrite content-type
fn generate_default_headers(service: &Service) -> Option<String> {
    if service.full_name() == "Amazon SageMaker Runtime" {
        return Some("if input.content_type.is_none() {
                         request.set_content_type(\"application/x-amz-json-1.1\".to_owned());
                     }".to_string());
    }
    Some("request.set_content_type(\"application/x-amz-json-1.1\".to_owned());".to_string())
}

// IoT has an endpoint_prefix and a signing_name that differ
fn generate_endpoint_modification(service: &Service) -> Option<String> {
    if service.signing_name() == service.endpoint_prefix() {
        None
    } else {
        Some(format!("request.set_endpoint_prefix(\"{}\".to_string());",
                     service.endpoint_prefix()))
    }
}

// IoT defines a lot of empty (and therefore unnecessary) request shapes
// don't clutter method signatures with them
fn generate_method_signature(operation: &Operation, shape: &Shape) -> String {
    if shape.members.is_some() && !shape.members.as_ref().unwrap().is_empty() {
        format!("fn {method_name}(&self, input: {input_type})",
                method_name = operation.name.to_snake_case(),
                input_type = operation.input_shape())
    } else {
        format!("fn {method_name}(&self)",
                method_name = operation.name.to_snake_case())
    }
}

// Figure out what, if anything, should be sent as the body of the http request
fn generate_payload(service: &Service, input_shape: &Shape) -> Option<String> {
    let declare_payload = match input_shape.payload {

        // if the input shape explicitly specifies a payload field, use that
        Some(ref payload_member_name) => {
            Some(declared_payload(input_shape, payload_member_name, service))
        }

        // otherwise see if the input_shape itself should be serialized as the payload
        None => {
            // only use the input_shape if it has non-query, non-header members
            // (i.e., location unspecified)
            if input_shape.members
                .as_ref()
                .unwrap()
                .iter()
                .any(|(_, member)| member.location.is_none()) {
                Some("let encoded = Some(serde_json::to_vec(&input).unwrap());".to_owned())
            } else {
                None
            }
        }
    };

    if declare_payload.is_some() {
        Some(declare_payload.unwrap() + "request.set_payload(encoded);")
    } else {
        None
    }
}

fn declared_payload(input_shape: &Shape, payload_member_name: &str, service: &Service) -> String {
    let payload_member_shape = &input_shape.members.as_ref().unwrap()[payload_member_name].shape;
    let payload_shape = &service.get_shape(payload_member_shape)
        .expect("Shape missing from service definition");

    let field_name = generate_field_name(payload_member_name);

    match payload_shape.shape_type {
        // if it's a String or a Blob, send it as the raw payload
        payload_type if payload_type == ShapeType::Blob || payload_type == ShapeType::String => {
            if input_shape.required(payload_member_name) {
                format!("let encoded = Some(input.{}.to_owned());", field_name)
            } else {
                format!("let encoded = if let Some(ref payload) = input.{} {{
                            Some(payload.to_owned())
                        }} else {{
                            None
                        }};",
                        field_name)
            }
        }

        // otherwise serialize the payload member as json and use that
        _ => {
            format!("let encoded = Some(serde_json::to_vec(&input.{}).unwrap());",
                    field_name)
        }
    }
}

// Do any input operations in the entire service use query parameters?
fn service_has_query_parameters(service: &Service) -> bool {
    service.operations()
        .iter()
        .map(|(_, operation)| operation.input_shape())
        .map(|input_type| service.get_shape(input_type).unwrap())
        .any(|input_shape| input_shape.has_query_parameters())
}

fn generate_documentation(operation: &Operation) -> Option<String> {
    operation.documentation
        .as_ref()
        .map(|docs| ::doco::Item(docs).to_string())
}

/// Generate code to plumb the response status code into any fields
/// in the output shape that specify it
fn generate_status_code_parser(operation: &Operation, service: &Service) -> String {
    if operation.output.is_none() {
        return "".to_owned();
    }

    let shape_name = &operation.output.as_ref().unwrap().shape;
    let output_shape = &service.get_shape(shape_name)
        .expect("Shape missing from service definition");

    let mut status_code_parser = "".to_string();

    for (member_name, member) in output_shape.members.as_ref().unwrap() {
        if let Some(ref location) = member.location {
            if location == "statusCode" {
                if output_shape.required(member_name) {
                    status_code_parser += &format!("result.{} = Some(response.status.as_u16());",
                                                   member_name.to_snake_case());
                } else {
                    status_code_parser += &format!("result.{} = Some(response.status.as_u16() as i64);",
                                                   member_name.to_snake_case());
                }
            }
        }
    }

    status_code_parser
}

/// Generate code to parse the http response body, either as a JSON object
/// deserialized with serde, or as a raw payload that's assigned to one of
/// the fields in the result object.
///
/// Needs to determine whether or not other fields in the result object
/// will be set later (e.g. from headers), so the compiler won't spit out
/// warnings about unnecessary mutability
fn generate_body_parser(operation: &Operation, service: &Service) -> String {

    if operation.output.is_none() {
        return "let result = ::std::mem::drop(response);".to_string();
    }

    let shape_name = &operation.output.as_ref().unwrap().shape;
    let output_shape = &service.get_shape(shape_name)
        .expect("Shape missing from service definition");

    let mutable_result = output_shape.members
        .as_ref()
        .unwrap()
        .iter()
        .any(|(_, member)| member.location.is_some()) || output_shape.payload.is_some();

    match output_shape.payload {
        None => json_body_parser(shape_name, mutable_result),
        Some(ref payload_member_name) => {
            let payload_member_shape = &output_shape.members.as_ref().unwrap()[payload_member_name]
                .shape;
            let payload_shape = &service.get_shape(payload_member_shape)
                .expect("Shape missing from service definition");
            // is the shape required?
            let payload_shape_required = match output_shape.required {
                Some(ref s) => {
                    // if there's any required shape present the body payload parser will handle it
                    s.len() > 0
                },
                None => false,
            };
            match payload_shape.shape_type {
                payload_type if payload_type == ShapeType::Blob ||
                                payload_type == ShapeType::String => {
                    payload_body_parser(payload_type,
                                        shape_name,
                                        payload_member_name,
                                        mutable_result,
                                        payload_shape_required)
                }
                _ => json_body_parser(shape_name, false),
            }
        }
    }
}

/// Take the raw http response body and assign it to the payload field
/// on the result object
fn payload_body_parser(payload_type: ShapeType,
                       output_shape: &str,
                       payload_member: &str,
                       mutable_result: bool,
                       payload_required: bool)
                       -> String {

    let response_body = match payload_required {
        true => {
            match payload_type {
                ShapeType::Blob => "response.body",
                _ => "String::from_utf8_lossy(response.body.as_ref())",
            }
        },
        false => {
            match payload_type {
                ShapeType::Blob => "Some(response.body)",
                _ => "Some(String::from_utf8_lossy(response.body.as_ref()).into_owned())",
            }
        }
    };

    format!("
        let {mutable} result = {output_shape}::default();
        result.{payload_member} = {response_body};
        ",
            output_shape = output_shape,
            payload_member = payload_member.to_snake_case(),
            response_body = response_body,
            mutable = if mutable_result { "mut" } else { "" })
}

/// Parse the http response body as a JSON object with serde, and use that
/// as the result object
fn json_body_parser(output_shape: &str, mutable_result: bool) -> String {
    // `serde-json` serializes field-less structs as "null", but AWS returns
    // "{{}}" for a field-less response, so we must check for this result
    // and convert it if necessary.
    format!("
            let mut body = response.body;

            if body == b\"null\" || body.is_empty() {{
                body = b\"{{}}\".to_vec();
            }}

            debug!(\"Response body: {{:?}}\", body);
            debug!(\"Response status: {{}}\", response.status);
            let {mutable} result = serde_json::from_slice::<{output_shape}>(&body).unwrap();
            ",
            output_shape = output_shape,
            mutable = if mutable_result { "mut" } else { "" })
}
<commit_msg>Unwrap redundant Option<commit_after>use std::io::Write;

use inflector::Inflector;

use botocore::{Operation, Shape, ShapeType};
use ::Service;
use super::{GenerateProtocol, error_type_name, FileWriter, IoResult, rest_response_parser,
            rest_request_generator, generate_field_name};

pub struct RestJsonGenerator;

impl GenerateProtocol for RestJsonGenerator {
    fn generate_method_signatures(&self, writer: &mut FileWriter, service: &Service) -> IoResult {
        for (operation_name, operation) in service.operations().iter() {
            let input_type = operation.input_shape();
            let output_type = operation.output_shape_or("()");

            // Retrieve the `Shape` for the input for this operation.
            let input_shape = &service.get_shape(input_type).unwrap();

            writeln!(writer,
                     "
                {documentation}
                {method_signature} -> \
                      RusotoFuture<{output_type}, {error_type}>;
                ",
                     documentation = generate_documentation(operation).unwrap_or_else(|| "".to_owned()),
                     method_signature = generate_method_signature(operation, input_shape),
                     error_type = error_type_name(service, operation_name),
                     output_type = output_type)?
        }
        Ok(())
    }

    fn generate_method_impls(&self, writer: &mut FileWriter, service: &Service) -> IoResult {
        for (operation_name, operation) in service.operations().iter() {
            let input_type = operation.input_shape();
            let output_type = operation.output_shape_or("()");

            // Retrieve the `Shape` for the input for this operation.
            let input_shape = &service.get_shape(input_type).unwrap();

            let (request_uri, _) = rest_request_generator::parse_query_string(&operation.http
                .request_uri);

            writeln!(writer,"
                {documentation}
                {method_signature} -> RusotoFuture<{output_type}, {error_type}> {{
                    {request_uri_formatter}

                    let mut request = SignedRequest::new(\"{http_method}\", \"{endpoint_prefix}\", &self.region, &request_uri);
                    {default_headers}
                    {set_headers}
                    {modify_endpoint_prefix}
                    {load_payload}
                    {load_headers}
                    {load_params}

                    self.client.sign_and_dispatch(request, |response| {{
                        if {status_check} {{
                            Box::new(response.buffer().from_err().map(|response| {{
                                {parse_body}
                                {parse_headers}
                                {parse_status_code}
                                result
                            }}))
                        }} else {{
                            Box::new(response.buffer().from_err().and_then(|response| {{
                                Err({error_type}::from_response(response))
                            }}))
                        }}
                    }})
                }}
                ",
                documentation = generate_documentation(operation).unwrap_or_else(|| "".to_owned()),
                method_signature = generate_method_signature(operation, input_shape),
                endpoint_prefix = service.signing_name(),
                modify_endpoint_prefix = generate_endpoint_modification(service).unwrap_or_else(|| "".to_owned()),
                http_method = operation.http.method,
                error_type = error_type_name(service, operation_name),
                status_check = http_code_expected(operation.http.response_code),
                parse_body = generate_body_parser(operation, service),
                parse_status_code = generate_status_code_parser(operation, service),
                output_type = output_type,
                load_headers = rest_request_generator::generate_headers(service, operation).unwrap_or_else(|| "".to_string()),
                parse_headers = rest_response_parser::generate_response_headers_parser(service, operation)
                    .unwrap_or_else(|| "".to_owned()),
                request_uri_formatter = rest_request_generator::generate_uri_formatter(
                    &request_uri,
                    service,
                    operation
                ).unwrap_or_else(|| "".to_string()),
                load_payload = generate_payload(service, input_shape).unwrap_or_else(|| "".to_string()),
                load_params = rest_request_generator::generate_params_loading_string(service, operation).unwrap_or_else(|| "".to_string()),
                default_headers = generate_default_headers(service),
                set_headers = generate_headers(service).unwrap_or_else(|| "".to_string()),
            )?
        }
        Ok(())
    }

    fn generate_prelude(&self, writer: &mut FileWriter, service: &Service) -> IoResult {
        writeln!(writer, "use serde_json;")?;

        // avoid unused imports when building services that don't use params
        if service_has_query_parameters(service) {
            writeln!(writer, "use rusoto_core::param::{{Params, ServiceParams}};")?;
        }

        writeln!(writer,
                 "use rusoto_core::signature::SignedRequest;
                  use serde_json::from_slice;
                  use serde_json::Value as SerdeJsonValue;")

    }

    fn serialize_trait(&self) -> Option<&'static str> {
        Some("Serialize")
    }

    fn deserialize_trait(&self) -> Option<&'static str> {
        Some("Deserialize")
    }

    fn timestamp_type(&self) -> &'static str {
        "f64"
    }
}

fn http_code_expected(code: Option<i32>) -> String {
    match code {
        Some(actual_code) => format!("response.status.as_u16() == {}", actual_code),
        // Some service definitions such as elastictranscoder don't specify
        // the response code, we'll assume 2xx is okay:
        None => "response.status.is_success()".to_string(),
    }
}

// Glacier needs a special header added, not included in botocore definition.  See
// http://docs.aws.amazon.com/amazonglacier/latest/dev/api-common-request-headers.html
fn generate_headers(service: &Service) -> Option<String> {
    if service.full_name() == "Amazon Glacier" {
        return Some("request.add_header(\"x-amz-glacier-version\", \"2012-06-01\");".to_string());
    }
    None
}

// SageMaker Runtime allows to overwrite content-type
fn generate_default_headers(service: &Service) -> String {
    if service.full_name() == "Amazon SageMaker Runtime" {
        return "if input.content_type.is_none() {
                    request.set_content_type(\"application/x-amz-json-1.1\".to_owned());
                }"
                .to_string();
    }
    "request.set_content_type(\"application/x-amz-json-1.1\".to_owned());".to_string()
}

// IoT has an endpoint_prefix and a signing_name that differ
fn generate_endpoint_modification(service: &Service) -> Option<String> {
    if service.signing_name() == service.endpoint_prefix() {
        None
    } else {
        Some(format!("request.set_endpoint_prefix(\"{}\".to_string());",
                     service.endpoint_prefix()))
    }
}

// IoT defines a lot of empty (and therefore unnecessary) request shapes
// don't clutter method signatures with them
fn generate_method_signature(operation: &Operation, shape: &Shape) -> String {
    if shape.members.is_some() && !shape.members.as_ref().unwrap().is_empty() {
        format!("fn {method_name}(&self, input: {input_type})",
                method_name = operation.name.to_snake_case(),
                input_type = operation.input_shape())
    } else {
        format!("fn {method_name}(&self)",
                method_name = operation.name.to_snake_case())
    }
}

// Figure out what, if anything, should be sent as the body of the http request
fn generate_payload(service: &Service, input_shape: &Shape) -> Option<String> {
    let declare_payload = match input_shape.payload {

        // if the input shape explicitly specifies a payload field, use that
        Some(ref payload_member_name) => {
            Some(declared_payload(input_shape, payload_member_name, service))
        }

        // otherwise see if the input_shape itself should be serialized as the payload
        None => {
            // only use the input_shape if it has non-query, non-header members
            // (i.e., location unspecified)
            if input_shape.members
                .as_ref()
                .unwrap()
                .iter()
                .any(|(_, member)| member.location.is_none()) {
                Some("let encoded = Some(serde_json::to_vec(&input).unwrap());".to_owned())
            } else {
                None
            }
        }
    };

    if declare_payload.is_some() {
        Some(declare_payload.unwrap() + "request.set_payload(encoded);")
    } else {
        None
    }
}

fn declared_payload(input_shape: &Shape, payload_member_name: &str, service: &Service) -> String {
    let payload_member_shape = &input_shape.members.as_ref().unwrap()[payload_member_name].shape;
    let payload_shape = &service.get_shape(payload_member_shape)
        .expect("Shape missing from service definition");

    let field_name = generate_field_name(payload_member_name);

    match payload_shape.shape_type {
        // if it's a String or a Blob, send it as the raw payload
        payload_type if payload_type == ShapeType::Blob || payload_type == ShapeType::String => {
            if input_shape.required(payload_member_name) {
                format!("let encoded = Some(input.{}.to_owned());", field_name)
            } else {
                format!("let encoded = if let Some(ref payload) = input.{} {{
                            Some(payload.to_owned())
                        }} else {{
                            None
                        }};",
                        field_name)
            }
        }

        // otherwise serialize the payload member as json and use that
        _ => {
            format!("let encoded = Some(serde_json::to_vec(&input.{}).unwrap());",
                    field_name)
        }
    }
}

// Do any input operations in the entire service use query parameters?
fn service_has_query_parameters(service: &Service) -> bool {
    service.operations()
        .iter()
        .map(|(_, operation)| operation.input_shape())
        .map(|input_type| service.get_shape(input_type).unwrap())
        .any(|input_shape| input_shape.has_query_parameters())
}

fn generate_documentation(operation: &Operation) -> Option<String> {
    operation.documentation
        .as_ref()
        .map(|docs| ::doco::Item(docs).to_string())
}

/// Generate code to plumb the response status code into any fields
/// in the output shape that specify it
fn generate_status_code_parser(operation: &Operation, service: &Service) -> String {
    if operation.output.is_none() {
        return "".to_owned();
    }

    let shape_name = &operation.output.as_ref().unwrap().shape;
    let output_shape = &service.get_shape(shape_name)
        .expect("Shape missing from service definition");

    let mut status_code_parser = "".to_string();

    for (member_name, member) in output_shape.members.as_ref().unwrap() {
        if let Some(ref location) = member.location {
            if location == "statusCode" {
                if output_shape.required(member_name) {
                    status_code_parser += &format!("result.{} = Some(response.status.as_u16());",
                                                   member_name.to_snake_case());
                } else {
                    status_code_parser += &format!("result.{} = Some(response.status.as_u16() as i64);",
                                                   member_name.to_snake_case());
                }
            }
        }
    }

    status_code_parser
}

/// Generate code to parse the http response body, either as a JSON object
/// deserialized with serde, or as a raw payload that's assigned to one of
/// the fields in the result object.
///
/// Needs to determine whether or not other fields in the result object
/// will be set later (e.g. from headers), so the compiler won't spit out
/// warnings about unnecessary mutability
fn generate_body_parser(operation: &Operation, service: &Service) -> String {

    if operation.output.is_none() {
        return "let result = ::std::mem::drop(response);".to_string();
    }

    let shape_name = &operation.output.as_ref().unwrap().shape;
    let output_shape = &service.get_shape(shape_name)
        .expect("Shape missing from service definition");

    let mutable_result = output_shape.members
        .as_ref()
        .unwrap()
        .iter()
        .any(|(_, member)| member.location.is_some()) || output_shape.payload.is_some();

    match output_shape.payload {
        None => json_body_parser(shape_name, mutable_result),
        Some(ref payload_member_name) => {
            let payload_member_shape = &output_shape.members.as_ref().unwrap()[payload_member_name]
                .shape;
            let payload_shape = &service.get_shape(payload_member_shape)
                .expect("Shape missing from service definition");
            // is the shape required?
            let payload_shape_required = match output_shape.required {
                Some(ref s) => {
                    // if there's any required shape present the body payload parser will handle it
                    s.len() > 0
                },
                None => false,
            };
            match payload_shape.shape_type {
                payload_type if payload_type == ShapeType::Blob ||
                                payload_type == ShapeType::String => {
                    payload_body_parser(payload_type,
                                        shape_name,
                                        payload_member_name,
                                        mutable_result,
                                        payload_shape_required)
                }
                _ => json_body_parser(shape_name, false),
            }
        }
    }
}

/// Take the raw http response body and assign it to the payload field
/// on the result object
fn payload_body_parser(payload_type: ShapeType,
                       output_shape: &str,
                       payload_member: &str,
                       mutable_result: bool,
                       payload_required: bool)
                       -> String {

    let response_body = match payload_required {
        true => {
            match payload_type {
                ShapeType::Blob => "response.body",
                _ => "String::from_utf8_lossy(response.body.as_ref())",
            }
        },
        false => {
            match payload_type {
                ShapeType::Blob => "Some(response.body)",
                _ => "Some(String::from_utf8_lossy(response.body.as_ref()).into_owned())",
            }
        }
    };

    format!("
        let {mutable} result = {output_shape}::default();
        result.{payload_member} = {response_body};
        ",
            output_shape = output_shape,
            payload_member = payload_member.to_snake_case(),
            response_body = response_body,
            mutable = if mutable_result { "mut" } else { "" })
}

/// Parse the http response body as a JSON object with serde, and use that
/// as the result object
fn json_body_parser(output_shape: &str, mutable_result: bool) -> String {
    // `serde-json` serializes field-less structs as "null", but AWS returns
    // "{{}}" for a field-less response, so we must check for this result
    // and convert it if necessary.
    format!("
            let mut body = response.body;

            if body == b\"null\" || body.is_empty() {{
                body = b\"{{}}\".to_vec();
            }}

            debug!(\"Response body: {{:?}}\", body);
            debug!(\"Response status: {{}}\", response.status);
            let {mutable} result = serde_json::from_slice::<{output_shape}>(&body).unwrap();
            ",
            output_shape = output_shape,
            mutable = if mutable_result { "mut" } else { "" })
}
<|endoftext|>