<commit_before>#![feature(rustc_private)]
#![feature(integer_atomics)]
#![feature(ip)]
#![feature(thread_id)]

extern crate mio ;

#[macro_use]
extern crate bitflags ;

#[macro_use]
extern crate log ;
extern crate env_logger ;

extern crate async_dnssd ;
extern crate futures;
extern crate tokio_core;
extern crate openssl ;

extern crate byteorder ;

extern crate chrono ;

pub mod nslogger ;

use nslogger::Logger ;

/// Initializes the global logger with a Logger instance.
///
/// This should be called early in the execution of a Rust program, and the
/// global logger may only be initialized once. Future initialization
/// attempts will return an error.
pub fn init() -> Result<(), log::SetLoggerError> {
    log::set_logger(|max_log_level| {
        max_log_level.set(log::LogLevelFilter::Info) ;
        Box::new(Logger::new())
    })
}

#[cfg(test)]
mod tests {
    use nslogger::{ Logger, Domain, Level } ;

    #[test]
    fn connects_via_bonjour_with_ssl() {
        let mut log = Logger::new() ;
        log.set_message_flushing(true) ;
        log.log_b(Some(Domain::App), Level::Warning, "test") ;
    }

    #[test]
    fn creates_logger_instance() {
        let log = Logger::new() ;
        log.log_b(Some(Domain::App), Level::Warning, "test") ;
        log.log_b(Some(Domain::DB), Level::Error, "test1") ;
        log.log_b(Some(Domain::DB), Level::Debug, "test2") ;
        log.log_b(Some(Domain::DB), Level::Warning, "test") ;
        log.log_b(Some(Domain::DB), Level::Error, "test1") ;
        log.log_b(Some(Domain::DB), Level::Debug, "test2") ;
        log.log_b(Some(Domain::Custom("MyCustomDomain".to_string())), Level::Debug, "Tag test!") ;
        log.log_c("Just a simple message") ;
        //thread::sleep(Duration::from_secs(100)) ;
    }

    #[test]
    fn logs_empty_domain() {
        let mut log = Logger::new() ;
        log.set_message_flushing(true) ;
        log.log_b(Some(Domain::Custom("".to_string())), Level::Warning, "no domain should appear") ;
    }

    #[test]
    fn parses_domain_from_string() {
        use std::str::FromStr ;
        assert_eq!(Domain::App, Domain::from_str("App").unwrap()) ;
        assert_eq!(Domain::DB, Domain::from_str("DB").unwrap()) ;
        assert_eq!(Domain::Custom("CustomTag".to_string()), Domain::from_str("CustomTag").unwrap()) ;

    }

    #[test]
    fn flushes_log_messages() {
        // TODO a better approach would probably be to write a small thread that crashes, with a
        // message that has to be passed before the crash?
        let mut log = Logger::new() ;
        log.set_remote_host("192.168.0.8", 50000, true) ; // SSL Will be on on the desktop client no matter the setting
        log.set_message_flushing(true) ;
        log.log_b(Some(Domain::App), Level::Warning, "flush test") ;
        log.log_b(Some(Domain::DB), Level::Error, "flush test1") ;
        log.log_b(Some(Domain::DB), Level::Debug, "flush test2") ;
        log.log_b(Some(Domain::DB), Level::Warning, "flush test") ;
        log.log_b(Some(Domain::DB), Level::Error, "flush test1") ;
        log.log_b(Some(Domain::DB), Level::Debug, "flush test2") ;
    }

    #[test]
    fn logs_mark(){
        let mut log = Logger::new() ;
        log.set_remote_host("192.168.0.8", 50000, true) ;
        log.set_message_flushing(true) ;
        log.log_b(Some(Domain::App), Level::Warning, "before mark 1") ;
        log.log_b(Some(Domain::DB), Level::Error, "before mark 2") ;
        log.log_mark(Some("this is a mark")) ;
        log.log_b(Some(Domain::DB), Level::Debug, "after mark") ;
    }

    #[test]
    fn logs_empty_mark(){
        let mut log = Logger::new() ;
        log.set_remote_host("192.168.0.8", 50000, true) ;
        log.set_message_flushing(true) ;
        log.log_b(Some(Domain::App), Level::Warning, "before mark 1") ;
        log.log_b(Some(Domain::DB), Level::Error, "before mark 2") ;
        log.log_mark(None) ;
        log.log_b(Some(Domain::DB), Level::Debug, "after mark") ;
    }

    #[test]
    fn logs_image() {
        use std::fs::File;
        use std::env ;
        use std::io::Read ;
        let image_path = &env::current_dir().unwrap().join("tests/fixtures/zebra.png") ;
        let mut file_handle = File::open(image_path).unwrap() ;
        let mut buffer:Vec<u8> = vec![] ;

        file_handle.read_to_end(&mut buffer).unwrap() ;

        let mut log = Logger::new() ;
        log.set_remote_host("192.168.0.8", 50000, true) ;
        log.set_message_flushing(true) ;
        log.log_image(None, None, None, None, Level::Warning, &buffer) ;
    }

    #[test]
    fn logs_binary_data() {
        let bytes:[u8;8] = [ 0x6c, 0x6f, 0x67, 0x20, 0x74, 0x65, 0x73, 0x74 ] ;
        // should read 'log test'

        let mut log = Logger::new() ;
        log.set_remote_host("192.168.0.8", 50000, true) ;
        log.set_message_flushing(true) ;
        log.log_data(None, None, None, None, Level::Warning, &bytes) ;
    }
}
<commit_msg>- Sets test addresses to loopback.<commit_after>#![feature(rustc_private)]
#![feature(integer_atomics)]
#![feature(ip)]
#![feature(thread_id)]

extern crate mio ;

#[macro_use]
extern crate bitflags ;

#[macro_use]
extern crate log ;
extern crate env_logger ;

extern crate async_dnssd ;
extern crate futures;
extern crate tokio_core;
extern crate openssl ;

extern crate byteorder ;

extern crate chrono ;

pub mod nslogger ;

use nslogger::Logger ;

/// Initializes the global logger with a Logger instance.
///
/// This should be called early in the execution of a Rust program, and the
/// global logger may only be initialized once. Future initialization
/// attempts will return an error.
pub fn init() -> Result<(), log::SetLoggerError> {
    log::set_logger(|max_log_level| {
        max_log_level.set(log::LogLevelFilter::Info) ;
        Box::new(Logger::new())
    })
}

#[cfg(test)]
mod tests {
    use nslogger::{ Logger, Domain, Level } ;

    #[test]
    fn connects_via_bonjour_with_ssl() {
        let mut log = Logger::new() ;
        log.set_message_flushing(true) ;
        log.log_b(Some(Domain::App), Level::Warning, "test") ;
    }

    #[test]
    fn creates_logger_instance() {
        let log = Logger::new() ;
        log.log_b(Some(Domain::App), Level::Warning, "test") ;
        log.log_b(Some(Domain::DB), Level::Error, "test1") ;
        log.log_b(Some(Domain::DB), Level::Debug, "test2") ;
        log.log_b(Some(Domain::DB), Level::Warning, "test") ;
        log.log_b(Some(Domain::DB), Level::Error, "test1") ;
        log.log_b(Some(Domain::DB), Level::Debug, "test2") ;
        log.log_b(Some(Domain::Custom("MyCustomDomain".to_string())), Level::Debug, "Tag test!") ;
        log.log_c("Just a simple message") ;
        //thread::sleep(Duration::from_secs(100)) ;
    }

    #[test]
    fn logs_empty_domain() {
        let mut log = Logger::new() ;
        log.set_message_flushing(true) ;
        log.log_b(Some(Domain::Custom("".to_string())), Level::Warning, "no domain should appear") ;
    }

    #[test]
    fn parses_domain_from_string() {
        use std::str::FromStr ;
        assert_eq!(Domain::App, Domain::from_str("App").unwrap()) ;
        assert_eq!(Domain::DB, Domain::from_str("DB").unwrap()) ;
        assert_eq!(Domain::Custom("CustomTag".to_string()), Domain::from_str("CustomTag").unwrap()) ;

    }

    #[test]
    fn flushes_log_messages() {
        // TODO a better approach would probably be to write a small thread that crashes, with a
        // message that has to be passed before the crash?
        let mut log = Logger::new() ;
        log.set_remote_host("127.0.0.1", 50000, true) ; // SSL Will be on on the desktop client no matter the setting
        log.set_message_flushing(true) ;
        log.log_b(Some(Domain::App), Level::Warning, "flush test") ;
        log.log_b(Some(Domain::DB), Level::Error, "flush test1") ;
        log.log_b(Some(Domain::DB), Level::Debug, "flush test2") ;
        log.log_b(Some(Domain::DB), Level::Warning, "flush test") ;
        log.log_b(Some(Domain::DB), Level::Error, "flush test1") ;
        log.log_b(Some(Domain::DB), Level::Debug, "flush test2") ;
    }

    #[test]
    fn logs_mark(){
        let mut log = Logger::new() ;
        log.set_remote_host("127.0.0.1", 50000, true) ;
        log.set_message_flushing(true) ;
        log.log_b(Some(Domain::App), Level::Warning, "before mark 1") ;
        log.log_b(Some(Domain::DB), Level::Error, "before mark 2") ;
        log.log_mark(Some("this is a mark")) ;
        log.log_b(Some(Domain::DB), Level::Debug, "after mark") ;
    }

    #[test]
    fn logs_empty_mark(){
        let mut log = Logger::new() ;
        log.set_remote_host("127.0.0.1", 50000, true) ;
        log.set_message_flushing(true) ;
        log.log_b(Some(Domain::App), Level::Warning, "before mark 1") ;
        log.log_b(Some(Domain::DB), Level::Error, "before mark 2") ;
        log.log_mark(None) ;
        log.log_b(Some(Domain::DB), Level::Debug, "after mark") ;
    }

    #[test]
    fn logs_image() {
        use std::fs::File;
        use std::env ;
        use std::io::Read ;
        let image_path = &env::current_dir().unwrap().join("tests/fixtures/zebra.png") ;
        let mut file_handle = File::open(image_path).unwrap() ;
        let mut buffer:Vec<u8> = vec![] ;

        file_handle.read_to_end(&mut buffer).unwrap() ;

        let mut log = Logger::new() ;
        log.set_remote_host("127.0.0.1", 50000, true) ;
        log.set_message_flushing(true) ;
        log.log_image(None, None, None, None, Level::Warning, &buffer) ;
    }

    #[test]
    fn logs_binary_data() {
        let bytes:[u8;8] = [ 0x6c, 0x6f, 0x67, 0x20, 0x74, 0x65, 0x73, 0x74 ] ;
        // should read 'log test'

        let mut log = Logger::new() ;
        log.set_remote_host("127.0.0.1", 50000, true) ;
        log.set_message_flushing(true) ;
        log.log_data(None, None, None, None, Level::Warning, &bytes) ;
    }
}
<|endoftext|>


<commit_before>//! # Derive Getters

#[macro_use] extern crate quote;
extern crate proc_macro;
extern crate syn;

use std::convert::From;

use proc_macro::TokenStream;

use syn::{Data, Type, Ident, Fields, Field, token::Comma};
use syn::punctuated::Punctuated;

static INVALID_STRUCT: &str = "Struct must be a named struct. Not unnamed or unit.";
static INVALID_VARIANT: &str = "Variant must be a struct. Not an enum or union.";
static GETTER_PREFIX: &str = "get_";

type TraitSlots = Vec<TraitSlot>;
type TraitName = Ident;
type StructName = Ident;

/// For building a list of methods that need to be in the trait.
struct TraitSlot {
    label: Ident,
    name: Ident,
    ty: Type,
}

impl TraitSlot {
    fn new(label: Ident, name: Ident, ty: Type) -> TraitSlot {
        TraitSlot {
            label: label,
            name: name,
            ty: ty,
        }
    }
}

fn field_to_trait_slot(field: &Field) -> TraitSlot {
    let name = field.ident.as_ref().unwrap().clone();
    let label = Ident::from(format!("{}{}", GETTER_PREFIX, &name).as_str());
    TraitSlot::new(label, name, field.ty.clone())
}

#[proc_macro_derive(Getters)]
pub fn getters(input: TokenStream) -> TokenStream {
    let ast = syn::parse(input).unwrap();
    let (trait_tokens, struct_name, trait_name, trait_slots) = setup_getters_trait(&ast);
    let impl_tokens = setup_trait_impl(struct_name, trait_name, trait_slots);

    let mut tokens = quote::Tokens::new();
    tokens.append_all(trait_tokens.into_iter());
    tokens.append_all(impl_tokens.into_iter());

    //println!("Tokens: {}", &tokens);
    tokens.into()
}

/// Fetch the slots (aka fields) in a structure. If the passed in ast is not a `struct`, it
/// return `None`.
fn get_slots<'a>(
    data: &'a syn::Data
) -> Result<&'a Punctuated<Field, Comma>, &'static str> {
    match data {
        &Data::Struct(ref body) => {
            match &body.fields {
                &Fields::Named(ref named) => Ok(&named.named),
                _ => Err(INVALID_STRUCT),
            }
        },
        _ => Err(INVALID_VARIANT),
    }
}

fn setup_getters_trait<'a>(
    ast: &'a syn::DeriveInput
) -> (quote::Tokens, StructName, TraitName, TraitSlots)  {
    let slots: Vec<TraitSlot> = get_slots(&ast.data)
        .unwrap_or_else(|e| panic!("Couldn't autogenerate: {}", e))
        .iter()
        .map(field_to_trait_slot)
        .collect();

    let trait_methods: Vec<quote::Tokens> = slots
        .iter()
        .map(|slot| {
            let label = slot.label.clone();
            let ty = slot.ty.clone();
            quote! {
                fn #label(&self) -> &#ty;
            }
        })
        .collect();

    let struct_name = ast.ident.clone();
    let trait_name = Ident::from(format!("{}Getters", &struct_name).as_str());

    let tokens = quote! {
        pub trait #trait_name {
            #(#trait_methods)*
        }
    };

    (tokens, struct_name, trait_name, slots)
}

fn setup_trait_impl(
    struct_name: StructName, trait_name: TraitName, slots: TraitSlots
) -> quote::Tokens {
    let trait_methods: Vec<quote::Tokens> = slots
        .into_iter()
        .map(|slot| {
            let label = slot.label.clone();
            let name = slot.name.clone();
            let ty = slot.ty.clone();
            quote! {
                fn #label(&self) -> &#ty {
                    &self.#name
                }
            }
        })
        .collect();

    quote! {
        impl #trait_name for #struct_name {
            #(#trait_methods)*
        }
    }
}
<commit_msg>WIP: Do some docs.<commit_after>//! Derive Getters
//! ===
//!
//! Macro for autogenerating getters. Can only be used on named structs. Will generate
//! getters that will reside in a new trait so as not to pollute the struct namespace.
//!
//! # Usage
//! Add to your project Cargo.toml;
//! ```toml
//! [dependencies]
//! derive-getters = "0.0.4"
//! ```
//!
//! In lib.rs or main.rs;
//! ```
//! #[macro_use]
//! extern crate derive_getters;
//! #
//! # fn main() { }
//! ```
//!
//! # Example
//! ```
//! #[macro_use]
//! extern crate derive_getters;
//!
//! #[derive(Getters)]
//! struct Number {
//!    num: u64,
//! }
//!
//! fn main() {
//!    let number = Number { num: 655 };
//!    assert!(number.get_num() == &655);
//! }
//! ```

#[macro_use] extern crate quote;
extern crate proc_macro;
extern crate syn;

use std::convert::From;

use proc_macro::TokenStream;

use syn::{Data, Type, Ident, Fields, Field, token::Comma};
use syn::punctuated::Punctuated;

static INVALID_STRUCT: &str = "Struct must be a named struct. Not unnamed or unit.";
static INVALID_VARIANT: &str = "Variant must be a struct. Not an enum or union.";
static GETTER_PREFIX: &str = "get_";

type TraitSlots = Vec<TraitSlot>;
type TraitName = Ident;
type StructName = Ident;

/// Derive getters into a seperate trait for the named struct.
#[proc_macro_derive(Getters)]
pub fn getters(input: TokenStream) -> TokenStream {
    let ast = syn::parse(input).unwrap();
    let (trait_tokens, struct_name, trait_name, trait_slots) = setup_getters_trait(&ast);
    let impl_tokens = setup_trait_impl(struct_name, trait_name, trait_slots);

    let mut tokens = quote::Tokens::new();
    tokens.append_all(trait_tokens.into_iter());
    tokens.append_all(impl_tokens.into_iter());

    //println!("Tokens: {}", &tokens);
    tokens.into()
}

// For building a list of methods that need to be in the trait.
struct TraitSlot {
    label: Ident,
    name: Ident,
    ty: Type,
}

impl TraitSlot {
    fn new(label: Ident, name: Ident, ty: Type) -> TraitSlot {
        TraitSlot {
            label: label,
            name: name,
            ty: ty,
        }
    }
}

fn field_to_trait_slot(field: &Field) -> TraitSlot {
    let name = field.ident.as_ref().unwrap().clone();
    let label = Ident::from(format!("{}{}", GETTER_PREFIX, &name).as_str());
    TraitSlot::new(label, name, field.ty.clone())
}

// Fetch the slots (aka fields) in a structure. If the passed in ast is not a `struct`, it
// return `None`.
fn get_slots<'a>(
    data: &'a syn::Data
) -> Result<&'a Punctuated<Field, Comma>, &'static str> {
    match data {
        &Data::Struct(ref body) => {
            match &body.fields {
                &Fields::Named(ref named) => Ok(&named.named),
                _ => Err(INVALID_STRUCT),
            }
        },
        _ => Err(INVALID_VARIANT),
    }
}

fn setup_getters_trait<'a>(
    ast: &'a syn::DeriveInput
) -> (quote::Tokens, StructName, TraitName, TraitSlots)  {
    let slots: Vec<TraitSlot> = get_slots(&ast.data)
        .unwrap_or_else(|e| panic!("Couldn't autogenerate: {}", e))
        .iter()
        .map(field_to_trait_slot)
        .collect();

    let trait_methods: Vec<quote::Tokens> = slots
        .iter()
        .map(|slot| {
            let label = slot.label.clone();
            let ty = slot.ty.clone();
            quote! {
                fn #label(&self) -> &#ty;
            }
        })
        .collect();

    let struct_name = ast.ident.clone();
    let trait_name = Ident::from(format!("{}Getters", &struct_name).as_str());

    let tokens = quote! {
        pub trait #trait_name {
            #(#trait_methods)*
        }
    };

    (tokens, struct_name, trait_name, slots)
}

fn setup_trait_impl(
    struct_name: StructName, trait_name: TraitName, slots: TraitSlots
) -> quote::Tokens {
    let trait_methods: Vec<quote::Tokens> = slots
        .into_iter()
        .map(|slot| {
            let label = slot.label.clone();
            let name = slot.name.clone();
            let ty = slot.ty.clone();
            quote! {
                fn #label(&self) -> &#ty {
                    &self.#name
                }
            }
        })
        .collect();

    quote! {
        impl #trait_name for #struct_name {
            #(#trait_methods)*
        }
    }
}
<|endoftext|>


<commit_before>//! `bincode` is a crate for encoding and decoding using a tiny binary
//! serialization strategy.
//!
//! There are simple functions for encoding to `Vec<u8>` and decoding from
//! `&[u8]`, but the meat of the library is the `encode_into` and `decode_from`
//! functions which respectively allow encoding into a `std::io::Writer`
//! and decoding from a `std::io::Buffer`.
//!
//! ## Modules
//! There are two ways to encode and decode structs using `bincode`, either using `rustc_serialize`
//! or the `serde` crate.  `rustc_serialize` and `serde` are crates and and also the names of their
//! corresponding modules inside of `bincode`.  Both modules have exactly equivalant functions, and
//! and the only difference is whether or not the library user wants to use `rustc_serialize` or
//! `serde`.
//!
//! ### Using Basic Functions
//!
//! ```rust
//! #![allow(unstable)]
//! extern crate bincode;
//! use bincode::rustc_serialize::{encode, decode};
//! fn main() {
//!     // The object that we will serialize.
//!     let target = Some("hello world".to_string());
//!     // The maximum size of the encoded message.
//!     let limit = bincode::SizeLimit::Bounded(20);
//!
//!     let encoded: Vec<u8>        = encode(&target, limit).unwrap();
//!     let decoded: Option<String> = decode(&encoded[..]).unwrap();
//!     assert_eq!(target, decoded);
//! }
//! ```

#![crate_name = "bincode"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]

#![doc(html_logo_url = "./icon.png")]

extern crate rustc_serialize as rustc_serialize_crate;
extern crate byteorder;
extern crate num;
extern crate serde as serde_crate;

pub use refbox::{RefBox, StrBox, SliceBox};

mod refbox;
pub mod rustc_serialize;
pub mod serde;

/// A limit on the amount of bytes that can be read or written.
///
/// Size limits are an incredibly important part of both encoding and decoding.
///
/// In order to prevent DOS attacks on a decoder, it is important to limit the
/// amount of bytes that a single encoded message can be; otherwise, if you
/// are decoding bytes right off of a TCP stream for example, it would be
/// possible for an attacker to flood your server with a 3TB vec, causing the
/// decoder to run out of memory and crash your application!
/// Because of this, you can provide a maximum-number-of-bytes that can be read
/// during decoding, and the decoder will explicitly fail if it has to read
/// any more than that.
///
/// On the other side, you want to make sure that you aren't encoding a message
/// that is larger than your decoder expects.  By supplying a size limit to an
/// encoding function, the encoder will verify that the structure can be encoded
/// within that limit.  This verification occurs before any bytes are written to
/// the Writer, so recovering from an the error is easy.
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum SizeLimit {
    Infinite,
    Bounded(u64)
}

<commit_msg>fix typo<commit_after>//! `bincode` is a crate for encoding and decoding using a tiny binary
//! serialization strategy.
//!
//! There are simple functions for encoding to `Vec<u8>` and decoding from
//! `&[u8]`, but the meat of the library is the `encode_into` and `decode_from`
//! functions which respectively allow encoding into a `std::io::Writer`
//! and decoding from a `std::io::Buffer`.
//!
//! ## Modules
//! There are two ways to encode and decode structs using `bincode`, either using `rustc_serialize`
//! or the `serde` crate.  `rustc_serialize` and `serde` are crates and and also the names of their
//! corresponding modules inside of `bincode`.  Both modules have exactly equivalant functions, and
//! and the only difference is whether or not the library user wants to use `rustc_serialize` or
//! `serde`.
//!
//! ### Using Basic Functions
//!
//! ```rust
//! #![allow(unstable)]
//! extern crate bincode;
//! use bincode::rustc_serialize::{encode, decode};
//! fn main() {
//!     // The object that we will serialize.
//!     let target = Some("hello world".to_string());
//!     // The maximum size of the encoded message.
//!     let limit = bincode::SizeLimit::Bounded(20);
//!
//!     let encoded: Vec<u8>        = encode(&target, limit).unwrap();
//!     let decoded: Option<String> = decode(&encoded[..]).unwrap();
//!     assert_eq!(target, decoded);
//! }
//! ```

#![crate_name = "bincode"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]

#![doc(html_logo_url = "./icon.png")]

extern crate rustc_serialize as rustc_serialize_crate;
extern crate byteorder;
extern crate num;
extern crate serde as serde_crate;

pub use refbox::{RefBox, StrBox, SliceBox};

mod refbox;
pub mod rustc_serialize;
pub mod serde;

/// A limit on the amount of bytes that can be read or written.
///
/// Size limits are an incredibly important part of both encoding and decoding.
///
/// In order to prevent DOS attacks on a decoder, it is important to limit the
/// amount of bytes that a single encoded message can be; otherwise, if you
/// are decoding bytes right off of a TCP stream for example, it would be
/// possible for an attacker to flood your server with a 3TB vec, causing the
/// decoder to run out of memory and crash your application!
/// Because of this, you can provide a maximum-number-of-bytes that can be read
/// during decoding, and the decoder will explicitly fail if it has to read
/// any more than that.
///
/// On the other side, you want to make sure that you aren't encoding a message
/// that is larger than your decoder expects.  By supplying a size limit to an
/// encoding function, the encoder will verify that the structure can be encoded
/// within that limit.  This verification occurs before any bytes are written to
/// the Writer, so recovering from an error is easy.
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum SizeLimit {
    Infinite,
    Bounded(u64)
}

<|endoftext|>


<commit_before>#![feature(conservative_impl_trait)]

pub mod instructions;
<commit_msg>didn't need that feature<commit_after>pub mod instructions;
<|endoftext|>


<commit_before>/* Copyright 2016-2022 Torbjørn Birch Moltu
 * Copyright 2018 Aljoscha Meyer
 *
 * 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.
 */

/*!
Miscellaneous UTF-8 and UTF-16 types and methods.

# Optional features:
* `#![no_std]`-mode: There are a few differences:
  * `Error` doesn't exist, but `description()` is made available as an inherent impl.
  * `Extend`/`FromIterator`-implementations for `String`/`Vec<u8>`/`Vec<u16>` are missing.
  * There is no `io`, so `Utf8Iterator` and `Utf8CharSplitter` doesn't implement `Read`.

  This feature is enabled by setting `default-features=false` in `Cargo.toml`:
  `encode_unicode = {version="0.3.4", default-features=false}`
* Integration with the [ascii](https://tomprogrammer.github.io/rust-ascii/ascii/index.html) crate:
  Convert `Utf8Char` and `Utf16Char` to and from
  [`ascii::AsciiChar`](https://tomprogrammer.github.io/rust-ascii/ascii/enum.AsciiChar.html).

# Minimum supported Rust version

The minimum supported Rust version for 1.0.\* releases is 1.56.
Later 1.y.0 releases might require newer Rust versions, but the three most
recent stable releases at the time of publishing will always be supported.
For example this means that if the current stable Rust version is 1.66 when
`encode_unicode` 1.1.0 is released, then `encode_unicode` 1.1.\* will
not require a newer Rust version than 1.63.

[crates.io page](https://crates.io/crates/encode_unicode)
[github repository](https://github.com/tormol/encode_unicode)

*/

#![cfg_attr(not(feature="std"), no_std)]

#![warn(missing_docs, unsafe_op_in_unsafe_fn)]
#![allow(
    clippy::unusual_byte_groupings,// I sometimes group into UTF-8 control part and codepoint part
    clippy::derive_hash_xor_eq,// tested
    clippy::len_without_is_empty,// the character types are never empty
    clippy::needless_return,// `foo.bar();\n foo` looks unfinished
    clippy::redundant_closure_call,// not redundant in macros
    clippy::cast_lossless,// the sizes are part of the struct name and so won't change
    clippy::many_single_char_names,// the variables are in different scopes
    clippy::cmp_owned,// smaller than pointer, and no allocations anyway
    clippy::wrong_self_convention,// smaller than pointer
    clippy::needless_range_loop,// the suggested iterator chains are less intuitive
    clippy::identity_op,// applying a set of opereations with varying arguments to many elements looks nice
)]
#![warn(clippy::doc_markdown, clippy::manual_filter_map)]
// opt-in lints that might be interesting to recheck once in a while:
//#![warn(clippy::unwrap_used)]

mod errors;
mod traits;
mod utf8_char;
mod utf8_iterators;
mod utf16_char;
mod utf16_iterators;
mod decoding_iterators;

pub use traits::{CharExt, U8UtfExt, U16UtfExt, StrExt, IterExt, SliceExt};
pub use utf8_char::Utf8Char;
pub use utf16_char::Utf16Char;

pub mod error {// keeping the public interface in one file
    //! Errors returned by various conversion methods in this crate.
    pub use crate::errors::{FromStrError, EmptyStrError};
    pub use crate::errors::{CodepointError, NonAsciiError, NonBmpError};
    pub use crate::errors::{Utf8Error, Utf8ErrorKind};
    pub use crate::errors::{InvalidUtf16Slice, InvalidUtf16Array, InvalidUtf16Tuple};
    pub use crate::errors::{InvalidUtf16FirstUnit, Utf16PairError};
}

pub mod iterator {
    //! Iterator types that you should rarely need to name
    pub use crate::utf8_iterators::{Utf8Iterator, Utf8CharSplitter, Utf8Chars, Utf8CharIndices};
    pub use crate::utf16_iterators::{Utf16Iterator, Utf16CharSplitter, Utf16Chars, Utf16CharIndices};
    pub use crate::decoding_iterators::{Utf8CharMerger, Utf8CharDecoder};
    pub use crate::decoding_iterators::{Utf16CharMerger, Utf16CharDecoder};
}
<commit_msg>Disable more clippy lints<commit_after>/* Copyright 2016-2022 Torbjørn Birch Moltu
 * Copyright 2018 Aljoscha Meyer
 *
 * 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.
 */

/*!
Miscellaneous UTF-8 and UTF-16 types and methods.

# Optional features:
* `#![no_std]`-mode: There are a few differences:
  * `Error` doesn't exist, but `description()` is made available as an inherent impl.
  * `Extend`/`FromIterator`-implementations for `String`/`Vec<u8>`/`Vec<u16>` are missing.
  * There is no `io`, so `Utf8Iterator` and `Utf8CharSplitter` doesn't implement `Read`.

  This feature is enabled by setting `default-features=false` in `Cargo.toml`:
  `encode_unicode = {version="0.3.4", default-features=false}`
* Integration with the [ascii](https://tomprogrammer.github.io/rust-ascii/ascii/index.html) crate:
  Convert `Utf8Char` and `Utf16Char` to and from
  [`ascii::AsciiChar`](https://tomprogrammer.github.io/rust-ascii/ascii/enum.AsciiChar.html).

# Minimum supported Rust version

The minimum supported Rust version for 1.0.\* releases is 1.56.
Later 1.y.0 releases might require newer Rust versions, but the three most
recent stable releases at the time of publishing will always be supported.
For example this means that if the current stable Rust version is 1.66 when
`encode_unicode` 1.1.0 is released, then `encode_unicode` 1.1.\* will
not require a newer Rust version than 1.63.

[crates.io page](https://crates.io/crates/encode_unicode)
[github repository](https://github.com/tormol/encode_unicode)

*/

#![cfg_attr(not(feature="std"), no_std)]

#![warn(missing_docs, unsafe_op_in_unsafe_fn)]
#![allow(
    clippy::unusual_byte_groupings,// I sometimes group into UTF-8 control part and codepoint part
    clippy::derive_hash_xor_eq,// tested
    clippy::len_without_is_empty,// the character types are never empty
    clippy::needless_return,// `foo.bar();\n foo` looks unfinished
    clippy::redundant_closure_call,// not redundant in macros
    clippy::cast_lossless,// the sizes are part of the struct name and so won't change
    clippy::many_single_char_names,// the variables are in different scopes
    clippy::cmp_owned,// smaller than pointer, and no allocations anyway
    clippy::wrong_self_convention,// smaller than pointer
    clippy::needless_range_loop,// the suggested iterator chains are less intuitive
    clippy::identity_op,// applying a set of opereations with varying arguments to many elements looks nice
    clippy::get_first,// .get(0), .get(1) is more readable
    clippy::question_mark,// I prefer it very explicit
)]
#![warn(clippy::doc_markdown, clippy::manual_filter_map)]
// opt-in lints that might be interesting to recheck once in a while:
//#![warn(clippy::unwrap_used)]

mod errors;
mod traits;
mod utf8_char;
mod utf8_iterators;
mod utf16_char;
mod utf16_iterators;
mod decoding_iterators;

pub use traits::{CharExt, U8UtfExt, U16UtfExt, StrExt, IterExt, SliceExt};
pub use utf8_char::Utf8Char;
pub use utf16_char::Utf16Char;

pub mod error {// keeping the public interface in one file
    //! Errors returned by various conversion methods in this crate.
    pub use crate::errors::{FromStrError, EmptyStrError};
    pub use crate::errors::{CodepointError, NonAsciiError, NonBmpError};
    pub use crate::errors::{Utf8Error, Utf8ErrorKind};
    pub use crate::errors::{InvalidUtf16Slice, InvalidUtf16Array, InvalidUtf16Tuple};
    pub use crate::errors::{InvalidUtf16FirstUnit, Utf16PairError};
}

pub mod iterator {
    //! Iterator types that you should rarely need to name
    pub use crate::utf8_iterators::{Utf8Iterator, Utf8CharSplitter, Utf8Chars, Utf8CharIndices};
    pub use crate::utf16_iterators::{Utf16Iterator, Utf16CharSplitter, Utf16Chars, Utf16CharIndices};
    pub use crate::decoding_iterators::{Utf8CharMerger, Utf8CharDecoder};
    pub use crate::decoding_iterators::{Utf16CharMerger, Utf16CharDecoder};
}
<|endoftext|>


<commit_before>#![crate_name = "locale"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![feature(core, env, path, io, std_misc)]

//! Localisation is hard.
//!
//! Getting your program to work well in multiple languages is a world fraught with edge-cases,
//! minor grammatical errors, and most importantly, subtle things that don't map over well that you
//! have absolutely no idea are different in other cultures.
//!
//! Many people are aware of the simpler ones, such as whether to use decimal points or decimal
//! commas, or that the names of the months are different in other languages. But there are also
//! different ways to format dates and times, or variations on what day the week begins. It's
//! perfectly possible to write your program unaware of how these things have to be changed at all,
//! and that's why it's so hard.

use std::old_io::fs::PathExtensions;
use std::old_io::{IoResult, File, BufferedReader};
use std::env::var;
use std::num::{Int, Float};
use std::fmt::Display;

/// The directory inside which locale files are found.
///
/// For example, the set of Korean files will be in
/// `/usr/share/locale/ko`.
static LOCALE_DIR: &'static str = "/usr/share/locale";

#[derive(Debug, Clone)]
enum LocaleType {
    Numeric, Time,
}

fn find_locale_path(locale_type: LocaleType) -> Option<Path> {
    if let Ok(numeric_path) = var("LC_ALL") {
        let path = Path::new(numeric_path);
        if path.exists() {
            return Some(path);
        }
    }

    let file_name = match locale_type {
        LocaleType::Numeric => "LC_NUMERIC",
        LocaleType::Time    => "LC_TIME",
    };

    if let Ok(numeric_path) = var(file_name) {
        let path = Path::new(numeric_path);
        if path.exists() {
            return Some(path);
        }
    }

    if let Ok(lang) = var("LANG") {
        let path = Path::new(LOCALE_DIR).join(Path::new(lang)).join(Path::new(file_name));

        if path.exists() {
            return Some(path);
        }
    }

    None
}

/// Information on how to format numbers.
#[derive(Debug, Clone)]
pub struct Numeric {
    /// The punctuation that separates the decimal part of a non-integer number. Usually a decimal
    /// point or a decimal comma.
    pub decimal_sep: String,

    /// The punctuation that separates groups of digits in long numbers.
    pub thousands_sep: String,
}

impl Numeric {
    pub fn load_user_locale() -> IoResult<Numeric> {
        let path = find_locale_path(LocaleType::Numeric);

        if let Some(path) = path {
            let mut file = BufferedReader::new(File::open(&path));
            let lines: Vec<String> = file.lines().map(|x| x.unwrap()).collect();

            Ok(Numeric {
                decimal_sep: lines[0].trim().to_string(),
                thousands_sep: lines[1].trim().to_string(),
            })
        }
        else {
            return Ok(Numeric::english());
        }
    }

    pub fn english() -> Numeric {
        Numeric::new(".", ",")
    }

    pub fn new(decimal_sep: &str, thousands_sep: &str) -> Numeric {
        Numeric {
            decimal_sep: decimal_sep.to_string(),
            thousands_sep: thousands_sep.to_string(),
        }
    }

    pub fn format_int<I: Int + Display>(&self, input: I) -> String {
        let s = input.to_string();
        let mut buf = String::new();

        for (i, c) in s.chars().enumerate() {
            buf.push(c);
            if (s.len() - i - 1) % 3 == 0 && i != s.len() - 1 {
                buf.push_str(&self.thousands_sep[]);
            }
        }

        buf
    }

    pub fn format_float<F: Float>(&self, input: F, decimal_places: usize) -> String {
        use std::num::strconv;
        strconv::float_to_str_common(input, 10, false, strconv::SignFormat::SignNone, strconv::SignificantDigits::DigExact(decimal_places), strconv::ExponentFormat::ExpNone, false).0.replace(".", self.decimal_sep.as_slice())
    }
}

// ---- time stuff ---

#[derive(Debug, Clone)]
pub struct Time {
    month_names: Vec<String>,
    long_month_names: Vec<String>,
    day_names: Vec<String>,
    long_day_names: Vec<String>,
}

impl Time {
    pub fn load_user_locale() -> IoResult<Time> {
        let path = find_locale_path(LocaleType::Time);

        if let Some(path) = path {
            let mut file = BufferedReader::new(File::open(&path));
            let mut iter = file.lines().map(|x| x.unwrap().trim().to_string());

            let month_names      = iter.by_ref().take(12).collect();
            let long_month_names = iter.by_ref().take(12).collect();
            let day_names        = iter.by_ref().take(7).collect();
            let long_day_names   = iter.by_ref().take(7).collect();

            Ok(Time {
                month_names:      month_names,
                long_month_names: long_month_names,
                day_names:        day_names,
                long_day_names:   long_day_names,
            })
        }
        else {
            return Ok(Time::english());
        }
    }

    pub fn english() -> Time {
        Time {
            month_names: vec![
                "Jan".to_string(),  "Feb".to_string(),  "Mar".to_string(),
                "Apr".to_string(),  "May".to_string(),  "Jun".to_string(),
                "Jul".to_string(),  "Aug".to_string(),  "Sep".to_string(),
                "Oct".to_string(),  "Nov".to_string(),  "Dec".to_string(),
            ],
            long_month_names: vec![
                "January".to_string(),    "February".to_string(),
                "March".to_string(),      "April".to_string(),
                "May".to_string(),        "June".to_string(),
                "July".to_string(),       "August".to_string(),
                "September".to_string(),  "October".to_string(),
                "November".to_string(),   "December".to_string(),
            ],
            day_names: vec![
                "Sun".to_string(),
                "Mon".to_string(),  "Tue".to_string(),  "Wed".to_string(),
                "Thu".to_string(),  "Fri".to_string(),  "Sat".to_string(),
            ],
            long_day_names: vec![
                "Sunday".to_string(),
                "Monday".to_string(),    "Tuesday".to_string(),  "Wednesday".to_string(),
                "Thursday".to_string(),  "Friday".to_string(),   "Saturday".to_string(),
            ],
        }
    }

    pub fn long_month_name(&self, months_from_january: usize) -> String {
        self.long_month_names[months_from_january].clone()
    }

    pub fn short_month_name(&self, months_from_january: usize) -> String {
        self.month_names[months_from_january].clone()
    }

    pub fn long_day_name(&self, days_from_sunday: usize) -> String {
        self.day_names[days_from_sunday].clone()
    }

    pub fn short_day_name(&self, days_from_sunday: usize) -> String {
        self.day_names[days_from_sunday].clone()
    }

}

// ---- tests ----

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

    #[test]
    fn thousands_separator() {
        let numeric_options = Numeric::new("/", "=");
        assert_eq!("1=234=567".to_string(), numeric_options.format_int(1234567))
    }

    #[test]
    fn thousands_separator_2() {
        let numeric_options = Numeric::new("/", "=");
        assert_eq!("123=456".to_string(), numeric_options.format_int(123456))
    }

    #[test]
    fn thousands_separator_3() {
        let numeric_options = Numeric::new("/", "=");
        assert_eq!("12=345=678".to_string(), numeric_options.format_int(12345678))
    }
}
<commit_msg>Remove silly note<commit_after>#![crate_name = "locale"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![feature(core, env, path, io, std_misc)]

//! Localisation is hard.
//!
//! Getting your program to work well in multiple languages is a world fraught with edge-cases,
//! minor grammatical errors, and most importantly, subtle things that don't map over well that you
//! have absolutely no idea are different in other cultures.
//!
//! Many people are aware of the simpler ones, such as whether to use decimal points or decimal
//! commas, or that the names of the months are different in other languages. But there are also
//! different ways to format dates and times, or variations on what day the week begins. It's
//! perfectly possible to write your program unaware of how these things have to be changed at all,
//! and that's why it's so hard.

use std::old_io::fs::PathExtensions;
use std::old_io::{IoResult, File, BufferedReader};
use std::env::var;
use std::num::{Int, Float};
use std::fmt::Display;

/// The directory inside which locale files are found.
///
/// For example, the set of Korean files will be in
/// `/usr/share/locale/ko`.
static LOCALE_DIR: &'static str = "/usr/share/locale";

#[derive(Debug, Clone)]
enum LocaleType {
    Numeric, Time,
}

fn find_locale_path(locale_type: LocaleType) -> Option<Path> {
    if let Ok(numeric_path) = var("LC_ALL") {
        let path = Path::new(numeric_path);
        if path.exists() {
            return Some(path);
        }
    }

    let file_name = match locale_type {
        LocaleType::Numeric => "LC_NUMERIC",
        LocaleType::Time    => "LC_TIME",
    };

    if let Ok(numeric_path) = var(file_name) {
        let path = Path::new(numeric_path);
        if path.exists() {
            return Some(path);
        }
    }

    if let Ok(lang) = var("LANG") {
        let path = Path::new(LOCALE_DIR).join(Path::new(lang)).join(Path::new(file_name));

        if path.exists() {
            return Some(path);
        }
    }

    None
}

/// Information on how to format numbers.
#[derive(Debug, Clone)]
pub struct Numeric {
    /// The punctuation that separates the decimal part of a non-integer number. Usually a decimal
    /// point or a decimal comma.
    pub decimal_sep: String,

    /// The punctuation that separates groups of digits in long numbers.
    pub thousands_sep: String,
}

impl Numeric {
    pub fn load_user_locale() -> IoResult<Numeric> {
        let path = find_locale_path(LocaleType::Numeric);

        if let Some(path) = path {
            let mut file = BufferedReader::new(File::open(&path));
            let lines: Vec<String> = file.lines().map(|x| x.unwrap()).collect();

            Ok(Numeric {
                decimal_sep: lines[0].trim().to_string(),
                thousands_sep: lines[1].trim().to_string(),
            })
        }
        else {
            return Ok(Numeric::english());
        }
    }

    pub fn english() -> Numeric {
        Numeric::new(".", ",")
    }

    pub fn new(decimal_sep: &str, thousands_sep: &str) -> Numeric {
        Numeric {
            decimal_sep: decimal_sep.to_string(),
            thousands_sep: thousands_sep.to_string(),
        }
    }

    pub fn format_int<I: Int + Display>(&self, input: I) -> String {
        let s = input.to_string();
        let mut buf = String::new();

        for (i, c) in s.chars().enumerate() {
            buf.push(c);
            if (s.len() - i - 1) % 3 == 0 && i != s.len() - 1 {
                buf.push_str(&self.thousands_sep[..]);
            }
        }

        buf
    }

    pub fn format_float<F: Float>(&self, input: F, decimal_places: usize) -> String {
        use std::num::strconv;
        strconv::float_to_str_common(input, 10, false, strconv::SignFormat::SignNone, strconv::SignificantDigits::DigExact(decimal_places), strconv::ExponentFormat::ExpNone, false).0.replace(".", self.decimal_sep.as_slice())
    }
}

// ---- time stuff ---

#[derive(Debug, Clone)]
pub struct Time {
    month_names: Vec<String>,
    long_month_names: Vec<String>,
    day_names: Vec<String>,
    long_day_names: Vec<String>,
}

impl Time {
    pub fn load_user_locale() -> IoResult<Time> {
        let path = find_locale_path(LocaleType::Time);

        if let Some(path) = path {
            let mut file = BufferedReader::new(File::open(&path));
            let mut iter = file.lines().map(|x| x.unwrap().trim().to_string());

            let month_names      = iter.by_ref().take(12).collect();
            let long_month_names = iter.by_ref().take(12).collect();
            let day_names        = iter.by_ref().take(7).collect();
            let long_day_names   = iter.by_ref().take(7).collect();

            Ok(Time {
                month_names:      month_names,
                long_month_names: long_month_names,
                day_names:        day_names,
                long_day_names:   long_day_names,
            })
        }
        else {
            return Ok(Time::english());
        }
    }

    pub fn english() -> Time {
        Time {
            month_names: vec![
                "Jan".to_string(),  "Feb".to_string(),  "Mar".to_string(),
                "Apr".to_string(),  "May".to_string(),  "Jun".to_string(),
                "Jul".to_string(),  "Aug".to_string(),  "Sep".to_string(),
                "Oct".to_string(),  "Nov".to_string(),  "Dec".to_string(),
            ],
            long_month_names: vec![
                "January".to_string(),    "February".to_string(),
                "March".to_string(),      "April".to_string(),
                "May".to_string(),        "June".to_string(),
                "July".to_string(),       "August".to_string(),
                "September".to_string(),  "October".to_string(),
                "November".to_string(),   "December".to_string(),
            ],
            day_names: vec![
                "Sun".to_string(),
                "Mon".to_string(),  "Tue".to_string(),  "Wed".to_string(),
                "Thu".to_string(),  "Fri".to_string(),  "Sat".to_string(),
            ],
            long_day_names: vec![
                "Sunday".to_string(),
                "Monday".to_string(),    "Tuesday".to_string(),  "Wednesday".to_string(),
                "Thursday".to_string(),  "Friday".to_string(),   "Saturday".to_string(),
            ],
        }
    }

    pub fn long_month_name(&self, months_from_january: usize) -> String {
        self.long_month_names[months_from_january].clone()
    }

    pub fn short_month_name(&self, months_from_january: usize) -> String {
        self.month_names[months_from_january].clone()
    }

    pub fn long_day_name(&self, days_from_sunday: usize) -> String {
        self.day_names[days_from_sunday].clone()
    }

    pub fn short_day_name(&self, days_from_sunday: usize) -> String {
        self.day_names[days_from_sunday].clone()
    }

}

// ---- tests ----

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

    #[test]
    fn thousands_separator() {
        let numeric_options = Numeric::new("/", "=");
        assert_eq!("1=234=567".to_string(), numeric_options.format_int(1234567))
    }

    #[test]
    fn thousands_separator_2() {
        let numeric_options = Numeric::new("/", "=");
        assert_eq!("123=456".to_string(), numeric_options.format_int(123456))
    }

    #[test]
    fn thousands_separator_3() {
        let numeric_options = Numeric::new("/", "=");
        assert_eq!("12=345=678".to_string(), numeric_options.format_int(12345678))
    }
}
<|endoftext|>


<commit_before>#![feature(macro_rules)]
use std::collections::{HashMap, TreeMap, VecMap, TrieMap};
use std::hash::Hash;

/// Conversion from an `Iterator` of pairs.
pub trait Groupable<K, V> {
    /// Loops through the entire iterator, grouping all keys into a container
    /// implementing `FromKeyedIterator` with a container of values per key.
    /// The values will be aggregated per key into a container implementing
    /// `Extendable` for the value type.
    ///
    /// # Example
    ///
    /// ```rust
    /// use std::collections::HashMap;
    /// use groupable::Groupable;
    ///
    /// let evens = range(0u, 10).map(|i| (i % 2 == 0, i))
    ///                         .group::<HashMap<bool, Vec<uint>>>();
    ///
    /// assert_eq!(evens[true].as_slice(), [0, 2, 4, 6, 8].as_slice());
    /// assert_eq!(evens[false].as_slice(), [1, 3, 5, 7, 9].as_slice());
    /// ```
    fn group<B: FromKeyedIterator<K, V>>(&mut self) -> B;
}

impl<K, V, I: Iterator<(K, V)>> Groupable<K, V> for I {
    fn group<B: FromKeyedIterator<K, V>>(&mut self) -> B {
        FromKeyedIterator::from_keyed_iter(self.by_ref())
    }
}

/// Conversion from an `Iterator` of key-value pairs.
pub trait FromKeyedIterator<K, V> {
    /// Build a container with groups of elements from an external iterator.
    fn from_keyed_iter<I: Iterator<(K, V)>>(I) -> Self;
}

macro_rules! group_into(
    ($iter:ident, $map:ident) => (
        for (key, val) in $iter {
            let val_iter = Some(val).into_iter();
            match $map.find_mut(&key) {
                Some(collection) => {
                    collection.extend(val_iter);
                    continue
                },
                None => {} // insert below
            }

            $map.insert(key, FromIterator::from_iter(val_iter));
        }
    )
)

macro_rules! impl_keyed_iter (
    ($name:ident: $($bounds:ident),+) => (
        impl <K: $($bounds+)+, V, U: Extend<V> + FromIterator<V>> FromKeyedIterator<K, V> for $name<K, U> {
            fn from_keyed_iter<T: Iterator<(K, V)>>(mut iter: T) -> $name<K, U> {
                let mut map = $name::<K, U>::new();
                group_into!(iter, map);
                map
            }
        }
    )
)

macro_rules! impl_uint_keyed_iter (
    ($name:ident) => (
        impl <V, U: Extend<V> + FromIterator<V>> FromKeyedIterator<uint, V> for $name<U> {
            fn from_keyed_iter<T: Iterator<(uint, V)>>(mut iter: T) -> $name<U> {
                let mut map = $name::<U>::new();
                group_into!(iter, map)
                map
            }
        }
    )
)

impl_keyed_iter!(HashMap: Ord, Hash)
impl_keyed_iter!(TreeMap: Ord)
impl_uint_keyed_iter!(VecMap)
impl_uint_keyed_iter!(TrieMap)
<commit_msg>Fix compilation errors with rust v0.13.0<commit_after>#![feature(macro_rules)]
use std::collections::{HashMap, TreeMap, VecMap, TrieMap};
use std::hash::Hash;

/// Conversion from an `Iterator` of pairs.
pub trait Groupable<K, V> {
    /// Loops through the entire iterator, grouping all keys into a container
    /// implementing `FromKeyedIterator` with a container of values per key.
    /// The values will be aggregated per key into a container implementing
    /// `Extendable` for the value type.
    ///
    /// # Example
    ///
    /// ```rust
    /// use std::collections::HashMap;
    /// use groupable::Groupable;
    ///
    /// let evens = range(0u, 10).map(|i| (i % 2 == 0, i))
    ///                         .group::<HashMap<bool, Vec<uint>>>();
    ///
    /// assert_eq!(evens[true].as_slice(), [0, 2, 4, 6, 8].as_slice());
    /// assert_eq!(evens[false].as_slice(), [1, 3, 5, 7, 9].as_slice());
    /// ```
    fn group<B: FromKeyedIterator<K, V>>(&mut self) -> B;
}

impl<K, V, I: Iterator<(K, V)>> Groupable<K, V> for I {
    fn group<B: FromKeyedIterator<K, V>>(&mut self) -> B {
        FromKeyedIterator::from_keyed_iter(self.by_ref())
    }
}

/// Conversion from an `Iterator` of key-value pairs.
pub trait FromKeyedIterator<K, V> {
    /// Build a container with groups of elements from an external iterator.
    fn from_keyed_iter<I: Iterator<(K, V)>>(I) -> Self;
}

macro_rules! group_into(
    ($iter:ident, $map:ident) => (
        for (key, val) in $iter {
            let val_iter = Some(val).into_iter();
            match $map.find_mut(&key) {
                Some(collection) => {
                    collection.extend(val_iter);
                    continue
                },
                None => {} // insert below
            }

            $map.insert(key, FromIterator::from_iter(val_iter));
        }
    )
);

macro_rules! impl_keyed_iter (
    ($name:ident: $($bounds:ident),+) => (
        impl <K: $($bounds+)+, V, U: Extend<V> + FromIterator<V>> FromKeyedIterator<K, V> for $name<K, U> {
            fn from_keyed_iter<T: Iterator<(K, V)>>(mut iter: T) -> $name<K, U> {
                let mut map = $name::<K, U>::new();
                group_into!(iter, map);
                map
            }
        }
    )
);

macro_rules! impl_uint_keyed_iter (
    ($name:ident) => (
        impl <V, U: Extend<V> + FromIterator<V>> FromKeyedIterator<uint, V> for $name<U> {
            fn from_keyed_iter<T: Iterator<(uint, V)>>(mut iter: T) -> $name<U> {
                let mut map = $name::<U>::new();
                group_into!(iter, map);
                map
            }
        }
    )
);

impl_keyed_iter!(HashMap: Ord, Hash);
impl_keyed_iter!(TreeMap: Ord);
impl_uint_keyed_iter!(VecMap);
impl_uint_keyed_iter!(TrieMap);
<|endoftext|>


<commit_before>// See erts-6.1/doc/html/erl_ext_dist.html

#![feature(struct_variant)]     // this is for enum Eterm
#![allow(non_camel_case_types)] // this is for enum ErlTermTag
#![feature(macro_rules)]        // for try_io!

extern crate num;
extern crate collections;
extern crate debug;

use std::string::String;
use std::vec::Vec;
use std::num::FromPrimitive;
use std::io;
use num::bigint::BigInt;
use collections::bitv::Bitv;

#[deriving(FromPrimitive, Show)]
enum ErlTermTag {
    // ATOM_CACHE_REF = 82,
    SMALL_INTEGER_EXT = 97,
    INTEGER_EXT = 98,
    FLOAT_EXT = 99,
    ATOM_EXT = 100,
    REFERENCE_EXT = 101,
    PORT_EXT = 102,
    PID_EXT = 103,
    SMALL_TUPLE_EXT = 104,
    LARGE_TUPLE_EXT = 105,
    MAP_EXT = 116,
    NIL_EXT = 106,
    STRING_EXT = 107,
    LIST_EXT = 108,
    BINARY_EXT = 109,
    SMALL_BIG_EXT = 110,
    LARGE_BIG_EXT = 111,
    NEW_REFERENCE_EXT = 114,
    SMALL_ATOM_EXT = 115,
    FUN_EXT = 117,
    NEW_FUN_EXT = 112,
    EXPORT_EXT = 113,
    BIT_BINARY_EXT = 77,
    NEW_FLOAT_EXT = 70,
    ATOM_UTF8_EXT = 118,
    SMALL_ATOM_UTF8_EXT = 119,
}

//#[deriving(PartialOrd, PartialEq, Ord, Eq)]
#[deriving(Show)]
pub enum Eterm {
    SmallInteger(u8),           // small_integer
    Integer(int),               // integer
    Float(f64),                 // float, new_float
    Atom(Atom),                 // atom, small_atom, atom_utf8, small_atom_utf8
    Reference {                 // reference, new_reference
        node: Atom,
        id: Vec<u8>,
        creation: u8},
    Port {                      // poort
        node: Atom,
        id: u8,
        creation: u8},
    Pid(Pid),                   // pid
    Tuple(Tuple),               // small_tuple, large_tuple
    Map(EMap),                  // map
    Nil,                        // nil
    String(String),             // string
    List(List),                 // list
    Binary(Vec<u8>),            // binary
    BigNum(BigInt),             // small_big, large_big
    Fun {                       // fun
        pid: Pid,
        module: Atom,
        index: u32,
        uniq: u32,
        free_vars: Vec<Eterm>},
    NewFun {                    // new_fun
        arity: u8,
        uniq: u16,
        index: u32,
        module: Atom,
        old_index: u32,
        old_uniq: u16,
        pid: Pid,
        free_vars: Vec<Eterm>},
    Export {                    // export
        module: Atom,
        function: Atom,
        arity: u8,
    },
    BitBinary(Bitv),            // bit_binary; XXX: maybe choose some other representation?
}
pub type Atom = String;
pub type Tuple = Vec<Eterm>;
pub type EMap = Vec<(Eterm, Eterm)>; // k-v pairs //TreeMap<Eterm, Eterm>;
pub type List = Vec<Eterm>;

//#[deriving(PartialOrd, PartialEq, Ord, Eq)]
#[deriving(Show)]
pub struct Pid {                // moved out from enum because it used in Eterm::{Fun,NewFun}
    node: Atom,
    id: u8,
    serial: u32,
    creation: u8,
}

// enum BuildError {
//     IoError(io::IoError),
//     BErr,
// }
type BuildResult = io::IoResult<Eterm>;//, BuildError>;

struct Builder<T> {
    rdr: T,
}

// macro_rules! try_io(
//     ($e:expr) => (
//         match $e {
//             Ok(e) => e,
//             Err(e) => return IoError(e)
//         }
//         )
// )

impl<T: io::Reader> Builder<T> {
    fn new(rdr: T) -> Builder<T> {
        Builder{rdr: rdr}
    }
    fn read_prelude(&mut self) -> io::IoResult<bool> {
        Ok(131 == try!(self.rdr.read_u8()))
    }
    fn build(&mut self) -> BuildResult {
        let int_tag = try!(self.rdr.read_u8());
        let tag: Option<ErlTermTag> = FromPrimitive::from_u8(int_tag);
        match tag {
            Some(SMALL_INTEGER_EXT) =>
                Ok(SmallInteger(try!(self.rdr.read_u8()))),
            Some(INTEGER_EXT) =>
                Ok(Integer(try!(self.rdr.read_be_int()))),
            Some(ATOM_EXT) | Some(ATOM_UTF8_EXT) => {
                let len = try!(self.rdr.read_be_u16());
                let data = try!(self.rdr.read_exact(len as uint));
                // XXX: data is in latin1 in case of ATOM_EXT
                match String::from_utf8(data) {
                    Ok(atom) =>
                        Ok(Atom(atom)),
                    Err(_) =>
                        Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Bad utf-8",
                            detail: None // format!("{}", data)
                            })
                }
            },
            Some(REFERENCE_EXT) => {
                let node = match try!(self.build()) {
                    Atom(a) =>
                        a,
                    _ =>
                        return Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Assertion failed: Atom expected",
                            detail: None
                        })
                };
                let id = try!(self.rdr.read_exact(4));
                let creation = try!(self.rdr.read_u8());
                Ok(Reference {
                    node: node,
                    id: id,
                    creation: creation
                })
            },
            Some(SMALL_TUPLE_EXT) => {
                let mut tuple: Tuple = Vec::new();
                let arity = try!(self.rdr.read_u8());
                for _ in range(0, arity) {
                    let term = try!(self.build());
                    tuple.push(term)
                }
                Ok(Tuple(tuple))
            },
            Some(MAP_EXT) => {
                let mut map: EMap = Vec::new();
                let arity = try!(self.rdr.read_be_u32());
                for _ in range(0, arity) {
                    let key = try!(self.build());
                    let val = try!(self.build());
                    map.push((key, val))
                }
                Ok(Map(map))
            },
            Some(NIL_EXT) =>
                Ok(Nil),
            Some(BINARY_EXT) => {
                let len = try!(self.rdr.read_be_u32());
                Ok(Binary(try!(self.rdr.read_exact(len as uint))))
            },
            Some(NEW_REFERENCE_EXT) => {
                let len = try!(self.rdr.read_be_u16());
                let node = match try!(self.build()) {
                    Atom(a) =>
                        a,
                    _ =>
                        return Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Assertion failed: Atom expected",
                            detail: None
                        })
                };
                let creation = try!(self.rdr.read_u8());
                let id = try!(self.rdr.read_exact(4 * len as uint));
                Ok(Reference{
                    node: node,
                    id: id, // here id should be Vec<u32>, but since it's not interpreted, leave it as is
                    creation: creation
                })
            },
            Some(SMALL_ATOM_EXT) | Some(SMALL_ATOM_UTF8_EXT) => {
                let len = try!(self.rdr.read_u8());
                let data = try!(self.rdr.read_exact(len as uint));
                // XXX: data is in latin1 in case of ATOM_EXT
                match String::from_utf8(data) {
                    Ok(atom) =>
                        Ok(Atom(atom)),
                    Err(_) =>
                        Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Bad utf-8",
                            detail: None // format!("{}", data)
                            })
                }
            },
            Some(t) =>
                Err(io::IoError{
                    kind: io::OtherIoError,
                    desc: "Tag not implemented",
                    detail: Some(format!("Tag: #{} - {}", int_tag, t))
                }),
            None =>
                Err(io::IoError{
                    kind: io::OtherIoError,
                    desc: "Invalid data type",
                    detail: Some(format!("Tag: #{}", int_tag))
                })
        }
    }
}

fn main() {
    use std::io::{File,BufferedReader};

    for i in range(70, 120) {
        let tag: Option<ErlTermTag> = FromPrimitive::from_int(i);
        println!("{} => {:?}", i, tag);
    }
    println!("==============================");
    let map = vec!( (Atom("my_map_key".to_string()), Nil) );

    let term: Eterm = NewFun {
        arity: 3,
        uniq: 1234,
        index: 10,
        module: "my_mod".to_string(),
        old_index: 1212,
        old_uniq: 1234,
        pid: Pid {node: "wasd".to_string(), id: 1, serial: 123, creation: 2},
        free_vars: vec!(//Float(3.14),
                        Nil,
                        Binary(vec!(1, 2, 3, 4)),
                        Export {
                            module: "my_mod".to_string(),
                            function: "my_func".to_string(),
                            arity: 4},
                        List(vec!(SmallInteger(1), Integer(1000000), Nil)),
                        Tuple(vec!(Atom("record".to_string()), Map(map))),
                        ),
    };
    println!("{:?}", term);
    println!("==============================");
    match term {
        NewFun {free_vars: vars, ..} =>
            println!("{:?}", vars.as_slice()),
        _ => ()
    }
    println!("==============================");
    let f = BufferedReader::new(File::open(&Path::new("test/test_terms.bin")));
    let mut builder = Builder::new(f);
    match builder.read_prelude() {
        Ok(true) =>
            println!("Valid eterm"),
        Ok(false) =>
            println!("Invalid eterm!"),
        Err(io::IoError{desc: d, ..}) => {
            println!("IoError: {}", d);
            return
        }
    }
    println!("{}", builder.build());
}
<commit_msg>Mostly all types are implemented * Floats * Port * Pid * Large_tuple * String * List * Small Big int * Funs<commit_after>// See erts-6.1/doc/html/erl_ext_dist.html

#![feature(struct_variant)]     // this is for enum Eterm
#![allow(non_camel_case_types)] // this is for enum ErlTermTag
#![feature(macro_rules)]        // for try_io!

extern crate num;
extern crate collections;
extern crate debug;

use std::string::String;
use std::vec::Vec;
use std::num::FromPrimitive;
use std::io;
use num::bigint;
use collections::bitv::Bitv;

#[deriving(FromPrimitive, Show)]
enum ErlTermTag {
    // ATOM_CACHE_REF = 82,
    SMALL_INTEGER_EXT = 97,
    INTEGER_EXT = 98,
    FLOAT_EXT = 99,
    ATOM_EXT = 100,
    REFERENCE_EXT = 101,
    PORT_EXT = 102,
    PID_EXT = 103,
    SMALL_TUPLE_EXT = 104,
    LARGE_TUPLE_EXT = 105,
    MAP_EXT = 116,
    NIL_EXT = 106,
    STRING_EXT = 107,
    LIST_EXT = 108,
    BINARY_EXT = 109,
    SMALL_BIG_EXT = 110,
    LARGE_BIG_EXT = 111,
    NEW_REFERENCE_EXT = 114,
    SMALL_ATOM_EXT = 115,
    FUN_EXT = 117,
    NEW_FUN_EXT = 112,
    EXPORT_EXT = 113,
    BIT_BINARY_EXT = 77,
    NEW_FLOAT_EXT = 70,
    ATOM_UTF8_EXT = 118,
    SMALL_ATOM_UTF8_EXT = 119,
}

//#[deriving(PartialOrd, PartialEq, Ord, Eq)]
#[deriving(Show)]
pub enum Eterm {
    SmallInteger(u8),           // small_integer
    Integer(int),               // integer
    Float(f64),                 // float, new_float
    Atom(Atom),                 // atom, small_atom, atom_utf8, small_atom_utf8
    Reference {                 // reference, new_reference
        node: Atom,
        id: Vec<u8>,
        creation: u8},
    Port {                      // poort
        node: Atom,
        id: u8,
        creation: u8},
    Pid(Pid),                   // pid
    Tuple(Tuple),               // small_tuple, large_tuple
    Map(EMap),                  // map
    Nil,                        // nil
    String(Vec<u8>),             // string XXX: maybe eliminate this in favour of List?
    List(List),                 // list
    Binary(Vec<u8>),            // binary
    BigNum(bigint::BigInt),     // small_big, large_big
    Fun {                       // fun
        pid: Pid,
        module: Atom,
        index: u32,
        uniq: u32,
        free_vars: Vec<Eterm>},
    NewFun {                    // new_fun
        arity: u8,
        uniq: Vec<u8>, //[u8, ..16],
        index: u32,
        module: Atom,
        old_index: u32,
        old_uniq: u32,
        pid: Pid,
        free_vars: Vec<Eterm>},
    Export {                    // export
        module: Atom,
        function: Atom,
        arity: u8,
    },
    BitBinary(Bitv),            // bit_binary; XXX: maybe choose some other representation?
}
pub type Atom = String;
pub type Tuple = Vec<Eterm>;
pub type EMap = Vec<(Eterm, Eterm)>; // k-v pairs //TreeMap<Eterm, Eterm>;
pub type List = Vec<Eterm>;

//#[deriving(PartialOrd, PartialEq, Ord, Eq)]
#[deriving(Show)]
pub struct Pid {                // moved out from enum because it used in Eterm::{Fun,NewFun}
    node: Atom,
    id: u32,
    serial: u32,
    creation: u8,
}

// enum BuildError {
//     IoError(io::IoError),
//     BErr,
// }
type BuildResult = io::IoResult<Eterm>;//, BuildError>;

struct Builder<T> {
    rdr: T,
}

// macro_rules! try_io(
//     ($e:expr) => (
//         match $e {
//             Ok(e) => e,
//             Err(e) => return IoError(e)
//         }
//         )
// )

impl<T: io::Reader> Builder<T> {
    fn new(rdr: T) -> Builder<T> {
        Builder{rdr: rdr}
    }
    fn read_prelude(&mut self) -> io::IoResult<bool> {
        Ok(131 == try!(self.rdr.read_u8()))
    }
    fn build(&mut self) -> BuildResult {
        let int_tag = try!(self.rdr.read_u8());
        let tag: Option<ErlTermTag> = FromPrimitive::from_u8(int_tag);
        match tag {
            Some(SMALL_INTEGER_EXT) =>
                Ok(SmallInteger(try!(self.rdr.read_u8()))),
            Some(INTEGER_EXT) =>
                Ok(Integer(try!(self.rdr.read_be_int()))),
            Some(FLOAT_EXT) => {
                // XXX: there should be more elegant way...
                let float_vec = try!(self.rdr.read_exact(31));
                let float_str: &str = match std::str::from_utf8(float_vec.as_slice()) {
                    Some(s) => s,
                    None =>
                        return Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Bad utf-8",
                            detail: None
                        })
                };
                match from_str::<f32>(float_str) {
                    Some(num) => Ok(Float(num as f64)),
                    _ =>
                        Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Bad float",
                            detail: None // format!("{}", data)
                        })
                }
            },
            Some(ATOM_EXT) | Some(ATOM_UTF8_EXT) => {
                let len = try!(self.rdr.read_be_u16());
                let data = try!(self.rdr.read_exact(len as uint));
                // XXX: data is in latin1 in case of ATOM_EXT
                match String::from_utf8(data) {
                    Ok(atom) =>
                        Ok(Atom(atom)),
                    Err(_) =>
                        Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Bad utf-8",
                            detail: None // format!("{}", data)
                        })
                }
            },
            Some(REFERENCE_EXT) => {
                let node = match try!(self.build()) {
                    Atom(a) =>
                        a,
                    _ =>
                        return Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Assertion failed: Atom expected",
                            detail: None
                        })
                };
                let id = try!(self.rdr.read_exact(4));
                let creation = try!(self.rdr.read_u8());
                Ok(Reference {
                    node: node,
                    id: id,
                    creation: creation
                })
            },
            Some(PORT_EXT) => {
                let node = match try!(self.build()) {
                    Atom(a) =>
                        a,
                    _ =>
                        return Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Assertion failed: Atom expected",
                            detail: None
                        })
                };
                let id = try!(self.rdr.read_exact(4));
                let creation = try!(self.rdr.read_u8());
                Ok(Reference {
                    node: node,
                    id: id,
                    creation: creation
                })
            },
            Some(PID_EXT) => {
                let node = match try!(self.build()) {
                    Atom(a) =>
                        a,
                    _ =>
                        return Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Assertion failed: Atom expected",
                            detail: None
                        })
                };
                let id = try!(self.rdr.read_be_u32());
                let serial = try!(self.rdr.read_be_u32()); // maybe [u8, ..4]?
                let creation = try!(self.rdr.read_u8());
                Ok(Pid(Pid {
                    node: node,
                    id: id,
                    serial: serial,
                    creation: creation
                }))
            },
            Some(SMALL_TUPLE_EXT) => {
                let arity = try!(self.rdr.read_u8());
                let mut tuple: Tuple = Vec::with_capacity(arity as uint);
                for _ in range(0, arity) {
                    let term = try!(self.build());
                    tuple.push(term)
                }
                Ok(Tuple(tuple))
            },
            Some(LARGE_TUPLE_EXT) => {
                let arity = try!(self.rdr.read_be_u32());
                let mut tuple: Tuple = Vec::with_capacity(arity as uint);
                for _ in range(0, arity) {
                    let term = try!(self.build());
                    tuple.push(term)
                }
                Ok(Tuple(tuple))
            }
            Some(MAP_EXT) => {
                let mut map: EMap = Vec::new();
                let arity = try!(self.rdr.read_be_u32());
                for _ in range(0, arity) {
                    let key = try!(self.build());
                    let val = try!(self.build());
                    map.push((key, val))
                }
                Ok(Map(map))
            },
            Some(NIL_EXT) =>
                Ok(Nil),
            Some(STRING_EXT) => {
                let len = try!(self.rdr.read_be_u16());
                Ok(String(try!(self.rdr.read_exact(len as uint))))
            },
            Some(LIST_EXT) => {
                let len = try!(self.rdr.read_be_u32()) + 1;
                let mut list = Vec::with_capacity(len as uint);
                for _ in range(0, len) {
                    let term = try!(self.build());
                    list.push(term)
                }
                Ok(List(list))
            },
            Some(BINARY_EXT) => {
                let len = try!(self.rdr.read_be_u32());
                Ok(Binary(try!(self.rdr.read_exact(len as uint))))
            },
            Some(SMALL_BIG_EXT) => {
                let n = try!(self.rdr.read_u8());
                let sign_int = try!(self.rdr.read_u8());
                let sign = if sign_int == 0 {
                    bigint::Plus
                } else {
                    bigint::Minus
                };
                // In erlang:
                // B = 256 % base is 2^8
                // (d0*B^0 + d1*B^1 + d2*B^2 + ... d(N-1)*B^(n-1))
                // In rust:
                // BigDigit::base is 2^32
                // (a + b * BigDigit::base + c * BigDigit::base^2)
                let mut numbers = Vec::<u32>::with_capacity((n / 4) as uint);
                let mut cur_num: u32 = 0;
                for i in range(0, n) {
                    let byte = try!(self.rdr.read_u8()) as u32;
                    cur_num = match i % 4 {
                        0 => cur_num + byte,
                        1 => cur_num + byte * 256,
                        2 => cur_num + byte * 65536,
                        _ => {
                            numbers.push(cur_num + byte * 16777216);
                            0
                        }
                    }
                }
                if cur_num != 0 { // if 'n' isn't multiple of 4
                    numbers.push(cur_num)
                }
                Ok(BigNum(bigint::BigInt::new(sign, numbers)))
            },
            // Some(LARGE_BIG_EXT) => {
            // TODO
            // },
            Some(NEW_REFERENCE_EXT) => {
                let len = try!(self.rdr.read_be_u16());
                let node = match try!(self.build()) {
                    Atom(a) =>
                        a,
                    _ =>
                        return Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Assertion failed: Atom expected",
                            detail: None
                        })
                };
                let creation = try!(self.rdr.read_u8());
                let id = try!(self.rdr.read_exact(4 * len as uint));
                Ok(Reference{
                    node: node,
                    id: id, // here id should be Vec<u32>, but since it's not interpreted, leave it as is
                    creation: creation
                })
            },
            Some(SMALL_ATOM_EXT) | Some(SMALL_ATOM_UTF8_EXT) => {
                let len = try!(self.rdr.read_u8());
                let data = try!(self.rdr.read_exact(len as uint));
                // XXX: data is in latin1 in case of ATOM_EXT
                match String::from_utf8(data) {
                    Ok(atom) =>
                        Ok(Atom(atom)),
                    Err(_) =>
                        Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Bad utf-8",
                            detail: None // format!("{}", data)
                            })
                }
            },
            Some(FUN_EXT) => {
                // TODO: cleanup error handling (generalize)
                let num_free = try!(self.rdr.read_be_u32());
                let pid = match try!(self.build()) {
                    Pid(pid) => pid,
                    _ =>
                        return Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Assertion failed: Pid expected",
                            detail: None
                        })
                };
                let module = match try!(self.build()) {
                    Atom(atom) => atom,
                    _ =>
                        return Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Assertion failed: Atom expected",
                            detail: None
                        })
                };
                let index = match try!(self.build()) {
                    SmallInteger(idx) => idx as u32,
                    Integer(idx) => idx as u32,
                    _ =>
                        return Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Assertion failed: Integer expected",
                            detail: None
                        })
                };
                let uniq = match try!(self.build()) {
                    SmallInteger(uq) => uq as u32,
                    Integer(uq) => uq as u32,
                    _ =>
                        return Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Assertion failed: Integer expected",
                            detail: None
                        })
                };
                let mut free_vars = Vec::<Eterm>::with_capacity(num_free as uint);
                for _ in range(0, num_free) {
                    free_vars.push(try!(self.build()));
                }
                Ok(Fun {
                    pid: pid,
                    module: module,
                    index: index,
                    uniq: uniq,
                    free_vars: free_vars,
                })
            },
            Some(NEW_FUN_EXT) => {
                let _size = try!(self.rdr.read_be_u32());
                let arity = try!(self.rdr.read_u8());
                let uniq = try!(self.rdr.read_exact(16));
                let index = try!(self.rdr.read_be_u32());
                let num_free = try!(self.rdr.read_be_u32());
                // XXX: following code is copy-pasted!
                let module = match try!(self.build()) {
                    Atom(atom) => atom,
                    _ =>
                        return Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Assertion failed: Atom expected",
                            detail: None
                        })
                };
                let old_index = match try!(self.build()) {
                    SmallInteger(idx) => idx as u32,
                    Integer(idx) => idx as u32,
                    _ =>
                        return Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Assertion failed: Integer expected",
                            detail: None
                        })
                };
                let old_uniq = match try!(self.build()) {
                    SmallInteger(uq) => uq as u32,
                    Integer(uq) => uq as u32,
                    _ =>
                        return Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Assertion failed: Integer expected",
                            detail: None
                        })
                };
                let pid = match try!(self.build()) {
                    Pid(pid) => pid,
                    _ =>
                        return Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Assertion failed: Pid expected",
                            detail: None
                        })
                };
                let mut free_vars = Vec::<Eterm>::with_capacity(num_free as uint);
                for _ in range(0, num_free) {
                    free_vars.push(try!(self.build()));
                }
                // END copy-pasted
                Ok(NewFun {
                    arity: arity,
                    uniq: uniq,
                    index: index,
                    module: module,
                    old_index: old_index,
                    old_uniq: old_uniq,
                    pid: pid,
                    free_vars: free_vars,
                })
            },
            Some(EXPORT_EXT) => {
                let module = match try!(self.build()) {
                    Atom(atom) => atom,
                    _ =>
                        return Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Assertion failed: Atom expected",
                            detail: None
                        })
                };
                let function = match try!(self.build()) {
                    Atom(atom) => atom,
                    _ =>
                        return Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Assertion failed: Atom expected",
                            detail: None
                        })
                };
                let arity = match try!(self.build()) {
                    SmallInteger(uq) => uq,
                    _ =>
                        return Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Assertion failed: SmallInteger expected",
                            detail: None
                        })
                };
                Ok(Export {
                    module: module,
                    function: function,
                    arity: arity, // arity > u8 possible in practice
                })
            },
            // Some(BIT_BINARY_EXT) => {
            //     TODO
            //     let len = try!(self.rdr.read_be_u32()) as uint;
            //     let bits = try!(self.rdr.read_u8()) as uint;
            //     let mut bitv = Bitv::new(len * 4 + bits); // XXX: uint overflow possible!
            //     // unfortunately, bitv isn't resizable, so we couldn't use 'from_bytes'
            // },
            Some(NEW_FLOAT_EXT) =>
                Ok(Float(try!(self.rdr.read_be_f64()))),
            Some(t) =>
                Err(io::IoError{
                    kind: io::OtherIoError,
                    desc: "Tag not implemented",
                    detail: Some(format!("Tag: #{} - {}", int_tag, t))
                }),
            None =>
                Err(io::IoError{
                    kind: io::OtherIoError,
                    desc: "Invalid data type",
                    detail: Some(format!("Tag: #{}", int_tag))
                })
        }
    }
}

fn main() {
    use std::io::{File,BufferedReader};

    for i in range(70, 120) {
        let tag: Option<ErlTermTag> = FromPrimitive::from_int(i);
        println!("{} => {:?}", i, tag);
    }
    println!("==============================");
    let map = vec!( (Atom("my_map_key".to_string()), Nil) );

    let term: Eterm = NewFun {
        arity: 3,
        uniq: vec!(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16),
        index: 10,
        module: "my_mod".to_string(),
        old_index: 1212,
        old_uniq: 1234,
        pid: Pid {node: "wasd".to_string(), id: 1, serial: 123, creation: 2},
        free_vars: vec!(//Float(3.14),
                        Nil,
                        Binary(vec!(1, 2, 3, 4)),
                        Export {
                            module: "my_mod".to_string(),
                            function: "my_func".to_string(),
                            arity: 4},
                        List(vec!(SmallInteger(1), Integer(1000000), Nil)),
                        Tuple(vec!(Atom("record".to_string()), Map(map))),
                        ),
    };
    println!("{}", term);
    println!("==============================");
    let f = BufferedReader::new(File::open(&Path::new("test/test_terms.bin")));
    let mut builder = Builder::new(f);
    match builder.read_prelude() {
        Ok(true) =>
            println!("Valid eterm"),
        Ok(false) =>
            println!("Invalid eterm!"),
        Err(io::IoError{desc: d, ..}) => {
            println!("IoError: {}", d);
            return
        }
    }
    println!("{}", builder.build());
}
<|endoftext|>