<commit_before>use std::path::Path;
use dataset::Dataset;
use tiledata::{TILE_SIZE};
use tile::Tile;
use tiledata::TileData;
use colormap::{ColorMap,rgb};
use scale::{Scale,normalize};
use image;

/// This struct represents an image tile,
/// it holds all the pixel values needed to build
/// an image file (PNG) from it.
pub struct ImgTile {
    /// Array of pixel values (flattened)
    pub pixels: [u8; 4 * TILE_SIZE * TILE_SIZE],
    /// Web mercator x coordinate of the tile
    pub x: u32,
    /// Web mercator y coordinate of the tile
    pub y: u32,
    /// Zoom level
    pub z: u32,
}
impl ImgTile {
    /// Export the ImgTile as a PNG file.
    pub fn save(&self, path: &str) {
        let _ = image::save_buffer(
            &Path::new(path),
            &self.pixels,
            TILE_SIZE as u32,
            TILE_SIZE as u32,
            image::RGBA(8)
        );
    }
}

/// Provides convenient functions to render a `Dataset` instance into `ImgTile`s
pub struct Renderer {
    color_map: ColorMap,
    scale: Scale,
    dataset: Dataset
}
impl Renderer {
    /** Create a `Renderer` instance from a dataset.
     *
     * # Args
     * * `dataset`: A dataset instance (which wraps an netCDF file)
     * * `min`: the minimum value of the colorbar
     * * `max`: the maximum value of the colorbar
     * * `color_map`: a ColorMap variant, which defines the *value* => *color* mapping
     */
    pub fn from_dataset(dataset: Dataset, scale: Scale, color_map: ColorMap)
            -> Result<Self, String> {
        Ok(
            Self {
                color_map: color_map,
                scale: scale,
                dataset: dataset
            }
        )
    }

    /**
     * Returns a pixel value (RGBA) from a value, according to the
     * renderer colormap, and the scale
     */
    #[inline]
    pub fn value_to_rgba(&self, value: f32) -> [u8; 4] {
        if value.is_nan() {
            return [0u8, 0u8, 0u8, 0u8];
        }
        let scaled_value = normalize(&self.scale, value);
        let rgb = rgb(scaled_value, &self.color_map);
        [rgb[0], rgb[1], rgb[2], 255u8]
    }

    fn values_to_colors(&self, values: [[f32; TILE_SIZE]; TILE_SIZE])
            -> [u8; 4 * TILE_SIZE * TILE_SIZE] {
        let mut colors = [0u8; 4* TILE_SIZE * TILE_SIZE];
        let mut count: usize = 0;
        // iter latitude in reverse, to fit the image X,Y orientation
        for i_lat in (0..TILE_SIZE).rev() {
            for i_lon in 0..TILE_SIZE {
                let rgba = self.value_to_rgba(values[i_lat][i_lon]);
                colors[count + 0] = rgba[0];
                colors[count + 1] = rgba[1];
                colors[count + 2] = rgba[2];
                colors[count + 3] = rgba[3];
                count += 4;
            }
        }
        colors
    }

    /// Return the value stored at (lat, lon)
    pub fn value_at_coordinates(&self, lat: f64, lon: f64) -> Result<f32,String> {
        self.dataset.value_at_coordinates(lat, lon)
    }

    /**
     * Render a Tile into an ImgTile.
     *
     * #Details
     *
     * Extract values from the renderer dataset,
     * interpolate them into a TILE_SIZE * TILE_SIZE grid,
     * and convert them into pixel values.
     */
    pub fn render_tile(&self, tile: &Tile) -> Result<ImgTile, String> {
        let tile_data = self.dataset.get_tile_data(tile)?;
        let data = tile_data.to_tile_grid();
        let colors = self.values_to_colors(data);
        Ok(
            ImgTile {
                pixels: colors,
                x: tile.x,
                y: tile.y,
                z: tile.z,
            }
        )
    }

    /// This function render a Tildata and its `level` sub-levels into ImgTile,
    /// by *RECURSIVELY* calling itself using `data.sub_tiledata`.
    fn render_n_tiledata_zoom(&self, data: &TileData, level: u8) -> Vec<ImgTile> {
        let mut imgs: Vec<ImgTile> = Vec::new();
        imgs.push(
            ImgTile {
                pixels: self.values_to_colors(data.to_tile_grid()),
                x: data.tile.x,
                y: data.tile.y,
                z: data.tile.z,
            }
        );

        if level > 0 {
            for sub_data in data.sub_tiledata() {
                imgs.extend(self.render_n_tiledata_zoom(&sub_data, level -1));
            }
        }
        return imgs;

    }

    /// This function renders a tile and its `level` sub-levels into ImgTile.
    /// It only extracts values from the dataset once, and recursively renders `level` levels
    /// of tiles using those values.
    pub fn render_n_level_tile(&self, tile: &Tile, level: u8) -> Result<Vec<ImgTile>, String> {
        let tile_data = self.dataset.get_tile_data(tile)?;
        return Ok(self.render_n_tiledata_zoom(&tile_data, level));
    }

}
<commit_msg>Change `&str` to `AsRef<Path>`, (thx @juleskers)<commit_after>use std::path::Path;
use dataset::Dataset;
use tiledata::{TILE_SIZE};
use tile::Tile;
use tiledata::TileData;
use colormap::{ColorMap,rgb};
use scale::{Scale,normalize};
use image;

/// This struct represents an image tile,
/// it holds all the pixel values needed to build
/// an image file (PNG) from it.
pub struct ImgTile {
    /// Array of pixel values (flattened)
    pub pixels: [u8; 4 * TILE_SIZE * TILE_SIZE],
    /// Web mercator x coordinate of the tile
    pub x: u32,
    /// Web mercator y coordinate of the tile
    pub y: u32,
    /// Zoom level
    pub z: u32,
}
impl ImgTile {
    /// Export the ImgTile as a PNG file.
    pub fn save<P: AsRef<Path>>(&self, path: P) {
        let _ = image::save_buffer(
            path.as_ref(),
            &self.pixels,
            TILE_SIZE as u32,
            TILE_SIZE as u32,
            image::RGBA(8)
        );
    }
}

/// Provides convenient functions to render a `Dataset` instance into `ImgTile`s
pub struct Renderer {
    color_map: ColorMap,
    scale: Scale,
    dataset: Dataset
}
impl Renderer {
    /** Create a `Renderer` instance from a dataset.
     *
     * # Args
     * * `dataset`: A dataset instance (which wraps an netCDF file)
     * * `min`: the minimum value of the colorbar
     * * `max`: the maximum value of the colorbar
     * * `color_map`: a ColorMap variant, which defines the *value* => *color* mapping
     */
    pub fn from_dataset(dataset: Dataset, scale: Scale, color_map: ColorMap)
            -> Result<Self, String> {
        Ok(
            Self {
                color_map: color_map,
                scale: scale,
                dataset: dataset
            }
        )
    }

    /**
     * Returns a pixel value (RGBA) from a value, according to the
     * renderer colormap, and the scale
     */
    #[inline]
    pub fn value_to_rgba(&self, value: f32) -> [u8; 4] {
        if value.is_nan() {
            return [0u8, 0u8, 0u8, 0u8];
        }
        let scaled_value = normalize(&self.scale, value);
        let rgb = rgb(scaled_value, &self.color_map);
        [rgb[0], rgb[1], rgb[2], 255u8]
    }

    fn values_to_colors(&self, values: [[f32; TILE_SIZE]; TILE_SIZE])
            -> [u8; 4 * TILE_SIZE * TILE_SIZE] {
        let mut colors = [0u8; 4* TILE_SIZE * TILE_SIZE];
        let mut count: usize = 0;
        // iter latitude in reverse, to fit the image X,Y orientation
        for i_lat in (0..TILE_SIZE).rev() {
            for i_lon in 0..TILE_SIZE {
                let rgba = self.value_to_rgba(values[i_lat][i_lon]);
                colors[count + 0] = rgba[0];
                colors[count + 1] = rgba[1];
                colors[count + 2] = rgba[2];
                colors[count + 3] = rgba[3];
                count += 4;
            }
        }
        colors
    }

    /// Return the value stored at (lat, lon)
    pub fn value_at_coordinates(&self, lat: f64, lon: f64) -> Result<f32,String> {
        self.dataset.value_at_coordinates(lat, lon)
    }

    /**
     * Render a Tile into an ImgTile.
     *
     * #Details
     *
     * Extract values from the renderer dataset,
     * interpolate them into a TILE_SIZE * TILE_SIZE grid,
     * and convert them into pixel values.
     */
    pub fn render_tile(&self, tile: &Tile) -> Result<ImgTile, String> {
        let tile_data = self.dataset.get_tile_data(tile)?;
        let data = tile_data.to_tile_grid();
        let colors = self.values_to_colors(data);
        Ok(
            ImgTile {
                pixels: colors,
                x: tile.x,
                y: tile.y,
                z: tile.z,
            }
        )
    }

    /// This function render a Tildata and its `level` sub-levels into ImgTile,
    /// by *RECURSIVELY* calling itself using `data.sub_tiledata`.
    fn render_n_tiledata_zoom(&self, data: &TileData, level: u8) -> Vec<ImgTile> {
        let mut imgs: Vec<ImgTile> = Vec::new();
        imgs.push(
            ImgTile {
                pixels: self.values_to_colors(data.to_tile_grid()),
                x: data.tile.x,
                y: data.tile.y,
                z: data.tile.z,
            }
        );

        if level > 0 {
            for sub_data in data.sub_tiledata() {
                imgs.extend(self.render_n_tiledata_zoom(&sub_data, level -1));
            }
        }
        return imgs;

    }

    /// This function renders a tile and its `level` sub-levels into ImgTile.
    /// It only extracts values from the dataset once, and recursively renders `level` levels
    /// of tiles using those values.
    pub fn render_n_level_tile(&self, tile: &Tile, level: u8) -> Result<Vec<ImgTile>, String> {
        let tile_data = self.dataset.get_tile_data(tile)?;
        return Ok(self.render_n_tiledata_zoom(&tile_data, level));
    }

}
<|endoftext|>


<commit_before>// Copyright 2018 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate term;

use std::net::SocketAddr;

use api;
use p2p;
use grin::ServerConfig;

pub fn show_status(config: &ServerConfig) {
	println!();
	let title = format!("Grin Server Status");
	let mut t = term::stdout().unwrap();
	let mut e = term::stdout().unwrap();
	t.fg(term::color::MAGENTA).unwrap();
	writeln!(t, "{}", title).unwrap();
	writeln!(t, "--------------------------").unwrap();
	t.reset().unwrap();
	match get_status_from_node(config) {
		Ok(status) => {
			writeln!(e, "Protocol version: {}", status.protocol_version).unwrap();
			writeln!(e, "User agent: {}", status.user_agent).unwrap();
			writeln!(e, "Connections: {}", status.connections).unwrap();
			writeln!(e, "Chain height: {}", status.tip.height).unwrap();
			writeln!(e, "Last block hash: {}", status.tip.last_block_pushed).unwrap();
			writeln!(e, "Previous block hash: {}", status.tip.prev_block_to_last).unwrap();
			writeln!(e, "Total difficulty: {}", status.tip.total_difficulty).unwrap();
		}
		Err(_) => writeln!(
			e,
			"WARNING: Client failed to get data. Is your `grin server` offline or broken?"
		).unwrap(),
	};
	e.reset().unwrap();
	println!()
}

pub fn ban_peer(config: &ServerConfig, peer_addr: &SocketAddr) {
	let params = "";
	let mut e = term::stdout().unwrap();
	let url = format!(
		"http://{}/v1/peers/{}/ban",
		config.api_http_addr,
		peer_addr.to_string()
	);
	match api::client::post(url.as_str(), &params).map_err(|e| Error::API(e)) {
		Ok(_) => writeln!(e, "Successfully banned peer {}", peer_addr.to_string()).unwrap(),
		Err(_) => writeln!(e, "Failed to ban peer {}", peer_addr).unwrap(),
	};
	e.reset().unwrap();
}

pub fn unban_peer(config: &ServerConfig, peer_addr: &SocketAddr) {
	let params = "";
	let mut e = term::stdout().unwrap();
	let url = format!(
		"http://{}/v1/peers/{}/unban",
		config.api_http_addr,
		peer_addr.to_string()
	);
	match api::client::post(url.as_str(), &params).map_err(|e| Error::API(e)) {
		Ok(_) => writeln!(e, "Successfully unbanned peer {}", peer_addr).unwrap(),
		Err(_) => writeln!(e, "Failed to unban peer {}", peer_addr).unwrap(),
	};
	e.reset().unwrap();
}

pub fn list_connected_peers(config: &ServerConfig) {
	let mut e = term::stdout().unwrap();
	let url = format!("http://{}/v1/peers/connected", config.api_http_addr);
	match api::client::get::<Vec<p2p::PeerInfo>>(url.as_str()).map_err(|e| Error::API(e)) {
		Ok(connected_peers) => {
			let mut index = 0;
			for connected_peer in connected_peers {
				writeln!(e, "Peer {}:", index).unwrap();
				writeln!(e, "Capabilities: {:?}", connected_peer.capabilities).unwrap();
				writeln!(e, "User agent: {}", connected_peer.user_agent).unwrap();
				writeln!(e, "Version: {}", connected_peer.version).unwrap();
				writeln!(e, "Peer address: {}", connected_peer.addr).unwrap();
				writeln!(e, "Total difficulty: {}", connected_peer.total_difficulty).unwrap();
				println!();
				index = index + 1;
			}
		}
		Err(_) => writeln!(e, "Failed to get connected peers").unwrap(),
	};
	e.reset().unwrap();
}

fn get_status_from_node(config: &ServerConfig) -> Result<api::Status, Error> {
	let url = format!("http://{}/v1/status", config.api_http_addr);
	api::client::get::<api::Status>(url.as_str()).map_err(|e| Error::API(e))
}

/// Error type wrapping underlying module errors.
#[derive(Debug)]
enum Error {
	/// Error originating from HTTP API calls.
	API(api::Error),
}
<commit_msg>Add direction in grin client listconnectedpeers (#854)<commit_after>// Copyright 2018 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate term;

use std::net::SocketAddr;

use api;
use p2p;
use grin::ServerConfig;

pub fn show_status(config: &ServerConfig) {
	println!();
	let title = format!("Grin Server Status");
	let mut t = term::stdout().unwrap();
	let mut e = term::stdout().unwrap();
	t.fg(term::color::MAGENTA).unwrap();
	writeln!(t, "{}", title).unwrap();
	writeln!(t, "--------------------------").unwrap();
	t.reset().unwrap();
	match get_status_from_node(config) {
		Ok(status) => {
			writeln!(e, "Protocol version: {}", status.protocol_version).unwrap();
			writeln!(e, "User agent: {}", status.user_agent).unwrap();
			writeln!(e, "Connections: {}", status.connections).unwrap();
			writeln!(e, "Chain height: {}", status.tip.height).unwrap();
			writeln!(e, "Last block hash: {}", status.tip.last_block_pushed).unwrap();
			writeln!(e, "Previous block hash: {}", status.tip.prev_block_to_last).unwrap();
			writeln!(e, "Total difficulty: {}", status.tip.total_difficulty).unwrap();
		}
		Err(_) => writeln!(
			e,
			"WARNING: Client failed to get data. Is your `grin server` offline or broken?"
		).unwrap(),
	};
	e.reset().unwrap();
	println!()
}

pub fn ban_peer(config: &ServerConfig, peer_addr: &SocketAddr) {
	let params = "";
	let mut e = term::stdout().unwrap();
	let url = format!(
		"http://{}/v1/peers/{}/ban",
		config.api_http_addr,
		peer_addr.to_string()
	);
	match api::client::post(url.as_str(), &params).map_err(|e| Error::API(e)) {
		Ok(_) => writeln!(e, "Successfully banned peer {}", peer_addr.to_string()).unwrap(),
		Err(_) => writeln!(e, "Failed to ban peer {}", peer_addr).unwrap(),
	};
	e.reset().unwrap();
}

pub fn unban_peer(config: &ServerConfig, peer_addr: &SocketAddr) {
	let params = "";
	let mut e = term::stdout().unwrap();
	let url = format!(
		"http://{}/v1/peers/{}/unban",
		config.api_http_addr,
		peer_addr.to_string()
	);
	match api::client::post(url.as_str(), &params).map_err(|e| Error::API(e)) {
		Ok(_) => writeln!(e, "Successfully unbanned peer {}", peer_addr).unwrap(),
		Err(_) => writeln!(e, "Failed to unban peer {}", peer_addr).unwrap(),
	};
	e.reset().unwrap();
}

pub fn list_connected_peers(config: &ServerConfig) {
	let mut e = term::stdout().unwrap();
	let url = format!("http://{}/v1/peers/connected", config.api_http_addr);
	match api::client::get::<Vec<p2p::PeerInfo>>(url.as_str()).map_err(|e| Error::API(e)) {
		Ok(connected_peers) => {
			let mut index = 0;
			for connected_peer in connected_peers {
				writeln!(e, "Peer {}:", index).unwrap();
				writeln!(e, "Capabilities: {:?}", connected_peer.capabilities).unwrap();
				writeln!(e, "User agent: {}", connected_peer.user_agent).unwrap();
				writeln!(e, "Version: {}", connected_peer.version).unwrap();
				writeln!(e, "Peer address: {}", connected_peer.addr).unwrap();
				writeln!(e, "Total difficulty: {}", connected_peer.total_difficulty).unwrap();
				writeln!(e, "Direction: {:?}", connected_peer.direction).unwrap();
				println!();
				index = index + 1;
			}
		}
		Err(_) => writeln!(e, "Failed to get connected peers").unwrap(),
	};
	e.reset().unwrap();
}

fn get_status_from_node(config: &ServerConfig) -> Result<api::Status, Error> {
	let url = format!("http://{}/v1/status", config.api_http_addr);
	api::client::get::<api::Status>(url.as_str()).map_err(|e| Error::API(e))
}

/// Error type wrapping underlying module errors.
#[derive(Debug)]
enum Error {
	/// Error originating from HTTP API calls.
	API(api::Error),
}
<|endoftext|>


<commit_before>use std::fmt;

use self::Rope::*;

fn max<T: PartialOrd + Copy>(x: T, y: T) -> T {
    if x >= y {
        x
    } else {
        y
    }
}

#[derive(Clone)]
pub enum Rope {
    FlatCharVec { seq: Vec<char> },
    Concatenation { left: Box<Rope>, right: Box<Rope>, depth: usize, length: usize },
    Reverse { rope: Box<Rope> },
    SubString { rope: Box<Rope>, offset: usize, length: usize },
}

fn write_rope_to_string(rope: &Rope, s: &mut String) {
    match *rope {
        FlatCharVec { ref seq }                   => {
            s.push_str(&seq.iter().map(Clone::clone).collect::<String>());
        }
        Concatenation { ref left, ref right, .. } => {
            write_rope_to_string(left, s);
            write_rope_to_string(right, s);
        },
        Reverse { ref rope }                      => {
            let inner = rope.to_string();
            s.push_str(&inner.chars().rev().collect::<String>());
        }
        SubString { ref rope, offset, length }    => {
            let inner = rope.to_string();
            s.push_str(&inner[offset..offset+length]);
        }
    }
}

fn concatenate(left: Rope, right: Rope) -> Rope {
    if left.len() == 0 {
        return right;
    }
    if right.len() == 0 {
        return left;
    }

    const COMBINE_LENGTH: usize = 17;
    let length = left.len() + right.len();
    if length < COMBINE_LENGTH {
        let mut lchs = left.into_chars();
        lchs.extend(right.into_chars());
        return FlatCharVec { seq: lchs };
    }
    let depth = max(left.depth(), right.depth()) + 1;
    Concatenation { left: Box::new(left), right: Box::new(right), depth: depth, length: length }
}

impl Rope {
    pub fn from_str(s: &str) -> Rope {
        Rope::from_vec(s.chars().collect())
    }

    pub fn from_vec(seq: Vec<char>) -> Rope {
        Rope::FlatCharVec { seq: seq }
    }

    pub fn len(&self) -> usize {
        match *self {
            FlatCharVec { ref seq }      => seq.len(),
            Concatenation { length, .. } => length,
            Reverse { ref rope }         => rope.len(),
            SubString { length, .. }     => length
        }
    }

    pub fn depth(&self) -> usize {
        match *self {
            FlatCharVec { .. }          => 0,
            Concatenation { depth, .. } => depth,
            Reverse { ref rope }        => rope.depth(),
            SubString { ref rope, .. }  => rope.depth()
        }
    }

    pub fn append<RHS: IntoRope>(self, rhs: RHS) -> Self {
        concatenate(self, rhs.into_rope())
    }

    pub fn to_string(&self) -> String {
        let mut s = String::new();
        write_rope_to_string(self, &mut s);
        s
    }

    pub fn reverse(self) -> Self {
        match self {
            Reverse { rope } => *rope,
            Concatenation { left, right, .. } => {
                concatenate(right.reverse(), left.reverse())
            },
            this => Reverse { rope: Box::new(this) }
        }
    }

    // FIXME: clone?
    pub fn delete(self, start: usize, end: usize) -> Self {
        let tail = self.clone().slice_from(end);
        self.slice_to(start).append(tail)
    }

    pub fn slice_from(self, start: usize) -> Self {
        let len = self.len();
        self.slice(start, len)
    }

    pub fn slice_to(self, end: usize) -> Self {
        self.slice(0, end)
    }

    pub fn slice(self, start: usize, end: usize) -> Self {
        assert!(end <= self.len(), "illegal slice()");
        let slen = self.len();
        if start == 0 && end == slen {
            self
        } else {
            match self {
                Concatenation { left, right, .. } => {
                    let llen = left.len();
                    if end <= llen {
                        left.slice(start, end)
                    } else if start >= llen {
                        right.slice(start-llen, end-llen)
                    } else  {
                        concatenate(left.slice(start, llen), right.slice(0, end-llen))
                    }
                },
                this@FlatCharVec { .. } => {
                    if end - start < 16 {
                        if let FlatCharVec { seq } = this {
                            FlatCharVec { seq: seq[start..end].to_vec() }
                        } else {
                            unreachable!()
                        }
                    } else {
                        SubString { rope: Box::new(this), offset: start, length: end-start }
                    }
                },
                SubString { rope, offset, length }  => {
                    assert!(end - start <= length);
                    SubString { rope: rope, offset: offset + start, length: end - start }
                },
                Reverse { rope } => {
                    rope.slice(slen-end, slen-start).reverse()
                }
            }
        }
    }

    fn into_chars(self) -> Vec<char> {
        match self {
            FlatCharVec { seq }         => seq,
            Concatenation { left, right, .. } => {
                let mut lchs = left.into_chars();
                let rchs = right.into_chars();
                lchs.extend(rchs);
                lchs
            }
            Reverse { rope }        => {
                let mut inner = rope.into_chars();
                inner.reverse();
                inner
            }
            SubString { rope, offset, length }  => {
                let inner = rope.into_chars();
                inner[offset .. offset+length].to_vec()
            }
        }
    }

    fn is_balanced(&self) -> bool {
        true
    }
}

impl fmt::Display for Rope {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.to_string())
    }
}

impl fmt::Debug for Rope {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "R{:?}", self.to_string())
    }
}

pub trait IntoRope {
    fn into_rope(self) -> Rope;
}

impl IntoRope for char {
    fn into_rope(self) -> Rope {
        FlatCharVec { seq: vec![self] }
    }
}

impl IntoRope for String {
    fn into_rope(self) -> Rope {
        FlatCharVec { seq: self.chars().collect() }
    }
}

impl IntoRope for Rope {
    fn into_rope(self) -> Rope {
        self
    }
}

impl<'a> IntoRope for &'a str {
    fn into_rope(self) -> Rope {
        FlatCharVec { seq: self.chars().collect() }
    }
}

#[test]
fn test_rope() {
    let s = Rope::from_str("Hello").append(' ').append("World").append('!');

    println!("depth => {}", s.depth());
    println!("len => {}", s.len());
    println!("got => {:?}", s);
    println!("got => {:?}", s.reverse());
}

#[test]
fn test_rope2() {
    let s = Rope::from_str("Hello")
        .append(" abcdefghijklmnopqrstuvwxyz")
        .append(" World")
        .append(" abcdefghijklmnopqrstuvwxyz")
        .append(" abcdefghijklmnopqrstuvwxyz")
        .append(" abcdefghijklmnopqrstuvwxyz");

    println!("depth => {}", s.depth());
    println!("len => {}", s.len());
    println!("got => {:?}", s);
    let s2 = s.slice(10, 50);
    println!("got => {:?}", s2);
    println!("len => {:?}", s2.len());
}

// pub trait Rope {
//     fn find(&self, ch: char) -> Option<usize>;
//     // fn find(&self, pat: &str) -> Option<usize>;
//     fn insert(self, offset: usize, s: &str) -> Self;

//     fn rebalance(self) -> Self;

//     fn trim_left(self) -> Self;
//     fn trim_right(self) -> Self;
//     fn trim(self) -> Self {
//         self.trim_left().trim_right()
//     }

//     fn starts_with(self, prefix: &str) -> bool;
//     fn ends_with(self, suffix: &str) -> bool;
// }
<commit_msg>rope: add insert()<commit_after>use std::fmt;

use self::Rope::*;

fn max<T: PartialOrd + Copy>(x: T, y: T) -> T {
    if x >= y {
        x
    } else {
        y
    }
}

#[derive(Clone)]
pub enum Rope {
    FlatCharVec { seq: Vec<char> },
    Concatenation { left: Box<Rope>, right: Box<Rope>, depth: usize, length: usize },
    Reverse { rope: Box<Rope> },
    SubString { rope: Box<Rope>, offset: usize, length: usize },
}

fn write_rope_to_string(rope: &Rope, s: &mut String) {
    match *rope {
        FlatCharVec { ref seq }                   => {
            s.push_str(&seq.iter().map(Clone::clone).collect::<String>());
        }
        Concatenation { ref left, ref right, .. } => {
            write_rope_to_string(left, s);
            write_rope_to_string(right, s);
        },
        Reverse { ref rope }                      => {
            let inner = rope.to_string();
            s.push_str(&inner.chars().rev().collect::<String>());
        }
        SubString { ref rope, offset, length }    => {
            let inner = rope.to_string();
            s.push_str(&inner[offset..offset+length]);
        }
    }
}

fn concatenate(left: Rope, right: Rope) -> Rope {
    if left.len() == 0 {
        return right;
    }
    if right.len() == 0 {
        return left;
    }

    const COMBINE_LENGTH: usize = 17;
    let length = left.len() + right.len();
    if length < COMBINE_LENGTH {
        let mut lchs = left.into_chars();
        lchs.extend(right.into_chars());
        return FlatCharVec { seq: lchs };
    }
    let depth = max(left.depth(), right.depth()) + 1;
    Concatenation { left: Box::new(left), right: Box::new(right), depth: depth, length: length }
}

impl Rope {
    pub fn from_str(s: &str) -> Rope {
        Rope::from_vec(s.chars().collect())
    }

    pub fn from_vec(seq: Vec<char>) -> Rope {
        Rope::FlatCharVec { seq: seq }
    }

    pub fn len(&self) -> usize {
        match *self {
            FlatCharVec { ref seq }      => seq.len(),
            Concatenation { length, .. } => length,
            Reverse { ref rope }         => rope.len(),
            SubString { length, .. }     => length
        }
    }

    pub fn depth(&self) -> usize {
        match *self {
            FlatCharVec { .. }          => 0,
            Concatenation { depth, .. } => depth,
            Reverse { ref rope }        => rope.depth(),
            SubString { ref rope, .. }  => rope.depth()
        }
    }

    pub fn append<RHS: IntoRope>(self, rhs: RHS) -> Self {
        concatenate(self, rhs.into_rope())
    }

    pub fn to_string(&self) -> String {
        let mut s = String::new();
        write_rope_to_string(self, &mut s);
        s
    }

    pub fn reverse(self) -> Self {
        match self {
            Reverse { rope } => *rope,
            Concatenation { left, right, .. } => {
                concatenate(right.reverse(), left.reverse())
            },
            this => Reverse { rope: Box::new(this) }
        }
    }

    // FIXME: clone?
    pub fn delete(self, start: usize, end: usize) -> Self {
        let tail = self.clone().slice_from(end);
        self.slice_to(start).append(tail)
    }

    pub fn slice_from(self, start: usize) -> Self {
        let len = self.len();
        self.slice(start, len)
    }

    pub fn slice_to(self, end: usize) -> Self {
        self.slice(0, end)
    }

    pub fn slice(self, start: usize, end: usize) -> Self {
        assert!(end <= self.len(), "illegal slice()");
        let slen = self.len();
        if start == 0 && end == slen {
            self
        } else {
            match self {
                Concatenation { left, right, .. } => {
                    let llen = left.len();
                    if end <= llen {
                        left.slice(start, end)
                    } else if start >= llen {
                        right.slice(start-llen, end-llen)
                    } else  {
                        concatenate(left.slice(start, llen), right.slice(0, end-llen))
                    }
                },
                this@FlatCharVec { .. } => {
                    if end - start < 16 {
                        if let FlatCharVec { seq } = this {
                            FlatCharVec { seq: seq[start..end].to_vec() }
                        } else {
                            unreachable!()
                        }
                    } else {
                        SubString { rope: Box::new(this), offset: start, length: end-start }
                    }
                },
                SubString { rope, offset, length }  => {
                    assert!(end - start <= length);
                    SubString { rope: rope, offset: offset + start, length: end - start }
                },
                Reverse { rope } => {
                    rope.slice(slen-end, slen-start).reverse()
                }
            }
        }
    }

    pub fn insert<T: IntoRope>(self, offset: usize, s: T) -> Self {
        assert!(offset <= self.len());
        let r = s.into_rope();
        if offset == 0 {
            r.append(self)
        } else if offset == self.len() {
            self.append(r)
        } else {
            self.clone().slice_to(offset).append(r).append(self.slice_from(offset))
        }
    }

    fn into_chars(self) -> Vec<char> {
        match self {
            FlatCharVec { seq }         => seq,
            Concatenation { left, right, .. } => {
                let mut lchs = left.into_chars();
                let rchs = right.into_chars();
                lchs.extend(rchs);
                lchs
            }
            Reverse { rope }        => {
                let mut inner = rope.into_chars();
                inner.reverse();
                inner
            }
            SubString { rope, offset, length }  => {
                let inner = rope.into_chars();
                inner[offset .. offset+length].to_vec()
            }
        }
    }

    fn is_balanced(&self) -> bool {
        true
    }
}

impl fmt::Display for Rope {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.to_string())
    }
}

impl fmt::Debug for Rope {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "R{:?}", self.to_string())
    }
}

pub trait IntoRope {
    fn into_rope(self) -> Rope;
}

impl IntoRope for char {
    fn into_rope(self) -> Rope {
        FlatCharVec { seq: vec![self] }
    }
}

impl IntoRope for String {
    fn into_rope(self) -> Rope {
        FlatCharVec { seq: self.chars().collect() }
    }
}

impl IntoRope for Rope {
    fn into_rope(self) -> Rope {
        self
    }
}

impl<'a> IntoRope for &'a str {
    fn into_rope(self) -> Rope {
        FlatCharVec { seq: self.chars().collect() }
    }
}

#[test]
fn test_rope() {
    let s = Rope::from_str("Hello").append(' ').append("World").append('!');

    println!("depth => {}", s.depth());
    println!("len => {}", s.len());
    println!("got => {:?}", s);

    let s = s.insert(0, "Oh, man, ");

    println!("got => {:?}", s);

    let s = s.insert(9, "wait! ");
    println!("got => {:?}", s);
    println!("got => {:?}", s.reverse());
}

#[test]
fn test_rope2() {
    let s = Rope::from_str("Hello")
        .append(" abcdefghijklmnopqrstuvwxyz")
        .append(" World")
        .append(" abcdefghijklmnopqrstuvwxyz")
        .append(" abcdefghijklmnopqrstuvwxyz")
        .append(" abcdefghijklmnopqrstuvwxyz");

    println!("depth => {}", s.depth());
    println!("len => {}", s.len());
    println!("got => {:?}", s);
    let s2 = s.slice(10, 50);
    println!("got => {:?}", s2);
    println!("len => {:?}", s2.len());
}

// pub trait Rope {
//     fn find(&self, ch: char) -> Option<usize>;
//     // fn find(&self, pat: &str) -> Option<usize>;
//     fn insert(self, offset: usize, s: &str) -> Self;

//     fn rebalance(self) -> Self;

//     fn trim_left(self) -> Self;
//     fn trim_right(self) -> Self;
//     fn trim(self) -> Self {
//         self.trim_left().trim_right()
//     }

//     fn starts_with(self, prefix: &str) -> bool;
//     fn ends_with(self, suffix: &str) -> bool;
// }
<|endoftext|>


<commit_before>#![crate_name = "sdl2"]
#![crate_type = "lib"]

extern crate num;
extern crate libc as libc_crate;
pub use libc_crate as libc;

#[macro_use]
extern crate lazy_static;

#[macro_use]
extern crate bitflags;
extern crate sdl2_sys as sys;

pub use sdl::*;

pub mod clipboard;
pub mod cpuinfo;
#[macro_use] pub mod macros;
pub mod event;
pub mod filesystem;
pub mod gesture;
pub mod touch;
pub mod joystick;
pub mod controller;
pub mod haptic;
pub mod keyboard;
pub mod mouse;
pub mod rect;
pub mod surface;
pub mod pixels;
pub mod video;
pub mod timer;
pub mod render;
pub mod rwops;
mod sdl;
pub mod audio;
pub mod version;
pub mod messagebox;
pub mod hint;

mod common;
// Export return types and such from the common module.
pub use common::IntegerOrSdlError;
<commit_msg>Fix E0446 warning<commit_after>#![crate_name = "sdl2"]
#![crate_type = "lib"]

extern crate num;
pub extern crate libc;

#[macro_use]
extern crate lazy_static;

#[macro_use]
extern crate bitflags;
extern crate sdl2_sys as sys;

pub use sdl::*;

pub mod clipboard;
pub mod cpuinfo;
#[macro_use] pub mod macros;
pub mod event;
pub mod filesystem;
pub mod gesture;
pub mod touch;
pub mod joystick;
pub mod controller;
pub mod haptic;
pub mod keyboard;
pub mod mouse;
pub mod rect;
pub mod surface;
pub mod pixels;
pub mod video;
pub mod timer;
pub mod render;
pub mod rwops;
mod sdl;
pub mod audio;
pub mod version;
pub mod messagebox;
pub mod hint;

mod common;
// Export return types and such from the common module.
pub use common::IntegerOrSdlError;
<|endoftext|>


<commit_before>use std::cmp::min;
use std::ops::BitOr;
use std::num::FromPrimitive;
use std::fmt::{self, Debug, Formatter};
use std::iter::IteratorExt;
use std::collections::{BTreeMap, VecDeque};
use std::string::ToString;
use serialize::json::Json;

use self::TemplateElement::{RawString, Expression, HelperExpression,
                            HTMLExpression, HelperBlock, Comment};

#[derive(PartialEq, Clone)]
pub struct Template {
    pub elements: Vec<TemplateElement>
}

#[derive(PartialEq, Debug)]
enum ParserState {
    Text,
    HtmlExpression,
    Comment,
    HelperStart,
    HelperEnd,
    Expression,
    Invalid
}

#[derive(PartialEq, Clone)]
pub struct Helper {
    name: String,
    params: Vec<String>,
    hash: BTreeMap<String, Json>,
    template: Option<Template>,
    inverse: Option<Template>,
    block: bool
}

impl Helper {
    pub fn name(&self) -> &String {
        &self.name
    }

    pub fn params(&self) -> &Vec<String> {
        &self.params
    }

    pub fn hash(&self) -> &BTreeMap<String, Json> {
        &self.hash
    }

    pub fn template(&self) -> Option<&Template> {
        match self.template {
            Some(ref t) => {
                Some(t)
            },
            None => None
        }
    }

    pub fn inverse(&self) -> Option<&Template> {
        match self.inverse {
            Some(ref t) => {
                Some(t)
            },
            None => None
        }
    }

    pub fn is_block(&self) -> bool {
        self.block
    }
}

impl ToString for Helper {
    fn to_string(&self) -> String {

        let mut buf = String::new();

        if self.block {
            buf.push_str(format!("{{{{#{}", self.name).as_slice());
        } else {
            buf.push_str(format!("{{{{{}", self.name).as_slice());
        }

        for p in self.params.iter() {
            buf.push_str(format!(" {}", p).as_slice());
        }

        for k in self.hash.keys() {
            buf.push_str(format!(" {}={}", k, self.hash.get(k).unwrap()).as_slice());
        }

        buf.push_str("}}");

        if self.block {
            let tpl = self.template();
            if tpl.is_some() {
                buf.push_str(tpl.unwrap().to_string().as_slice());
            }
            let ivs = self.inverse();
            if ivs.is_some() {
                buf.push_str("{{else}}");
                buf.push_str(ivs.unwrap().to_string().as_slice());
            }
            buf.push_str(format!("{{{{/{}}}}}", self.name).as_slice());
        }
        buf
    }
}

#[derive(Debug, Copy)]
pub struct TemplateError;

impl Helper {
    fn parse(source: String, block: bool) -> Result<Helper, TemplateError> {
        let mut tokens = source.split(|&: c: char| c.is_whitespace())
            .filter(|s: &&str| !(*s).is_empty());

        let name = tokens.next();
        match name {
            Some(n) => {
                let mut params: Vec<String> = Vec::new();
                let mut hash: BTreeMap<String, Json> = BTreeMap::new();

                for t in tokens {
                    if t.contains('=') {
                        let kv = t.split('=').collect::<Vec<&str>>();
                        hash.insert(kv.get(0).unwrap().to_string(),
                                    kv.get(1).unwrap().parse::<Json>().unwrap());
                    } else {
                        params.push(t.to_string());
                    }
                }

                Ok(Helper{
                    name: n.to_string(),
                    params: params,
                    hash: hash,
                    template: Option::None,
                    inverse: Option::None,
                    block: block
                })
            },
            None =>
                Err(TemplateError)
        }
    }
}

#[derive(PartialEq, FromPrimitive)]
enum WhiteSpaceOmit {
    Left = 0x01,
    Right = 0x10,
    Both = 0x11,
    None = 0x00
}

impl BitOr<WhiteSpaceOmit> for WhiteSpaceOmit {
    type Output = WhiteSpaceOmit;

    fn bitor(self, right: WhiteSpaceOmit) -> WhiteSpaceOmit {
        FromPrimitive::from_u8((self as u8) | (right as u8)).unwrap()
    }
}

fn process_whitespace(buf: &String, wso: &mut WhiteSpaceOmit) -> String {
    let result = match *wso {
        WhiteSpaceOmit::Left => {
            buf.trim_left().to_string()
        },
        WhiteSpaceOmit::Right => {
            buf.trim_right().to_string()
        },
        WhiteSpaceOmit::Both => {
            buf.trim().to_string()
        },
        WhiteSpaceOmit::None => {
            buf.clone()
        }
    };
    *wso = WhiteSpaceOmit::None;
    result
}

impl Template {
    pub fn compile(source: String) -> Result<Template, TemplateError> {
        let mut helper_stack: VecDeque<Helper> = VecDeque::new();
        let mut template_stack: VecDeque<Template> = VecDeque::new();
        template_stack.push_front(Template{ elements: Vec::new() });

        let mut buffer: String = String::new();
        let mut state = ParserState::Text;

        let mut c:usize = 0;
        let mut ws_omitter = WhiteSpaceOmit::None;
        let source_len = source.chars().count();
        while c < source_len {
            let mut slice = source.slice_chars(c, min(c+3, source_len)).to_string();
            if slice == "{{~" {
                ws_omitter = ws_omitter | WhiteSpaceOmit::Right;
                // read another char and remove ~
                slice = source.slice_chars(c, min(c+4, source_len)).to_string();
                slice.remove(2);
                c += 1;
            }
            if slice == "~}}" {
                ws_omitter = ws_omitter | WhiteSpaceOmit::Left;
                c += 1;
                slice = source.slice_chars(c, min(c+3, source_len)).to_string();
            }
            state = match slice.as_slice() {
                "{{{" | "{{!" | "{{#" | "{{/" => {
                    c += 2;
                    if !buffer.is_empty() {
                        let mut t = template_stack.front_mut().unwrap();
                        let buf_clone = process_whitespace(&buffer, &mut ws_omitter);
                        t.elements.push(RawString(buf_clone));
                        buffer.clear();
                    }
                    match slice.as_slice() {
                        "{{{" => ParserState::HtmlExpression,
                        "{{!" => ParserState::Comment,
                        "{{#" => ParserState::HelperStart,
                        "{{/" => {
                            let t = template_stack.pop_front().unwrap();
                            let h = helper_stack.front_mut().unwrap();
                            if h.template.is_some() {
                                h.inverse = Some(t);
                            } else {
                                h.template = Some(t);
                            }
                            ParserState::HelperEnd
                        },
                        _ => ParserState::Invalid
                    }
                },
                "}}}" => {
                    c += 2;
                    let mut t = template_stack.front_mut().unwrap();
                    t.elements.push(HTMLExpression(
                        buffer.clone().trim_matches(' ').to_string()));
                    buffer.clear();
                    ParserState::Text
                },
                _ => {
                    match if slice.len() > 2 { slice.slice_chars(0, 2) } else { slice.as_slice() } {
                        "{{" => {
                            c += 1;
                            if !buffer.is_empty() {
                                let mut t = template_stack.front_mut().unwrap();
                                let buf_clone = process_whitespace(&buffer, &mut ws_omitter);
                                t.elements.push(RawString(buf_clone));
                                buffer.clear();
                            }
                            ParserState::Expression
                        },
                        "}}" => {
                            c += 1;
                            match state {
                                ParserState::Expression => {
                                    if !buffer.is_empty() {
                                        // {{else}} or {{^}} within a helper block
                                        if buffer.trim() == "else" || buffer.trim() == "^" {
                                            buffer.clear(); // drop else
                                            let t = template_stack.pop_front().unwrap();
                                            let h = helper_stack.front_mut().unwrap();
                                            h.template = Some(t);
                                            template_stack.push_front(Template{ elements: Vec::new() });
                                            ParserState::Text
                                        } else if buffer.contains(' ') {
                                            //inline helper
                                            match Helper::parse(buffer.clone(), false){
                                                Ok(helper) => {
                                                    let mut t = template_stack.front_mut().unwrap();
                                                    t.elements.push(HelperExpression(helper));
                                                    buffer.clear();
                                                    ParserState::Text
                                                },
                                                Err(_) => ParserState::Invalid
                                            }
                                        } else {
                                            let mut t = template_stack.front_mut().unwrap();
                                            t.elements.push(Expression(
                                                buffer.clone().trim_matches(' ').to_string()));
                                            buffer.clear();
                                            ParserState::Text
                                        }

                                    } else {
                                        ParserState::Invalid
                                    }
                                },
                                ParserState::Comment => {
                                    let mut t = template_stack.front_mut().unwrap();
                                    t.elements.push(Comment(buffer.clone()));
                                    buffer.clear();
                                    ParserState::Text
                                },
                                ParserState::HelperStart => {
                                    match Helper::parse(buffer.clone(), true) {
                                        Ok(helper) => {
                                            helper_stack.push_front(helper);
                                            template_stack.push_front(Template{ elements: Vec::new() });

                                            buffer.clear();
                                            ParserState::Text
                                        },
                                        Err(_) => ParserState::Invalid
                                    }
                                },
                                ParserState::HelperEnd => {
                                    let name = buffer.trim_matches(' ').to_string();
                                    if name == helper_stack.front().unwrap().name {
                                        let h = helper_stack.pop_front().unwrap();
                                        let mut t = template_stack.front_mut().unwrap();
                                        t.elements.push(HelperBlock(h));
                                        buffer.clear();
                                        ParserState::Text
                                    } else {
                                        ParserState::Invalid
                                    }
                                },
                                _ => ParserState::Invalid
                            }
                        },
                        _ => {
                            buffer.push(slice.char_at(0));
                            state
                        }
                    }
                }
            };
            if state == ParserState::Invalid {
                return Err(TemplateError);
            }
            c += 1;
        }

        if !buffer.is_empty() {
            let mut t = template_stack.front_mut().unwrap();
            let buf_clone = process_whitespace(&buffer, &mut ws_omitter);
            t.elements.push(TemplateElement::RawString(buf_clone));
        }

        if !helper_stack.is_empty() {
            return Err(TemplateError);
        }

        return Ok(template_stack.pop_front().unwrap());
    }
}

impl ToString for Template {
    fn to_string(&self) -> String {
        let mut buf = String::new();
        for v in self.elements.iter() {
            buf.push_str(v.to_string().as_slice());
        }
        buf
    }
}

#[derive(PartialEq, Clone)]
pub enum TemplateElement {
    RawString(String),
    Expression(String),
    HTMLExpression(String),
    HelperExpression(Helper),
    HelperBlock(Helper),
    Comment(String),
}

impl ToString for TemplateElement {
    fn to_string(&self) -> String {
        match *self {
            RawString(ref v) => {
                v.clone()
            },
            Expression(ref v) => {
                // {{ escape to {
                format!("{{{{{}}}}}", v)
            },
            HTMLExpression(ref v) => {
                format!("{{{{{{{}}}}}}}", v)
            },
            HelperExpression(ref helper) => {
                helper.to_string()
            }
            HelperBlock(ref helper) => {
                helper.to_string()
            }
            Comment(ref v) => {
                format!("{{!{}}}", v)
            }
        }
    }
}

impl Debug for TemplateElement {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        try!(writeln!(f, "{:?}", self.to_string()));
        Ok(())
    }
}

#[test]
fn test_parse_helper_start_tag() {
    let source = "if not name compare=1".to_string();
    let h = Helper::parse(source, true).ok().unwrap();

    assert_eq!(h.name, "if".to_string());
    assert_eq!(h.params, vec::<String>!["not".to_string(), "name".to_string()]);

    let key = "compare".to_string();
    let value = h.hash.get(&key).unwrap();
    assert_eq!(value.as_u64().unwrap(), 1u64);
}

#[test]
fn test_parse_template() {
    let source = "
{{title}} 你好
 {{{content}}}
{{#if date}}
good
{{else}}
bad
{{/if}}{{foo bar}}{{#unless true}}kitkat{{^}}lollipop{{/unless}}";
    let t = Template::compile(source.to_string()).ok().unwrap();

    assert_eq!(t.elements.len(), 9);
    assert_eq!((*t.elements.get(0).unwrap()).to_string(), "
".to_string());
    assert_eq!((*t.elements.get(1).unwrap()).to_string(),
               Expression("title".to_string()).to_string());

    assert_eq!((*t.elements.get(3).unwrap()).to_string(), "{{{content}}}".to_string());

    match *t.elements.get(5).unwrap() {
        HelperBlock(ref h) => {
            assert_eq!(h.name, "if".to_string());
            assert_eq!(h.params.len(), 1);
            assert_eq!(h.template.as_ref().unwrap().elements.len(), 1);
        },
        _ => {
            panic!("Helper expected here.");
        }
    };

    match *t.elements.get(7).unwrap() {
        HelperExpression(ref h) => {
            assert_eq!(h.name, "foo".to_string());
            assert_eq!(h.params.len(), 1);
            assert_eq!(*(h.params.get(0).unwrap()), "bar".to_string());
        },
        _ => {
            panic!("Helper expression here");
        }
    };

    match *t.elements.get(8).unwrap() {
        HelperBlock(ref h) => {
            assert_eq!(h.name, "unless".to_string());
            assert_eq!(h.params.len(), 1);
            assert_eq!(h.inverse.as_ref().unwrap().elements.len(), 1);
        },
        _ => {
            panic!("Helper expression here");
        }
    };

}

#[test]
fn test_helper_to_string() {
    let source = "{{#ifequals name compare=\"hello\"}}hello{{else}}good{{/ifequals}}".to_string();

    let t = Template::compile(source.to_string()).ok().unwrap();

    assert_eq!(t.elements.len(), 1);
    assert_eq!(t.elements.get(0).unwrap().to_string(), source);
}

#[test]
fn test_parse_error() {
    let source = "{{#ifequals name compare=\"hello\"}}hello{{else}}good";

    let t = Template::compile(source.to_string());

    assert!(t.is_err());
}

#[test]
fn test_white_space_omitter() {
    let source = "hello~     {{~world~}} \n  !{{~#if true}}else{{/if~}}".to_string();
    let t = Template::compile(source).ok().unwrap();

    assert_eq!(t.elements.len(), 4);

    assert_eq!(t.elements[0], RawString(String::from_str("hello~")));
    assert_eq!(t.elements[1], Expression(String::from_str("world")));
    assert_eq!(t.elements[2], RawString(String::from_str("!")));
}
<commit_msg>(fix) closure type is inferred now<commit_after>use std::cmp::min;
use std::ops::BitOr;
use std::num::FromPrimitive;
use std::fmt::{self, Debug, Formatter};
use std::iter::IteratorExt;
use std::collections::{BTreeMap, VecDeque};
use std::string::ToString;
use serialize::json::Json;

use self::TemplateElement::{RawString, Expression, HelperExpression,
                            HTMLExpression, HelperBlock, Comment};

#[derive(PartialEq, Clone)]
pub struct Template {
    pub elements: Vec<TemplateElement>
}

#[derive(PartialEq, Debug)]
enum ParserState {
    Text,
    HtmlExpression,
    Comment,
    HelperStart,
    HelperEnd,
    Expression,
    Invalid
}

#[derive(PartialEq, Clone)]
pub struct Helper {
    name: String,
    params: Vec<String>,
    hash: BTreeMap<String, Json>,
    template: Option<Template>,
    inverse: Option<Template>,
    block: bool
}

impl Helper {
    pub fn name(&self) -> &String {
        &self.name
    }

    pub fn params(&self) -> &Vec<String> {
        &self.params
    }

    pub fn hash(&self) -> &BTreeMap<String, Json> {
        &self.hash
    }

    pub fn template(&self) -> Option<&Template> {
        match self.template {
            Some(ref t) => {
                Some(t)
            },
            None => None
        }
    }

    pub fn inverse(&self) -> Option<&Template> {
        match self.inverse {
            Some(ref t) => {
                Some(t)
            },
            None => None
        }
    }

    pub fn is_block(&self) -> bool {
        self.block
    }
}

impl ToString for Helper {
    fn to_string(&self) -> String {

        let mut buf = String::new();

        if self.block {
            buf.push_str(format!("{{{{#{}", self.name).as_slice());
        } else {
            buf.push_str(format!("{{{{{}", self.name).as_slice());
        }

        for p in self.params.iter() {
            buf.push_str(format!(" {}", p).as_slice());
        }

        for k in self.hash.keys() {
            buf.push_str(format!(" {}={}", k, self.hash.get(k).unwrap()).as_slice());
        }

        buf.push_str("}}");

        if self.block {
            let tpl = self.template();
            if tpl.is_some() {
                buf.push_str(tpl.unwrap().to_string().as_slice());
            }
            let ivs = self.inverse();
            if ivs.is_some() {
                buf.push_str("{{else}}");
                buf.push_str(ivs.unwrap().to_string().as_slice());
            }
            buf.push_str(format!("{{{{/{}}}}}", self.name).as_slice());
        }
        buf
    }
}

#[derive(Debug, Copy)]
pub struct TemplateError;

impl Helper {
    fn parse(source: String, block: bool) -> Result<Helper, TemplateError> {
        let mut tokens = source.split(|c: char| c.is_whitespace())
            .filter(|s: &&str| !(*s).is_empty());

        let name = tokens.next();
        match name {
            Some(n) => {
                let mut params: Vec<String> = Vec::new();
                let mut hash: BTreeMap<String, Json> = BTreeMap::new();

                for t in tokens {
                    if t.contains('=') {
                        let kv = t.split('=').collect::<Vec<&str>>();
                        hash.insert(kv.get(0).unwrap().to_string(),
                                    kv.get(1).unwrap().parse::<Json>().unwrap());
                    } else {
                        params.push(t.to_string());
                    }
                }

                Ok(Helper{
                    name: n.to_string(),
                    params: params,
                    hash: hash,
                    template: Option::None,
                    inverse: Option::None,
                    block: block
                })
            },
            None =>
                Err(TemplateError)
        }
    }
}

#[derive(PartialEq, FromPrimitive)]
enum WhiteSpaceOmit {
    Left = 0x01,
    Right = 0x10,
    Both = 0x11,
    None = 0x00
}

impl BitOr<WhiteSpaceOmit> for WhiteSpaceOmit {
    type Output = WhiteSpaceOmit;

    fn bitor(self, right: WhiteSpaceOmit) -> WhiteSpaceOmit {
        FromPrimitive::from_u8((self as u8) | (right as u8)).unwrap()
    }
}

fn process_whitespace(buf: &String, wso: &mut WhiteSpaceOmit) -> String {
    let result = match *wso {
        WhiteSpaceOmit::Left => {
            buf.trim_left().to_string()
        },
        WhiteSpaceOmit::Right => {
            buf.trim_right().to_string()
        },
        WhiteSpaceOmit::Both => {
            buf.trim().to_string()
        },
        WhiteSpaceOmit::None => {
            buf.clone()
        }
    };
    *wso = WhiteSpaceOmit::None;
    result
}

impl Template {
    pub fn compile(source: String) -> Result<Template, TemplateError> {
        let mut helper_stack: VecDeque<Helper> = VecDeque::new();
        let mut template_stack: VecDeque<Template> = VecDeque::new();
        template_stack.push_front(Template{ elements: Vec::new() });

        let mut buffer: String = String::new();
        let mut state = ParserState::Text;

        let mut c:usize = 0;
        let mut ws_omitter = WhiteSpaceOmit::None;
        let source_len = source.chars().count();
        while c < source_len {
            let mut slice = source.slice_chars(c, min(c+3, source_len)).to_string();
            if slice == "{{~" {
                ws_omitter = ws_omitter | WhiteSpaceOmit::Right;
                // read another char and remove ~
                slice = source.slice_chars(c, min(c+4, source_len)).to_string();
                slice.remove(2);
                c += 1;
            }
            if slice == "~}}" {
                ws_omitter = ws_omitter | WhiteSpaceOmit::Left;
                c += 1;
                slice = source.slice_chars(c, min(c+3, source_len)).to_string();
            }
            state = match slice.as_slice() {
                "{{{" | "{{!" | "{{#" | "{{/" => {
                    c += 2;
                    if !buffer.is_empty() {
                        let mut t = template_stack.front_mut().unwrap();
                        let buf_clone = process_whitespace(&buffer, &mut ws_omitter);
                        t.elements.push(RawString(buf_clone));
                        buffer.clear();
                    }
                    match slice.as_slice() {
                        "{{{" => ParserState::HtmlExpression,
                        "{{!" => ParserState::Comment,
                        "{{#" => ParserState::HelperStart,
                        "{{/" => {
                            let t = template_stack.pop_front().unwrap();
                            let h = helper_stack.front_mut().unwrap();
                            if h.template.is_some() {
                                h.inverse = Some(t);
                            } else {
                                h.template = Some(t);
                            }
                            ParserState::HelperEnd
                        },
                        _ => ParserState::Invalid
                    }
                },
                "}}}" => {
                    c += 2;
                    let mut t = template_stack.front_mut().unwrap();
                    t.elements.push(HTMLExpression(
                        buffer.clone().trim_matches(' ').to_string()));
                    buffer.clear();
                    ParserState::Text
                },
                _ => {
                    match if slice.len() > 2 { slice.slice_chars(0, 2) } else { slice.as_slice() } {
                        "{{" => {
                            c += 1;
                            if !buffer.is_empty() {
                                let mut t = template_stack.front_mut().unwrap();
                                let buf_clone = process_whitespace(&buffer, &mut ws_omitter);
                                t.elements.push(RawString(buf_clone));
                                buffer.clear();
                            }
                            ParserState::Expression
                        },
                        "}}" => {
                            c += 1;
                            match state {
                                ParserState::Expression => {
                                    if !buffer.is_empty() {
                                        // {{else}} or {{^}} within a helper block
                                        if buffer.trim() == "else" || buffer.trim() == "^" {
                                            buffer.clear(); // drop else
                                            let t = template_stack.pop_front().unwrap();
                                            let h = helper_stack.front_mut().unwrap();
                                            h.template = Some(t);
                                            template_stack.push_front(Template{ elements: Vec::new() });
                                            ParserState::Text
                                        } else if buffer.contains(' ') {
                                            //inline helper
                                            match Helper::parse(buffer.clone(), false){
                                                Ok(helper) => {
                                                    let mut t = template_stack.front_mut().unwrap();
                                                    t.elements.push(HelperExpression(helper));
                                                    buffer.clear();
                                                    ParserState::Text
                                                },
                                                Err(_) => ParserState::Invalid
                                            }
                                        } else {
                                            let mut t = template_stack.front_mut().unwrap();
                                            t.elements.push(Expression(
                                                buffer.clone().trim_matches(' ').to_string()));
                                            buffer.clear();
                                            ParserState::Text
                                        }

                                    } else {
                                        ParserState::Invalid
                                    }
                                },
                                ParserState::Comment => {
                                    let mut t = template_stack.front_mut().unwrap();
                                    t.elements.push(Comment(buffer.clone()));
                                    buffer.clear();
                                    ParserState::Text
                                },
                                ParserState::HelperStart => {
                                    match Helper::parse(buffer.clone(), true) {
                                        Ok(helper) => {
                                            helper_stack.push_front(helper);
                                            template_stack.push_front(Template{ elements: Vec::new() });

                                            buffer.clear();
                                            ParserState::Text
                                        },
                                        Err(_) => ParserState::Invalid
                                    }
                                },
                                ParserState::HelperEnd => {
                                    let name = buffer.trim_matches(' ').to_string();
                                    if name == helper_stack.front().unwrap().name {
                                        let h = helper_stack.pop_front().unwrap();
                                        let mut t = template_stack.front_mut().unwrap();
                                        t.elements.push(HelperBlock(h));
                                        buffer.clear();
                                        ParserState::Text
                                    } else {
                                        ParserState::Invalid
                                    }
                                },
                                _ => ParserState::Invalid
                            }
                        },
                        _ => {
                            buffer.push(slice.char_at(0));
                            state
                        }
                    }
                }
            };
            if state == ParserState::Invalid {
                return Err(TemplateError);
            }
            c += 1;
        }

        if !buffer.is_empty() {
            let mut t = template_stack.front_mut().unwrap();
            let buf_clone = process_whitespace(&buffer, &mut ws_omitter);
            t.elements.push(TemplateElement::RawString(buf_clone));
        }

        if !helper_stack.is_empty() {
            return Err(TemplateError);
        }

        return Ok(template_stack.pop_front().unwrap());
    }
}

impl ToString for Template {
    fn to_string(&self) -> String {
        let mut buf = String::new();
        for v in self.elements.iter() {
            buf.push_str(v.to_string().as_slice());
        }
        buf
    }
}

#[derive(PartialEq, Clone)]
pub enum TemplateElement {
    RawString(String),
    Expression(String),
    HTMLExpression(String),
    HelperExpression(Helper),
    HelperBlock(Helper),
    Comment(String),
}

impl ToString for TemplateElement {
    fn to_string(&self) -> String {
        match *self {
            RawString(ref v) => {
                v.clone()
            },
            Expression(ref v) => {
                // {{ escape to {
                format!("{{{{{}}}}}", v)
            },
            HTMLExpression(ref v) => {
                format!("{{{{{{{}}}}}}}", v)
            },
            HelperExpression(ref helper) => {
                helper.to_string()
            }
            HelperBlock(ref helper) => {
                helper.to_string()
            }
            Comment(ref v) => {
                format!("{{!{}}}", v)
            }
        }
    }
}

impl Debug for TemplateElement {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        try!(writeln!(f, "{:?}", self.to_string()));
        Ok(())
    }
}

#[test]
fn test_parse_helper_start_tag() {
    let source = "if not name compare=1".to_string();
    let h = Helper::parse(source, true).ok().unwrap();

    assert_eq!(h.name, "if".to_string());
    assert_eq!(h.params, vec::<String>!["not".to_string(), "name".to_string()]);

    let key = "compare".to_string();
    let value = h.hash.get(&key).unwrap();
    assert_eq!(value.as_u64().unwrap(), 1u64);
}

#[test]
fn test_parse_template() {
    let source = "
{{title}} 你好
 {{{content}}}
{{#if date}}
good
{{else}}
bad
{{/if}}{{foo bar}}{{#unless true}}kitkat{{^}}lollipop{{/unless}}";
    let t = Template::compile(source.to_string()).ok().unwrap();

    assert_eq!(t.elements.len(), 9);
    assert_eq!((*t.elements.get(0).unwrap()).to_string(), "
".to_string());
    assert_eq!((*t.elements.get(1).unwrap()).to_string(),
               Expression("title".to_string()).to_string());

    assert_eq!((*t.elements.get(3).unwrap()).to_string(), "{{{content}}}".to_string());

    match *t.elements.get(5).unwrap() {
        HelperBlock(ref h) => {
            assert_eq!(h.name, "if".to_string());
            assert_eq!(h.params.len(), 1);
            assert_eq!(h.template.as_ref().unwrap().elements.len(), 1);
        },
        _ => {
            panic!("Helper expected here.");
        }
    };

    match *t.elements.get(7).unwrap() {
        HelperExpression(ref h) => {
            assert_eq!(h.name, "foo".to_string());
            assert_eq!(h.params.len(), 1);
            assert_eq!(*(h.params.get(0).unwrap()), "bar".to_string());
        },
        _ => {
            panic!("Helper expression here");
        }
    };

    match *t.elements.get(8).unwrap() {
        HelperBlock(ref h) => {
            assert_eq!(h.name, "unless".to_string());
            assert_eq!(h.params.len(), 1);
            assert_eq!(h.inverse.as_ref().unwrap().elements.len(), 1);
        },
        _ => {
            panic!("Helper expression here");
        }
    };

}

#[test]
fn test_helper_to_string() {
    let source = "{{#ifequals name compare=\"hello\"}}hello{{else}}good{{/ifequals}}".to_string();

    let t = Template::compile(source.to_string()).ok().unwrap();

    assert_eq!(t.elements.len(), 1);
    assert_eq!(t.elements.get(0).unwrap().to_string(), source);
}

#[test]
fn test_parse_error() {
    let source = "{{#ifequals name compare=\"hello\"}}hello{{else}}good";

    let t = Template::compile(source.to_string());

    assert!(t.is_err());
}

#[test]
fn test_white_space_omitter() {
    let source = "hello~     {{~world~}} \n  !{{~#if true}}else{{/if~}}".to_string();
    let t = Template::compile(source).ok().unwrap();

    assert_eq!(t.elements.len(), 4);

    assert_eq!(t.elements[0], RawString(String::from_str("hello~")));
    assert_eq!(t.elements[1], Expression(String::from_str("world")));
    assert_eq!(t.elements[2], RawString(String::from_str("!")));
}
<|endoftext|>


<commit_before>extern crate sdl2;
extern crate sdl2_image;

use std::path::Path;
use sdl2::EventPump;
use sdl2::event::Event;
use sdl2::Sdl;
use sdl2::pixels;
use sdl2::rect::Rect;
use sdl2::render::Renderer;
use sdl2_image::{LoadTexture, INIT_PNG};

use sprites::SpriteSheet;
use colors::Color;

struct Cell {
    glyph: char,
    fg: Color,
    bg: Color,
}

pub struct FontDefinition {
    pub image_path: &'static str,
    pub width: u32,
    pub height: u32,
    pub padding: u32,
    pub transparent: bool,
}

pub struct Terminal {
    sprite_sheet: SpriteSheet,
    grid: Vec<Cell>,
    renderer: Renderer<'static>,
    sdl_context: Sdl,
    font: FontDefinition,

    pub event_pump: EventPump,
    pub columns: u32,
    pub rows: u32,
    pub quit: bool,
}

impl Terminal {
    pub fn new(columns: u32, rows: u32, font: FontDefinition) -> Self {
        let sdl_context = sdl2::init().unwrap();
        let _image_context = sdl2_image::init(INIT_PNG).unwrap();
        let video_subsystem = sdl_context.video().unwrap();
        let window = video_subsystem.window("davokar-rl",
                    (columns * font.width) as u32,
                    (rows * font.height) as u32)
            .position_centered()
            .opengl()
            .build()
            .unwrap();
        let renderer = window.renderer()
            .accelerated()
            // .present_vsync()
            .build()
            .unwrap();

        let sprite_sheet = Terminal::load_font_sheet(&font, &renderer).unwrap();

        let mut grid = vec![];

        let total_cells = columns * rows;
        for _ in 0..total_cells {
            grid.push(Cell {
                glyph: ' ',
                fg: Color {
                    r: 255,
                    g: 255,
                    b: 255,
                    a: 255,
                },
                bg: Color {
                    r: 0,
                    g: 0,
                    b: 0,
                    a: 255,
                },
            });
        }

        let event = sdl_context.event_pump().unwrap();

        Terminal {
            columns: columns,
            rows: rows,
            sprite_sheet: sprite_sheet,
            sdl_context: sdl_context,
            renderer: renderer,
            grid: grid,
            font: font,
            event_pump: event,
            quit: false,
        }
    }

    fn load_font_sheet(font: &FontDefinition,
                       renderer: &Renderer)
                       -> Result<SpriteSheet, &'static str> {
        let texture_result = renderer.load_texture(Path::new(font.image_path));

        match texture_result {
            Ok(texture) => Ok(SpriteSheet::new(texture, font.width, font.height, font.padding)),
            Err(_) => Err("failed to load font texture"),
        }

    }

    pub fn set_cell(&mut self, x: i32, y: i32, c: char, fg: Color, bg: Color) {
        let index = self.columns as i32 * y + x;
        if x >= 0 && y >= 0 && x < self.columns as i32 && y < self.rows as i32 {
            self.grid[index as usize] = Cell {
                glyph: c,
                fg: fg,
                bg: bg,
            }
        }
    }

    pub fn print(&mut self, x: i32, y: i32, text: &str, fg: Color, bg: Color) {
        let mut string_chars = text.chars();
        for i in 0..text.len() {
            let c = string_chars.next().unwrap();
            self.set_cell(x + i as i32, y, c, fg, bg);
        }
    }

    pub fn print_center(&mut self, y: i32, text: &str, fg: Color, bg: Color) {
        let center_x = self.columns / 2;
        let str_len_mid = text.len() / 2;
        let target_x = center_x - str_len_mid as u32;
        self.print(target_x as i32, y, text, fg, bg);
    }

    pub fn draw(&mut self) {

        for event in self.event_pump.poll_iter() {
            match event {
                Event::Quit { .. } => self.quit = true,
                _ => {}
            }
        }

        self.renderer.clear();

        let total_cells = self.columns * self.rows;
        for i in 0..total_cells {
            let cell = &self.grid[i as usize];

            self.sprite_sheet.texture.set_color_mod(cell.fg.r, cell.fg.g, cell.fg.b);
            let sprite = self.sprite_sheet.get_sprite((cell.glyph as u8) as usize);

            let x = i % self.columns;
            let y = i / self.columns;
            let px = x * self.font.width;
            let py = y * self.font.height;

            // draw the background for the cell
            self.renderer
                .set_draw_color(pixels::Color::RGBA(cell.bg.r, cell.bg.g, cell.bg.b, cell.bg.a));
            let _t = self.renderer
                .fill_rect(Rect::new(px as i32,
                                     py as i32,
                                     sprite.size.width(),
                                     sprite.size.height()));

            // draw the actual character
            sprite.draw(px as i32, py as i32, &mut self.renderer);
        }
        self.renderer.present();
    }
}
<commit_msg>added an ugly input vector<commit_after>extern crate sdl2;
extern crate sdl2_image;

use std::path::Path;
use sdl2::EventPump;
use sdl2::event::Event;
use sdl2::Sdl;
use sdl2::pixels;
use sdl2::rect::Rect;
use sdl2::render::Renderer;
use sdl2_image::{LoadTexture, INIT_PNG};

use sprites::SpriteSheet;
use colors::Color;

struct Cell {
    glyph: char,
    fg: Color,
    bg: Color,
}

pub struct FontDefinition {
    pub image_path: &'static str,
    pub width: u32,
    pub height: u32,
    pub padding: u32,
    pub transparent: bool,
}

pub struct Terminal {
    sprite_sheet: SpriteSheet,
    grid: Vec<Cell>,
    renderer: Renderer<'static>,
    sdl_context: Sdl,
    font: FontDefinition,

    pub event_pump: EventPump,
    pub columns: u32,
    pub rows: u32,
    pub quit: bool,
    pub keys_down: Vec<String>,
}

impl Terminal {
    pub fn new(columns: u32, rows: u32, font: FontDefinition) -> Self {
        let sdl_context = sdl2::init().unwrap();
        let _image_context = sdl2_image::init(INIT_PNG).unwrap();
        let video_subsystem = sdl_context.video().unwrap();
        let window = video_subsystem.window("davokar-rl",
                    (columns * font.width) as u32,
                    (rows * font.height) as u32)
            .position_centered()
            .opengl()
            .build()
            .unwrap();
        let renderer = window.renderer()
            .accelerated()
            // .present_vsync()
            .build()
            .unwrap();

        let sprite_sheet = Terminal::load_font_sheet(&font, &renderer).unwrap();

        let mut grid = vec![];

        let total_cells = columns * rows;
        for _ in 0..total_cells {
            grid.push(Cell {
                glyph: ' ',
                fg: Color {
                    r: 255,
                    g: 255,
                    b: 255,
                    a: 255,
                },
                bg: Color {
                    r: 0,
                    g: 0,
                    b: 0,
                    a: 255,
                },
            });
        }

        let event = sdl_context.event_pump().unwrap();

        Terminal {
            columns: columns,
            rows: rows,
            sprite_sheet: sprite_sheet,
            sdl_context: sdl_context,
            renderer: renderer,
            grid: grid,
            font: font,
            event_pump: event,
            quit: false,
            keys_down: vec![],
        }
    }

    fn load_font_sheet(font: &FontDefinition,
                       renderer: &Renderer)
                       -> Result<SpriteSheet, &'static str> {
        let texture_result = renderer.load_texture(Path::new(font.image_path));

        match texture_result {
            Ok(texture) => Ok(SpriteSheet::new(texture, font.width, font.height, font.padding)),
            Err(_) => Err("failed to load font texture"),
        }

    }

    pub fn set_cell(&mut self, x: i32, y: i32, c: char, fg: Color, bg: Color) {
        let index = self.columns as i32 * y + x;
        if x >= 0 && y >= 0 && x < self.columns as i32 && y < self.rows as i32 {
            self.grid[index as usize] = Cell {
                glyph: c,
                fg: fg,
                bg: bg,
            }
        }
    }

    pub fn print(&mut self, x: i32, y: i32, text: &str, fg: Color, bg: Color) {
        let mut string_chars = text.chars();
        for i in 0..text.len() {
            let c = string_chars.next().unwrap();
            self.set_cell(x + i as i32, y, c, fg, bg);
        }
    }

    pub fn print_center(&mut self, y: i32, text: &str, fg: Color, bg: Color) {
        let center_x = self.columns / 2;
        let str_len_mid = text.len() / 2;
        let target_x = center_x - str_len_mid as u32;
        self.print(target_x as i32, y, text, fg, bg);
    }

    pub fn draw(&mut self) {

        self.keys_down = vec![];

        for event in self.event_pump.poll_iter() {
            match event {
                Event::Quit { .. } => self.quit = true,
                Event::KeyDown { keycode: kc, .. } => {
                    self.keys_down.push(kc.unwrap().to_string());
                }
                _ => {}
            }
        }

        self.renderer.clear();

        let total_cells = self.columns * self.rows;
        for i in 0..total_cells {
            let cell = &self.grid[i as usize];

            self.sprite_sheet.texture.set_color_mod(cell.fg.r, cell.fg.g, cell.fg.b);
            let sprite = self.sprite_sheet.get_sprite((cell.glyph as u8) as usize);

            let x = i % self.columns;
            let y = i / self.columns;
            let px = x * self.font.width;
            let py = y * self.font.height;

            // draw the background for the cell
            self.renderer
                .set_draw_color(pixels::Color::RGBA(cell.bg.r, cell.bg.g, cell.bg.b, cell.bg.a));
            let _t = self.renderer
                .fill_rect(Rect::new(px as i32,
                                     py as i32,
                                     sprite.size.width(),
                                     sprite.size.height()));

            // draw the actual character
            sprite.draw(px as i32, py as i32, &mut self.renderer);
        }
        self.renderer.present();
    }
}
<|endoftext|>


<commit_before>use hdr_histogram::simple_hdr_histogram::*;

#[test]
fn count_at_value_on_empty() {
    let h = init_histo(1, 100000, 3);

    assert_eq!(0, h.get_count_at_value(1).unwrap());
    assert_eq!(0, h.get_count_at_value(5000).unwrap());
    assert_eq!(0, h.get_count_at_value(100000).unwrap());
}

#[test]
fn count_at_value_after_record() {
    let mut h = init_histo(1, 100000, 3);

    h.record_single_value(5000).unwrap();

    assert_eq!(0, h.get_count_at_value(1).unwrap());
    assert_eq!(1, h.get_count_at_value(5000).unwrap());
    assert_eq!(0, h.get_count_at_value(100000).unwrap());
}

#[test]
fn can_get_count_after_record() {
    let mut h = init_histo(1, 100000, 3);
    h.record_single_value(5000).unwrap();

    let count = h.get_count();
    assert_eq!(1, count);
}

#[test]
fn can_get_max_after_record() {
    let mut h = init_histo(1, 100000, 3);
    h.record_single_value(5000).unwrap();

    let max = h.get_max();
    assert_eq!(5000, max);
}

#[test]
fn can_record_single_value() {
    let mut h = init_histo(1, 100000, 3);
    h.record_single_value(5000).unwrap();
}

#[test]
fn can_compute_indexes_for_smallest_value() {
    let h = init_histo(1, 100000, 3);
    let value = 1;
    assert_eq!(0, h.get_bucket_index(value));
    assert_eq!(1, h.get_sub_bucket_index(value, 0));
    assert_eq!(1, h.counts_array_index(value));
}

#[test]
fn can_compute_counts_array_index() {
    let h = init_histo(1, 100000, 3);
    let result = h.counts_array_index(5000);

    assert_eq!(3298, result);
}

#[test]
fn can_get_bucket_index() {
    let h = init_histo(1, 100000, 3);
    let result = h.get_bucket_index(5000);
    assert_eq!(2, result)
}

#[test]
fn can_get_sub_bucket_index() {
    let h = init_histo(1, 100000, 3);
    let result = h.get_sub_bucket_index(5000, 2);
    assert_eq!(1250, result)
}

#[test]
fn sub_bucket_count_medium_precision() {
    let h = init_histo(1, 100000, 3);

    assert_eq!(2048_usize, h.sub_bucket_count);
    assert_eq!(1024_usize, h.sub_bucket_half_count);
    assert_eq!(10, h.sub_bucket_half_count_magnitude);
    assert_eq!(2047, h.sub_bucket_mask);
}

#[test]
fn sub_bucket_count_min_precision() {
    let h = init_histo(1, 100000, 0);

    assert_eq!(2_usize, h.sub_bucket_count);
    assert_eq!(1_usize, h.sub_bucket_half_count);
    assert_eq!(0, h.sub_bucket_half_count_magnitude);
    assert_eq!(1, h.sub_bucket_mask);
}

#[test]
fn sub_bucket_count_max_precision() {
    let h = init_histo(1, 100000, 5);

    assert_eq!(262144_usize, h.sub_bucket_count);
    assert_eq!(131072usize, h.sub_bucket_half_count);
    assert_eq!(17, h.sub_bucket_half_count_magnitude);
    assert_eq!(262143, h.sub_bucket_mask);
}

/// lowest_discernible_value: must be >= 1
/// highest_trackable_value: must be >= 2 * lowest_discernible_value
/// num_significant_digits: must be <= 5
fn init_histo(lowest_discernible_value: u64, highest_trackable_value: u64, num_significant_digits: u32) -> SimpleHdrHistogram<u64> {

    assert!(lowest_discernible_value >= 1);
    assert!(highest_trackable_value >= 2 * lowest_discernible_value);
    assert!(num_significant_digits <= 5);

    let largest_value_with_single_unit_resolution = 2_u64 * 10_u64.pow(num_significant_digits);

    let unit_magnitude = ((lowest_discernible_value as f64).ln() / 2_f64.ln()) as u32;
    let unit_magnitude_mask: u64  = (1_u64 << unit_magnitude) - 1;

    // find nearest power of 2 to largest_value_with_single_unit_resolution
    let sub_bucket_count_magnitude: u32 =
    ((largest_value_with_single_unit_resolution as f64).ln() / 2_f64.ln()).ceil() as u32;

    // ugly looking... how should ternaries be done?
    let sub_bucket_half_count_magnitude: u32 = (if sub_bucket_count_magnitude > 1 { sub_bucket_count_magnitude } else { 1 }) - 1;
    let sub_bucket_count: usize = 2_usize.pow(sub_bucket_half_count_magnitude + 1);
    let sub_bucket_half_count: usize = sub_bucket_count / 2;
    // TODO is this cast OK?
    let sub_bucket_mask = ((sub_bucket_count - 1) << unit_magnitude) as u64;

    let counts_arr_len = counts_arr_len(highest_trackable_value, sub_bucket_count, unit_magnitude);
    let bucket_count = buckets_needed_for_value(highest_trackable_value, sub_bucket_count, unit_magnitude);

    let leading_zero_count_base: usize = (64_u32 - unit_magnitude - sub_bucket_half_count_magnitude - 1) as usize;

    SimpleHdrHistogram {
        leading_zeros_count_base: leading_zero_count_base,
        unit_magnitude: unit_magnitude,
        sub_bucket_mask: sub_bucket_mask,
        sub_bucket_count: sub_bucket_count,
        sub_bucket_half_count: sub_bucket_half_count,
        sub_bucket_half_count_magnitude: sub_bucket_half_count_magnitude,
        counts: vec![0; counts_arr_len],
        counts_array_length: counts_arr_len,
        normalizing_index_offset: 0_usize, // 0 for normal Histogram ctor in Java impl
        min_non_zero_value: u64::max_value(),
        total_count: 0,
        max_value: 0,
        unit_magnitude_mask: unit_magnitude_mask
    }
}

fn buckets_needed_for_value(value: u64, sub_bucket_count: usize, unit_magnitude: u32) -> usize {

    // TODO is this cast ok?
    let mut smallest_untrackable_value: u64 = (sub_bucket_count << unit_magnitude) as u64;
    let mut buckets_needed: usize = 1;

    while smallest_untrackable_value <= value {
        if smallest_untrackable_value > u64::max_value() / 2 {
            return buckets_needed + 1;
        }

        smallest_untrackable_value = smallest_untrackable_value << 1;
        buckets_needed += 1;
    }

    return buckets_needed;
}

fn counts_arr_len_for_buckets(buckets: usize, sub_bucket_count: usize) -> usize {
    (buckets + 1) * (sub_bucket_count / 2)
}

fn counts_arr_len(value: u64, sub_bucket_count: usize, unit_magnitude: u32) -> usize {
    counts_arr_len_for_buckets(buckets_needed_for_value(value, sub_bucket_count, unit_magnitude), sub_bucket_count)
}<commit_msg>add unit magnitude tests<commit_after>use hdr_histogram::simple_hdr_histogram::*;

#[test]
fn count_at_value_on_empty() {
    let h = init_histo(1, 100000, 3);

    assert_eq!(0, h.get_count_at_value(1).unwrap());
    assert_eq!(0, h.get_count_at_value(5000).unwrap());
    assert_eq!(0, h.get_count_at_value(100000).unwrap());
}

#[test]
fn count_at_value_after_record() {
    let mut h = init_histo(1, 100000, 3);

    h.record_single_value(5000).unwrap();

    assert_eq!(0, h.get_count_at_value(1).unwrap());
    assert_eq!(1, h.get_count_at_value(5000).unwrap());
    assert_eq!(0, h.get_count_at_value(100000).unwrap());
}

#[test]
fn can_get_count_after_record() {
    let mut h = init_histo(1, 100000, 3);
    h.record_single_value(5000).unwrap();

    let count = h.get_count();
    assert_eq!(1, count);
}

#[test]
fn can_get_max_after_record() {
    let mut h = init_histo(1, 100000, 3);
    h.record_single_value(5000).unwrap();

    let max = h.get_max();
    assert_eq!(5000, max);
}

#[test]
fn can_record_single_value() {
    let mut h = init_histo(1, 100000, 3);
    h.record_single_value(5000).unwrap();
}

#[test]
fn can_compute_indexes_for_smallest_value() {
    let h = init_histo(1, 100000, 3);
    let value = 1;
    assert_eq!(0, h.get_bucket_index(value));
    assert_eq!(1, h.get_sub_bucket_index(value, 0));
    assert_eq!(1, h.counts_array_index(value));
}

#[test]
fn can_compute_counts_array_index() {
    let h = init_histo(1, 100000, 3);
    let result = h.counts_array_index(5000);

    assert_eq!(3298, result);
}

#[test]
fn can_get_bucket_index() {
    let h = init_histo(1, 100000, 3);
    let result = h.get_bucket_index(5000);
    assert_eq!(2, result)
}

#[test]
fn can_get_sub_bucket_index() {
    let h = init_histo(1, 100000, 3);
    let result = h.get_sub_bucket_index(5000, 2);
    assert_eq!(1250, result)
}

#[test]
fn sub_bucket_count_medium_precision() {
    let h = init_histo(1, 100000, 3);

    assert_eq!(2048_usize, h.sub_bucket_count);
    assert_eq!(1024_usize, h.sub_bucket_half_count);
    assert_eq!(10, h.sub_bucket_half_count_magnitude);
    assert_eq!(2047, h.sub_bucket_mask);
}

#[test]
fn sub_bucket_count_min_precision() {
    let h = init_histo(1, 100000, 0);

    assert_eq!(2_usize, h.sub_bucket_count);
    assert_eq!(1_usize, h.sub_bucket_half_count);
    assert_eq!(0, h.sub_bucket_half_count_magnitude);
    assert_eq!(1, h.sub_bucket_mask);
}

#[test]
fn sub_bucket_count_max_precision() {
    let h = init_histo(1, 100000, 5);

    assert_eq!(262144_usize, h.sub_bucket_count);
    assert_eq!(131072usize, h.sub_bucket_half_count);
    assert_eq!(17, h.sub_bucket_half_count_magnitude);
    assert_eq!(262143, h.sub_bucket_mask);
}

#[test]
fn unit_magnitude_mask_1() {
    let h = init_histo(1, 100000, 5);

    assert_eq!(0, h.unit_magnitude);
    assert_eq!(0, h.unit_magnitude_mask);
}

#[test]
fn unit_magnitude_mask_2() {
    let h = init_histo(2, 100000, 5);

    assert_eq!(1, h.unit_magnitude);
    assert_eq!(1, h.unit_magnitude_mask);
}

#[test]
fn unit_magnitude_mask_3() {
    let h = init_histo(3, 100000, 5);

    assert_eq!(1, h.unit_magnitude);
    assert_eq!(1, h.unit_magnitude_mask);
}

#[test]
fn unit_magnitude_mask_1000() {
    let h = init_histo(1000, 100000, 5);

    assert_eq!(9, h.unit_magnitude);
    assert_eq!(511, h.unit_magnitude_mask);
}

/// lowest_discernible_value: must be >= 1
/// highest_trackable_value: must be >= 2 * lowest_discernible_value
/// num_significant_digits: must be <= 5
fn init_histo(lowest_discernible_value: u64, highest_trackable_value: u64, num_significant_digits: u32) -> SimpleHdrHistogram<u64> {

    assert!(lowest_discernible_value >= 1);
    assert!(highest_trackable_value >= 2 * lowest_discernible_value);
    assert!(num_significant_digits <= 5);

    let largest_value_with_single_unit_resolution = 2_u64 * 10_u64.pow(num_significant_digits);

    let unit_magnitude = ((lowest_discernible_value as f64).ln() / 2_f64.ln()) as u32;
    let unit_magnitude_mask: u64  = (1_u64 << unit_magnitude) - 1;

    // find nearest power of 2 to largest_value_with_single_unit_resolution
    let sub_bucket_count_magnitude: u32 =
    ((largest_value_with_single_unit_resolution as f64).ln() / 2_f64.ln()).ceil() as u32;

    // ugly looking... how should ternaries be done?
    let sub_bucket_half_count_magnitude: u32 = (if sub_bucket_count_magnitude > 1 { sub_bucket_count_magnitude } else { 1 }) - 1;
    let sub_bucket_count: usize = 2_usize.pow(sub_bucket_half_count_magnitude + 1);
    let sub_bucket_half_count: usize = sub_bucket_count / 2;
    // TODO is this cast OK?
    let sub_bucket_mask = ((sub_bucket_count - 1) << unit_magnitude) as u64;

    let counts_arr_len = counts_arr_len(highest_trackable_value, sub_bucket_count, unit_magnitude);
    let bucket_count = buckets_needed_for_value(highest_trackable_value, sub_bucket_count, unit_magnitude);

    let leading_zero_count_base: usize = (64_u32 - unit_magnitude - sub_bucket_half_count_magnitude - 1) as usize;

    SimpleHdrHistogram {
        leading_zeros_count_base: leading_zero_count_base,
        unit_magnitude: unit_magnitude,
        sub_bucket_mask: sub_bucket_mask,
        sub_bucket_count: sub_bucket_count,
        sub_bucket_half_count: sub_bucket_half_count,
        sub_bucket_half_count_magnitude: sub_bucket_half_count_magnitude,
        counts: vec![0; counts_arr_len],
        counts_array_length: counts_arr_len,
        normalizing_index_offset: 0_usize, // 0 for normal Histogram ctor in Java impl
        min_non_zero_value: u64::max_value(),
        total_count: 0,
        max_value: 0,
        unit_magnitude_mask: unit_magnitude_mask
    }
}

fn buckets_needed_for_value(value: u64, sub_bucket_count: usize, unit_magnitude: u32) -> usize {

    // TODO is this cast ok?
    let mut smallest_untrackable_value: u64 = (sub_bucket_count << unit_magnitude) as u64;
    let mut buckets_needed: usize = 1;

    while smallest_untrackable_value <= value {
        if smallest_untrackable_value > u64::max_value() / 2 {
            return buckets_needed + 1;
        }

        smallest_untrackable_value = smallest_untrackable_value << 1;
        buckets_needed += 1;
    }

    return buckets_needed;
}

fn counts_arr_len_for_buckets(buckets: usize, sub_bucket_count: usize) -> usize {
    (buckets + 1) * (sub_bucket_count / 2)
}

fn counts_arr_len(value: u64, sub_bucket_count: usize, unit_magnitude: u32) -> usize {
    counts_arr_len_for_buckets(buckets_needed_for_value(value, sub_bucket_count, unit_magnitude), sub_bucket_count)
}<|endoftext|>