<commit_before>use std::rc::{Rc, Weak};
use std::cell::RefCell;
use std::borrow::Borrow;
use dovahkiin::types::custom_types::id::Id;
use smallvec::SmallVec;
use std::sync::Arc;
use ram::chunk::Chunks;
use ram::cell::Cell;
use dovahkiin::types;
use dovahkiin::types::custom_types::map::key_hash;
use dovahkiin::types::value::{ValueIter};
use dovahkiin::types::{Value, PrimitiveArray};
use utils::lru_cache::LRUCache;
use parking_lot::{Mutex, MutexGuard};
use std::io::Cursor;
use std::ops::Index;
use std::cmp::Ordering;

lazy_static! {
    pub static ref ENTRIES_KEY_HASH : u64 = key_hash("entries");
    pub static ref KEYS_KEY_HASH : u64 = key_hash("keys");
    pub static ref PREV_PAGE_KEY_HASH : u64 = key_hash("prev");
    pub static ref NEXT_PAGE_KEY_HASH : u64 = key_hash("next");
}

type EntryKey = SmallVec<[u8; 32]>;
type CachedExtNodeRef = Arc<CachedExtNode>;

pub struct LSMTree {
    num_levels: u8,
    levels: Vec<Rc<BPlusTree>>,
}

 trait BPlusTree {
      fn root(&self) -> &Node;
      fn root_mut(&self) -> &mut Node;
      fn get_height(&self) -> u32;
      fn set_height(&mut self) -> u32;
      fn get_num_nodes(&self) -> u32;
      fn set_num_nodes(&self) -> u32;
      fn page_size(&self) -> u32;
      fn chunks(&self) -> &Arc<Chunks>;
      fn page_cache(&self) -> MutexGuard<LRUCache<Id, CachedExtNodeRef>>;
      fn get_page_direct(&self, id: &Id) -> CachedExtNodeRef {
          let cell = self.chunks().read_cell(id).unwrap(); // should crash if not exists
          let keys = &cell.data[*KEYS_KEY_HASH];
          let keys_len = keys.len().unwrap();
          let mut v_node = CachedExtNode {
              keys: Vec::new(),
              ids: Vec::new(),
              cap: self.page_size()
          };
          for key in keys.iter_value().unwrap() {
              let key = if let Value::PrimArray(PrimitiveArray::U8(ref array)) = key {
                  EntryKey::from_slice(array.as_slice())
              } else { panic!("invalid entry") };
              let id = id_from_key(&key);
              v_node.keys.push(EntryKey::from_slice(key.as_slice()));
              v_node.ids.push(Delimiter::External(id));
          }
          return Arc::new(v_node);
      }
      fn get_page(&self, id: &Id) -> CachedExtNodeRef {
          self.page_cache().get_or_fetch(id).unwrap().clone()
      }
      fn get(&self, key: &EntryKey) -> Option<Id> {
          self.search(self.root(), key, self.get_height())
      }
      fn search<'a>(&self, node: &'a Node, key: &EntryKey, ht: u32) -> Option<Id> {
          if ht == 0 { // persist page
              let search_result = node.keys()
                  .search(key);
              let index = if let Ok(i) = search_result { i } else { return None };
              match &node.delimiters().get(index) {
                  Some(Delimiter::External(id)) => Some(*id),
                  Some(Delimiter::Internal(_)) => panic!("Error on leaf delimiter type"),
                  None => panic!("Error on leaf delimiter is none")
              }
          } else {
              let index = node.keys()
                  .search(key)
                  .map(|i| i + 1)
                  .unwrap_or_else(|i| i);
              match &node.delimiters().get(index) {
                  Some(Delimiter::External(ref id)) => {
                      assert_eq!(ht, 0);
                      // search in leaf page
                      let page = self.get_page(id);
                      return self.search(&*page, key, ht - 1);
                  },
                  Some(Delimiter::Internal(node)) => return self.search(node.borrow(), key, ht - 1),
                  None => return None
              }
          }
      }
     fn insert(&self, key: &EntryKey, value: &Id) {
         unimplemented!()
     }
     fn put<'a>(&self, node: &'a Node, key: &EntryKey, ht: u32) {
         let keys = node.keys();
         let index = keys
             .search(key)
             .map(|i| i + 1)
             .unwrap_or_else(|i| i);
         if ht == 0 {

         }
     }
 }

enum Delimiter {
    External(Id), // to leaf
    Internal(Box<Node>) // to higher level node
}

trait Array<T> where T: Ord {
    #[inline]
    fn index_of(&self, index: usize) -> &T;
    #[inline]
    fn size(&self) -> usize;
    #[inline]
    fn search(&self, item: &T) -> Result<usize, usize>;
    #[inline]
    fn get(&self, index: usize) -> Option<&T>;
}

trait Keys : Array<EntryKey> {}
trait Delimiters : Array<Delimiter> {}
impl Keys for [EntryKey] {}
impl Delimiters for [Delimiter] {}
impl <T> Array<T> for [T] where T: Ord {
    #[inline]
    fn index_of(&self, index: usize) -> &T { &self[index] }

    #[inline]
    fn size(&self) -> usize { self.len() }

    #[inline]
    fn search(&self, item: &T) -> Result<usize, usize> { self.binary_search(item) }

    #[inline]
    fn get(&self, index: usize) -> Option<&T> {
        if index >= self.size() { None } else { Some(&self[index]) }
    }
}
impl <T> Array<T> for Vec<T> where T: Ord {
    #[inline]
    fn index_of(&self, index: usize) -> &T {
        self.get(index).unwrap()
    }

    #[inline]
    fn size(&self) -> usize {
        self.len()
    }

    #[inline]
    fn search(&self, item: &T) -> Result<usize, usize> {
        self.as_slice().binary_search(item)
    }

    #[inline]
    fn get(&self, index: usize) -> Option<&T> {
        if index >= self.len() { None } else { Some(&self[index]) }
    }
}
impl Delimiters for Vec<Delimiter> {}
impl Keys for Vec<EntryKey> {}

trait Node {
    #[inline]
    fn keys(&self) -> &Keys;
    #[inline]
    fn delimiters(&self) -> &Delimiters;
    #[inline]
    fn add(&mut self, key: EntryKey) -> Option<Node>;
    #[inline]
    fn del(&mut self, key: &EntryKey);
    #[inline]
    fn merge(&mut self, x: Node);
}

struct CachedExtNode {
    keys: Vec<EntryKey>,
    ids: Vec<Delimiter>,
    cap: u32,
}

impl Node for CachedExtNode {
    #[inline]
    fn keys(&self) -> &Keys {
        &self.keys
    }

    #[inline]
    fn delimiters(&self) -> &Delimiters {
        &self.ids
    }

    fn add(&mut self, key: EntryKey) -> Option<Self> {
        let insert_pos = match self.keys.binary_search(&key) {
            Ok(_) => return None, // existed
            Err(i) => i
        };
        let id = id_from_key(&key);
        self.keys.insert(insert_pos, key);
        self.ids.insert(insert_pos, Delimiter::External(id));
        if self.keys.len() > self.cap as usize {
            // need to split
            let mid = self.keys.len() / 2;
            let keys_2: Vec<_> = self.keys.drain(mid..).collect();
            let ids_2: Vec<_> = self.ids.drain(mid..).collect();
            return Some(CachedExtNode {
                keys: keys_2,
                ids: ids_2,
                cap: self.cap
            });
        } else {
            return None;
        }
    }

    fn del(&mut self, key: &EntryKey) {
        let pos = match self.keys.binary_search(&key) {
            Ok(i) => i, // existed
            Err(i) => return
        };
        self.keys.remove(pos);
        self.ids.remove(pos);
    }

    fn merge(&mut self, mut x: Self) {
        self.keys.append(&mut x.keys);
        self.ids.append(&mut x.ids);
    }
}

trait InterNode {

}

impl <T> Index<usize> for Array<T> where T: Ord {
    type Output = T;

    fn index(&self, index: usize) -> &<Self as Index<usize>>::Output {
        self.index_of(index)
    }
}

macro_rules! impl_nodes {
    ($(($level: ident, $entry_size: expr, $delimiter_size: expr)),+) => {
        $(
            mod $level {
                use super::*;

                type LEntryKeys = [EntryKey; $entry_size];

                impl Array<EntryKey> for LEntryKeys {
                    #[inline]
                    fn size(&self) -> usize { $entry_size }
                    #[inline]
                    fn index_of(&self, index: usize) -> &EntryKey { &self[index] }
                    #[inline]
                    fn search(&self, item: &EntryKey) -> Result<usize, usize> {
                        self.binary_search(item)
                    }
                    #[inline]
                    fn get(&self, index: usize) -> Option<&EntryKey> {
                        if index >= $entry_size { None } else { Some(&self[index]) }
                    }
                }

                type LDelimiter = [Delimiter; $delimiter_size];

                impl Array<Delimiter> for LDelimiter {
                    #[inline]
                    fn size(&self) -> usize { $delimiter_size }
                    #[inline]
                    fn index_of(&self, index: usize) -> &Delimiter { &self[index] }
                    #[inline]
                    fn search(&self, item: &Delimiter) -> Result<usize, usize> {
                        self.binary_search(item)
                    }
                    #[inline]
                    fn get(&self, index: usize) -> Option<&Delimiter> {
                        if index >= $entry_size { None } else { Some(&self[index]) }
                    }
                }

                impl Delimiters for LDelimiter {}
                impl Keys for LEntryKeys {}

                struct LNode {
                    keys: LEntryKeys,
                    delimeters: LDelimiter,
                    pos: u32
                }

                impl Node for LNode {
                    #[inline]
                    fn keys(&self) -> &Keys {
                        &self.keys
                    }
                    #[inline]
                    fn delimiters(&self) -> &Delimiters {
                        &self.delimeters
                    }
                    #[inline]
                    fn add(&mut self, key: EntryKey) -> Option<Node> {
                        unimplemented!()
                    }
                    #[inline]
                    fn del(&mut self, key: &EntryKey) {
                        unimplemented!()
                    }
                    #[inline]
                    fn merge(&mut self, x: Self) {
                        unimplemented!()
                    }
                }
            }
        )+
    };
}

impl_nodes!((level_0, 2, 3), (level_1, 20, 21), (level_2, 200, 201), (level_3, 2000, 2001), (level_4, 20000, 20001));

impl Ord for Delimiter {
    fn cmp(&self, other: &Self) -> Ordering {
        panic!()
    }
}
impl PartialOrd for Delimiter {
    fn partial_cmp(&self, other: &Delimiter) -> Option<Ordering> {
        panic!()
    }
}
impl Eq for Delimiter {}
impl PartialEq for Delimiter {
    fn eq(&self, other: &Delimiter) -> bool {
        panic!()
    }
}

fn id_from_key(key: &EntryKey) -> Id {
    let mut id_cursor = Cursor::new(&key[key.len() - 16 ..]);
    return Id::from_binary(&mut id_cursor).unwrap(); // read id from tailing 128 bits
}<commit_msg>generic leveled b-tree<commit_after>use std::rc::{Rc, Weak};
use std::cell::RefCell;
use std::borrow::Borrow;
use dovahkiin::types::custom_types::id::Id;
use smallvec::SmallVec;
use std::sync::Arc;
use ram::chunk::Chunks;
use ram::cell::Cell;
use dovahkiin::types;
use dovahkiin::types::custom_types::map::key_hash;
use dovahkiin::types::value::{ValueIter};
use dovahkiin::types::{Value, PrimitiveArray};
use utils::lru_cache::LRUCache;
use parking_lot::{Mutex, MutexGuard};
use std::io::Cursor;
use std::ops::Index;
use std::cmp::Ordering;

lazy_static! {
    pub static ref ENTRIES_KEY_HASH : u64 = key_hash("entries");
    pub static ref KEYS_KEY_HASH : u64 = key_hash("keys");
    pub static ref PREV_PAGE_KEY_HASH : u64 = key_hash("prev");
    pub static ref NEXT_PAGE_KEY_HASH : u64 = key_hash("next");
}

type EntryKey = SmallVec<[u8; 32]>;
type CachedExtNodeRef = Arc<CachedExtNode>;

pub struct LSMTree {
    num_levels: u8,
}

 trait BPlusTree<N> where N: Node {
      fn root(&self) -> &N;
      fn root_mut(&self) -> &mut N;
      fn get_height(&self) -> u32;
      fn set_height(&mut self) -> u32;
      fn get_num_nodes(&self) -> u32;
      fn set_num_nodes(&self) -> u32;
      fn page_size(&self) -> u32;
      fn chunks(&self) -> &Arc<Chunks>;
      fn page_cache(&self) -> MutexGuard<LRUCache<Id, CachedExtNodeRef>>;
      fn get_page_direct(&self, id: &Id) -> CachedExtNodeRef {
          let cell = self.chunks().read_cell(id).unwrap(); // should crash if not exists
          let keys = &cell.data[*KEYS_KEY_HASH];
          let keys_len = keys.len().unwrap();
          let mut v_node = CachedExtNode {
              keys: Vec::new(),
              ids: Vec::new(),
              cap: self.page_size()
          };
          for key in keys.iter_value().unwrap() {
              let key = if let Value::PrimArray(PrimitiveArray::U8(ref array)) = key {
                  EntryKey::from_slice(array.as_slice())
              } else { panic!("invalid entry") };
              let id = id_from_key(&key);
              v_node.keys.push(EntryKey::from_slice(key.as_slice()));
              v_node.ids.push(Delimiter::External(id));
          }
          return Arc::new(v_node);
      }
      fn get_page(&self, id: &Id) -> CachedExtNodeRef {
          self.page_cache().get_or_fetch(id).unwrap().clone()
      }
      fn get(&self, key: &EntryKey) -> Option<Id> {
          self.search(self.root(), key, self.get_height())
      }
      fn search<'a>(&self, node: &N, key: &EntryKey, ht: u32) -> Option<Id> {
          if ht == 0 { // persist page
              unreachable!();
          } else {
              let index = node.keys()
                  .search(key)
                  .map(|i| i + 1)
                  .unwrap_or_else(|i| i);
              match &node.delimiters().get(index) {
                  Some(Delimiter::External(ref id)) => {
                      assert_eq!(ht, 1);
                      // search in leaf page
                      let page = self.get_page(id);
                      let search_result = page.keys()
                          .search(key);
                      let index = if let Ok(i) = search_result { i } else { return None };
                      match &node.delimiters().get(index) {
                          Some(Delimiter::External(id)) => return Some(*id),
                          Some(Delimiter::Internal(_)) => panic!("Error on leaf delimiter type"),
                          None => panic!("Error on leaf delimiter is none")
                      };
                  },
                  Some(Delimiter::Internal(node)) => {
                      let node: &N = node.borrow();
                      return self.search(node, key, ht - 1);
                  },
                  None => return None
              }
          }
      }
     fn insert(&self, key: &EntryKey, value: &Id) {
         unimplemented!()
     }
     fn put<'a>(&self, node: &N, key: &EntryKey, ht: u32) {
         let keys = node.keys();
         let index = keys
             .search(key)
             .map(|i| i + 1)
             .unwrap_or_else(|i| i);
         if ht == 0 {

         }
     }
 }

enum Delimiter<N> {
    External(Id), // to leaf
    Internal(Box<N>) // to higher level node
}

trait Array<T> where T: Ord {
    #[inline]
    fn index_of(&self, index: usize) -> &T;
    #[inline]
    fn size(&self) -> usize;
    #[inline]
    fn search(&self, item: &T) -> Result<usize, usize>;
    #[inline]
    fn get(&self, index: usize) -> Option<&T>;
}

trait Keys : Array<EntryKey> {}
trait Delimiters<N> : Array<Delimiter<N>> {}
impl Keys for [EntryKey] {}
impl <N> Delimiters<N> for [Delimiter<N>] {}
impl <T> Array<T> for [T] where T: Ord {
    #[inline]
    fn index_of(&self, index: usize) -> &T { &self[index] }

    #[inline]
    fn size(&self) -> usize { self.len() }

    #[inline]
    fn search(&self, item: &T) -> Result<usize, usize> { self.binary_search(item) }

    #[inline]
    fn get(&self, index: usize) -> Option<&T> {
        if index >= self.size() { None } else { Some(&self[index]) }
    }
}
impl <T> Array<T> for Vec<T> where T: Ord {
    #[inline]
    fn index_of(&self, index: usize) -> &T {
        self.get(index).unwrap()
    }

    #[inline]
    fn size(&self) -> usize {
        self.len()
    }

    #[inline]
    fn search(&self, item: &T) -> Result<usize, usize> {
        self.as_slice().binary_search(item)
    }

    #[inline]
    fn get(&self, index: usize) -> Option<&T> {
        if index >= self.len() { None } else { Some(&self[index]) }
    }
}
impl <N> Delimiters<N> for Vec<Delimiter<N>> {}
impl Keys for Vec<EntryKey> {}

trait Node {
    #[inline]
    fn keys(&self) -> &Keys;
    #[inline]
    fn delimiters(&self) -> &Delimiters<Self>;
    #[inline]
    fn add(&mut self, key: EntryKey) -> Option<Self> where Self: Sized;
    #[inline]
    fn del(&mut self, key: &EntryKey);
    #[inline]
    fn merge(&mut self, x: Self);
}

struct CachedExtNode {
    keys: Vec<EntryKey>,
    ids: Vec<Delimiter<CachedExtNode>>,
    cap: u32,
}

impl Node for CachedExtNode {
    #[inline]
    fn keys(&self) -> &Keys {
        &self.keys
    }

    #[inline]
    fn delimiters(&self) -> &Delimiters<Self> {
        &self.ids
    }

    fn add(&mut self, key: EntryKey) -> Option<Self> {
        let insert_pos = match self.keys.binary_search(&key) {
            Ok(_) => return None, // existed
            Err(i) => i
        };
        let id = id_from_key(&key);
        self.keys.insert(insert_pos, key);
        self.ids.insert(insert_pos, Delimiter::External(id));
        if self.keys.len() > self.cap as usize {
            // need to split
            let mid = self.keys.len() / 2;
            let keys_2: Vec<_> = self.keys.drain(mid..).collect();
            let ids_2: Vec<_> = self.ids.drain(mid..).collect();
            return Some(CachedExtNode {
                keys: keys_2,
                ids: ids_2,
                cap: self.cap
            });
        } else {
            return None;
        }
    }

    fn del(&mut self, key: &EntryKey) {
        let pos = match self.keys.binary_search(&key) {
            Ok(i) => i, // existed
            Err(i) => return
        };
        self.keys.remove(pos);
        self.ids.remove(pos);
    }

    fn merge(&mut self, mut x: Self) {
        self.keys.append(&mut x.keys);
        self.ids.append(&mut x.ids);
    }
}

trait InterNode {

}

impl <T> Index<usize> for Array<T> where T: Ord {
    type Output = T;

    fn index(&self, index: usize) -> &<Self as Index<usize>>::Output {
        self.index_of(index)
    }
}

macro_rules! impl_nodes {
    ($(($level: ident, $entry_size: expr, $delimiter_size: expr)),+) => {
        $(
            mod $level {
                use super::*;

                type LEntryKeys = [EntryKey; $entry_size];

                impl Array<EntryKey> for LEntryKeys {
                    #[inline]
                    fn size(&self) -> usize { $entry_size }
                    #[inline]
                    fn index_of(&self, index: usize) -> &EntryKey { &self[index] }
                    #[inline]
                    fn search(&self, item: &EntryKey) -> Result<usize, usize> {
                        self.binary_search(item)
                    }
                    #[inline]
                    fn get(&self, index: usize) -> Option<&EntryKey> {
                        if index >= $entry_size { None } else { Some(&self[index]) }
                    }
                }

                type LDelimiter = [Delimiter<LNode>; $delimiter_size];

                impl Array<Delimiter<LNode>> for LDelimiter {
                    #[inline]
                    fn size(&self) -> usize { $delimiter_size }
                    #[inline]
                    fn index_of(&self, index: usize) -> &Delimiter<LNode> { &self[index] }
                    #[inline]
                    fn search(&self, item: &Delimiter<LNode>) -> Result<usize, usize> {
                        unreachable!()
                    }
                    #[inline]
                    fn get(&self, index: usize) -> Option<&Delimiter<LNode>> {
                        if index >= $entry_size { None } else { Some(&self[index]) }
                    }
                }

                impl Delimiters<LNode> for LDelimiter {}
                impl Keys for LEntryKeys {}

                struct LNode {
                    keys: LEntryKeys,
                    delimeters: LDelimiter,
                    pos: u32
                }

                impl Node for LNode {
                    #[inline]
                    fn keys(&self) -> &Keys {
                        &self.keys
                    }
                    #[inline]
                    fn delimiters(&self) -> &Delimiters<Self> {
                        &self.delimeters
                    }
                    #[inline]
                    fn add(&mut self, key: EntryKey) -> Option<Self> {
                        unimplemented!()
                    }
                    #[inline]
                    fn del(&mut self, key: &EntryKey) {
                        unimplemented!()
                    }
                    #[inline]
                    fn merge(&mut self, x: Self) {
                        unimplemented!()
                    }
                }
            }
        )+
    };
}

impl_nodes!((level_0, 2, 3), (level_1, 20, 21), (level_2, 200, 201), (level_3, 2000, 2001), (level_4, 20000, 20001));

impl <N> Ord for Delimiter<N> {
    fn cmp(&self, other: &Self) -> Ordering {
        panic!()
    }
}
impl <N> PartialOrd for Delimiter<N> {
    fn partial_cmp(&self, other: &Delimiter<N>) -> Option<Ordering> {
        panic!()
    }
}
impl <N> Eq for Delimiter<N> {}
impl <N> PartialEq for Delimiter<N> {
    fn eq(&self, other: &Delimiter<N>) -> bool {
        panic!()
    }
}

fn id_from_key(key: &EntryKey) -> Id {
    let mut id_cursor = Cursor::new(&key[key.len() - 16 ..]);
    return Id::from_binary(&mut id_cursor).unwrap(); // read id from tailing 128 bits
}<|endoftext|>


<commit_before>#[macro_use]
extern crate error_chain;

use std::char;
use std::fs::File;
use std::io::BufReader;
use std::io::BufRead;
use std::path::Path;

error_chain! {
    errors { }

    foreign_links {
        Io(::std::io::Error) #[cfg(unix)];
    }
}

static TO_BASE64: [char; 64] =
[
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
    'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
    'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
    'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
    'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
    'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
    '8', '9', '+', '/'
];

//Ordered by second entry
static FROM_BASE64: [(u8, char); 64] =
[
    (62, '+'), (63, '/'),
    (52, '0'), (53, '1'), (54, '2'), (55, '3'), (56, '4'), (57, '5'), (58, '6'), (59, '7'), (60, '8'), (61, '9'),
    (0, 'A'),  (1, 'B'),  (2, 'C'),  (3, 'D'),  (4, 'E'),  (5, 'F'),  (6, 'G'),  (7, 'H'),  (8, 'I'),  (9, 'J'),
    (10, 'K'), (11, 'L'), (12, 'M'), (13, 'N'), (14, 'O'), (15, 'P'), (16, 'Q'), (17, 'R'), (18, 'S'), (19, 'T'),
    (20, 'U'), (21, 'V'), (22, 'W'), (23, 'X'), (24, 'Y'), (25, 'Z'), (26, 'a'), (27, 'b'), (28, 'c'), (29, 'd'),
    (30, 'e'), (31, 'f'), (32, 'g'), (33, 'h'), (34, 'i'), (35, 'j'), (36, 'k'), (37, 'l'), (38, 'm'), (39, 'n'),
    (40, 'o'), (41, 'p'), (42, 'q'), (43, 'r'), (44, 's'), (45, 't'), (46, 'u'), (47, 'v'), (48, 'w'), (49, 'x'),
    (50, 'y'), (51, 'z'),
];

pub trait Serialize {
    fn to_base64(&self) -> String;
    fn to_hex(&self) -> String;
}

impl Serialize for [u8] {
    fn to_base64(&self) -> String {
        let mut base64 = String::with_capacity(4*self.len()/3);
        for block in self.chunks(3) {
            block_to_base64(block, &mut base64);
        }

        if self.len() % 3 >= 1 {
            base64.pop();
            if self.len() % 3 == 1 {
                base64.pop();
                base64.push('=');
            }
            base64.push('=');
        }

        base64
    }

    fn to_hex(&self) -> String {
        let mut u4 = Vec::with_capacity(2*self.len());
        for u in self {
            u4.push(u >> 4);
            u4.push(u & 0xf);
        }
        u4.iter().map(|&u| char::from_digit(u as u32, 16).unwrap()).collect()
    }
}

pub fn from_base64(s: &str) -> Result<Vec<u8>> {
    if s.len() % 4 != 0 {
        bail!("input length needs to be multiple of 4");
    }

    let mut n = s.len();
    if s.as_bytes()[n - 1] == b'=' {
        if s.as_bytes()[n - 2] == b'=' {
            n -= 1;
        }
        n -= 1;
    }

    let mut digits = Vec::with_capacity(n);
    for c in s.chars().take(n) {
        digits.push(u8_from_base64(c).chain_err(|| format!("not a valid base64 string: {}", s))?);
    }

    let mut u = Vec::with_capacity(3*s.len()/4);
    for b in digits.chunks(4) {
        u.push((b[0] << 2) + (b[1] >> 4));
        if b.len() == 2 {
            if b[1] << 4 != 0 { bail!("input not padded with zero"); }
            break;
        }

        u.push((b[1] << 4) + (b[2] >> 2));
        if b.len() == 3 {
            if b[2] << 6 != 0 { bail!("input not padded with zero"); }
            break;
        }

        u.push((b[2] << 6) + b[3]);
    }
    Ok(u)
}

pub fn from_base64_file(path: &Path) -> Result<Vec<u8>> {
    let mut content = String::new();
    let file = File::open(&path)?;
    let reader = BufReader::new(file);
    for line in reader.lines() {
        content.push_str(line.unwrap().trim());
    }
    from_base64(&content)
}

pub fn from_base64_lines(path: &Path) -> Result<Vec<Vec<u8>>> {
    from_lines(path, from_base64)
}

pub fn from_hex_lines(path: &Path) -> Result<Vec<Vec<u8>>> {
    from_lines(path, from_hex)
}

fn from_lines(path: &Path, converter: fn(&str) -> Result<Vec<u8>>) -> Result<Vec<Vec<u8>>> {
    let mut content = Vec::new();
    let file = File::open(&path)?;
    let reader = BufReader::new(file);
    for line in reader.lines() {
        content.push(converter(line.unwrap().trim())?);
    }
    Ok(content)
}

pub fn from_hex(s: &str) -> Result<Vec<u8>> {
    if s.len() % 2 != 0 {
        bail!("input length needs to be multiple of 2");
    }

    let mut digits = Vec::with_capacity(s.len());
    for c in s.chars() {
        digits.push(u8_from_hex(c).chain_err(|| format!("not a valid hex string: {}", s))?);
    }
    Ok(digits.chunks(2).map(|c| (c[0]<<4) + c[1]).collect::<Vec<u8>>())
}

fn u8_from_hex(c: char) -> Result<u8> {
    match c.to_digit(16) {
        Some(i) => Ok(i as u8),
        _ => bail!(format!("invalid character {}", c)),
    }
}

fn u8_to_base64(u: u8) -> char {
    assert!(u <= 63);
    TO_BASE64[u as usize]
}

fn block_to_base64(block: &[u8], base64: &mut String) {
    let (a, b, c) = match block.len() {
        3 => (block[0], block[1], block[2]),
        2 => (block[0], block[1], 0),
        1 => (block[0], 0,        0),
        _ => return,
    };
    base64.push(u8_to_base64(a >> 2));                  // Upper 6 bits of a
    base64.push(u8_to_base64(a % 4 * 16 + (b >> 4)));   // Lower 2 bits of a, upper 4 bits of b
    base64.push(u8_to_base64(b % 16 * 4 + (c >> 6)));   // Lower 4 bits of b, upper 2 bits of c
    base64.push(u8_to_base64(c & 0x3f));                // Lower 6 bits of c
}

fn u8_from_base64(c: char) -> Result<u8> {
    match FROM_BASE64.binary_search_by(|&(_, d)| d.cmp(&c)) {
        Ok(i) => Ok(FROM_BASE64[i].0),
        _ => bail!(format!("invalid character {}", c)),
    }
}
<commit_msg>Simplify base64 conversion<commit_after>#[macro_use]
extern crate error_chain;

use std::char;
use std::fs::File;
use std::io::BufReader;
use std::io::BufRead;
use std::path::Path;

error_chain! {
    errors { }

    foreign_links {
        Io(::std::io::Error) #[cfg(unix)];
    }
}

pub trait Serialize {
    fn to_base64(&self) -> String;
    fn to_hex(&self) -> String;
}

impl Serialize for [u8] {
    fn to_base64(&self) -> String {
        let mut base64 = String::with_capacity(4*self.len()/3);
        for block in self.chunks(3) {
            block_to_base64(block, &mut base64);
        }

        if self.len() % 3 >= 1 {
            base64.pop();
            if self.len() % 3 == 1 {
                base64.pop();
                base64.push('=');
            }
            base64.push('=');
        }

        base64
    }

    fn to_hex(&self) -> String {
        let mut u4 = Vec::with_capacity(2*self.len());
        for u in self {
            u4.push(u >> 4);
            u4.push(u & 0xf);
        }
        u4.iter().map(|&u| char::from_digit(u as u32, 16).unwrap()).collect()
    }
}

pub fn from_base64(s: &str) -> Result<Vec<u8>> {
    if s.len() % 4 != 0 {
        bail!("input length needs to be multiple of 4");
    }

    let mut n = s.len();
    if s.as_bytes()[n - 1] == b'=' {
        if s.as_bytes()[n - 2] == b'=' {
            n -= 1;
        }
        n -= 1;
    }

    let mut digits = Vec::with_capacity(n);
    for c in s.chars().take(n) {
        digits.push(u8_from_base64(c).chain_err(|| format!("not a valid base64 string: {}", s))?);
    }

    let mut u = Vec::with_capacity(3*s.len()/4);
    for b in digits.chunks(4) {
        u.push((b[0] << 2) + (b[1] >> 4));
        if b.len() == 2 {
            if b[1] << 4 != 0 { bail!("input not padded with zero"); }
            break;
        }

        u.push((b[1] << 4) + (b[2] >> 2));
        if b.len() == 3 {
            if b[2] << 6 != 0 { bail!("input not padded with zero"); }
            break;
        }

        u.push((b[2] << 6) + b[3]);
    }
    Ok(u)
}

pub fn from_base64_file(path: &Path) -> Result<Vec<u8>> {
    let mut content = String::new();
    let file = File::open(&path)?;
    let reader = BufReader::new(file);
    for line in reader.lines() {
        content.push_str(line.unwrap().trim());
    }
    from_base64(&content)
}

pub fn from_base64_lines(path: &Path) -> Result<Vec<Vec<u8>>> {
    from_lines(path, from_base64)
}

pub fn from_hex_lines(path: &Path) -> Result<Vec<Vec<u8>>> {
    from_lines(path, from_hex)
}

fn from_lines(path: &Path, converter: fn(&str) -> Result<Vec<u8>>) -> Result<Vec<Vec<u8>>> {
    let mut content = Vec::new();
    let file = File::open(&path)?;
    let reader = BufReader::new(file);
    for line in reader.lines() {
        content.push(converter(line.unwrap().trim())?);
    }
    Ok(content)
}

pub fn from_hex(s: &str) -> Result<Vec<u8>> {
    if s.len() % 2 != 0 {
        bail!("input length needs to be multiple of 2");
    }

    let mut digits = Vec::with_capacity(s.len());
    for c in s.chars() {
        digits.push(u8_from_hex(c).chain_err(|| format!("not a valid hex string: {}", s))?);
    }
    Ok(digits.chunks(2).map(|c| (c[0]<<4) + c[1]).collect::<Vec<u8>>())
}

fn u8_from_hex(c: char) -> Result<u8> {
    match c.to_digit(16) {
        Some(i) => Ok(i as u8),
        _ => bail!(format!("invalid character {}", c)),
    }
}

fn block_to_base64(block: &[u8], base64: &mut String) {
    let (a, b, c) = match block.len() {
        3 => (block[0], block[1], block[2]),
        2 => (block[0], block[1], 0),
        1 => (block[0], 0,        0),
        _ => return,
    };
    base64.push(u8_to_base64(a >> 2));                  // Upper 6 bits of a
    base64.push(u8_to_base64(a % 4 * 16 + (b >> 4)));   // Lower 2 bits of a, upper 4 bits of b
    base64.push(u8_to_base64(b % 16 * 4 + (c >> 6)));   // Lower 4 bits of b, upper 2 bits of c
    base64.push(u8_to_base64(c & 0x3f));                // Lower 6 bits of c
}

fn u8_to_base64(u: u8) -> char {
    match u {
        0...25 => ('A' as u8 + u) as char,
        26...51 => ('a' as u8 + (u - 26)) as char,
        52...61 => ('0' as u8 + (u - 52)) as char,
        62 => '+',
        63 => '/',
        _ => panic!("input exceeded range"),
    }
}

fn u8_from_base64(c: char) -> Result<u8> {
    match c {
        'A'...'Z' => Ok(c as u8 - 'A' as u8),
        'a'...'z' => Ok(26 + (c as u8 - 'a' as u8)),
        '0'...'9' => Ok(52 + (c as u8 - '0' as u8)),
        '+' => Ok(62),
        '/' => Ok(63),
        _ => bail!(format!("invalid character {}", c)),
    }
}
<|endoftext|>


<commit_before>// rustc -L ../libs/rustsqlite sqlite.rs

extern mod sqlite;
extern mod types;

use sqlite::*;
use types::*;

error!("error here")

fn main() {
    match open(~"play.sqlite") {
        Ok(database) => {
            // TODO Check ResultCode enum
            let res:types::SqliteResult<bool> = database.exec("CREATE TABLE IF NOT EXISTS test (foo TEXT);");
            match res {
                Ok(true) => {"Boo Ya";},
                Ok(false) => {"Hmmm"; },
                Err(e) => {println(fmt!("%?", e));}
            }
            println(fmt!("CREATE %?", res));
            println(fmt!("INSERT %?", database.exec("INSERT INTO test (foo) VALUES (\"hamster dance\");")));
            let cur = database.prepare("SELECT * FROM test", &None);
            match cur {
                Ok(ref stmt) => {
                    debug!("DEBUG Statement: %?", stmt);
                    stmt.step();
                    debug!("DEBUG Statment: %?", stmt.get_text(0));
                },
                Err(ref e) => {println(fmt!("%?", e));}
            }
            println(fmt!("SELECT %?", cur));
        },
        Err(e) => {
            fail!("Unable to open sqlite database")
        }
    }
}<commit_msg>Remove prefixes to debugging<commit_after>// rustc -L ../libs/rustsqlite sqlite.rs

extern mod sqlite;
extern mod types;

use sqlite::*;
use types::*;

error!("error here")

fn main() {
    match open(~"play.sqlite") {
        Ok(database) => {
            // TODO Check ResultCode enum
            let res:types::SqliteResult<bool> = database.exec("CREATE TABLE IF NOT EXISTS test (foo TEXT);");
            match res {
                Ok(true) => {"Boo Ya";},
                Ok(false) => {"Hmmm"; },
                Err(e) => {println(fmt!("%?", e));}
            }
            println(fmt!("CREATE %?", res));
            println(fmt!("INSERT %?", database.exec("INSERT INTO test (foo) VALUES (\"hamster dance\");")));
            let cur = database.prepare("SELECT * FROM test", &None);
            match cur {
                Ok(ref stmt) => {
                    debug!(stmt);
                    stmt.step();
                    debug!(stmt.get_text(0));
                },
                Err(ref e) => {println(fmt!("%?", e));}
            }
            println(fmt!("SELECT %?", cur));
        },
        Err(e) => {
            fail!("Unable to open sqlite database")
        }
    }
}<|endoftext|>


<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A wrapper around any Reader to treat it as an RNG.

use container::Container;
use option::{Some, None};
use io::Reader;

use rand::Rng;

/// An RNG that reads random bytes straight from a `Reader`. This will
/// work best with an infinite reader, but this is not required.
///
/// It will fail if it there is insufficient data to fulfill a request.
///
/// # Example
///
/// ```rust
/// use std::rand::{reader, Rng};
/// use std::io::mem;
///
/// let mut rng = reader::ReaderRng::new(mem::MemReader::new(~[1,2,3,4,5,6,7,8]));
/// println!("{:x}", rng.gen::<uint>());
/// ```
pub struct ReaderRng<R> {
    priv reader: R
}

impl<R: Reader> ReaderRng<R> {
    /// Create a new `ReaderRng` from a `Reader`.
    pub fn new(r: R) -> ReaderRng<R> {
        ReaderRng {
            reader: r
        }
    }
}

impl<R: Reader> Rng for ReaderRng<R> {
    fn next_u32(&mut self) -> u32 {
        // This is designed for speed: reading a LE integer on a LE
        // platform just involves blitting the bytes into the memory
        // of the u32, similarly for BE on BE; avoiding byteswapping.
        if cfg!(target_endian="little") {
            self.reader.read_le_u32()
        } else {
            self.reader.read_be_u32()
        }
    }
    fn next_u64(&mut self) -> u64 {
        // see above for explanation.
        if cfg!(target_endian="little") {
            self.reader.read_le_u64()
        } else {
            self.reader.read_be_u64()
        }
    }
    fn fill_bytes(&mut self, v: &mut [u8]) {
        if v.len() == 0 { return }
        match self.reader.read(v) {
            Some(n) if n == v.len() => return,
            Some(n) => fail!("ReaderRng.fill_bytes could not fill buffer: \
                              read {} out of {} bytes.", n, v.len()),
            None => fail!("ReaderRng.fill_bytes reached eof.")
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use io::mem::MemReader;
    use cast;
    use rand::*;
    use prelude::*;

    #[test]
    fn test_reader_rng_u64() {
        // transmute from the target to avoid endianness concerns.
        let v = ~[1u64, 2u64, 3u64];
        let bytes: ~[u8] = unsafe {cast::transmute(v)};
        let mut rng = ReaderRng::new(MemReader::new(bytes));

        assert_eq!(rng.next_u64(), 1);
        assert_eq!(rng.next_u64(), 2);
        assert_eq!(rng.next_u64(), 3);
    }
    #[test]
    fn test_reader_rng_u32() {
        // transmute from the target to avoid endianness concerns.
        let v = ~[1u32, 2u32, 3u32];
        let bytes: ~[u8] = unsafe {cast::transmute(v)};
        let mut rng = ReaderRng::new(MemReader::new(bytes));

        assert_eq!(rng.next_u32(), 1);
        assert_eq!(rng.next_u32(), 2);
        assert_eq!(rng.next_u32(), 3);
    }
    #[test]
    fn test_reader_rng_fill_bytes() {
        let v = [1u8, 2, 3, 4, 5, 6, 7, 8];
        let mut w = [0u8, .. 8];

        let mut rng = ReaderRng::new(MemReader::new(v.to_owned()));
        rng.fill_bytes(w);

        assert_eq!(v, w);
    }

    #[test]
    #[should_fail]
    fn test_reader_rng_insufficient_bytes() {
        let mut rng = ReaderRng::new(MemReader::new(~[]));
        let mut v = [0u8, .. 3];
        rng.fill_bytes(v);
    }
}
<commit_msg>Tweak the interface of std::io<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A wrapper around any Reader to treat it as an RNG.

use container::Container;
use option::{Some, None};
use io::Reader;

use rand::Rng;

/// An RNG that reads random bytes straight from a `Reader`. This will
/// work best with an infinite reader, but this is not required.
///
/// It will fail if it there is insufficient data to fulfill a request.
///
/// # Example
///
/// ```rust
/// use std::rand::{reader, Rng};
/// use std::io::MemReader;
///
/// let mut rng = reader::ReaderRng::new(MemReader::new(~[1,2,3,4,5,6,7,8]));
/// println!("{:x}", rng.gen::<uint>());
/// ```
pub struct ReaderRng<R> {
    priv reader: R
}

impl<R: Reader> ReaderRng<R> {
    /// Create a new `ReaderRng` from a `Reader`.
    pub fn new(r: R) -> ReaderRng<R> {
        ReaderRng {
            reader: r
        }
    }
}

impl<R: Reader> Rng for ReaderRng<R> {
    fn next_u32(&mut self) -> u32 {
        // This is designed for speed: reading a LE integer on a LE
        // platform just involves blitting the bytes into the memory
        // of the u32, similarly for BE on BE; avoiding byteswapping.
        if cfg!(target_endian="little") {
            self.reader.read_le_u32()
        } else {
            self.reader.read_be_u32()
        }
    }
    fn next_u64(&mut self) -> u64 {
        // see above for explanation.
        if cfg!(target_endian="little") {
            self.reader.read_le_u64()
        } else {
            self.reader.read_be_u64()
        }
    }
    fn fill_bytes(&mut self, v: &mut [u8]) {
        if v.len() == 0 { return }
        match self.reader.read(v) {
            Some(n) if n == v.len() => return,
            Some(n) => fail!("ReaderRng.fill_bytes could not fill buffer: \
                              read {} out of {} bytes.", n, v.len()),
            None => fail!("ReaderRng.fill_bytes reached eof.")
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use io::MemReader;
    use cast;
    use rand::*;
    use prelude::*;

    #[test]
    fn test_reader_rng_u64() {
        // transmute from the target to avoid endianness concerns.
        let v = ~[1u64, 2u64, 3u64];
        let bytes: ~[u8] = unsafe {cast::transmute(v)};
        let mut rng = ReaderRng::new(MemReader::new(bytes));

        assert_eq!(rng.next_u64(), 1);
        assert_eq!(rng.next_u64(), 2);
        assert_eq!(rng.next_u64(), 3);
    }
    #[test]
    fn test_reader_rng_u32() {
        // transmute from the target to avoid endianness concerns.
        let v = ~[1u32, 2u32, 3u32];
        let bytes: ~[u8] = unsafe {cast::transmute(v)};
        let mut rng = ReaderRng::new(MemReader::new(bytes));

        assert_eq!(rng.next_u32(), 1);
        assert_eq!(rng.next_u32(), 2);
        assert_eq!(rng.next_u32(), 3);
    }
    #[test]
    fn test_reader_rng_fill_bytes() {
        let v = [1u8, 2, 3, 4, 5, 6, 7, 8];
        let mut w = [0u8, .. 8];

        let mut rng = ReaderRng::new(MemReader::new(v.to_owned()));
        rng.fill_bytes(w);

        assert_eq!(v, w);
    }

    #[test]
    #[should_fail]
    fn test_reader_rng_insufficient_bytes() {
        let mut rng = ReaderRng::new(MemReader::new(~[]));
        let mut v = [0u8, .. 3];
        rng.fill_bytes(v);
    }
}
<|endoftext|>


<commit_before>#[macro_use] extern crate custom_derive;
extern crate rustc_serialize;

macro_rules! StableEncodable {
    (
        () $(pub)* struct $name:ident { $($body:tt)* }
    ) => {
        StableEncodable! {
            @impl $name,
            bounds(),
            ty_clss(),
            { $($body)* }
        }
    };

    (
        () $(pub)* struct $name:ident < $($tail:tt)*
    ) => {
        StableEncodable! {
            @extract_gen_args $name,
            ($($tail)*)
            -> bounds(), ty_clss(where)
        }
    };

    (
        @impl $name:ident,
        bounds($($bounds:tt)*),
        ty_clss($($ty_clss:tt)*),
        { $($fnames:ident: $_ftys:ty),* $(,)* }
    ) => {
        StableEncodable! {
            @as_item
            impl<$($bounds)*> rustc_serialize::Encodable for $name<$($bounds)*>
            $($ty_clss)* {
                fn encode<S>(&self, s: &mut S) -> Result<(), S::Error>
                where S: rustc_serialize::Encoder {
                    const NUM_FIELDS: usize = StableEncodable!(@count_tts $($fnames)*);
                    try!(s.emit_struct(stringify!($name), NUM_FIELDS, |s| {
                        // Poor man's enumerate!($($fnames)):
                        let mut idx = 0;
                        $(
                            try!(s.emit_struct_field(stringify!($fnames), idx, |s| {
                                self.$fnames.encode(s)
                            }));
                            idx += 1;
                        )*
                        let _ = idx;
                        Ok(())
                    }));
                    Ok(())
                }
            }
        }
    };

    (@as_item $i:item) => {$i};

    (
        @extract_gen_args $name:ident,
        (> { $($tail:tt)* })
        -> bounds($($bounds:tt)*), ty_clss($($ty_clss:tt)*)
    ) => {
        StableEncodable! {
            @impl $name,
            bounds($($bounds)*),
            ty_clss($($ty_clss)*),
            { $($tail)* }
        }
    };

    (
        @extract_gen_args $name:ident,
        ($ty_name:ident: $($tail)*)
        -> bounds($($bounds:tt)*), ty_clss($($ty_clss:tt)*)
    ) => {
        StableEncodable! {
            @skip_inline_bound $name,
            ($($tail)*)
            -> bounds($($bounds)* $ty_name:),
               ty_clss($($ty_clss)* $ty_name: ::rustc_serialize::Encodable,)
        }
    };

    (
        @extract_gen_args $name:ident,
        ($ty_name:ident $($tail:tt)*)
        -> bounds($($bounds:tt)*), ty_clss($($ty_clss:tt)*)
    ) => {
        StableEncodable! {
            @extract_gen_args $name,
            ($($tail)*)
            -> bounds($($bounds)* $ty_name),
               ty_clss($($ty_clss)* $ty_name: ::rustc_serialize::Encodable,)
        }
    };

    (
        @extract_gen_args $name:ident,
        (, $($tail:tt)*)
        -> bounds($($bounds:tt)*), ty_clss($($ty_clss:tt)*)
    ) => {
        StableEncodable! {
            @extract_gen_args $name,
            ($($tail)*)
            -> bounds($($bounds)* ,), ty_clss($($ty_clss)*)
        }
    };

    (
        @extract_gen_args $name:ident,
        ($lt:tt $($tail:tt)*)
        -> bounds($($bounds:tt)*), ty_clss($($ty_clss:tt)*)
    ) => {
        StableEncodable! {
            @extract_gen_args $name,
            ($($tail)*)
            -> bounds($($bounds)* $lt), ty_clss($($ty_clss)*)
        }
    };

    (
        @skip_inline_bound $name:ident,
        (, $($tail:tt)*)
        -> bounds($($bounds:tt)*), ty_clss($($ty_clss:tt)*)
    ) => {
        StableEncodable! {
            @extract_gen_args $name,
            ($($tail)*)
            -> bounds($($bounds)* ,), ty_clss($($ty_clss)*)
        }
    };

    (
        @skip_inline_bound $name:ident,
        (> { $($tail:tt)* })
        -> bounds($($bounds:tt)*), ty_clss($($ty_clss:tt)*)
    ) => {
        StableEncodable! {
            @impl $name,
            bounds($($bounds)*),
            ty_clss($($ty_clss)*),
            { $($tail)* }
        }
    };

    (@count_tts) => {0usize};
    (@count_tts $_tt:tt $($tail:tt)*) => {1usize + StableEncodable!(@count_tts $($tail)*)};
}

custom_derive! {
    #[derive(Debug, StableEncodable)]
    struct LazyEg<A> { a: A, b: i32, c: (u8, u8, u8) }
}

#[test]
fn test_stable_encodable() {
    let lazy_eg = LazyEg {
        a: String::from("Oh hai!"),
        b: 42,
        c: (1, 3, 0),
    };
    let lazy_eg_s = rustc_serialize::json::encode(&lazy_eg).unwrap();
    assert_eq!(&*lazy_eg_s, r#"{"a":"Oh hai!","b":42,"c":[1,3,0]}"#);
}
<commit_msg>Expanded StableEncodable! with enum support.<commit_after>#[macro_use] extern crate custom_derive;
extern crate rustc_serialize;

macro_rules! StableEncodable {
    (
        () $(pub)* enum $name:ident < $($tail:tt)*
    ) => {
        StableEncodable! {
            @extract_gen_args (enum $name),
            ($($tail)*)
            -> bounds(), ty_clss(where)
        }
    };

    (
        () $(pub)* enum $name:ident { $($body:tt)* }
    ) => {
        StableEncodable! {
            @impl enum $name,
            bounds(),
            ty_clss(),
            { $($body)* }
        }
    };

    (
        () $(pub)* struct $name:ident { $($body:tt)* }
    ) => {
        StableEncodable! {
            @impl struct $name,
            bounds(),
            ty_clss(),
            { $($body)* }
        }
    };

    (
        () $(pub)* struct $name:ident < $($tail:tt)*
    ) => {
        StableEncodable! {
            @extract_gen_args (struct $name),
            ($($tail)*)
            -> bounds(), ty_clss(where)
        }
    };

    (
        @impl enum $name:ident,
        bounds($($bounds:tt)*),
        ty_clss($($ty_clss:tt)*),
        { $($body:tt)* }
    ) => {
        StableEncodable! {
            @parse_variants (enum $name, bounds($($bounds)*), ty_clss($($ty_clss)*)),
            0usize, ($($body)*,) -> ()
        }
    };

    (
        @impl struct $name:ident,
        bounds($($bounds:tt)*),
        ty_clss($($ty_clss:tt)*),
        { $($fnames:ident: $_ftys:ty),* $(,)* }
    ) => {
        StableEncodable! {
            @as_item
            impl<$($bounds)*> rustc_serialize::Encodable for $name<$($bounds)*>
            $($ty_clss)* {
                fn encode<StableEncodableEncoder>(
                    &self,
                    s: &mut StableEncodableEncoder
                ) -> Result<(), StableEncodableEncoder::Error>
                where StableEncodableEncoder: rustc_serialize::Encoder {
                    const NUM_FIELDS: usize = StableEncodable!(@count_tts $($fnames)*);
                    try!(s.emit_struct(stringify!($name), NUM_FIELDS, |s| {
                        // Poor man's enumerate!($($fnames)):
                        let mut idx = 0;
                        $(
                            try!(s.emit_struct_field(stringify!($fnames), idx, |s| {
                                self.$fnames.encode(s)
                            }));
                            idx += 1;
                        )*
                        let _ = idx;
                        Ok(())
                    }));
                    Ok(())
                }
            }
        }
    };

    (@as_item $i:item) => {$i};

    (
        @extract_gen_args ($kind:ident $name:ident),
        (> { $($tail:tt)* })
        -> bounds($($bounds:tt)*), ty_clss($($ty_clss:tt)*)
    ) => {
        StableEncodable! {
            @impl $kind $name,
            bounds($($bounds)*),
            ty_clss($($ty_clss)*),
            { $($tail)* }
        }
    };

    (
        @extract_gen_args $fixed:tt,
        ($ty_name:ident: $($tail)*)
        -> bounds($($bounds:tt)*), ty_clss($($ty_clss:tt)*)
    ) => {
        StableEncodable! {
            @skip_inline_bound $fixed,
            ($($tail)*)
            -> bounds($($bounds)* $ty_name:),
               ty_clss($($ty_clss)* $ty_name: ::rustc_serialize::Encodable,)
        }
    };

    (
        @extract_gen_args $fixed:tt,
        ($ty_name:ident $($tail:tt)*)
        -> bounds($($bounds:tt)*), ty_clss($($ty_clss:tt)*)
    ) => {
        StableEncodable! {
            @extract_gen_args $fixed,
            ($($tail)*)
            -> bounds($($bounds)* $ty_name),
               ty_clss($($ty_clss)* $ty_name: ::rustc_serialize::Encodable,)
        }
    };

    (
        @extract_gen_args $fixed:tt,
        (, $($tail:tt)*)
        -> bounds($($bounds:tt)*), ty_clss($($ty_clss:tt)*)
    ) => {
        StableEncodable! {
            @extract_gen_args $fixed,
            ($($tail)*)
            -> bounds($($bounds)* ,), ty_clss($($ty_clss)*)
        }
    };

    (
        @extract_gen_args $fixed:tt,
        ($lt:tt $($tail:tt)*)
        -> bounds($($bounds:tt)*), ty_clss($($ty_clss:tt)*)
    ) => {
        StableEncodable! {
            @extract_gen_args $fixed,
            ($($tail)*)
            -> bounds($($bounds)* $lt), ty_clss($($ty_clss)*)
        }
    };

    (
        @skip_inline_bound $fixed:tt,
        (, $($tail:tt)*)
        -> bounds($($bounds:tt)*), ty_clss($($ty_clss:tt)*)
    ) => {
        StableEncodable! {
            @extract_gen_args $fixed,
            ($($tail)*)
            -> bounds($($bounds)* ,), ty_clss($($ty_clss)*)
        }
    };

    (
        @skip_inline_bound $fixed:tt,
        (> { $($tail:tt)* })
        -> bounds($($bounds:tt)*), ty_clss($($ty_clss:tt)*)
    ) => {
        StableEncodable! {
            @impl $fixed,
            bounds($($bounds)*),
            ty_clss($($ty_clss)*),
            { $($tail)* }
        }
    };

    (
        @parse_variants (enum $name:ident, bounds($($bounds:tt)*), ty_clss($($ty_clss:tt)*)),
        $_id:expr, ($(,)*) -> ($($variants:tt)*)
    ) => {
        StableEncodable! {
            @as_item
            impl<$($bounds)*> rustc_serialize::Encodable for $name<$($bounds)*>
            $($ty_clss)* {
                fn encode<StableEncodableEncoder>(
                    &self,
                    s: &mut StableEncodableEncoder)
                -> Result<(), StableEncodableEncoder::Error>
                where StableEncodableEncoder: rustc_serialize::Encoder {
                    s.emit_enum(stringify!($name), |s| {
                        $(
                            StableEncodable!(@encode_variant $name, $variants, self, s);
                        )*
                        unreachable!();
                    })
                }
            }
        }
    };

    (
        @parse_variants $fixed:tt,
        $id:expr, ($var_name:ident, $($tail:tt)*) -> ($($variants:tt)*)
    ) => {
        StableEncodable! {
            @parse_variants $fixed,
            ($id + 1usize), ($($tail)*) -> ($($variants)* ($var_name, $id))
        }
    };

    (
        @parse_variants $fixed:tt,
        $id:expr, ($var_name:ident($(,)*), $($tail:tt)*) -> ($($variants:tt)*)
    ) => {
        StableEncodable! {
            @parse_variants $fixed,
            ($id + 1usize), ($($tail)*) -> ($($variants)*
                ($var_name, $id))
        }
    };

    (
        @parse_variants $fixed:tt,
        $id:expr, ($var_name:ident($_vta:ty), $($tail:tt)*) -> ($($variants:tt)*)
    ) => {
        StableEncodable! {
            @parse_variants $fixed,
            ($id + 1usize), ($($tail)*) -> ($($variants)*
                ($var_name, $id, (a)))
        }
    };

    (
        @parse_variants $fixed:tt,
        $id:expr, ($var_name:ident($_vta:ty, $_vtb:ty), $($tail:tt)*) -> ($($variants:tt)*)
    ) => {
        StableEncodable! {
            @parse_variants $fixed,
            ($id + 1usize), ($($tail)*) -> ($($variants)*
                ($var_name, $id, (a, b)))
        }
    };

    (
        @parse_variants $fixed:tt,
        $id:expr, ($var_name:ident($_vta:ty, $_vtb:ty, $_vtc:ty), $($tail:tt)*) -> ($($variants:tt)*)
    ) => {
        StableEncodable! {
            @parse_variants $fixed,
            ($id + 1usize), ($($tail)*) -> ($($variants)*
                ($var_name, $id, (a, b, c)))
        }
    };

    (
        @parse_variants $fixed:tt,
        $id:expr, ($var_name:ident { $($vfn:ident: $_vft:ty),* $(,)* }, $($tail:tt)*) -> ($($variants:tt)*)
    ) => {
        StableEncodable! {
            @parse_variants $fixed,
            ($id + 1usize), ($($tail)*) -> ($($variants)*
                ($var_name, $id, {$($vfn),*}))
        }
    };

    (
        @encode_variant $name:ident,
        ($var_name:ident, $var_id:expr),
        $self_:expr, $s:ident
    ) => {
        {
            if let $name::$var_name = *$self_ {
                return $s.emit_enum_variant(stringify!($var_name), $var_id, 0, |_| Ok(()));
            }
        }
    };

    (
        @encode_variant $name:ident,
        ($var_name:ident, $var_id:expr, ($($tup_elems:ident),*)),
        $self_:expr, $s:ident
    ) => {
        {
            if let $name::$var_name($(ref $tup_elems),*) = *$self_ {
                return $s.emit_enum_variant(
                    stringify!($var_name),
                    $var_id,
                    StableEncodable!(@count_tts $($tup_elems)*),
                    |s| {
                        let mut idx = 0;
                        $(
                            try!(s.emit_enum_variant_arg(idx, |s| $tup_elems.encode(s)));
                            idx += 1;
                        )*
                        let _ = idx;
                        Ok(())
                    }
                );
            }
        }
    };

    (
        @encode_variant $name:ident,
        ($var_name:ident, $var_id:expr, {$($str_fields:ident),*}),
        $self_:expr, $s:ident
    ) => {
        {
            if let $name::$var_name { $(ref $str_fields),* } = *$self_ {
                return $s.emit_enum_struct_variant(
                    stringify!($var_name),
                    $var_id,
                    StableEncodable!(@count_tts $($str_fields)*),
                    |s| {
                        let mut idx = 0;
                        $(
                            try!(s.emit_enum_struct_variant_field(
                                stringify!($str_fields),
                                idx,
                                |s| $str_fields.encode(s)
                            ));
                            idx += 1;
                        )*
                        let _ = idx;
                        Ok(())
                    }
                );
            }
        }
    };

    (@count_tts) => {0usize};
    (@count_tts $_tt:tt $($tail:tt)*) => {1usize + StableEncodable!(@count_tts $($tail)*)};
}

custom_derive! {
    #[derive(Debug, StableEncodable)]
    struct LazyEg<A> { a: A, b: i32, c: (u8, u8, u8) }
}

custom_derive! {
    #[derive(Clone, StableEncodable)]
    enum Wonky<S> { Flim, Flam, Flom(i32), Bees { say: S } }
}

#[test]
fn test_stable_encodable() {
    macro_rules! json {
        ($e:expr) => (rustc_serialize::json::encode(&$e).unwrap());
    }

    let lazy_eg = LazyEg {
        a: String::from("Oh hai!"),
        b: 42,
        c: (1, 3, 0),
    };
    assert_eq!(&*json!(lazy_eg), r#"{"a":"Oh hai!","b":42,"c":[1,3,0]}"#);

    assert_eq!(&*json!(Wonky::Flim::<()>), r#""Flim""#);
    assert_eq!(&*json!(Wonky::Flam::<()>), r#""Flam""#);
    assert_eq!(&*json!(Wonky::Flom::<()>(42)), r#"{"variant":"Flom","fields":[42]}"#);
    assert_eq!(&*json!(Wonky::Bees{say:"aaaaah!"}), r#"{"variant":"Bees","fields":["aaaaah!"]}"#);
}
<|endoftext|>


<commit_before>// Copyright 2016 GFX developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use super::*;

use block::Block;

#[repr(u32)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum MTLCommandBufferStatus {
    NotEnqueued = 0,
    Enqueued = 1,
    Committed = 2,
    Scheduled = 3,
    Completed = 4,
    Error = 5,
}

#[repr(u32)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum MTLCommandBufferError {
    None = 0,
    Internal = 1,
    Timeout = 2,
    PageFault = 3,
    Blacklisted = 4,
    NotPermitted = 7,
    OutOfMemory = 8,
    InvalidResource = 9,
    Memoryless = 10,
    DeviceRemoved = 11,
}

#[repr(u32)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum MTLDispatchType {
    Serial = 0,
    Concurrent = 1,
}

type CommandBufferHandler<'a> = Block<(&'a CommandBufferRef,), ()>;

pub enum MTLCommandBuffer {}

foreign_obj_type! {
    type CType = MTLCommandBuffer;
    pub struct CommandBuffer;
    pub struct CommandBufferRef;
}

impl CommandBufferRef {
    pub fn label(&self) -> &str {
        unsafe {
            let label = msg_send![self, label];
            crate::nsstring_as_str(label)
        }
    }

    pub fn set_label(&self, label: &str) {
        unsafe {
            let nslabel = crate::nsstring_from_str(label);
            let () = msg_send![self, setLabel: nslabel];
        }
    }

    pub fn enqueue(&self) {
        unsafe { msg_send![self, enqueue] }
    }

    pub fn commit(&self) {
        unsafe { msg_send![self, commit] }
    }

    pub fn status(&self) -> MTLCommandBufferStatus {
        unsafe { msg_send![self, status] }
    }

    pub fn present_drawable(&self, drawable: &DrawableRef) {
        unsafe { msg_send![self, presentDrawable: drawable] }
    }

    pub fn wait_until_completed(&self) {
        unsafe { msg_send![self, waitUntilCompleted] }
    }

    pub fn wait_until_scheduled(&self) {
        unsafe { msg_send![self, waitUntilScheduled] }
    }

    pub fn add_completed_handler(&self, block: &CommandBufferHandler) {
        unsafe { msg_send![self, addCompletedHandler: block] }
    }

    pub fn new_blit_command_encoder(&self) -> &BlitCommandEncoderRef {
        unsafe { msg_send![self, blitCommandEncoder] }
    }

    pub fn new_compute_command_encoder(&self) -> &ComputeCommandEncoderRef {
        unsafe { msg_send![self, computeCommandEncoder] }
    }

    pub fn new_render_command_encoder(
        &self,
        descriptor: &RenderPassDescriptorRef,
    ) -> &RenderCommandEncoderRef {
        unsafe { msg_send![self, renderCommandEncoderWithDescriptor: descriptor] }
    }

    pub fn new_parallel_render_command_encoder(
        &self,
        descriptor: &RenderPassDescriptorRef,
    ) -> &ParallelRenderCommandEncoderRef {
        unsafe { msg_send![self, parallelRenderCommandEncoderWithDescriptor: descriptor] }
    }

    pub fn compute_command_encoder_with_dispatch_type(
        &self,
        ty: MTLDispatchType,
    ) -> &ComputeCommandEncoderRef {
        unsafe { msg_send![self, computeCommandEncoderWithDispatchType: ty] }
    }

    pub fn encode_signal_event(&self, event: &EventRef, new_value: u64) {
        unsafe {
            msg_send![self,
                encodeSignalEvent: event
                value: new_value
            ]
        }
    }

    pub fn encode_wait_for_event(&self, event: &EventRef, value: u64) {
        unsafe {
            msg_send![self,
                encodeWaitForEvent: event
                value: value
            ]
        }
    }
}
<commit_msg>Add push | pop_debug_group fn to CommandBuffer (#232)<commit_after>// Copyright 2016 GFX developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use super::*;

use block::Block;

#[repr(u32)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum MTLCommandBufferStatus {
    NotEnqueued = 0,
    Enqueued = 1,
    Committed = 2,
    Scheduled = 3,
    Completed = 4,
    Error = 5,
}

#[repr(u32)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum MTLCommandBufferError {
    None = 0,
    Internal = 1,
    Timeout = 2,
    PageFault = 3,
    Blacklisted = 4,
    NotPermitted = 7,
    OutOfMemory = 8,
    InvalidResource = 9,
    Memoryless = 10,
    DeviceRemoved = 11,
}

#[repr(u32)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum MTLDispatchType {
    Serial = 0,
    Concurrent = 1,
}

type CommandBufferHandler<'a> = Block<(&'a CommandBufferRef,), ()>;

pub enum MTLCommandBuffer {}

foreign_obj_type! {
    type CType = MTLCommandBuffer;
    pub struct CommandBuffer;
    pub struct CommandBufferRef;
}

impl CommandBufferRef {
    pub fn label(&self) -> &str {
        unsafe {
            let label = msg_send![self, label];
            crate::nsstring_as_str(label)
        }
    }

    pub fn set_label(&self, label: &str) {
        unsafe {
            let nslabel = crate::nsstring_from_str(label);
            let () = msg_send![self, setLabel: nslabel];
        }
    }

    pub fn enqueue(&self) {
        unsafe { msg_send![self, enqueue] }
    }

    pub fn commit(&self) {
        unsafe { msg_send![self, commit] }
    }

    pub fn status(&self) -> MTLCommandBufferStatus {
        unsafe { msg_send![self, status] }
    }

    pub fn present_drawable(&self, drawable: &DrawableRef) {
        unsafe { msg_send![self, presentDrawable: drawable] }
    }

    pub fn wait_until_completed(&self) {
        unsafe { msg_send![self, waitUntilCompleted] }
    }

    pub fn wait_until_scheduled(&self) {
        unsafe { msg_send![self, waitUntilScheduled] }
    }

    pub fn add_completed_handler(&self, block: &CommandBufferHandler) {
        unsafe { msg_send![self, addCompletedHandler: block] }
    }

    pub fn new_blit_command_encoder(&self) -> &BlitCommandEncoderRef {
        unsafe { msg_send![self, blitCommandEncoder] }
    }

    pub fn new_compute_command_encoder(&self) -> &ComputeCommandEncoderRef {
        unsafe { msg_send![self, computeCommandEncoder] }
    }

    pub fn new_render_command_encoder(
        &self,
        descriptor: &RenderPassDescriptorRef,
    ) -> &RenderCommandEncoderRef {
        unsafe { msg_send![self, renderCommandEncoderWithDescriptor: descriptor] }
    }

    pub fn new_parallel_render_command_encoder(
        &self,
        descriptor: &RenderPassDescriptorRef,
    ) -> &ParallelRenderCommandEncoderRef {
        unsafe { msg_send![self, parallelRenderCommandEncoderWithDescriptor: descriptor] }
    }

    pub fn compute_command_encoder_with_dispatch_type(
        &self,
        ty: MTLDispatchType,
    ) -> &ComputeCommandEncoderRef {
        unsafe { msg_send![self, computeCommandEncoderWithDispatchType: ty] }
    }

    pub fn encode_signal_event(&self, event: &EventRef, new_value: u64) {
        unsafe {
            msg_send![self,
                encodeSignalEvent: event
                value: new_value
            ]
        }
    }

    pub fn encode_wait_for_event(&self, event: &EventRef, value: u64) {
        unsafe {
            msg_send![self,
                encodeWaitForEvent: event
                value: value
            ]
        }
    }

    pub fn push_debug_group(&self, name: &str) {
        unsafe {
            let nslabel = crate::nsstring_from_str(name);
            msg_send![self, pushDebugGroup: nslabel]
        }
    }

    pub fn pop_debug_group(&self) {
        unsafe { msg_send![self, popDebugGroup] }
    }
}
<|endoftext|>


<commit_before>//! An asynchronous, pipelined, PostgreSQL client.
//!
//! # Example
//!
//! ```no_run
//! use tokio_postgres::{NoTls, Error};
//!
//! # #[cfg(not(feature = "runtime"))] fn main() {}
//! # #[cfg(feature = "runtime")]
//! #[tokio::main] // By default, tokio_postgres uses the tokio crate as its runtime.
//! async fn main() -> Result<(), Error> {
//!     // Connect to the database.
//!     let (client, connection) =
//!         tokio_postgres::connect("host=localhost user=postgres", NoTls).await?;
//!
//!     // The connection object performs the actual communication with the database,
//!     // so spawn it off to run on its own.
//!     tokio::spawn(async move {
//!         if let Err(e) = connection.await {
//!             eprintln!("connection error: {}", e);
//!         }
//!     });
//!
//!     // Now we can execute a simple statement that just returns its parameter.
//!     let rows = client
//!         .query("SELECT $1::TEXT", &[&"hello world"])
//!         .await?;
//!
//!     // And then check that we got back the same string we sent over.
//!     let value: &str = rows[0].get(0);
//!     assert_eq!(value, "hello world");
//!
//!     Ok(())
//! }
//! ```
//!
//! # Behavior
//!
//! Calling a method like `Client::query` on its own does nothing. The associated request is not sent to the database
//! until the future returned by the method is first polled. Requests are executed in the order that they are first
//! polled, not in the order that their futures are created.
//!
//! # Pipelining
//!
//! The client supports *pipelined* requests. Pipelining can improve performance in use cases in which multiple,
//! independent queries need to be executed. In a traditional workflow, each query is sent to the server after the
//! previous query completes. In contrast, pipelining allows the client to send all of the queries to the server up
//! front, minimizing time spent by one side waiting for the other to finish sending data:
//!
//! ```not_rust
//!             Sequential                              Pipelined
//! | Client         | Server          |    | Client         | Server          |
//! |----------------|-----------------|    |----------------|-----------------|
//! | send query 1   |                 |    | send query 1   |                 |
//! |                | process query 1 |    | send query 2   | process query 1 |
//! | receive rows 1 |                 |    | send query 3   | process query 2 |
//! | send query 2   |                 |    | receive rows 1 | process query 3 |
//! |                | process query 2 |    | receive rows 2 |                 |
//! | receive rows 2 |                 |    | receive rows 3 |                 |
//! | send query 3   |                 |
//! |                | process query 3 |
//! | receive rows 3 |                 |
//! ```
//!
//! In both cases, the PostgreSQL server is executing the queries sequentially - pipelining just allows both sides of
//! the connection to work concurrently when possible.
//!
//! Pipelining happens automatically when futures are polled concurrently (for example, by using the futures `join`
//! combinator):
//!
//! ```rust
//! use futures::future;
//! use std::future::Future;
//! use tokio_postgres::{Client, Error, Statement};
//!
//! async fn pipelined_prepare(
//!     client: &Client,
//! ) -> Result<(Statement, Statement), Error>
//! {
//!     future::try_join(
//!         client.prepare("SELECT * FROM foo"),
//!         client.prepare("INSERT INTO bar (id, name) VALUES ($1, $2)")
//!     ).await
//! }
//! ```
//!
//! # Runtime
//!
//! The client works with arbitrary `AsyncRead + AsyncWrite` streams. Convenience APIs are provided to handle the
//! connection process, but these are gated by the `runtime` Cargo feature, which is enabled by default. If disabled,
//! all dependence on the tokio runtime is removed.
//!
//! # SSL/TLS support
//!
//! TLS support is implemented via external libraries. `Client::connect` and `Config::connect` take a TLS implementation
//! as an argument. The `NoTls` type in this crate can be used when TLS is not required. Otherwise, the
//! `postgres-openssl` and `postgres-native-tls` crates provide implementations backed by the `openssl` and `native-tls`
//! crates, respectively.
//!
//! # Features
//!
//! The following features can be enabled from `Cargo.toml`:
//!
//! | Feature | Description | Extra dependencies | Default |
//! | ------- | ----------- | ------------------ | ------- |
//! | `runtime` | Enable convenience API for the connection process based on the `tokio` crate. | [tokio](https://crates.io/crates/tokio) 0.2 with the features `dns`, `net` and `time` | yes |
//! | `with-bit-vec-0_6` | Enable support for the `bit-vec` crate. | [bit-vec](https://crates.io/crates/bit-vec) 0.6 | no |
//! | `with-chrono-0_4` | Enable support for the `chrono` crate. | [chrono](https://crates.io/crates/chrono) 0.4 | no |
//! | `with-eui48-0_4` | Enable support for the `eui48` crate. | [eui48](https://crates.io/crates/eui48) 0.4 | no |
//! | `with-geo-types-0_4` | Enable support for the 0.4 version of the `geo-types` crate. | [geo-types](https://crates.io/crates/geo-types/0.4.0) 0.4 | no |
//! | `with-geo-types-0_5` | Enable support for the 0.5 version of the `geo-types` crate. | [geo-types](https://crates.io/crates/geo-types/0.5.0) 0.5 | no |
//! | `with-serde_json-1` | Enable support for the `serde_json` crate. | [serde_json](https://crates.io/crates/serde_json) 1.0 | no |
//! | `with-uuid-0_8` | Enable support for the `uuid` crate. | [uuid](https://crates.io/crates/uuid) 0.8 | no |
//! | `with-time-0_2` | Enable support for the `time` crate. | [time](https://crates.io/crates/time) 0.2 | no |
#![doc(html_root_url = "https://docs.rs/tokio-postgres/0.5")]
#![warn(rust_2018_idioms, clippy::all, missing_docs)]

pub use crate::cancel_token::CancelToken;
pub use crate::client::Client;
pub use crate::config::Config;
pub use crate::connection::Connection;
pub use crate::copy_in::CopyInSink;
pub use crate::copy_out::CopyOutStream;
use crate::error::DbError;
pub use crate::error::Error;
pub use crate::generic_client::GenericClient;
pub use crate::portal::Portal;
pub use crate::query::RowStream;
pub use crate::row::{Row, SimpleQueryRow};
pub use crate::simple_query::SimpleQueryStream;
#[cfg(feature = "runtime")]
pub use crate::socket::Socket;
pub use crate::statement::{Column, Statement};
#[cfg(feature = "runtime")]
use crate::tls::MakeTlsConnect;
pub use crate::tls::NoTls;
pub use crate::to_statement::ToStatement;
pub use crate::transaction::Transaction;
pub use crate::transaction_builder::{IsolationLevel, TransactionBuilder};
use crate::types::ToSql;

pub mod binary_copy;
mod bind;
#[cfg(feature = "runtime")]
mod cancel_query;
mod cancel_query_raw;
mod cancel_token;
mod client;
mod codec;
pub mod config;
#[cfg(feature = "runtime")]
mod connect;
mod connect_raw;
#[cfg(feature = "runtime")]
mod connect_socket;
mod connect_tls;
mod connection;
mod copy_in;
mod copy_out;
pub mod error;
mod generic_client;
mod maybe_tls_stream;
mod portal;
mod prepare;
mod query;
pub mod row;
mod simple_query;
#[cfg(feature = "runtime")]
mod socket;
mod statement;
pub mod tls;
mod to_statement;
mod transaction;
mod transaction_builder;
pub mod types;

/// A convenience function which parses a connection string and connects to the database.
///
/// See the documentation for [`Config`] for details on the connection string format.
///
/// Requires the `runtime` Cargo feature (enabled by default).
///
/// [`Config`]: config/struct.Config.html
#[cfg(feature = "runtime")]
pub async fn connect<T>(
    config: &str,
    tls: T,
) -> Result<(Client, Connection<Socket, T::Stream>), Error>
where
    T: MakeTlsConnect<Socket>,
{
    let config = config.parse::<Config>()?;
    config.connect(tls).await
}

/// An asynchronous notification.
#[derive(Clone, Debug)]
pub struct Notification {
    process_id: i32,
    channel: String,
    payload: String,
}

impl Notification {
    /// The process ID of the notifying backend process.
    pub fn process_id(&self) -> i32 {
        self.process_id
    }

    /// The name of the channel that the notify has been raised on.
    pub fn channel(&self) -> &str {
        &self.channel
    }

    /// The "payload" string passed from the notifying process.
    pub fn payload(&self) -> &str {
        &self.payload
    }
}

/// An asynchronous message from the server.
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum AsyncMessage {
    /// A notice.
    ///
    /// Notices use the same format as errors, but aren't "errors" per-se.
    Notice(DbError),
    /// A notification.
    ///
    /// Connections can subscribe to notifications with the `LISTEN` command.
    Notification(Notification),
}

/// Message returned by the `SimpleQuery` stream.
#[non_exhaustive]
pub enum SimpleQueryMessage {
    /// A row of data.
    Row(SimpleQueryRow),
    /// A statement in the query has completed.
    ///
    /// The number of rows modified or selected is returned.
    CommandComplete(u64),
}

fn slice_iter<'a>(
    s: &'a [&'a (dyn ToSql + Sync)],
) -> impl ExactSizeIterator<Item = &'a dyn ToSql> + 'a {
    s.iter().map(|s| *s as _)
}
<commit_msg>Fix tokio features for runtime feature in the docs<commit_after>//! An asynchronous, pipelined, PostgreSQL client.
//!
//! # Example
//!
//! ```no_run
//! use tokio_postgres::{NoTls, Error};
//!
//! # #[cfg(not(feature = "runtime"))] fn main() {}
//! # #[cfg(feature = "runtime")]
//! #[tokio::main] // By default, tokio_postgres uses the tokio crate as its runtime.
//! async fn main() -> Result<(), Error> {
//!     // Connect to the database.
//!     let (client, connection) =
//!         tokio_postgres::connect("host=localhost user=postgres", NoTls).await?;
//!
//!     // The connection object performs the actual communication with the database,
//!     // so spawn it off to run on its own.
//!     tokio::spawn(async move {
//!         if let Err(e) = connection.await {
//!             eprintln!("connection error: {}", e);
//!         }
//!     });
//!
//!     // Now we can execute a simple statement that just returns its parameter.
//!     let rows = client
//!         .query("SELECT $1::TEXT", &[&"hello world"])
//!         .await?;
//!
//!     // And then check that we got back the same string we sent over.
//!     let value: &str = rows[0].get(0);
//!     assert_eq!(value, "hello world");
//!
//!     Ok(())
//! }
//! ```
//!
//! # Behavior
//!
//! Calling a method like `Client::query` on its own does nothing. The associated request is not sent to the database
//! until the future returned by the method is first polled. Requests are executed in the order that they are first
//! polled, not in the order that their futures are created.
//!
//! # Pipelining
//!
//! The client supports *pipelined* requests. Pipelining can improve performance in use cases in which multiple,
//! independent queries need to be executed. In a traditional workflow, each query is sent to the server after the
//! previous query completes. In contrast, pipelining allows the client to send all of the queries to the server up
//! front, minimizing time spent by one side waiting for the other to finish sending data:
//!
//! ```not_rust
//!             Sequential                              Pipelined
//! | Client         | Server          |    | Client         | Server          |
//! |----------------|-----------------|    |----------------|-----------------|
//! | send query 1   |                 |    | send query 1   |                 |
//! |                | process query 1 |    | send query 2   | process query 1 |
//! | receive rows 1 |                 |    | send query 3   | process query 2 |
//! | send query 2   |                 |    | receive rows 1 | process query 3 |
//! |                | process query 2 |    | receive rows 2 |                 |
//! | receive rows 2 |                 |    | receive rows 3 |                 |
//! | send query 3   |                 |
//! |                | process query 3 |
//! | receive rows 3 |                 |
//! ```
//!
//! In both cases, the PostgreSQL server is executing the queries sequentially - pipelining just allows both sides of
//! the connection to work concurrently when possible.
//!
//! Pipelining happens automatically when futures are polled concurrently (for example, by using the futures `join`
//! combinator):
//!
//! ```rust
//! use futures::future;
//! use std::future::Future;
//! use tokio_postgres::{Client, Error, Statement};
//!
//! async fn pipelined_prepare(
//!     client: &Client,
//! ) -> Result<(Statement, Statement), Error>
//! {
//!     future::try_join(
//!         client.prepare("SELECT * FROM foo"),
//!         client.prepare("INSERT INTO bar (id, name) VALUES ($1, $2)")
//!     ).await
//! }
//! ```
//!
//! # Runtime
//!
//! The client works with arbitrary `AsyncRead + AsyncWrite` streams. Convenience APIs are provided to handle the
//! connection process, but these are gated by the `runtime` Cargo feature, which is enabled by default. If disabled,
//! all dependence on the tokio runtime is removed.
//!
//! # SSL/TLS support
//!
//! TLS support is implemented via external libraries. `Client::connect` and `Config::connect` take a TLS implementation
//! as an argument. The `NoTls` type in this crate can be used when TLS is not required. Otherwise, the
//! `postgres-openssl` and `postgres-native-tls` crates provide implementations backed by the `openssl` and `native-tls`
//! crates, respectively.
//!
//! # Features
//!
//! The following features can be enabled from `Cargo.toml`:
//!
//! | Feature | Description | Extra dependencies | Default |
//! | ------- | ----------- | ------------------ | ------- |
//! | `runtime` | Enable convenience API for the connection process based on the `tokio` crate. | [tokio](https://crates.io/crates/tokio) 0.3 with the `io-util` feature | yes |
//! | `with-bit-vec-0_6` | Enable support for the `bit-vec` crate. | [bit-vec](https://crates.io/crates/bit-vec) 0.6 | no |
//! | `with-chrono-0_4` | Enable support for the `chrono` crate. | [chrono](https://crates.io/crates/chrono) 0.4 | no |
//! | `with-eui48-0_4` | Enable support for the `eui48` crate. | [eui48](https://crates.io/crates/eui48) 0.4 | no |
//! | `with-geo-types-0_4` | Enable support for the 0.4 version of the `geo-types` crate. | [geo-types](https://crates.io/crates/geo-types/0.4.0) 0.4 | no |
//! | `with-geo-types-0_5` | Enable support for the 0.5 version of the `geo-types` crate. | [geo-types](https://crates.io/crates/geo-types/0.5.0) 0.5 | no |
//! | `with-serde_json-1` | Enable support for the `serde_json` crate. | [serde_json](https://crates.io/crates/serde_json) 1.0 | no |
//! | `with-uuid-0_8` | Enable support for the `uuid` crate. | [uuid](https://crates.io/crates/uuid) 0.8 | no |
//! | `with-time-0_2` | Enable support for the `time` crate. | [time](https://crates.io/crates/time) 0.2 | no |
#![doc(html_root_url = "https://docs.rs/tokio-postgres/0.6")]
#![warn(rust_2018_idioms, clippy::all, missing_docs)]

pub use crate::cancel_token::CancelToken;
pub use crate::client::Client;
pub use crate::config::Config;
pub use crate::connection::Connection;
pub use crate::copy_in::CopyInSink;
pub use crate::copy_out::CopyOutStream;
use crate::error::DbError;
pub use crate::error::Error;
pub use crate::generic_client::GenericClient;
pub use crate::portal::Portal;
pub use crate::query::RowStream;
pub use crate::row::{Row, SimpleQueryRow};
pub use crate::simple_query::SimpleQueryStream;
#[cfg(feature = "runtime")]
pub use crate::socket::Socket;
pub use crate::statement::{Column, Statement};
#[cfg(feature = "runtime")]
use crate::tls::MakeTlsConnect;
pub use crate::tls::NoTls;
pub use crate::to_statement::ToStatement;
pub use crate::transaction::Transaction;
pub use crate::transaction_builder::{IsolationLevel, TransactionBuilder};
use crate::types::ToSql;

pub mod binary_copy;
mod bind;
#[cfg(feature = "runtime")]
mod cancel_query;
mod cancel_query_raw;
mod cancel_token;
mod client;
mod codec;
pub mod config;
#[cfg(feature = "runtime")]
mod connect;
mod connect_raw;
#[cfg(feature = "runtime")]
mod connect_socket;
mod connect_tls;
mod connection;
mod copy_in;
mod copy_out;
pub mod error;
mod generic_client;
mod maybe_tls_stream;
mod portal;
mod prepare;
mod query;
pub mod row;
mod simple_query;
#[cfg(feature = "runtime")]
mod socket;
mod statement;
pub mod tls;
mod to_statement;
mod transaction;
mod transaction_builder;
pub mod types;

/// A convenience function which parses a connection string and connects to the database.
///
/// See the documentation for [`Config`] for details on the connection string format.
///
/// Requires the `runtime` Cargo feature (enabled by default).
///
/// [`Config`]: config/struct.Config.html
#[cfg(feature = "runtime")]
pub async fn connect<T>(
    config: &str,
    tls: T,
) -> Result<(Client, Connection<Socket, T::Stream>), Error>
where
    T: MakeTlsConnect<Socket>,
{
    let config = config.parse::<Config>()?;
    config.connect(tls).await
}

/// An asynchronous notification.
#[derive(Clone, Debug)]
pub struct Notification {
    process_id: i32,
    channel: String,
    payload: String,
}

impl Notification {
    /// The process ID of the notifying backend process.
    pub fn process_id(&self) -> i32 {
        self.process_id
    }

    /// The name of the channel that the notify has been raised on.
    pub fn channel(&self) -> &str {
        &self.channel
    }

    /// The "payload" string passed from the notifying process.
    pub fn payload(&self) -> &str {
        &self.payload
    }
}

/// An asynchronous message from the server.
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum AsyncMessage {
    /// A notice.
    ///
    /// Notices use the same format as errors, but aren't "errors" per-se.
    Notice(DbError),
    /// A notification.
    ///
    /// Connections can subscribe to notifications with the `LISTEN` command.
    Notification(Notification),
}

/// Message returned by the `SimpleQuery` stream.
#[non_exhaustive]
pub enum SimpleQueryMessage {
    /// A row of data.
    Row(SimpleQueryRow),
    /// A statement in the query has completed.
    ///
    /// The number of rows modified or selected is returned.
    CommandComplete(u64),
}

fn slice_iter<'a>(
    s: &'a [&'a (dyn ToSql + Sync)],
) -> impl ExactSizeIterator<Item = &'a dyn ToSql> + 'a {
    s.iter().map(|s| *s as _)
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate nom;

use nom::{IResult,Needed,FlatMapOpt,line_ending,not_line_ending, space, alphanumeric, multispace};
use nom::IResult::*;

use std::str;
use std::collections::HashMap;

fn empty_result(i:&[u8]) -> IResult<&[u8], ()> { Done(i,()) }
tag!(semicolon ";");
tag!(lsb       "[");
tag!(rsb       "]");
tag!(equal     "=");

take_until_and_leave!(category_bytes "]");
fn category_name(input: &[u8]) -> IResult<&[u8], &str> {
  category_bytes(input).map_res(str::from_utf8)
}

take_until!(not_equal      "=");
take_until_either_and_leave!(value_bytes "\n;");

fn value_parser(input:&[u8]) -> IResult<&[u8], &str> {
  value_bytes(input).map_res(str::from_utf8)
}

fn parameter_parser(input: &[u8]) -> IResult<&[u8], &str> {
  alphanumeric(input).map_res(str::from_utf8)
}

o!(comment_body     <&[u8], &[u8]>       semicolon ~ [ not_line_ending ]);
o!(comment          <&[u8], ()>          comment_body ~ line_ending ~ [ empty_result ]);
opt!(opt_comment    <&[u8], &[u8]>       comment_body);

chain!(category     <&[u8], &str>,
          tag!("[")       ~
    name: category_name   ~
          tag!("]")       ~
          multispace?     ,
    ||{ name }
);
chain!(key_value    <&[u8],(&str,&str)>,
    key: parameter_parser ~
         space?           ~
         tag!("=")        ~
         space?           ~
    val: value_parser     ~
         space?           ~
         comment_body?    ~
         multispace?      ,
    ||{(key, val)}
);

fn keys_and_values<'a>(input: &'a[u8], mut z: HashMap<&'a str, &'a str>) -> IResult<&'a[u8], HashMap<&'a str, &'a str> > {
  fold0_impl!(<&[u8], HashMap<&str, &str> >, | mut h:HashMap<&'a str, &'a str>, (k, v)| {
    h.insert(k,v);
    h
  }, key_value, input, z);
}

fn keys_and_values_wrapper<'a>(input:&'a[u8]) -> IResult<&'a[u8], HashMap<&'a str, &'a str> > {
  let h: HashMap<&str, &str> = HashMap::new();
  let res = keys_and_values(input, h);
  //println!("{:?}", res);
  res
}

chain!(category_and_keys<&[u8],(&str,HashMap<&str,&str>)>,
    category: category            ~
    keys: keys_and_values_wrapper ,
    move ||{(category, keys)}
);

fn categories<'a>(input: &'a[u8]) -> IResult<&'a[u8], HashMap<&'a str, HashMap<&'a str, &'a str> > > {
  let z: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
  fold0_impl!(<&[u8], HashMap<&str, HashMap<&str, &str> > >, |mut h:HashMap<&'a str, HashMap<&'a str, &'a str> >, (k, v)| {
    h.insert(k,v);
    h
  }, category_and_keys, input, z);
}

#[test]
fn parse_comment_test() {
  let ini_file = b";comment
[category]
parameter=value
key = value2

[other]
number = 1234
str = a b cc dd ; comment";

  let ini_without_comment = b"[category]
parameter=value
key = value2

[other]
number = 1234
str = a b cc dd ; comment";

  let res = comment(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_comment, ()));
}

#[test]
fn parse_category_test() {
  let ini_file = b"[category]

parameter=value
key = value2";

  let ini_without_category = b"parameter=value
key = value2";

  let res = category(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_category, "category"));
}

#[test]
fn parse_value_test() {
  let ini_file1 = b"value
key =";
  let end = b"
key =";

  let res = value_parser(ini_file1);
  println!("{:?}", res);
  match res {
    IResult::Done(i, o) => println!("i: {:?} | o: {:?})", str::from_utf8(i), o),
    _ => println!("error")
  }

  assert_eq!(res, Done(end,  "value"));

  let ini_file2 = b"value;blah
key =";
  let end2 = b";blah
key =";

  let res2 = value_parser(ini_file2);
  println!("{:?}", res2);
  match res2 {
    IResult::Done(i, o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  assert_eq!(res2, Done(end2,  "value"));
}

#[test]
fn parse_key_value_test() {
  let ini_file = b"parameter=value
key = value2";

  let ini_without_key_value = b"key = value2";

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_key_value, ("parameter", "value")));
}

#[test]
fn parse_key_value_with_space_test() {
  let ini_file = b"parameter = value
key = value2";

  let ini_without_key_value = b"key = value2";

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_key_value, ("parameter", "value")));
}

#[test]
fn parse_key_value_with_comment_test() {
  let ini_file = b"parameter=value;abc
key = value2";

  let ini_without_key_value = b"key = value2";

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_key_value, ("parameter", "value")));
}

#[test]
fn parse_multiple_keys_and_values_test() {
  let ini_file = b"parameter=value;abc

key = value2

[category]";

  let ini_without_key_value = b"[category]";

  let h: HashMap<&str, &str> = HashMap::new();
  let res = keys_and_values(ini_file, h);
  println!("{:?}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  let mut expected: HashMap<&str, &str> = HashMap::new();
  expected.insert("parameter", "value");
  expected.insert("key", "value2");
  assert_eq!(res, Done(ini_without_key_value, expected));
}

#[test]
fn parse_category_then_multiple_keys_and_values_test() {
  //FIXME: there can be an empty line or a comment line after a category
  let ini_file = b"[abcd]
parameter=value;abc

key = value2

[category]";

  let ini_after_parser = b"[category]";

  let res = category_and_keys(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  let mut expected_h: HashMap<&str, &str> = HashMap::new();
  expected_h.insert("parameter", "value");
  expected_h.insert("key", "value2");
  assert_eq!(res, Done(ini_after_parser, ("abcd", expected_h)));
}

#[test]
fn parse_multiple_categories_test() {
  let ini_file = b"[abcd]

parameter=value;abc

key = value2

[category]
parameter3=value3
key4 = value4
";

  let ini_after_parser = b"";

  let res = categories(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  let mut expected_1: HashMap<&str, &str> = HashMap::new();
  expected_1.insert("parameter", "value");
  expected_1.insert("key", "value2");
  let mut expected_2: HashMap<&str, &str> = HashMap::new();
  expected_2.insert("parameter3", "value3");
  expected_2.insert("key4", "value4");
  let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
  expected_h.insert("abcd",     expected_1);
  expected_h.insert("category", expected_2);
  assert_eq!(res, Done(ini_after_parser, expected_h));
}

<commit_msg>remove some usage of fold macros<commit_after>#[macro_use]
extern crate nom;

use nom::{IResult,Needed,FlatMapOpt,line_ending,not_line_ending, space, alphanumeric, multispace};
use nom::IResult::*;

use std::str;
use std::collections::HashMap;

fn empty_result(i:&[u8]) -> IResult<&[u8], ()> { Done(i,()) }
tag!(semicolon ";");
tag!(lsb       "[");
tag!(rsb       "]");
tag!(equal     "=");

take_until_and_leave!(category_bytes "]");
fn category_name(input: &[u8]) -> IResult<&[u8], &str> {
  category_bytes(input).map_res(str::from_utf8)
}

take_until!(not_equal      "=");
take_until_either_and_leave!(value_bytes "\n;");

fn value_parser(input:&[u8]) -> IResult<&[u8], &str> {
  value_bytes(input).map_res(str::from_utf8)
}

fn parameter_parser(input: &[u8]) -> IResult<&[u8], &str> {
  alphanumeric(input).map_res(str::from_utf8)
}

o!(comment_body     <&[u8], &[u8]>       semicolon ~ [ not_line_ending ]);
o!(comment          <&[u8], ()>          comment_body ~ line_ending ~ [ empty_result ]);
opt!(opt_comment    <&[u8], &[u8]>       comment_body);

chain!(category     <&[u8], &str>,
          tag!("[")       ~
    name: category_name   ~
          tag!("]")       ~
          multispace?     ,
    ||{ name }
);

chain!(key_value    <&[u8],(&str,&str)>,
    key: parameter_parser ~
         space?           ~
         tag!("=")        ~
         space?           ~
    val: value_parser     ~
         space?           ~
         comment_body?    ~
         multispace?      ,
    ||{(key, val)}
);

named!(keys_and_values_aggregator<&[u8], Vec<(&str,&str)> >, many0!(key_value));

fn keys_and_values(input:&[u8]) -> IResult<&[u8], HashMap<&str, &str> > {
  let mut h: HashMap<&str, &str> = HashMap::new();

  match keys_and_values_aggregator(input) {
    IResult::Done(i,tuple_vec) => {
      for &(k,v) in tuple_vec.iter() {
        h.insert(k, v);
      }
      IResult::Done(i, h)
    },
    IResult::Incomplete(a)     => IResult::Incomplete(a),
    IResult::Error(a)          => IResult::Error(a)
  }
}

chain!(category_and_keys<&[u8],(&str,HashMap<&str,&str>)>,
    category: category    ~
    keys: keys_and_values ,
    move ||{(category, keys)}
);

named!(categories_aggregator<&[u8], Vec<(&str, HashMap<&str,&str>)> >, many0!(category_and_keys));

fn categories(input: &[u8]) -> IResult<&[u8], HashMap<&str, HashMap<&str, &str> > > {
  let mut h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();

  match categories_aggregator(input) {
    IResult::Done(i,tuple_vec) => {
      for &(k,ref v) in tuple_vec.iter() {
        h.insert(k, v.clone());
      }
      IResult::Done(i, h)
    },
    IResult::Incomplete(a)     => IResult::Incomplete(a),
    IResult::Error(a)          => IResult::Error(a)
  }
}

#[test]
fn parse_comment_test() {
  let ini_file = b";comment
[category]
parameter=value
key = value2

[other]
number = 1234
str = a b cc dd ; comment";

  let ini_without_comment = b"[category]
parameter=value
key = value2

[other]
number = 1234
str = a b cc dd ; comment";

  let res = comment(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_comment, ()));
}

#[test]
fn parse_category_test() {
  let ini_file = b"[category]

parameter=value
key = value2";

  let ini_without_category = b"parameter=value
key = value2";

  let res = category(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_category, "category"));
}

#[test]
fn parse_value_test() {
  let ini_file1 = b"value
key =";
  let end = b"
key =";

  let res = value_parser(ini_file1);
  println!("{:?}", res);
  match res {
    IResult::Done(i, o) => println!("i: {:?} | o: {:?})", str::from_utf8(i), o),
    _ => println!("error")
  }

  assert_eq!(res, Done(end,  "value"));

  let ini_file2 = b"value;blah
key =";
  let end2 = b";blah
key =";

  let res2 = value_parser(ini_file2);
  println!("{:?}", res2);
  match res2 {
    IResult::Done(i, o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  assert_eq!(res2, Done(end2,  "value"));
}

#[test]
fn parse_key_value_test() {
  let ini_file = b"parameter=value
key = value2";

  let ini_without_key_value = b"key = value2";

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_key_value, ("parameter", "value")));
}

#[test]
fn parse_key_value_with_space_test() {
  let ini_file = b"parameter = value
key = value2";

  let ini_without_key_value = b"key = value2";

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_key_value, ("parameter", "value")));
}

#[test]
fn parse_key_value_with_comment_test() {
  let ini_file = b"parameter=value;abc
key = value2";

  let ini_without_key_value = b"key = value2";

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_key_value, ("parameter", "value")));
}

#[test]
fn parse_multiple_keys_and_values_test() {
  let ini_file = b"parameter=value;abc

key = value2

[category]";

  let ini_without_key_value = b"[category]";

  let res = keys_and_values(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  let mut expected: HashMap<&str, &str> = HashMap::new();
  expected.insert("parameter", "value");
  expected.insert("key", "value2");
  assert_eq!(res, Done(ini_without_key_value, expected));
}

#[test]
fn parse_category_then_multiple_keys_and_values_test() {
  //FIXME: there can be an empty line or a comment line after a category
  let ini_file = b"[abcd]
parameter=value;abc

key = value2

[category]";

  let ini_after_parser = b"[category]";

  let res = category_and_keys(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  let mut expected_h: HashMap<&str, &str> = HashMap::new();
  expected_h.insert("parameter", "value");
  expected_h.insert("key", "value2");
  assert_eq!(res, Done(ini_after_parser, ("abcd", expected_h)));
}

#[test]
fn parse_multiple_categories_test() {
  let ini_file = b"[abcd]

parameter=value;abc

key = value2

[category]
parameter3=value3
key4 = value4
";

  let ini_after_parser = b"";

  let res = categories(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  let mut expected_1: HashMap<&str, &str> = HashMap::new();
  expected_1.insert("parameter", "value");
  expected_1.insert("key", "value2");
  let mut expected_2: HashMap<&str, &str> = HashMap::new();
  expected_2.insert("parameter3", "value3");
  expected_2.insert("key4", "value4");
  let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
  expected_h.insert("abcd",     expected_1);
  expected_h.insert("category", expected_2);
  assert_eq!(res, Done(ini_after_parser, expected_h));
}

<|endoftext|>