<commit_before>//! Translation catalogues are key part of any localization infrastructure. They contain the lists
//! of messages from the application, possibly disambiguated with identifiers or contexts, and
//! corresponding translations.
//!
//! Catalogs are usually stored in one of two formats: [Portable Objects (`.po`)][PO], used
//! primarily by [GNU gettext][gettext], and [XML Localisation Interchange File Format
//! (XLIFF)][XLIFF], a more generic OASIS open standard.
//!
//! These formats can be converted to each other, and to and from many others, using
//! [translate-toolkit][tt].
//!
//! [XLIFF] is quite flexible and can be used in different ways, but this library focuses
//! primarily on using it in a way [gettext] and [translate-toolkit][tt] work, namely with separate
//! catalogue for each language.
//!
//! [PO]: https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html
//! [XLIFF]: https://www.oasis-open.org/committees/xliff/
//! [gettext]: https://www.gnu.org/software/gettext/
//! [tt]: http://toolkit.translatehouse.org/

#[macro_use]
extern crate lazy_static;

extern crate locale_config;

extern crate regex;

use std::collections::BTreeMap;
use locale_config::LanguageRange;

// Auxiliary macros for match checking and then not holding on to the value:
macro_rules! unpack {
    ($x:expr => $p:pat => $r:expr) => {{
        match $x {
            $p => Some($r),
            _ => None,
        }
    }};
    ($x:expr => $p:pat if $c:expr => $r:expr) => {{
        match $x {
            $p if $c => Some($r),
            _ => None,
        }
    }};
}

macro_rules! is {
    ($x:expr => $p:pat) => {{
        match $x {
            $p => true,
            _ => false,
        }
    }};
    ($x:expr => $p:pat if $c:expr) => {{
        match $x {
            $p if $c => true,
            _ => false,
        }
    }};
}

pub mod po;

/// Plural variants
///
/// Which variants are used depends on the language. In English it is easy: 1 is One and everything
/// else is Other. But other languages may have more cases, with Arabic having all six.
// TODO: When Count is in locale, use that version
#[derive(Copy,Clone,Debug,Eq,PartialEq,Ord,PartialOrd,Hash)]
pub enum Count {
    /// Zero has a separate variant in some langauges.
    Zero,
    /// One. In some languages also includes zero.
    One,
    /// Special case for two.
    Two,
    /// Small number. What is small number depends on the language.
    Few,
    /// Large number. What is large number depends on the language.
    Many,
    /// Any other number.
    Other,
}

impl Default for Count {
    fn default() -> Count { Count::One }
}

/// String wrapper possibly with plural variants.
///
/// This is used for source and target strings in translation Unit.
#[derive(Clone,Debug,Eq,PartialEq,Ord,PartialOrd,Hash)]
pub enum Message {
    /// Unset message, used for untranslated entries.
    Empty,
    /// Message independent of any count.
    Singular(String),
    /// Count-dependent message with some variants. Must have at least variant for Other.
    Plural(BTreeMap<Count, String>),
}

impl Message {
    pub fn is_empty(&self) -> bool {
        match self {
            &Message::Empty => true,
            _ => false,
        }
    }

    pub fn is_singular(&self) -> bool {
        match self {
            &Message::Singular(_) => true,
            _ => false,
        }
    }

    pub fn is_plural(&self) -> bool {
        match self {
            &Message::Plural(_) => true,
            _ => false,
        }
    }

    pub fn is_blank(&self) -> bool {
        match self {
            &Message::Empty => true,
            &Message::Singular(ref s) => s == "",
            &Message::Plural(ref m) => m.values().all(|s| s == ""),
        }
    }

    pub fn singular(&self) -> Option<&str> {
        match self {
            &Message::Singular(ref s) => Some(s.as_ref()),
            _ => None,
        }
    }
}

impl Default for Message {
    fn default() -> Message { Message::Empty }
}

/// Note (comment) origins.
#[derive(Clone,Debug,Eq,PartialEq,Ord,PartialOrd,Hash)]
pub enum Origin {
    /// Comment from developer.
    Developer,
    /// Comment from translator.
    Translator,
    /// Comment with explicit autor or context tag.
    Tag(String),
}

/// Translation state.
///
/// Indicates whether the translation is considered usable.
///
/// # TODO
///
/// - Obsolete, NeedsWork, Rejected, Unreviewed (from TT), possibly more.
#[derive(Copy,Clone,PartialEq,Eq,PartialOrd,Ord,Debug,Hash)]
pub enum State {
    /// The unit is not translated.
    Empty,
    /// The unit is a suggestion that might be embarrassingly wrong, possibly automatic. It needs
    /// checking by human translator before it can be used. (Used for `#,fuzzy` entries in `.po`.)
    NeedsWork,
    /// The unit is considered usable.
    Final,
}

impl Default for State {
    fn default() -> State { State::Empty }
}

/// Elementary unit of translation.
///
/// A translation unit contains:
///
/// - One *source* string, the original message.
/// - At most one *target* string, the translated message.
/// - Optional *context* string that disambiguates the original.
/// - A status. This indicates whether the unit is usable in the software.
///
/// # TODO
///
/// - Attributes:
///   - notes (comments)
///   - locations
///   - suggestions (alt-trans)
///   - flags
///   - …
/// - Types:
///   - Plurals (MultiString?)
///   - Segmentation (xliff 2?)
#[derive(Clone,Debug,Default)]
pub struct Unit {
    _context: Option<String>,
    _source: Message,
    _target: Message,
    _prev_context: Option<String>,
    _prev_source: Message,
    _notes: Vec<(Origin, String)>,
    _locations: Vec<String>,
    _state: State,
    _obsolete: bool,
}

impl Unit {
    /// Get the context string.
    pub fn context(&self) -> &Option<String> { &self._context }
    /// Get the source string.
    pub fn source(&self) -> &Message { &self._source }
    /// Get the target string.
    pub fn target(&self) -> &Message { &self._target }
    /// Get the previous context (in fuzzy units).
    pub fn prev_context(&self) -> &Option<String> { &self._prev_context }
    /// Get the previous source (in fuzzy units).
    pub fn prev_source(&self) -> &Message { &self._prev_source }
    /// Get the notes/comments.
    pub fn notes(&self) -> &Vec<(Origin, String)> { &self._notes }
    /// Get locations.
    pub fn locations(&self) -> &Vec<String> { &self._locations }
    /// Get the state.
    pub fn state(&self) -> State { self._state }
    /// Returns whether the unit should be used in application.
    pub fn is_translated(&self) -> bool { self._state == State::Final }
    /// Returns whether the unit is obsolete.
    pub fn is_obsolete(&self) -> bool { self._obsolete }
}

/// Catalogue reader.
///
/// Defines common interface of catalogue readers. Read the units by simply iterating over the
/// reader. The other methods are for the important metadata.
pub trait CatalogueReader : Iterator<Item = Result<Unit, Error>> {
    fn target_language(&self) -> &LanguageRange<'static>;
    // TODO: More attributes, possibly a generic API
}

/// Error in reading (and, in future, writing) a catalogue.
#[derive(Debug)]
pub enum Error {
    /// An I/O error from file operation.
    ///
    /// The first parameter is line number if applicable, the second is the system error.
    Io(usize, std::io::Error),
    /// A parse error.
    ///
    /// Parameters are line number, optional unexpected token and an array of expected tokens.
    /// Unset unexpected token means the parser is not smart enough to remember what it stopped on.
    /// Empty array of expected items means the parser is not smart enough to remember what it
    /// could have accepted instead.
    Parse(usize, Option<String>, Vec<&'static str>),
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            &Error::Io(0, ref err) => err.fmt(f),
            &Error::Io(line, ref err) => write!(f, "{} at line {}", err, line),
            &Error::Parse(line, ref got, ref exp) => {
                write!(f, "Parse error at line {}", line)?;
                if !exp.is_empty() {
                    let mut prefix = ", expected";
                    for e in exp {
                        write!(f, "{} ‘{}’", prefix, e)?;
                        prefix = " or";
                    }
                }
                if got.is_some() {
                    write!(f, ", got ‘{}’", got.as_ref().unwrap())?;
                }
                Ok(())
            }
        }
    }
}

impl std::error::Error for Error {
    fn description(&self) -> &str {
        self.cause().map(std::error::Error::description).unwrap_or("parse error")
    }

    fn cause(&self) -> Option<&std::error::Error> {
        match self {
            &Error::Io(_, ref err) => Some(err),
            &Error::Parse(..) => None,
        }
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<commit_msg>Some documentation updates.<commit_after>//! Translation catalogues are key part of any localization infrastructure. They contain the lists
//! of messages from the application, possibly disambiguated with identifiers or contexts, and
//! corresponding translations.
//!
//! Catalogs are usually stored in one of two formats: [Portable Objects (`.po`)][PO], used
//! primarily by [GNU gettext][gettext], and [XML Localisation Interchange File Format
//! (XLIFF)][XLIFF], a more generic OASIS open standard.
//!
//! These formats can be converted to each other, and to and from many others, using
//! [translate-toolkit][tt].
//!
//! [XLIFF] is quite flexible and can be used in different ways, but this library focuses
//! primarily on using it in a way [gettext] and [translate-toolkit][tt] work, namely with separate
//! catalogue for each language.
//!
//! [PO]: https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html
//! [XLIFF]: https://www.oasis-open.org/committees/xliff/
//! [gettext]: https://www.gnu.org/software/gettext/
//! [tt]: http://toolkit.translatehouse.org/

#[macro_use]
extern crate lazy_static;

extern crate locale_config;

extern crate regex;

use std::collections::BTreeMap;
use locale_config::LanguageRange;

// Auxiliary macros for match checking and then not holding on to the value:
macro_rules! unpack {
    ($x:expr => $p:pat => $r:expr) => {{
        match $x {
            $p => Some($r),
            _ => None,
        }
    }};
    ($x:expr => $p:pat if $c:expr => $r:expr) => {{
        match $x {
            $p if $c => Some($r),
            _ => None,
        }
    }};
}

macro_rules! is {
    ($x:expr => $p:pat) => {{
        match $x {
            $p => true,
            _ => false,
        }
    }};
    ($x:expr => $p:pat if $c:expr) => {{
        match $x {
            $p if $c => true,
            _ => false,
        }
    }};
}

pub mod po;

/// Plural variants
///
/// Which variants are used depends on the language. In English it is easy: 1 is One and everything
/// else is Other. But other languages may have more cases, with Arabic having all six.
// TODO: When Count is in locale, use that version
#[derive(Copy,Clone,Debug,Eq,PartialEq,Ord,PartialOrd,Hash)]
pub enum Count {
    /// Zero has a separate variant in some langauges.
    Zero,
    /// One. In some languages also includes zero.
    One,
    /// Special case for two.
    Two,
    /// Small number. What is small number depends on the language.
    Few,
    /// Large number. What is large number depends on the language.
    Many,
    /// Any other number.
    Other,
}

impl Default for Count {
    fn default() -> Count { Count::One }
}

/// String wrapper possibly with plural variants.
///
/// This is used for source and target strings in translation Unit.
#[derive(Clone,Debug,Eq,PartialEq,Ord,PartialOrd,Hash)]
pub enum Message {
    /// Unset message, used for untranslated entries.
    Empty,
    /// Message independent of any count.
    Singular(String),
    /// Count-dependent message with some variants. Must have at least variant for Other.
    Plural(BTreeMap<Count, String>),
}

impl Message {
    pub fn is_empty(&self) -> bool {
        match self {
            &Message::Empty => true,
            _ => false,
        }
    }

    pub fn is_singular(&self) -> bool {
        match self {
            &Message::Singular(_) => true,
            _ => false,
        }
    }

    pub fn is_plural(&self) -> bool {
        match self {
            &Message::Plural(_) => true,
            _ => false,
        }
    }

    pub fn is_blank(&self) -> bool {
        match self {
            &Message::Empty => true,
            &Message::Singular(ref s) => s == "",
            &Message::Plural(ref m) => m.values().all(|s| s == ""),
        }
    }

    pub fn singular(&self) -> Option<&str> {
        match self {
            &Message::Singular(ref s) => Some(s.as_ref()),
            _ => None,
        }
    }
}

impl Default for Message {
    fn default() -> Message { Message::Empty }
}

/// Note (comment) origins.
#[derive(Clone,Debug,Eq,PartialEq,Ord,PartialOrd,Hash)]
pub enum Origin {
    /// Comment from developer.
    Developer,
    /// Comment from translator.
    Translator,
    /// Comment with explicit author or context tag.
    Tag(String),
}

/// Translation state.
///
/// Indicates whether the translation is considered usable.
///
/// # TODO
///
/// - Rejected, Unreviewed, NeedsReview (from TT), possibly more (note: obsolete is a separate
///   flag)
#[derive(Copy,Clone,PartialEq,Eq,PartialOrd,Ord,Debug,Hash)]
pub enum State {
    /// The unit is not translated.
    Empty,
    /// The unit is a suggestion that might be embarrassingly wrong, possibly automatic. It needs
    /// checking by human translator before it can be used. (Used for `#,fuzzy` entries in `.po`.)
    NeedsWork,
    /// The unit is considered usable.
    Final,
}

impl Default for State {
    fn default() -> State { State::Empty }
}

/// Elementary unit of translation.
///
/// A translation unit contains:
///
/// - One *source* string, the original message.
/// - At most one *target* string, the translated message.
/// - Optional *context* string that disambiguates the original.
/// - A status. This indicates whether the unit is usable in the software.
///
/// Additionally, it can also contain:
///  - Notes, from developer or translator.
///  - References back into the source where the unit is used.
///  - Previous source and context if the target is automatic suggestion from fuzzy matching.
///  - Obsolete flag, indicating the unit is not currently in use.
#[derive(Clone,Debug,Default)]
pub struct Unit {
    _context: Option<String>,
    _source: Message,
    _target: Message,
    _prev_context: Option<String>,
    _prev_source: Message,
    _notes: Vec<(Origin, String)>,
    _locations: Vec<String>,
    _state: State,
    _obsolete: bool,
}

impl Unit {
    /// Get the context string.
    pub fn context(&self) -> &Option<String> { &self._context }
    /// Get the source string.
    pub fn source(&self) -> &Message { &self._source }
    /// Get the target string.
    pub fn target(&self) -> &Message { &self._target }
    /// Get the previous context (in fuzzy units).
    pub fn prev_context(&self) -> &Option<String> { &self._prev_context }
    /// Get the previous source (in fuzzy units).
    pub fn prev_source(&self) -> &Message { &self._prev_source }
    /// Get the notes/comments.
    pub fn notes(&self) -> &Vec<(Origin, String)> { &self._notes }
    /// Get locations.
    pub fn locations(&self) -> &Vec<String> { &self._locations }
    /// Get the state.
    pub fn state(&self) -> State { self._state }
    /// Returns whether the unit should be used in application.
    pub fn is_translated(&self) -> bool { self._state == State::Final }
    /// Returns whether the unit is obsolete.
    pub fn is_obsolete(&self) -> bool { self._obsolete }
}

/// Catalogue reader.
///
/// Defines common interface of catalogue readers. Read the units by simply iterating over the
/// reader. The other methods are for the important metadata.
pub trait CatalogueReader : Iterator<Item = Result<Unit, Error>> {
    fn target_language(&self) -> &LanguageRange<'static>;
    // TODO: More attributes, possibly a generic API
}

/// Error in reading (and, in future, writing) a catalogue.
#[derive(Debug)]
pub enum Error {
    /// An I/O error from file operation.
    ///
    /// The first parameter is line number if applicable, the second is the system error.
    Io(usize, std::io::Error),
    /// A parse error.
    ///
    /// Parameters are line number, optional unexpected token and an array of expected tokens.
    /// Unset unexpected token means the parser is not smart enough to remember what it stopped on.
    /// Empty array of expected items means the parser is not smart enough to remember what it
    /// could have accepted instead.
    Parse(usize, Option<String>, Vec<&'static str>),
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            &Error::Io(0, ref err) => err.fmt(f),
            &Error::Io(line, ref err) => write!(f, "{} at line {}", err, line),
            &Error::Parse(line, ref got, ref exp) => {
                write!(f, "Parse error at line {}", line)?;
                if !exp.is_empty() {
                    let mut prefix = ", expected";
                    for e in exp {
                        write!(f, "{} ‘{}’", prefix, e)?;
                        prefix = " or";
                    }
                }
                if got.is_some() {
                    write!(f, ", got ‘{}’", got.as_ref().unwrap())?;
                }
                Ok(())
            }
        }
    }
}

impl std::error::Error for Error {
    fn description(&self) -> &str {
        self.cause().map(std::error::Error::description).unwrap_or("parse error")
    }

    fn cause(&self) -> Option<&std::error::Error> {
        match self {
            &Error::Io(_, ref err) => Some(err),
            &Error::Parse(..) => None,
        }
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<|endoftext|>


<commit_before>#![recursion_limit = "1024"]

//! Collection of parsers for Conway's game of life.
//!
//! Currently supported file formats:
//!
//! * Life 1.05
//!
//! ## Usage
//!
//! ```
//! extern crate game_of_life_parsers;
//! // use std::fs::File;
//! use game_of_life_parsers::{Parser, Life105Parser};
//!
//! fn main() {
//! 	// let file = File::open("file.life").unwrap();
//! 	let file = "#N\n#P 0 0\n..*".as_bytes();
//! 	let mut parser = Life105Parser::new();
//! 	let game_descriptor =  parser.parse(Box::new(file)).unwrap();
//! 	for live_cell in game_descriptor.live_cells() {
//! 		// iterate overe live cells
//!		}
//! }
//! ```

#[macro_use]
extern crate error_chain;
extern crate regex;

/// Error types generated by `error_chain`.
pub mod errors {
	error_chain! {
		errors {
			InvalidRulesLine(line: String) {
				description("Invalid rules line"),
				display("Invalid rules definition in line '{}'", line),
			}
		}
	}
}

mod game_descriptor;
mod default_game_descriptor;
mod parsers;

pub use game_descriptor::GameDescriptor;
pub use game_descriptor::Coord;
pub use parsers::Parser;
pub use parsers::Life105Parser;
pub use parsers::Life106Parser;
<commit_msg>added IOError kind<commit_after>#![recursion_limit = "1024"]

//! Collection of parsers for Conway's game of life.
//!
//! Currently supported file formats:
//!
//! * Life 1.05
//!
//! ## Usage
//!
//! ```
//! extern crate game_of_life_parsers;
//! // use std::fs::File;
//! use game_of_life_parsers::{Parser, Life105Parser};
//!
//! fn main() {
//! 	// let file = File::open("file.life").unwrap();
//! 	let file = "#N\n#P 0 0\n..*".as_bytes();
//! 	let mut parser = Life105Parser::new();
//! 	let game_descriptor =  parser.parse(Box::new(file)).unwrap();
//! 	for live_cell in game_descriptor.live_cells() {
//! 		// iterate overe live cells
//!		}
//! }
//! ```

#[macro_use]
extern crate error_chain;
extern crate regex;

/// Error types generated by `error_chain`.
pub mod errors {
	use std::io;

	error_chain! {
		errors {
			InvalidRulesLine(line: String) {
				description("Invalid rules line"),
				display("Invalid rules definition in line '{}'", line),
			}
			IOError(error: io::ErrorKind)
		}
	}
}

mod game_descriptor;
mod default_game_descriptor;
mod parsers;

pub use game_descriptor::GameDescriptor;
pub use game_descriptor::Coord;
pub use parsers::Parser;
pub use parsers::Life105Parser;
pub use parsers::Life106Parser;
<|endoftext|>


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

//! A library to localize strings, translating them according to runtime options.
//!
//! Basically, this library allows your project to generate a `lformat!` macro, that behaves
//! similarly to `format!`, except the message string (the first argument) might get translated
//! (if you can find the appropriate string for the language).
//!
//! # Usage
//!
//! First, you'll need to add the following to your `Cargo.toml` file:
//!
//! ```toml
//! build = "build.rs"
//!
//! [build-dependencies]
//! crowbook-intl = "0.1.0"
//!
//! [dependencies]
//! crowbook-intl-runtime = "0.1.0"
//! ```
//!
//! You'll then need to create the `build.rs` file, which can look like this:
//!
//! ```rust,ignore
//! extern crate crowbook_intl;
//! use crowbook_intl::{Localizer, Extractor};
//!
//! fn main() {
//!     // Generate a `lang/default.pot` containing strings used to call `lformat!`
//!     let mut extractor = Extractor::new();
//!     extractor.add_messages_from_dir(concat!(env!("CARGO_MANIFEST_DIR"), "/src")).unwrap();
//!     extractor.write_pot_file(concat!(env!("CARGO_MANIFEST_DIR"), "/lang/default.pot")).unwrap();
//!
//!     // Generate the `localize_macros.rs` file
//!     let mut localizer = Localizer::new(&extractor);
//!     // Use env::var instead of env! to avoid problems when cross-compiling
//!     let dest_path = Path::new(&env::var("OUT_DIR").unwrap())
//!        .join("localize_macros.rs");
//!     localizer.write_macro_file(dest_path).unwrap();
//! }
//! ```
//!
//! This will create a `localize_macros.rs` at build time somewhere in `OUT_DIR`, containing the `lformat!` macro.
//! To actually use this macro, you have to create a `src/localize_macros.rs` file that includes it:
//!
//! ```rust,ignore
//! include!(concat!(env!("OUT_DIR"), "/localize_macros.rs"));
//! ```
//!
//! To use it, the last step is to modify your `src/lib/lib.rs` file:
//!
//! ```rust,ignore
//! extern crate crowbook_intl_runtime;
//! #[macro_use] mod localize_macros;
//! ```
//!
//! Once this is done, you can start replacing your calls to `format!` with calls to `lformat!`.
//!
//! In order to get translation, you'll need to actually translate the strings in separate
//! files, and set your `build.rs` to load them.
//!
//! E.g., if you have the following code:
//!
//! ```rust,ignore
//! println!("{}", lformat!("Hello, world!"));
//! ```
//!
//! and you want it translated in french, you'll have to create a `lang/fr.po` file
//! from the `lang/default.pot` file containing:
//!
//! ```text
//! msgid "Hello, world!";
//! msgstr "Bonjour le monde !";
//! ```
//!
//! And load it in your `build.rs` file:
//!
//! ```rust,ignore
//! let mut localizer = Localizer::new();
//! localizer.add_lang("fr", include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/lang/fr.mp"))).unwrap();
//! (...)
//! ```
//!
//! Once *this* is done, you can use the `localize_macros::set_lang` function
//! to switch the language at runtime:
//!
//! ```rust,ignore
//! use crowbook_intl_runtime::set_lang;
//! set_lang("en");
//! println!("{}", lformat!("Hello, world!")); // prints "Hello, world!"
//! set_lang("fr");
//! println!("{}", lformat!("Hello, world!")); // prints "Bonjour le monde !"
//! ```
//!
//! # Updating your translation
//!
//! When you add new strings that need to be translated (by more calls to `lformat!`),
//! or when you change the content of existing strings, you can use [Gettext's `msgmerge` and `msgcmp`](https://www.gnu.org/software/gettext/manual/html_node/msgmerge-Invocation.html)
//! commands to update your translation. While it is not guaranteed that the formats are
//! strictly identical, it should work. (That is, it is a bug if it doesn't; but at this
//! stage, this library is absolutely not guaranteed to be bug-free.)
//!
//! # Known limitations and bugs
//!
//! * Currently, `crowbook-intl` doesn't handle correctly raw string literals in `lformat!`
//!   (they won't be translated correctly).
//! * Multiple calls to the same string, but formatted differently (e.g. using a backslash
//!   before a newline to separate a string on multiple lines) will also cause problems.
//!
//! # Warning
//!
//! In case the complexity of the operation didn't discourage you, I should warn you
//! that this library is highly experimental at this time.

extern crate regex;
#[macro_use] extern crate lazy_static;
extern crate walkdir;

mod common;
mod macrogen;
mod lang;
mod error;
mod localizer;
mod message;
mod extractor;

pub use error::{Result, Error};
pub use localizer::Localizer;
pub use extractor::Extractor;
<commit_msg>Add license in the doc<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with
// this file, You can obtain one at https://mozilla.org/MPL/2.0/.

//! A library to localize strings, translating them according to runtime options.
//!
//! Basically, this library allows your project to generate a `lformat!` macro, that behaves
//! similarly to `format!`, except the message string (the first argument) might get translated
//! (if you can find the appropriate string for the language).
//!
//! # Usage
//!
//! First, you'll need to add the following to your `Cargo.toml` file:
//!
//! ```toml
//! build = "build.rs"
//!
//! [build-dependencies]
//! crowbook-intl = "0.1.0"
//!
//! [dependencies]
//! crowbook-intl-runtime = "0.1.0"
//! ```
//!
//! You'll then need to create the `build.rs` file, which can look like this:
//!
//! ```rust,ignore
//! extern crate crowbook_intl;
//! use crowbook_intl::{Localizer, Extractor};
//!
//! fn main() {
//!     // Generate a `lang/default.pot` containing strings used to call `lformat!`
//!     let mut extractor = Extractor::new();
//!     extractor.add_messages_from_dir(concat!(env!("CARGO_MANIFEST_DIR"), "/src")).unwrap();
//!     extractor.write_pot_file(concat!(env!("CARGO_MANIFEST_DIR"), "/lang/default.pot")).unwrap();
//!
//!     // Generate the `localize_macros.rs` file
//!     let mut localizer = Localizer::new(&extractor);
//!     // Use env::var instead of env! to avoid problems when cross-compiling
//!     let dest_path = Path::new(&env::var("OUT_DIR").unwrap())
//!        .join("localize_macros.rs");
//!     localizer.write_macro_file(dest_path).unwrap();
//! }
//! ```
//!
//! This will create a `localize_macros.rs` at build time somewhere in `OUT_DIR`, containing the `lformat!` macro.
//! To actually use this macro, you have to create a `src/localize_macros.rs` file that includes it:
//!
//! ```rust,ignore
//! include!(concat!(env!("OUT_DIR"), "/localize_macros.rs"));
//! ```
//!
//! To use it, the last step is to modify your `src/lib/lib.rs` file:
//!
//! ```rust,ignore
//! extern crate crowbook_intl_runtime;
//! #[macro_use] mod localize_macros;
//! ```
//!
//! Once this is done, you can start replacing your calls to `format!` with calls to `lformat!`.
//!
//! In order to get translation, you'll need to actually translate the strings in separate
//! files, and set your `build.rs` to load them.
//!
//! E.g., if you have the following code:
//!
//! ```rust,ignore
//! println!("{}", lformat!("Hello, world!"));
//! ```
//!
//! and you want it translated in french, you'll have to create a `lang/fr.po` file
//! from the `lang/default.pot` file containing:
//!
//! ```text
//! msgid "Hello, world!";
//! msgstr "Bonjour le monde !";
//! ```
//!
//! And load it in your `build.rs` file:
//!
//! ```rust,ignore
//! let mut localizer = Localizer::new();
//! localizer.add_lang("fr", include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/lang/fr.mp"))).unwrap();
//! (...)
//! ```
//!
//! Once *this* is done, you can use the `localize_macros::set_lang` function
//! to switch the language at runtime:
//!
//! ```rust,ignore
//! use crowbook_intl_runtime::set_lang;
//! set_lang("en");
//! println!("{}", lformat!("Hello, world!")); // prints "Hello, world!"
//! set_lang("fr");
//! println!("{}", lformat!("Hello, world!")); // prints "Bonjour le monde !"
//! ```
//!
//! # Updating your translation
//!
//! When you add new strings that need to be translated (by more calls to `lformat!`),
//! or when you change the content of existing strings, you can use [Gettext's `msgmerge` and `msgcmp`](https://www.gnu.org/software/gettext/manual/html_node/msgmerge-Invocation.html)
//! commands to update your translation. While it is not guaranteed that the formats are
//! strictly identical, it should work. (That is, it is a bug if it doesn't; but at this
//! stage, this library is absolutely not guaranteed to be bug-free.)
//!
//! # Known limitations and bugs
//!
//! * Currently, `crowbook-intl` doesn't handle correctly raw string literals in `lformat!`
//!   (they won't be translated correctly).
//! * Multiple calls to the same string, but formatted differently (e.g. using a backslash
//!   before a newline to separate a string on multiple lines) will also cause problems.
//!
//! # Warning
//!
//! In case the complexity of the operation didn't discourage you, I should warn you
//! that this library is highly experimental at this time.
//!
//! # License
//!
//! This is free software, published under the [Mozilla Public License,
//! version 2.0](https://www.mozilla.org/en-US/MPL/2.0/).

extern crate regex;
#[macro_use] extern crate lazy_static;
extern crate walkdir;

mod common;
mod macrogen;
mod lang;
mod error;
mod localizer;
mod message;
mod extractor;

pub use error::{Result, Error};
pub use localizer::Localizer;
pub use extractor::Extractor;
<|endoftext|>


<commit_before>extern crate libc;
// export it for now

use libc::{c_int, c_char, c_void};
use std::ffi::CString;
use std::ptr;

pub mod ffi;
pub use ffi::{IPaddress, _TCPsocket, _SDLNet_SocketSet};

pub struct TCPsocket {
    opaquePtr: *mut _TCPsocket,
}
pub struct SocketSet {
    opaquePtr: *mut _SDLNet_SocketSet,
}

pub fn init() -> () { unsafe { ffi::SDLNet_Init(); } }
pub fn quit() -> () { unsafe { ffi::SDLNet_Quit(); } }
pub fn get_error() -> String {
    unsafe {
        let raw = &ffi::SDLNet_GetError();
        std::str::from_utf8(std::ffi::c_str_to_bytes(raw)).unwrap().to_string()
    }
}

pub fn become_host(port: u16) -> Option<ffi::IPaddress> {
    let mut address = ffi::IPaddress { host: 0, port: 0};
    let mut result = 0;
    unsafe {
        result = ffi::SDLNet_ResolveHost(&mut address, ptr::null() , port)
    }
    if (result == 0){
        Some(address)
    } else {
        None
    }
}

pub fn resolve_host(host: &str, port: u16) -> Option<ffi::IPaddress> {
    let mut address = ffi::IPaddress { host: 0, port: 0 };
    let mut result = 0;
    unsafe {
        result = ffi::SDLNet_ResolveHost(&mut address, CString::from_slice(host.as_bytes()).as_ptr(), port)
    }
    if (result == 0) {
        Some(address)
    } else {
        None
    }
}

pub fn resolve_ip(mut address: IPaddress) -> String {
    unsafe {
        let raw = &ffi::SDLNet_ResolveIP(&mut address);
        std::str::from_utf8(std::ffi::c_str_to_bytes(raw)).unwrap().to_string()
    }
}

pub fn tcp_open() -> Option<TCPsocket> {
    let mut address = ffi::IPaddress { host: 0, port: 0};
    unsafe {
        let socket = ffi::SDLNet_TCP_Open(&mut address);
        if socket as *const _TCPsocket != ptr::null() {
            Some(TCPsocket { opaquePtr: socket })
        } else {
            None
        }
    }
}

pub fn tcp_close(sock: TCPsocket) -> () {
    unsafe {
        ffi::SDLNet_TCP_Close(sock.opaquePtr)
    }
}

pub fn tcp_accept(server: TCPsocket) -> TCPsocket {
    unsafe {
        TCPsocket { opaquePtr: ffi::SDLNet_TCP_Accept(server.opaquePtr) }
    }
}

pub fn tcp_get_peer_address(sock: TCPsocket) -> Box<*mut IPaddress> {
    unsafe {
        Box::new(ffi::SDLNet_TCP_GetPeerAddress(sock.opaquePtr))
    }
}

pub fn tcp_send(sock: TCPsocket, data: &[u8]) -> () {
    unsafe {
        ffi::SDLNet_TCP_Send(sock.opaquePtr, data.as_ptr() as *const c_void, data.len() as i32);
    }
}

pub fn tcp_recv(sock: TCPsocket, maxlen: i32) -> Vec<u8> {
    let mut data: Vec<u8> = Vec::with_capacity(maxlen as usize);
    unsafe {
        let read_ammnt = ffi::SDLNet_TCP_Recv(sock.opaquePtr, data.as_mut_ptr() as *mut c_void, data.len() as i32);
        data.set_len(read_ammnt as usize);
    }
    data
}

pub fn alloc_socket_set(maxsockets: i32) -> SocketSet {
    unsafe {
        SocketSet { opaquePtr: ffi::SDLNet_AllocSocketSet(maxsockets) }
    }
}

pub fn free_socket_set(set: SocketSet) -> () {
    unsafe {
        ffi::SDLNet_FreeSocketSet(set.opaquePtr);
    }
}

pub fn add_socket(set: SocketSet, sock: TCPsocket) -> i32 {
    unsafe {
        ffi::SDLNet_AddSocket(set.opaquePtr, sock.opaquePtr)
    }
}

// Maybe should take in the box here as it may get deleted.... not sure
pub fn del_socket(set: SocketSet, sock: TCPsocket) -> i32 {
    unsafe {
        ffi::SDLNet_DelSocket(set.opaquePtr, sock.opaquePtr)
    }
}

pub fn check_sockets(set: SocketSet, timeout: u32) -> i32 {
    unsafe {
        ffi::SDLNet_CheckSockets(set.opaquePtr, timeout)
    }
}

pub fn socket_ready(sock: TCPsocket) -> i32 {
    unsafe {
        ffi::SDLNet_SocketReady(sock.opaquePtr)
    }
}
<commit_msg>Testing lifetimes.<commit_after>extern crate libc;
// export it for now

use libc::{c_int, c_char, c_void};
use std::ffi::CString;
use std::ptr;

pub mod ffi;
pub use ffi::{IPaddress, _TCPsocket, _SDLNet_SocketSet};

pub struct TCPsocket {
    opaquePtr: *mut _TCPsocket,
}
pub struct SocketSet {
    opaquePtr: *mut _SDLNet_SocketSet,
}

pub fn init() -> () { unsafe { ffi::SDLNet_Init(); } }
pub fn quit() -> () { unsafe { ffi::SDLNet_Quit(); } }
pub fn get_error() -> String {
    unsafe {
        let raw = &ffi::SDLNet_GetError();
        std::str::from_utf8(std::ffi::c_str_to_bytes(raw)).unwrap().to_string()
    }
}

pub fn become_host(port: u16) -> Option<ffi::IPaddress> {
    let mut address = ffi::IPaddress { host: 0, port: 0};
    let mut result = 0;
    unsafe {
        result = ffi::SDLNet_ResolveHost(&mut address, ptr::null() , port)
    }
    if (result == 0){
        Some(address)
    } else {
        None
    }
}

pub fn resolve_host(host: &str, port: u16) -> Option<ffi::IPaddress> {
    let mut address = ffi::IPaddress { host: 0, port: 0 };
    let mut result = 0;
    unsafe {
        result = ffi::SDLNet_ResolveHost(&mut address, CString::from_slice(host.as_bytes()).as_ptr(), port)
    }
    if (result == 0) {
        Some(address)
    } else {
        None
    }
}

pub fn resolve_ip(mut address: IPaddress) -> String {
    unsafe {
        let raw = &ffi::SDLNet_ResolveIP(&mut address);
        std::str::from_utf8(std::ffi::c_str_to_bytes(raw)).unwrap().to_string()
    }
}

pub fn tcp_open() -> Option<TCPsocket> {
    let mut address = ffi::IPaddress { host: 0, port: 0};
    unsafe {
        let socket = ffi::SDLNet_TCP_Open(&mut address);
        if socket as *const _TCPsocket != ptr::null() {
            Some(TCPsocket { opaquePtr: socket })
        } else {
            None
        }
    }
}

pub fn tcp_close(sock: TCPsocket) -> () {
    unsafe {
        ffi::SDLNet_TCP_Close(sock.opaquePtr)
    }
}

pub fn tcp_accept(server: TCPsocket) -> TCPsocket {
    unsafe {
        TCPsocket { opaquePtr: ffi::SDLNet_TCP_Accept(server.opaquePtr) }
    }
}

pub fn tcp_get_peer_address(sock: TCPsocket) -> Box<*mut IPaddress> {
    unsafe {
        Box::new(ffi::SDLNet_TCP_GetPeerAddress(sock.opaquePtr))
    }
}

pub fn tcp_send(sock: TCPsocket, data: &[u8]) -> () {
    unsafe {
        ffi::SDLNet_TCP_Send(sock.opaquePtr, data.as_ptr() as *const c_void, data.len() as i32);
    }
}

pub fn tcp_recv(sock: TCPsocket, maxlen: i32) -> Vec<u8> {
    let mut data: Vec<u8> = Vec::with_capacity(maxlen as usize);
    unsafe {
        let read_ammnt = ffi::SDLNet_TCP_Recv(sock.opaquePtr, data.as_mut_ptr() as *mut c_void, data.len() as i32);
        data.set_len(read_ammnt as usize);
    }
    data
}

pub fn alloc_socket_set(maxsockets: i32) -> SocketSet {
    unsafe {
        SocketSet { opaquePtr: ffi::SDLNet_AllocSocketSet(maxsockets) }
    }
}

pub fn free_socket_set(set: SocketSet) -> () {
    unsafe {
        ffi::SDLNet_FreeSocketSet(set.opaquePtr);
    }
}

pub fn add_socket<'a>(set: &'a SocketSet, sock: &'a TCPsocket) -> i32 {
    unsafe {
        ffi::SDLNet_AddSocket(set.opaquePtr, sock.opaquePtr)
    }
}

// Maybe should take in the box here as it may get deleted.... not sure
pub fn del_socket(set: SocketSet, sock: TCPsocket) -> i32 {
    unsafe {
        ffi::SDLNet_DelSocket(set.opaquePtr, sock.opaquePtr)
    }
}

pub fn check_sockets(set: SocketSet, timeout: u32) -> i32 {
    unsafe {
        ffi::SDLNet_CheckSockets(set.opaquePtr, timeout)
    }
}

pub fn socket_ready(sock: TCPsocket) -> i32 {
    unsafe {
        ffi::SDLNet_SocketReady(sock.opaquePtr)
    }
}
<|endoftext|>


<commit_before>#![feature(proc_macro)]

extern crate proc_macro;
use proc_macro::TokenStream;

extern crate syn;
use syn::*;

#[macro_use]
extern crate quote;

#[derive(Debug)]
enum BitFieldPosition {
    Single(u8),
    Range(std::ops::Range<u8>)
}

#[derive(Debug)]
struct BitField {
    position: BitFieldPosition,
    ident: Ident,
    ty: Ty
}

fn output_struct(name: &Ident, bitfields: &Vec<BitField>) -> quote::Tokens {
    let mut impl_body = quote! {};

    let starts: Vec<u8> = bitfields.iter().map(|x| match &x.position {
        &BitFieldPosition::Single(x) => x,
        &BitFieldPosition::Range(ref range) => range.start
    }).collect();

    let max_starts = starts.iter().max().unwrap();

    let base_size: usize = (*max_starts as usize / 8) + 1;

    for bitfield in bitfields {
        println!("iter field {} @{:?}", bitfield.ident, bitfield.position);

        let getter_str = format!("get_{}", bitfield.ident.as_ref());
        let getter: Ident = From::from(getter_str.as_str());

        let setter_str = format!("set_{}", bitfield.ident.as_ref());
        let setter: Ident = From::from(setter_str.as_str());

        let ty = &bitfield.ty;

        match &bitfield.position {
            &BitFieldPosition::Single(x) => {
                let mask: u8 = 1 << (x % 8);
                let nmask: u8 = !mask;

                let byteidx: usize = x as usize / 8;

                let shift = x % 8;

                impl_body = quote! {
                    #impl_body

                    pub fn #getter(&self) -> #ty {
                        let raw: [u8;1] = [(self.0[#byteidx] & #mask) >> #shift];
                        return std::convert::From::from(raw);
                    }

                    pub fn #setter(&mut self, value: #ty) {
                        let raw: [u8;1] = std::convert::Into::into(value);
                        self.0[#byteidx] &= #nmask;
                        self.0[#byteidx] |= (raw[0] & 1) << #shift;
                    }
                }
            },
            &BitFieldPosition::Range(ref range) => {
                let from = range.start;
                let to = range.end;

                let size = to - from + 1;
                let type_mask: u8 = (1 << size) - 1;
                let mask: u8 = type_mask << (from % 8);
                let nmask: u8 = !mask;

                let byteidx: usize = from as usize / 8;

                let shift = from % 8;

                let typesize: usize = size as usize / 8 + 1;

                impl_body = quote! {
                    #impl_body

                    pub fn #getter(&self) -> #ty {
                        let raw: [u8;#typesize] = [(self.0[#byteidx] & #mask) >> #shift];
                        return std::convert::From::from(raw);
                    }

                    pub fn #setter(&mut self, value: #ty) {
                        let raw: [u8;#typesize] = std::convert::Into::into(value);
                        self.0[#byteidx] &= #nmask;
                        self.0[#byteidx] |= (raw[0] & #type_mask) << #shift;
                    }
                }
            },
        }
    };

    return quote! {
        struct #name ([u8;#base_size]);
        impl Default for #name {
            fn default() -> Self {
                return #name ([0;#base_size]);
            }
        }
        impl #name {

            #impl_body
        }
    }
}

#[proc_macro_attribute]
pub fn register(_: TokenStream, input: TokenStream) -> TokenStream {
    let s = input.to_string();
    let ast = parse_derive_input(&s).unwrap();

    let fields = match ast.body {
        Body::Enum(_) => panic!("enum not supported"),
        Body::Struct(x) => match x {
            VariantData::Struct(fields) => fields,
            _ => panic!("tuple and unit not supported")
        }
    };

    #[derive(Debug)]
    let mut bitfields: Vec<BitField> = vec![];

    for field in &fields {
        let ty = field.clone().ty;

        let mut from: Option<u8> = None;
        let mut to: Option<u8> = None;
        let mut at: Option<u8> = None;

        for attr in &field.attrs {
            if let MetaItem::List(attr_ident, attr_nest) = attr.clone().value {
                if attr_ident == "bitfield" {
                    for attr_nest_item in &attr_nest {
                        match attr_nest_item.clone() {
                            NestedMetaItem::MetaItem(MetaItem::NameValue(nv_ident, Lit::Int(nv_value, _))) => {
                                match nv_ident.as_ref() {
                                    "at" => (at = Some(nv_value as u8)),
                                    "from" => (from = Some(nv_value as u8)),
                                    "to" => (to = Some(nv_value as u8)),
                                    _ => panic!("unsupported param name (use 'at' or 'from'/'to')"),
                                }
                            }
                            _ => {}
                        }
                    }
                }
            }
        }

        if (from.is_some() || to.is_some()) && at.is_some() {
            panic!("select 'at' or 'from'/'to' parameters, not both");
        }

        if from.is_some() ^ to.is_some() {
            panic!("select 'from' and 'to' parameters together");
        }

        if from.is_none() && to.is_none() && at.is_none() {
            panic!("select bit parameters (use #[bitfield(at=x or from=x to=y)])");
        }

        let position: BitFieldPosition = if from.is_some() && to.is_some() {
            BitFieldPosition::Range(std::ops::Range{start: from.unwrap(), end: to.unwrap()})
        } else {
            BitFieldPosition::Single(at.unwrap())
        };

        let ident = field.ident.clone().unwrap();
        let ty_str = match &field.ty {
            &Ty::Path(_, ref path) => path.segments[0].ident.clone(),
            _ => panic!("only path types supported"),
        };

        println!("field {} @{:?}: {}", ident, position, ty_str);

        bitfields.push(BitField {position: position, ident: ident, ty: ty});
    }

    let name = &ast.ident;

    return output_struct(name, &bitfields).parse().unwrap();
}<commit_msg>multi-byte setter<commit_after>#![feature(proc_macro)]
#![recursion_limit = "128"]

extern crate proc_macro;
use proc_macro::TokenStream;

extern crate syn;
use syn::*;

#[macro_use]
extern crate quote;

#[derive(Debug)]
enum BitFieldPosition {
    Single(u8),
    Range(std::ops::Range<u8>)
}

#[derive(Debug)]
struct BitField {
    position: BitFieldPosition,
    ident: Ident,
    ty: Ty
}

fn output_struct(name: &Ident, bitfields: &Vec<BitField>) -> quote::Tokens {

    let ends: Vec<u8> = bitfields.iter().map(|x| match &x.position {
        &BitFieldPosition::Single(x) => x,
        &BitFieldPosition::Range(ref range) => range.end
    }).collect();

    let max_ends = ends.iter().max().unwrap();

    let base_size: usize = (*max_ends as usize / 8) + 1;

    let mut impl_body = quote! {};

    for bitfield in bitfields {
        println!("iter field {} @{:?}", bitfield.ident, bitfield.position);

        let getter_str = format!("get_{}", bitfield.ident.as_ref());
        let getter: Ident = From::from(getter_str.as_str());

        let setter_str = format!("set_{}", bitfield.ident.as_ref());
        let setter: Ident = From::from(setter_str.as_str());

        let ty = &bitfield.ty;

        match &bitfield.position {
            &BitFieldPosition::Single(x) => {
                let mask: u8 = 1 << (x % 8);
                let nmask: u8 = !mask;

                let byteidx: usize = x as usize / 8;

                let shift = x % 8;

                impl_body = quote! {
                    #impl_body

                    pub fn #getter(&self) -> #ty {
                        let raw: [u8;1] = [(self.0[#byteidx] & #mask) >> #shift];
                        return std::convert::From::from(raw);
                    }

                    pub fn #setter(&mut self, value: #ty) {
                        let raw: [u8;1] = std::convert::Into::into(value);
                        self.0[#byteidx] &= #nmask;
                        self.0[#byteidx] |= (raw[0] & 1) << #shift;
                    }
                }
            },
            &BitFieldPosition::Range(ref range) => {
                let from = range.start;
                let to = range.end;

                let value_size: usize = (to - from) as usize / 8 + 1;
                // let value_bit_size = to - from + 1;

                let from_byte: usize = from as usize / 8;
                let to_byte: usize = to as usize / 8;

                let raw_size = to_byte - from_byte + 1;

                println!("value_byte_size:{}", value_size);
                println!("from_byte:{}, to_byte:{}", from_byte, to_byte);
                println!("raw_size:{}", raw_size);

                // let type_mask: u8 = (1 << type_bit_size) - 1;
                // let mask: u8 = type_mask << (from % 8);
                // let nmask: u8 = !mask;

                let bit_shift = from % 8;
                let bit_end = to % 8;

                let mut setter_body = quote! {
                    let value_array: [u8;#value_size] = std::convert::Into::into(value);
                    let mut raw: u8 = 0;
                };

                setter_body = quote! {
                    #setter_body
                    // raw += (value_array[#i] << #bit_shift);
                };

                for i in 0..raw_size {

                    if i < value_size {
                        setter_body = quote! {
                            #setter_body
                            raw |= value_array[#i] << #bit_shift;
                        }
                    }

                    if i != 0 {
                        let i_1 = i - 1;
                        if bit_shift != 0 {
                            setter_body = quote! {
                                #setter_body
                                raw |= value_array[#i_1] >> (8 - #bit_shift);
                            }
                        }
                    }

                    let mask: u8 = if bit_end == 7 || i != raw_size - 1 {0xff}
                    else {(1 << (bit_end + 1)) - 1};

                    if mask != 0xff {
                        setter_body = quote! {
                            #setter_body
                            raw &= #mask;
                        };
                    }

                    let i_from_byte = i + from_byte;

                    setter_body = quote! {
                        #setter_body
                        self.0[#i_from_byte] &= !#mask;
                        self.0[#i_from_byte] |= raw;
                    }
                }

                impl_body = quote! {
                    #impl_body

                    pub fn #getter(&self) -> #ty {
                        // let raw: [u8;#type_byte_size] = [(self.0[#from_byte] & #mask) >> #shift];
                        //return std::convert::From::from(raw);
                        return std::convert::From::from([0u8, 0u8]);
                    }

                    pub fn #setter(&mut self, value: #ty) {
                        #setter_body

                        /*for idx in 0..*/
                        // self.0[#from_byte] &= #nmask;
                        // self.0[#from_byte] |= (raw[0] & #type_mask) << #shift;
                    }
                }
            },
        }
    };

    return quote! {
        struct #name ([u8;#base_size]);
        impl Default for #name {
            fn default() -> Self {
                return #name ([0;#base_size]);
            }
        }
        impl #name {

            #impl_body
        }
    }
}

#[proc_macro_attribute]
pub fn register(_: TokenStream, input: TokenStream) -> TokenStream {
    let s = input.to_string();
    let ast = parse_derive_input(&s).unwrap();

    let fields = match ast.body {
        Body::Enum(_) => panic!("enum not supported"),
        Body::Struct(x) => match x {
            VariantData::Struct(fields) => fields,
            _ => panic!("tuple and unit not supported")
        }
    };

    #[derive(Debug)]
    let mut bitfields: Vec<BitField> = vec![];

    for field in &fields {
        let ty = field.clone().ty;

        let mut from: Option<u8> = None;
        let mut to: Option<u8> = None;
        let mut at: Option<u8> = None;

        for attr in &field.attrs {
            if let MetaItem::List(attr_ident, attr_nest) = attr.clone().value {
                if attr_ident == "bitfield" {
                    for attr_nest_item in &attr_nest {
                        match attr_nest_item.clone() {
                            NestedMetaItem::MetaItem(MetaItem::NameValue(nv_ident, Lit::Int(nv_value, _))) => {
                                match nv_ident.as_ref() {
                                    "at" => (at = Some(nv_value as u8)),
                                    "from" => (from = Some(nv_value as u8)),
                                    "to" => (to = Some(nv_value as u8)),
                                    _ => panic!("unsupported param name (use 'at' or 'from'/'to')"),
                                }
                            }
                            _ => {}
                        }
                    }
                }
            }
        }

        if (from.is_some() || to.is_some()) && at.is_some() {
            panic!("select 'at' or 'from'/'to' parameters, not both");
        }

        if from.is_some() ^ to.is_some() {
            panic!("select 'from' and 'to' parameters together");
        }

        if from.is_none() && to.is_none() && at.is_none() {
            panic!("select bit parameters (use #[bitfield(at=x or from=x to=y)])");
        }

        let position: BitFieldPosition = if from.is_some() && to.is_some() {
            BitFieldPosition::Range(std::ops::Range{start: from.unwrap(), end: to.unwrap()})
        } else {
            BitFieldPosition::Single(at.unwrap())
        };

        let ident = field.ident.clone().unwrap();

        bitfields.push(BitField {position: position, ident: ident, ty: ty});
    }

    let name = &ast.ident;

    return output_struct(name, &bitfields).parse().unwrap();
}<|endoftext|>


<commit_before>extern crate lmdb_rs as lmdb;

use lmdb::{FromMdbValue, ToMdbValue};
use lmdb::core::{MdbResult, Transaction};
use std::marker::PhantomData;

pub trait Tablename {
    fn as_str() -> &'static str;
}

pub trait IsNativeInt {}
impl IsNativeInt for i32 {}
impl IsNativeInt for u32 {}
impl IsNativeInt for i64 {}
impl IsNativeInt for u64 {}

/// A table that maps an integer to a set of integers.
/// Keys K1 and K2 must be native integers!
#[derive(Clone)]
pub struct IntToIntsTable<K1, K2, T> {
    pub db: lmdb::DbHandle,
    _k1: PhantomData<K1>,
    _k2: PhantomData<K2>,
    _t: PhantomData<T>,
}

/// A table that maps an integer to a blob.
#[derive(Clone)]
pub struct IntToBlobTable<K, T> {
    pub db: lmdb::DbHandle,
    _k: PhantomData<K>,
    _t: PhantomData<T>,
}

/// A table that maps an object of type `K` to a blob.
#[derive(Clone)]
pub struct KeyToBlobTable<K, T> {
    pub db: lmdb::DbHandle,
    _k: PhantomData<K>,
    _t: PhantomData<T>,
}

/// A table that maps an integer to a record/struct.
#[derive(Clone)]
pub struct IntToRecordTable<K, V, T> {
    pub db: lmdb::DbHandle,
    _k: PhantomData<K>,
    _v: PhantomData<V>,
    _t: PhantomData<T>,
}

/// Bound IntToRecordTable.
pub struct BoundIntToRecordTable<'a, K, V, T> {
    db: lmdb::Database<'a>,
    _k: PhantomData<K>,
    _v: PhantomData<V>,
    _t: PhantomData<T>,
}

/* Implementations */

impl<K1, K2, T> IntToIntsTable<K1, K2, T>
where K1: Sized + ToMdbValue + IsNativeInt + FromMdbValue,
      K2: Sized + ToMdbValue + IsNativeInt,
      T: Tablename
{
    pub fn new(env: &lmdb::Environment) -> IntToIntsTable<K1, K2, T> {
        let flags = lmdb::core::DbIntKey |
                    lmdb::core::DbAllowDups | lmdb::core::DbAllowIntDups |
                    lmdb::core::DbDupFixed;

        let db = env.create_db(T::as_str(), flags).unwrap();
        IntToIntsTable::new_from_handle(db)
    }

    fn new_from_handle(db: lmdb::DbHandle) -> IntToIntsTable<K1, K2, T> {
        IntToIntsTable {db: db, _k1: PhantomData, _k2: PhantomData, _t: PhantomData}
    }

    // XXX: Can we optimize this, when iter is already sorted for example?
    pub fn insert_multiple<I>(&self, txn: &Transaction, iter: I) -> MdbResult<()> where I: Iterator<Item=(K1,K2)> {
        let db = txn.bind(&self.db);
        for (ref k1, ref k2) in iter {
            try!(db.set(k1, k2));
        }
        Ok(())
    }

    pub fn insert(&self, txn: &Transaction, kv: &(K1, K2)) -> MdbResult<()> {
        let db = txn.bind(&self.db);
        db.set(&kv.0, &kv.1)
    }

    fn last_key(&self, txn: &lmdb::core::ReadonlyTransaction) -> MdbResult<K1> {
        let db = txn.bind(&self.db);
        let mut cursor = try!(db.new_cursor());
        try!(cursor.to_last());
        cursor.get_key()
    }
}

impl<K, T> IntToBlobTable<K, T>
where K: Sized + ToMdbValue + IsNativeInt,
      T: Tablename
{
    pub fn new(env: &lmdb::Environment) -> IntToBlobTable<K, T> {
        let flags = lmdb::core::DbIntKey;

        let db = env.create_db(T::as_str(), flags).unwrap();
        IntToBlobTable::new_from_handle(db)
    }

    fn new_from_handle(db: lmdb::DbHandle) -> IntToBlobTable<K, T> {
        IntToBlobTable {db: db, _k: PhantomData, _t: PhantomData}
    }

    pub fn insert(&self, txn: &Transaction, key: &K, val: &[u8]) -> MdbResult<()> {
        let db = txn.bind(&self.db);
        db.insert(key, &val)
    }

    // fn lookup
}

impl<K, T> KeyToBlobTable<K, T>
where K: Sized + ToMdbValue,
      T: Tablename
{
    pub fn new(env: &lmdb::Environment) -> KeyToBlobTable<K, T> {
        let flags = lmdb::core::DbFlags::empty();

        let db = env.create_db(T::as_str(), flags).unwrap();
        KeyToBlobTable::new_from_handle(db)
    }

    fn new_from_handle(db: lmdb::DbHandle) -> KeyToBlobTable<K, T> {
        KeyToBlobTable {db: db, _k: PhantomData, _t: PhantomData}
    }

    pub fn insert(&self, txn: &Transaction, key: &K, val: &[u8]) -> MdbResult<()> {
        let db = txn.bind(&self.db);
        db.insert(key, &val)
    }

    // fn lookup
}

//
// A unique(native-int) -> sized record value lookup table
//

impl<K, V, T> IntToRecordTable<K, V, T>
where K: Sized + ToMdbValue + IsNativeInt,
      V: Sized + ToMdbValue + FromMdbValue,
      T: Tablename
{
    pub fn new(env: &lmdb::Environment) -> IntToRecordTable<K, V, T> {
        let flags = lmdb::core::DbIntKey;

        let db = env.create_db(T::as_str(), flags).unwrap();
        IntToRecordTable::new_from_handle(db)
    }

    fn new_from_handle(db: lmdb::DbHandle) -> IntToRecordTable<K, V, T> {
        IntToRecordTable {db: db, _k: PhantomData, _v: PhantomData, _t: PhantomData}
    }

    pub fn bind_to_txn<'a>(&self, txn: &'a lmdb::Transaction) -> BoundIntToRecordTable<'a, K, V, T> {
        BoundIntToRecordTable {
            db: txn.bind(&self.db),
            _k: PhantomData, _v: PhantomData, _t: PhantomData}
    }

    pub fn bind_to_ro_txn<'a>(&self, txn: &'a lmdb::ReadonlyTransaction) -> BoundIntToRecordTable<'a, K, V, T> {
        BoundIntToRecordTable {
            db: txn.bind(&self.db),
            _k: PhantomData, _v: PhantomData, _t: PhantomData}
    }
}

impl<'a, K, V, T> BoundIntToRecordTable<'a, K, V, T>
where K: Sized + ToMdbValue + IsNativeInt,
      V: Sized + ToMdbValue + FromMdbValue,
      T: Tablename
{
    // fails if key exists.
    pub fn insert(&self, key: &K, val: &V) -> MdbResult<()> {
        self.db.insert(key, val)
    }

    // XXX: Should go into ReadonlyBound...
    pub fn lookup(&self, key: &K) -> MdbResult<V> {
        self.db.get::<V>(key)
    }

    pub fn update<F>(&self, key: &K, update: F) -> MdbResult<()>
        where F: Fn(&mut V) -> bool {

        let mut cursor = try!(self.db.new_cursor());
        try!(cursor.to_key(key));

        let mut value: V = try!(cursor.get_value());

        let needs_update = update(&mut value);

        if needs_update {
            try!(cursor.replace(&value));
        }

        Ok(())
    }
}
<commit_msg>Implement open()/create() functions instead of new()<commit_after>extern crate lmdb_rs as lmdb;

use lmdb::{FromMdbValue, ToMdbValue};
use lmdb::core::{MdbResult, Transaction};
use std::marker::PhantomData;

pub trait Tablename {
    fn as_str() -> &'static str;
}

pub trait IsNativeInt {}
impl IsNativeInt for i32 {}
impl IsNativeInt for u32 {}
impl IsNativeInt for i64 {}
impl IsNativeInt for u64 {}

/// A table that maps an integer to a set of integers.
/// Keys K1 and K2 must be native integers!
#[derive(Clone)]
pub struct IntToIntsTable<K1, K2, T> {
    pub db: lmdb::DbHandle,
    _k1: PhantomData<K1>,
    _k2: PhantomData<K2>,
    _t: PhantomData<T>,
}

/// A table that maps an integer to a blob.
#[derive(Clone)]
pub struct IntToBlobTable<K, T> {
    pub db: lmdb::DbHandle,
    _k: PhantomData<K>,
    _t: PhantomData<T>,
}

/// A table that maps an object of type `K` to a blob.
#[derive(Clone)]
pub struct KeyToBlobTable<K, T> {
    pub db: lmdb::DbHandle,
    _k: PhantomData<K>,
    _t: PhantomData<T>,
}

/// A table that maps an integer to a record/struct.
#[derive(Clone)]
pub struct IntToRecordTable<K, V, T> {
    pub db: lmdb::DbHandle,
    _k: PhantomData<K>,
    _v: PhantomData<V>,
    _t: PhantomData<T>,
}

/// Bound IntToRecordTable.
pub struct BoundIntToRecordTable<'a, K, V, T> {
    db: lmdb::Database<'a>,
    _k: PhantomData<K>,
    _v: PhantomData<V>,
    _t: PhantomData<T>,
}

/* Implementations */

impl<K1, K2, T> IntToIntsTable<K1, K2, T>
where K1: Sized + ToMdbValue + IsNativeInt + FromMdbValue,
      K2: Sized + ToMdbValue + IsNativeInt,
      T: Tablename
{
    fn flags() -> lmdb::core::DbFlags {
        lmdb::core::DbIntKey |
        lmdb::core::DbAllowDups | lmdb::core::DbAllowIntDups |
        lmdb::core::DbDupFixed
    }

    pub fn create(env: &lmdb::Environment) -> IntToIntsTable<K1, K2, T> {
        IntToIntsTable::new_from_handle(env.create_db(T::as_str(), Self::flags()).unwrap())
    }

    pub fn open(env: &lmdb::Environment) -> IntToIntsTable<K1, K2, T> {
        IntToIntsTable::new_from_handle(env.get_db(T::as_str(), Self::flags()).unwrap())
    }

    fn new_from_handle(db: lmdb::DbHandle) -> IntToIntsTable<K1, K2, T> {
        IntToIntsTable {db: db, _k1: PhantomData, _k2: PhantomData, _t: PhantomData}
    }

    // XXX: Can we optimize this, when iter is already sorted for example?
    pub fn insert_multiple<I>(&self, txn: &Transaction, iter: I) -> MdbResult<()> where I: Iterator<Item=(K1,K2)> {
        let db = txn.bind(&self.db);
        for (ref k1, ref k2) in iter {
            try!(db.set(k1, k2));
        }
        Ok(())
    }

    pub fn insert(&self, txn: &Transaction, kv: &(K1, K2)) -> MdbResult<()> {
        let db = txn.bind(&self.db);
        db.set(&kv.0, &kv.1)
    }

    fn last_key(&self, txn: &lmdb::core::ReadonlyTransaction) -> MdbResult<K1> {
        let db = txn.bind(&self.db);
        let mut cursor = try!(db.new_cursor());
        try!(cursor.to_last());
        cursor.get_key()
    }
}

impl<K, T> IntToBlobTable<K, T>
where K: Sized + ToMdbValue + IsNativeInt,
      T: Tablename
{
    fn flags() -> lmdb::core::DbFlags {
        lmdb::core::DbIntKey
    }

    pub fn create(env: &lmdb::Environment) -> IntToBlobTable<K, T> {
        IntToBlobTable::new_from_handle(env.create_db(T::as_str(), Self::flags()).unwrap())
    }

    pub fn open(env: &lmdb::Environment) -> IntToBlobTable<K, T> {
        IntToBlobTable::new_from_handle(env.get_db(T::as_str(), Self::flags()).unwrap())
    }

    fn new_from_handle(db: lmdb::DbHandle) -> IntToBlobTable<K, T> {
        IntToBlobTable {db: db, _k: PhantomData, _t: PhantomData}
    }

    pub fn insert(&self, txn: &Transaction, key: &K, val: &[u8]) -> MdbResult<()> {
        let db = txn.bind(&self.db);
        db.insert(key, &val)
    }

    // fn lookup
}

impl<K, T> KeyToBlobTable<K, T>
where K: Sized + ToMdbValue,
      T: Tablename
{
    fn flags() -> lmdb::core::DbFlags {
        lmdb::core::DbFlags::empty()
    }

    pub fn create(env: &lmdb::Environment) -> KeyToBlobTable<K, T> {
        KeyToBlobTable::new_from_handle(env.create_db(T::as_str(), Self::flags()).unwrap())
    }

    pub fn open(env: &lmdb::Environment) -> KeyToBlobTable<K, T> {
        KeyToBlobTable::new_from_handle(env.get_db(T::as_str(), Self::flags()).unwrap())
    }

    fn new_from_handle(db: lmdb::DbHandle) -> KeyToBlobTable<K, T> {
        KeyToBlobTable {db: db, _k: PhantomData, _t: PhantomData}
    }

    pub fn insert(&self, txn: &Transaction, key: &K, val: &[u8]) -> MdbResult<()> {
        let db = txn.bind(&self.db);
        db.insert(key, &val)
    }

    // fn lookup
}

//
// A unique(native-int) -> sized record value lookup table
//

impl<K, V, T> IntToRecordTable<K, V, T>
where K: Sized + ToMdbValue + IsNativeInt,
      V: Sized + ToMdbValue + FromMdbValue,
      T: Tablename
{
    fn flags() -> lmdb::core::DbFlags {
        lmdb::core::DbIntKey
    }

    pub fn create(env: &lmdb::Environment) -> IntToRecordTable<K, V, T> {
        IntToRecordTable::new_from_handle(env.create_db(T::as_str(), Self::flags()).unwrap())
    }

    pub fn open(env: &lmdb::Environment) -> IntToRecordTable<K, V, T> {
        IntToRecordTable::new_from_handle(env.get_db(T::as_str(), Self::flags()).unwrap())
    }

    fn new_from_handle(db: lmdb::DbHandle) -> IntToRecordTable<K, V, T> {
        IntToRecordTable {db: db, _k: PhantomData, _v: PhantomData, _t: PhantomData}
    }

    pub fn bind_to_txn<'a>(&self, txn: &'a lmdb::Transaction) -> BoundIntToRecordTable<'a, K, V, T> {
        BoundIntToRecordTable {
            db: txn.bind(&self.db),
            _k: PhantomData, _v: PhantomData, _t: PhantomData}
    }

    pub fn bind_to_ro_txn<'a>(&self, txn: &'a lmdb::ReadonlyTransaction) -> BoundIntToRecordTable<'a, K, V, T> {
        BoundIntToRecordTable {
            db: txn.bind(&self.db),
            _k: PhantomData, _v: PhantomData, _t: PhantomData}
    }
}

impl<'a, K, V, T> BoundIntToRecordTable<'a, K, V, T>
where K: Sized + ToMdbValue + IsNativeInt,
      V: Sized + ToMdbValue + FromMdbValue,
      T: Tablename
{
    // fails if key exists.
    pub fn insert(&self, key: &K, val: &V) -> MdbResult<()> {
        self.db.insert(key, val)
    }

    // XXX: Should go into ReadonlyBound...
    pub fn lookup(&self, key: &K) -> MdbResult<V> {
        self.db.get::<V>(key)
    }

    pub fn update<F>(&self, key: &K, update: F) -> MdbResult<()>
        where F: Fn(&mut V) -> bool {

        let mut cursor = try!(self.db.new_cursor());
        try!(cursor.to_key(key));

        let mut value: V = try!(cursor.get_value());

        let needs_update = update(&mut value);

        if needs_update {
            try!(cursor.replace(&value));
        }

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


<commit_before>//! A data structure for tracking source positions in language implementations, inspired by the
//! [CodeMap type in rustc's libsyntax](https://github.com/rust-lang/rust/blob/master/src/libsyntax/codemap.rs).
//!
//! The `CodeMap` tracks all source files and maps positions within them to linear indexes as if all
//! source files were concatenated. This allows a source position to be represented by a small
//! 32-bit `Pos` indexing into the `CodeMap`, under the assumption that the total amount of parsed
//! source code will not exceed 4GiB. The `CodeMap` can look up the source file, line, and column
//! of a `Pos` or `Span`, as well as provide source code snippets for error reporting.
//!
//! # Example
//! ```
//! use codemap::CodeMap;
//! let mut codemap = CodeMap::new();
//! let file = codemap.add_file("test.rs".to_string(), "fn test(){\n    println!(\"Hello\");\n}\n".to_string());
//! let string_literal_span = file.span.subspan(24, 31);
//!
//! let location = codemap.look_up_span(string_literal_span);
//! assert_eq!(location.file.name(), "test.rs");
//! assert_eq!(location.begin.line, 1);
//! assert_eq!(location.begin.column, 13);
//! assert_eq!(location.end.line, 1);
//! assert_eq!(location.end.column, 20);
//! ```

use std::cmp::{self, Ordering};
use std::ops::{Add, Sub, Deref};
use std::fmt;
use std::sync::Arc;

/// A small, `Copy`, value representing a position in a `CodeMap`'s file.
#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug)]
pub struct Pos(u32);

impl Add<u64> for Pos {
    type Output = Pos;
    fn add(self, other: u64) -> Pos {
        Pos(self.0 + other as u32)
    }
}

impl Sub<Pos> for Pos {
    type Output = u64;
    fn sub(self, other: Pos) -> u64 {
        (self.0 - other.0) as u64
    }
}

/// A range of text within a CodeMap.
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
pub struct Span {
    /// The position in the codemap representing the first byte of the span.
    low: Pos,

    /// The position after the last byte of the span.
    high: Pos,
}

impl Span {
    /// Makes a span from offsets relative to the start of this span.
    ///
    /// # Panics
    ///   * If `end < begin`
    ///   * If `end` is beyond the length of the span
    pub fn subspan(&self, begin: u64, end: u64) -> Span {
        assert!(end >= begin);
        assert!(self.low + end <= self.high);
        Span {
            low: self.low + begin,
            high: self.low + end,
        }
    }

    /// Checks if a span is contained within this span.
    pub fn contains(&self, other: Span) -> bool {
        self.low <= other.low && self.high >= other.high
    }

    /// The position in the codemap representing the first byte of the span.
    pub fn low(&self) -> Pos {
        self.low
    }

    /// The position after the last byte of the span.
    pub fn high(&self) -> Pos {
        self.high
    }

    /// The length in bytes of the text of the span
    pub fn len(&self) -> u64 {
        self.high - self.low
    }

    /// Create a span that encloses both `self` and `other`.
    pub fn merge(&self, other: Span) -> Span {
        Span {
            low: cmp::min(self.low, other.low),
            high: cmp::max(self.high, other.high),
        }
    }
}

/// Associate a Span with a value of arbitrary type (e.g. an AST node).
#[derive(Clone, PartialEq, Eq, Hash, Debug, Copy)]
pub struct Spanned<T> {
    pub node: T,
    pub span: Span,
}

impl<T> Deref for Spanned<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.node
    }
}

/// A data structure recording source code files for position lookup.
pub struct CodeMap {
    files: Vec<Arc<File>>,
    _allow_priv: ()
}

impl CodeMap {
    /// Creates an empty `CodeMap`.
    pub fn new() -> CodeMap {
        CodeMap {
            files: vec![],
            _allow_priv: ()
        }
    }

    /// Adds a file with the given name and contents.
    ///
    /// Use the returned `File` and its `.span` property to create `Spans`
    /// representing substrings of the file.
    pub fn add_file(&mut self, name: String, source: String) -> Arc<File> {
        let low = self.end_pos() + 1;
        let high = low + source.len() as u64;
        let mut lines = vec![low];
        lines.extend(source.match_indices('\n').map(|(p, _)| { low + (p + 1) as u64 }));

        let file = Arc::new(File {
            span: Span { low: low, high: high },
            name: name,
            source: source,
            lines: lines,
        });

        self.files.push(file.clone());
        file
    }

    fn end_pos(&self) -> Pos {
        self.files.last().map(|x| x.span.high).unwrap_or(Pos(0))
    }

    /// Looks up the `File` that contains the specified position.
    pub fn find_file(&self, pos: Pos) -> &Arc<File> {
        self.files.binary_search_by(|file| {
            if file.span.high < pos {
                Ordering::Less
            } else if file.span.low > pos {
                Ordering::Greater
            } else {
                Ordering::Equal
            }
        }).ok().map(|i| &self.files[i]).expect("Mapping unknown source location")
    }

    /// Gets the file, line, and column represented by a `Pos`.
    pub fn look_up_pos(&self, pos: Pos) -> Loc {
        let file = self.find_file(pos);
        let position = file.find_line_col(pos);
        Loc { file: file.clone(), position }
    }

    /// Gets the file and its line and column ranges represented by a `Span`.
    pub fn look_up_span(&self, span: Span) -> SpanLoc {
        let file = self.find_file(span.low);
        let begin = file.find_line_col(span.low);
        let end = file.find_line_col(span.high);
        SpanLoc { file: file.clone(), begin, end }
    }
}

/// A `CodeMap`'s record of a source file.
pub struct File {
    /// The span representing the entire file.
    pub span: Span,

    /// The filename as it would be displayed in an error message.
    name: String,

    /// Contents of the file.
    source: String,

    /// Byte positions of line beginnings.
    lines: Vec<Pos>,
}

impl File {
    /// Gets the name of the file
    pub fn name(&self) -> &str {
        &self.name
    }

    /// Gets the line number of a Pos.
    ///
    /// The lines are 0-indexed (first line is numbered 0)
    ///
    /// # Panics
    ///
    ///  * If `pos` is not within this file's span
    pub fn find_line(&self, pos: Pos) -> usize {
        assert!(pos >= self.span.low);
        assert!(pos <= self.span.high);
        (match self.lines.binary_search(&pos) {
            Ok(i) => i,
            Err(i) => i - 1,
        })
    }

    /// Gets the line and column of a Pos.
    ///
    /// # Panics
    ///
    /// * If `pos` is not with this file's span
    /// * If `pos` points to a byte in the middle of a UTF-8 character
    pub fn find_line_col(&self, pos: Pos) -> LineCol {
        let line = self.find_line(pos);
        let line_span = self.line_span(line);
        let byte_col = pos - line_span.low;
        let col = self.source_slice(line_span)[..byte_col as usize].chars().count();

        LineCol{ line: line, column: col }
    }

    /// Gets the full source text of the file
    pub fn source(&self) -> &str {
        &self.source
    }

    /// Gets the source text of a Span.
    ///
    /// # Panics
    ///
    ///   * If `span` is not entirely within this file.
    pub fn source_slice(&self, span: Span) -> &str {
        assert!(self.span.contains(span));
        self.source[((span.low - self.span.low) as usize)..((span.high - self.span.low) as usize)].trim_right()
    }

    /// Gets the span representing a line by line number.
    ///
    /// The line number is 0-indexed (first line is numbered 0)
    ///
    /// # Panics
    ///
    ///  * If the line number is out of range
    pub fn line_span(&self, line: usize) -> Span {
        assert!(line < self.lines.len());
        Span {
            low: self.lines[line],
            high: *self.lines.get(line + 1).unwrap_or(&self.span.high)
        }
    }

    /// Gets the source text of a line.
    ///
    /// # Panics
    ///
    ///  * If the line number is out of range
    pub fn source_line(&self, line: usize) -> &str {
        self.source_slice(self.line_span(line))
    }

    /// Gets the number of lines in the file
    pub fn num_lines(&self) -> usize {
        self.lines.len()
    }
}

/// A line and column.
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
pub struct LineCol {
    /// The line number within the file (0-indexed).
    pub line: usize,

    /// The column within the line (0-indexed).
    pub column: usize,
}

/// A file, and a line and column within it.
#[derive(Clone)]
pub struct Loc {
    pub file: Arc<File>,
    pub position: LineCol,
}

impl fmt::Display for Loc {
    /// Formats the location as `filename:line:column`, with a 1-indexed
    /// line and column.
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{}:{}:{}", self.file.name, self.position.line+1, self.position.column+1)
    }
}

/// A file, and a line and column range within it.
#[derive(Clone)]
pub struct SpanLoc {
    pub file: Arc<File>,
    pub begin: LineCol,
    pub end: LineCol,
}

impl fmt::Display for SpanLoc {
    /// Formats the span as `filename:start_line:start_column: end_line:end_column`,
    /// or if the span is zero-length, `filename:line:column`, with a 1-indexed line and column.
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        if self.begin == self.end {
            write!(f, "{}:{}:{}", self.file.name, self.begin.line+1, self.begin.column+1)
        } else {
            write!(f, "{}:{}:{}: {}:{}", self.file.name, self.begin.line+1, self.begin.column+1, self.end.line+1, self.end.column+1)
        }
    }
}

#[test]
fn test_codemap() {
    let mut codemap = CodeMap::new();
    let f1 = codemap.add_file("test1.rs".to_string(), "abcd\nefghij\nqwerty".to_string());
    let f2 = codemap.add_file("test2.rs".to_string(), "foo\nbar".to_string());

    assert_eq!(codemap.find_file(f1.span.low()).name(), "test1.rs");
    assert_eq!(codemap.find_file(f1.span.high()).name(), "test1.rs");
    assert_eq!(codemap.find_file(f2.span.low()).name(), "test2.rs");
    assert_eq!(codemap.find_file(f2.span.high()).name(), "test2.rs");

    let x = f1.span.subspan(5, 10);
    let f = codemap.find_file(x.low);
    assert_eq!(f.name, "test1.rs");
    assert_eq!(f.find_line_col(f.span.low()), LineCol { line: 0, column: 0 });
    assert_eq!(f.find_line_col(f.span.low() + 4), LineCol { line: 0, column: 4 });
    assert_eq!(f.find_line_col(f.span.low() + 5), LineCol { line: 1, column: 0 });
    assert_eq!(f.find_line_col(f.span.low() + 16), LineCol { line: 2, column: 4 });

    let x = f2.span.subspan(4, 7);
    assert_eq!(codemap.find_file(x.low()).name(), "test2.rs");
    assert_eq!(codemap.find_file(x.high()).name(), "test2.rs");
}
<commit_msg>Fix handling of lines with trailing whitespace<commit_after>//! A data structure for tracking source positions in language implementations, inspired by the
//! [CodeMap type in rustc's libsyntax](https://github.com/rust-lang/rust/blob/master/src/libsyntax/codemap.rs).
//!
//! The `CodeMap` tracks all source files and maps positions within them to linear indexes as if all
//! source files were concatenated. This allows a source position to be represented by a small
//! 32-bit `Pos` indexing into the `CodeMap`, under the assumption that the total amount of parsed
//! source code will not exceed 4GiB. The `CodeMap` can look up the source file, line, and column
//! of a `Pos` or `Span`, as well as provide source code snippets for error reporting.
//!
//! # Example
//! ```
//! use codemap::CodeMap;
//! let mut codemap = CodeMap::new();
//! let file = codemap.add_file("test.rs".to_string(), "fn test(){\n    println!(\"Hello\");\n}\n".to_string());
//! let string_literal_span = file.span.subspan(24, 31);
//!
//! let location = codemap.look_up_span(string_literal_span);
//! assert_eq!(location.file.name(), "test.rs");
//! assert_eq!(location.begin.line, 1);
//! assert_eq!(location.begin.column, 13);
//! assert_eq!(location.end.line, 1);
//! assert_eq!(location.end.column, 20);
//! ```

use std::cmp::{self, Ordering};
use std::ops::{Add, Sub, Deref};
use std::fmt;
use std::sync::Arc;

/// A small, `Copy`, value representing a position in a `CodeMap`'s file.
#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug)]
pub struct Pos(u32);

impl Add<u64> for Pos {
    type Output = Pos;
    fn add(self, other: u64) -> Pos {
        Pos(self.0 + other as u32)
    }
}

impl Sub<Pos> for Pos {
    type Output = u64;
    fn sub(self, other: Pos) -> u64 {
        (self.0 - other.0) as u64
    }
}

/// A range of text within a CodeMap.
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
pub struct Span {
    /// The position in the codemap representing the first byte of the span.
    low: Pos,

    /// The position after the last byte of the span.
    high: Pos,
}

impl Span {
    /// Makes a span from offsets relative to the start of this span.
    ///
    /// # Panics
    ///   * If `end < begin`
    ///   * If `end` is beyond the length of the span
    pub fn subspan(&self, begin: u64, end: u64) -> Span {
        assert!(end >= begin);
        assert!(self.low + end <= self.high);
        Span {
            low: self.low + begin,
            high: self.low + end,
        }
    }

    /// Checks if a span is contained within this span.
    pub fn contains(&self, other: Span) -> bool {
        self.low <= other.low && self.high >= other.high
    }

    /// The position in the codemap representing the first byte of the span.
    pub fn low(&self) -> Pos {
        self.low
    }

    /// The position after the last byte of the span.
    pub fn high(&self) -> Pos {
        self.high
    }

    /// The length in bytes of the text of the span
    pub fn len(&self) -> u64 {
        self.high - self.low
    }

    /// Create a span that encloses both `self` and `other`.
    pub fn merge(&self, other: Span) -> Span {
        Span {
            low: cmp::min(self.low, other.low),
            high: cmp::max(self.high, other.high),
        }
    }
}

/// Associate a Span with a value of arbitrary type (e.g. an AST node).
#[derive(Clone, PartialEq, Eq, Hash, Debug, Copy)]
pub struct Spanned<T> {
    pub node: T,
    pub span: Span,
}

impl<T> Deref for Spanned<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.node
    }
}

/// A data structure recording source code files for position lookup.
pub struct CodeMap {
    files: Vec<Arc<File>>,
    _allow_priv: ()
}

impl CodeMap {
    /// Creates an empty `CodeMap`.
    pub fn new() -> CodeMap {
        CodeMap {
            files: vec![],
            _allow_priv: ()
        }
    }

    /// Adds a file with the given name and contents.
    ///
    /// Use the returned `File` and its `.span` property to create `Spans`
    /// representing substrings of the file.
    pub fn add_file(&mut self, name: String, source: String) -> Arc<File> {
        let low = self.end_pos() + 1;
        let high = low + source.len() as u64;
        let mut lines = vec![low];
        lines.extend(source.match_indices('\n').map(|(p, _)| { low + (p + 1) as u64 }));

        let file = Arc::new(File {
            span: Span { low: low, high: high },
            name: name,
            source: source,
            lines: lines,
        });

        self.files.push(file.clone());
        file
    }

    fn end_pos(&self) -> Pos {
        self.files.last().map(|x| x.span.high).unwrap_or(Pos(0))
    }

    /// Looks up the `File` that contains the specified position.
    pub fn find_file(&self, pos: Pos) -> &Arc<File> {
        self.files.binary_search_by(|file| {
            if file.span.high < pos {
                Ordering::Less
            } else if file.span.low > pos {
                Ordering::Greater
            } else {
                Ordering::Equal
            }
        }).ok().map(|i| &self.files[i]).expect("Mapping unknown source location")
    }

    /// Gets the file, line, and column represented by a `Pos`.
    pub fn look_up_pos(&self, pos: Pos) -> Loc {
        let file = self.find_file(pos);
        let position = file.find_line_col(pos);
        Loc { file: file.clone(), position }
    }

    /// Gets the file and its line and column ranges represented by a `Span`.
    pub fn look_up_span(&self, span: Span) -> SpanLoc {
        let file = self.find_file(span.low);
        let begin = file.find_line_col(span.low);
        let end = file.find_line_col(span.high);
        SpanLoc { file: file.clone(), begin, end }
    }
}

/// A `CodeMap`'s record of a source file.
pub struct File {
    /// The span representing the entire file.
    pub span: Span,

    /// The filename as it would be displayed in an error message.
    name: String,

    /// Contents of the file.
    source: String,

    /// Byte positions of line beginnings.
    lines: Vec<Pos>,
}

impl File {
    /// Gets the name of the file
    pub fn name(&self) -> &str {
        &self.name
    }

    /// Gets the line number of a Pos.
    ///
    /// The lines are 0-indexed (first line is numbered 0)
    ///
    /// # Panics
    ///
    ///  * If `pos` is not within this file's span
    pub fn find_line(&self, pos: Pos) -> usize {
        assert!(pos >= self.span.low);
        assert!(pos <= self.span.high);
        (match self.lines.binary_search(&pos) {
            Ok(i) => i,
            Err(i) => i - 1,
        })
    }

    /// Gets the line and column of a Pos.
    ///
    /// # Panics
    ///
    /// * If `pos` is not with this file's span
    /// * If `pos` points to a byte in the middle of a UTF-8 character
    pub fn find_line_col(&self, pos: Pos) -> LineCol {
        let line = self.find_line(pos);
        let line_span = self.line_span(line);
        let byte_col = pos - line_span.low;
        let col = self.source_slice(line_span)[..byte_col as usize].chars().count();

        LineCol{ line: line, column: col }
    }

    /// Gets the full source text of the file
    pub fn source(&self) -> &str {
        &self.source
    }

    /// Gets the source text of a Span.
    ///
    /// # Panics
    ///
    ///   * If `span` is not entirely within this file.
    pub fn source_slice(&self, span: Span) -> &str {
        assert!(self.span.contains(span));
        &self.source[((span.low - self.span.low) as usize)..((span.high - self.span.low) as usize)]
    }

    /// Gets the span representing a line by line number.
    ///
    /// The line number is 0-indexed (first line is numbered 0). The returned span includes the
    /// line terminator.
    ///
    /// # Panics
    ///
    ///  * If the line number is out of range
    pub fn line_span(&self, line: usize) -> Span {
        assert!(line < self.lines.len());
        Span {
            low: self.lines[line],
            high: *self.lines.get(line + 1).unwrap_or(&self.span.high)
        }
    }

    /// Gets the source text of a line.
    ///
    /// The string returned does not include the terminating \r or \n characters.
    ///
    /// # Panics
    ///
    ///  * If the line number is out of range
    pub fn source_line(&self, line: usize) -> &str {
        self.source_slice(self.line_span(line)).trim_right_matches(&['\n', '\r'][..])
    }

    /// Gets the number of lines in the file
    pub fn num_lines(&self) -> usize {
        self.lines.len()
    }
}

/// A line and column.
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
pub struct LineCol {
    /// The line number within the file (0-indexed).
    pub line: usize,

    /// The column within the line (0-indexed).
    pub column: usize,
}

/// A file, and a line and column within it.
#[derive(Clone)]
pub struct Loc {
    pub file: Arc<File>,
    pub position: LineCol,
}

impl fmt::Display for Loc {
    /// Formats the location as `filename:line:column`, with a 1-indexed
    /// line and column.
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{}:{}:{}", self.file.name, self.position.line+1, self.position.column+1)
    }
}

/// A file, and a line and column range within it.
#[derive(Clone)]
pub struct SpanLoc {
    pub file: Arc<File>,
    pub begin: LineCol,
    pub end: LineCol,
}

impl fmt::Display for SpanLoc {
    /// Formats the span as `filename:start_line:start_column: end_line:end_column`,
    /// or if the span is zero-length, `filename:line:column`, with a 1-indexed line and column.
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        if self.begin == self.end {
            write!(f, "{}:{}:{}", self.file.name, self.begin.line+1, self.begin.column+1)
        } else {
            write!(f, "{}:{}:{}: {}:{}", self.file.name, self.begin.line+1, self.begin.column+1, self.end.line+1, self.end.column+1)
        }
    }
}

#[test]
fn test_codemap() {
    let mut codemap = CodeMap::new();
    let f1 = codemap.add_file("test1.rs".to_string(), "abcd\nefghij\nqwerty".to_string());
    let f2 = codemap.add_file("test2.rs".to_string(), "foo\nbar".to_string());

    assert_eq!(codemap.find_file(f1.span.low()).name(), "test1.rs");
    assert_eq!(codemap.find_file(f1.span.high()).name(), "test1.rs");
    assert_eq!(codemap.find_file(f2.span.low()).name(), "test2.rs");
    assert_eq!(codemap.find_file(f2.span.high()).name(), "test2.rs");

    let x = f1.span.subspan(5, 10);
    let f = codemap.find_file(x.low);
    assert_eq!(f.name, "test1.rs");
    assert_eq!(f.find_line_col(f.span.low()), LineCol { line: 0, column: 0 });
    assert_eq!(f.find_line_col(f.span.low() + 4), LineCol { line: 0, column: 4 });
    assert_eq!(f.find_line_col(f.span.low() + 5), LineCol { line: 1, column: 0 });
    assert_eq!(f.find_line_col(f.span.low() + 16), LineCol { line: 2, column: 4 });

    let x = f2.span.subspan(4, 7);
    assert_eq!(codemap.find_file(x.low()).name(), "test2.rs");
    assert_eq!(codemap.find_file(x.high()).name(), "test2.rs");
}

#[test]
fn test_issue2() {
    let mut codemap = CodeMap::new();
    let content = "a \nxyz\r\n";
    let file = codemap.add_file("<test>".to_owned(), content.to_owned());

    let span = file.span.subspan(2, 3);
    assert_eq!(file.find_line_col(span.low()), LineCol { line: 0, column: 2 });
    assert_eq!(file.find_line_col(span.high()), LineCol { line: 1, column: 0 });

    assert_eq!(file.source_line(0), "a ");
    assert_eq!(file.source_line(1), "xyz");
    assert_eq!(file.source_line(2), "");
}
<|endoftext|>


<commit_before>use std::io::{IoResult, IoError, IoErrorKind, Reader};
use std::io::fs::File;

struct Frame;

// TODO: there should be a way to signal IO error.
// This will be handled by the error reform RFCs, I think.
// For now, use IO error to indicate any failure.
pub type Error = IoError;

fn mk_err() -> Error {
    IoError {
        kind: IoErrorKind::OtherIoError,
        desc: "FLAC decoding error",
        detail: None
    }
}

struct FlacStream {
    metadata_blocks: Vec<MetadataBlock>
}

#[cfg(test)]
fn get_foo_file() -> File {
    File::open(&Path::new("foo.flac")).unwrap()
}

// TODO: this should be private, but it must be public for the test for now.
pub fn read_stream_header(input: &mut Reader) -> Result<(), Error> {
    // A FLAC stream starts with a 32-bit header 'fLaC' (big endian).
    const HEADER: u32 = 0x66_4c_61_43;
    let header = try!(input.read_be_u32());
    if header != HEADER { return Err(mk_err()); } // TODO: provide more error info.
    Ok(())
}

#[test]
fn test_read_stream_header() {
    let mut input = get_foo_file();
    read_stream_header(&mut input).unwrap();
}

// TODO: should this be private or not?
#[deriving(Show)]
pub struct StreamInfo {
    pub min_block_size: u16,
    pub max_block_size: u16,
    pub min_frame_size: Option<u32>,
    pub max_frame_size: Option<u32>,
    pub sample_rate: u32,
    pub n_channels: u8,
    pub bits_per_sample: u8,
    pub n_samples: Option<u64>,
    pub md5sum: [u8, ..16]
}

// TODO: should this be private?
pub struct Seekpoint {
    pub sample: u64,
    pub offset: u64,
    pub n_samples: u16
}

// TODO: should this be private?
pub struct SeekTable {
    seekpoints: Vec<Seekpoint>
}

pub enum MetadataBlock {
    StreamInfoBlock(StreamInfo),
    PaddingBlock(u32),
    ApplicationBlock(u32, Vec<u8>),
    SeekTableBlock(SeekTable),
    VorbisCommentBlock, // TODO
    CuesheetBlock, // TODO
    PictureBlock // TODO
}

// TODO: this should be private.
pub struct MetadataBlockHeader {
    is_last: bool,
    block_type: u8,
    length: u32
}

// TODO: this should be private, but it must be public for the test for now.
pub fn read_metadata_block_header(input: &mut Reader) -> Result<MetadataBlockHeader, Error> {
    let byte = try!(input.read_u8());

    // The first bit specifies whether this is the last block, the next 7 bits
    // specify the type of the metadata block to follow.
    let is_last = (byte & 1) == 1;
    let block_type = byte >> 1;

    if block_type == 127 { return Err(mk_err()); } // TODO: "invalid, to avoid confusion with a frame sync code"
    if block_type > 6 { return Err(mk_err()); } // TODO: "reserved"

    // The length field is 24 bits, or 3 bytes.
    let length = try!(input.read_be_uint_n(3)) as u32;

    let header = MetadataBlockHeader {
        is_last: is_last,
        block_type: block_type,
        length: length
    };
    Ok(header)
}

// TODO: this should be private, but it must be public for the test for now.
pub fn read_streaminfo_block(input: &mut Reader) -> Result<StreamInfo, Error> {
    let min_block_size = try!(input.read_be_u16());
    let max_block_size = try!(input.read_be_u16());

    // The frame size fields are 24 bits, or 3 bytes.
    let min_frame_size = try!(input.read_be_uint_n(3)) as u32;
    let max_frame_size = try!(input.read_be_uint_n(3)) as u32;

    // Next up are 20 bits that determine the sample rate.
    let sample_rate_msb = try!(input.read_be_u16());
    let sample_rate_lsb = try!(input.read_byte());

    // Stitch together the value from the first 16 bits,
    // and then the 4 most significant bits of the next byte.
    let sample_rate = sample_rate_msb as u32 << 4 | sample_rate_lsb as u32 >> 4;

    // Next three bits are the number of channels - 1. Mask them out and add 1.
    let n_channels_bps = sample_rate_lsb;
    let n_channels = ((n_channels_bps >> 1) & 0x7) + 1;

    // The final bit is the most significant of bits per sample - 1. Bits per
    // sample - 1 is 5 bits in total.
    let bps_msb = n_channels_bps & 1;
    let bps_lsb_n_samples = try!(input.read_byte());

    // Stitch together these values, add 1 because # - 1 is stored.
    let bits_per_sample = (bps_msb << 5 | (bps_lsb_n_samples >> 4)) + 1;

    // Number of samples in 36 bits, we have 4 already, 32 to go.
    let n_samples_msb = bps_lsb_n_samples & 0xf;
    let n_samples_lsb = try!(input.read_be_u32());
    let n_samples = n_samples_msb as u64 << 32 | n_samples_lsb as u64;

    let mut md5sum = [0u8, ..16];
    try!(input.read_at_least(16, &mut md5sum));

    let stream_info = StreamInfo {
        min_block_size: min_block_size,
        max_block_size: max_block_size,
        min_frame_size: if min_frame_size == 0 { None } else { Some(min_frame_size) },
        max_frame_size: if max_frame_size == 0 { None } else { Some(max_frame_size) },
        sample_rate: sample_rate,
        n_channels: n_channels,
        bits_per_sample: bits_per_sample,
        n_samples: if n_samples == 0 { None } else { Some(n_samples) },
        md5sum: md5sum
    };

    Ok(stream_info)
}

impl FlacStream {
    pub fn new(input: &mut Reader) -> Result<FlacStream, Error> {
        try!(read_stream_header(input));

        let hdr = try!(read_metadata_block_header(input));
        println!("type: {}, length: {}, last: {}",
                 hdr.block_type, hdr.length, hdr.is_last);

        let streaminfo = try!(read_streaminfo_block(input));
        println!("streaminfo: {}", streaminfo);
        // Read the STREAMINFO block
        // Read any metadata
        // Read frames

        let flac_stream = FlacStream {
            metadata_blocks: Vec::new()
        };

        Ok(flac_stream)
    }
}

#[test]
fn test_open_stream() {
    let mut input = get_foo_file();
    let flac_stream = FlacStream::new(&mut input).unwrap();
}
<commit_msg>fix bug in reading bits per sample<commit_after>use std::io::{IoResult, IoError, IoErrorKind, Reader};
use std::io::fs::File;

struct Frame;

// TODO: there should be a way to signal IO error.
// This will be handled by the error reform RFCs, I think.
// For now, use IO error to indicate any failure.
pub type Error = IoError;

fn mk_err() -> Error {
    IoError {
        kind: IoErrorKind::OtherIoError,
        desc: "FLAC decoding error",
        detail: None
    }
}

struct FlacStream {
    metadata_blocks: Vec<MetadataBlock>
}

#[cfg(test)]
fn get_foo_file() -> File {
    File::open(&Path::new("foo.flac")).unwrap()
}

// TODO: this should be private, but it must be public for the test for now.
pub fn read_stream_header(input: &mut Reader) -> Result<(), Error> {
    // A FLAC stream starts with a 32-bit header 'fLaC' (big endian).
    const HEADER: u32 = 0x66_4c_61_43;
    let header = try!(input.read_be_u32());
    if header != HEADER { return Err(mk_err()); } // TODO: provide more error info.
    Ok(())
}

#[test]
fn test_read_stream_header() {
    let mut input = get_foo_file();
    read_stream_header(&mut input).unwrap();
}

// TODO: should this be private or not?
#[deriving(Show)]
pub struct StreamInfo {
    pub min_block_size: u16,
    pub max_block_size: u16,
    pub min_frame_size: Option<u32>,
    pub max_frame_size: Option<u32>,
    pub sample_rate: u32,
    pub n_channels: u8,
    pub bits_per_sample: u8,
    pub n_samples: Option<u64>,
    pub md5sum: [u8, ..16]
}

// TODO: should this be private?
pub struct Seekpoint {
    pub sample: u64,
    pub offset: u64,
    pub n_samples: u16
}

// TODO: should this be private?
pub struct SeekTable {
    seekpoints: Vec<Seekpoint>
}

pub enum MetadataBlock {
    StreamInfoBlock(StreamInfo),
    PaddingBlock(u32),
    ApplicationBlock(u32, Vec<u8>),
    SeekTableBlock(SeekTable),
    VorbisCommentBlock, // TODO
    CuesheetBlock, // TODO
    PictureBlock // TODO
}

// TODO: this should be private.
pub struct MetadataBlockHeader {
    is_last: bool,
    block_type: u8,
    length: u32
}

// TODO: this should be private, but it must be public for the test for now.
pub fn read_metadata_block_header(input: &mut Reader) -> Result<MetadataBlockHeader, Error> {
    let byte = try!(input.read_u8());

    // The first bit specifies whether this is the last block, the next 7 bits
    // specify the type of the metadata block to follow.
    let is_last = (byte & 1) == 1;
    let block_type = byte >> 1;

    if block_type == 127 { return Err(mk_err()); } // TODO: "invalid, to avoid confusion with a frame sync code"
    if block_type > 6 { return Err(mk_err()); } // TODO: "reserved"

    // The length field is 24 bits, or 3 bytes.
    let length = try!(input.read_be_uint_n(3)) as u32;

    let header = MetadataBlockHeader {
        is_last: is_last,
        block_type: block_type,
        length: length
    };
    Ok(header)
}

// TODO: this should be private, but it must be public for the test for now.
pub fn read_streaminfo_block(input: &mut Reader) -> Result<StreamInfo, Error> {
    let min_block_size = try!(input.read_be_u16());
    let max_block_size = try!(input.read_be_u16());

    // The frame size fields are 24 bits, or 3 bytes.
    let min_frame_size = try!(input.read_be_uint_n(3)) as u32;
    let max_frame_size = try!(input.read_be_uint_n(3)) as u32;

    // Next up are 20 bits that determine the sample rate.
    let sample_rate_msb = try!(input.read_be_u16());
    let sample_rate_lsb = try!(input.read_byte());

    // Stitch together the value from the first 16 bits,
    // and then the 4 most significant bits of the next byte.
    let sample_rate = sample_rate_msb as u32 << 4 | sample_rate_lsb as u32 >> 4;

    // Next three bits are the number of channels - 1. Mask them out and add 1.
    let n_channels_bps = sample_rate_lsb;
    let n_channels = ((n_channels_bps >> 1) & 0x7) + 1;

    // The final bit is the most significant of bits per sample - 1. Bits per
    // sample - 1 is 5 bits in total.
    let bps_msb = n_channels_bps & 1;
    let bps_lsb_n_samples = try!(input.read_byte());

    // Stitch together these values, add 1 because # - 1 is stored.
    let bits_per_sample = (bps_msb << 4 | (bps_lsb_n_samples >> 4)) + 1;

    // Number of samples in 36 bits, we have 4 already, 32 to go.
    let n_samples_msb = bps_lsb_n_samples & 0xf;
    let n_samples_lsb = try!(input.read_be_u32());
    let n_samples = n_samples_msb as u64 << 32 | n_samples_lsb as u64;

    let mut md5sum = [0u8, ..16];
    try!(input.read_at_least(16, &mut md5sum));

    let stream_info = StreamInfo {
        min_block_size: min_block_size,
        max_block_size: max_block_size,
        min_frame_size: if min_frame_size == 0 { None } else { Some(min_frame_size) },
        max_frame_size: if max_frame_size == 0 { None } else { Some(max_frame_size) },
        sample_rate: sample_rate,
        n_channels: n_channels,
        bits_per_sample: bits_per_sample,
        n_samples: if n_samples == 0 { None } else { Some(n_samples) },
        md5sum: md5sum
    };

    Ok(stream_info)
}

impl FlacStream {
    pub fn new(input: &mut Reader) -> Result<FlacStream, Error> {
        try!(read_stream_header(input));

        let hdr = try!(read_metadata_block_header(input));
        println!("type: {}, length: {}, last: {}",
                 hdr.block_type, hdr.length, hdr.is_last);

        let streaminfo = try!(read_streaminfo_block(input));
        println!("streaminfo: {}", streaminfo);
        // Read the STREAMINFO block
        // Read any metadata
        // Read frames

        let flac_stream = FlacStream {
            metadata_blocks: Vec::new()
        };

        Ok(flac_stream)
    }
}

#[test]
fn test_open_stream() {
    let mut input = get_foo_file();
    let flac_stream = FlacStream::new(&mut input).unwrap();
}
<|endoftext|>