<commit_before>use num::Integer;
use euclid::Point2D;
use hex::Dir6;

/// Field of view iterator for a hexagonal map.
///
/// Takes a function that maps cells into user-specified values and indicates when traversal should
/// stop.
pub struct HexFov<F, T> {
    /// Predicate for whether a given point will block the field of view.
    f: F,
    stack: Vec<Sector<T>>,
    /// Returns true if given cell is a wallform and should have fake isometric hack applied.
    is_wall_f: Box<Fn(Point2D<i32>, &T) -> bool>,
    /// Extra values generated by special cases.
    side_channel: Vec<(Point2D<i32>, T)>,
}

impl<F, T> HexFov<F, T>
    where F: Fn(Point2D<i32>, &T) -> Option<T>,
          T: Eq + Clone
{
    pub fn new(init: T, f: F) -> HexFov<F, T> {
        // We could run f for (0, 0) here, but the traditional way for the FOV to work is to only
        // consider your surroundings, not the origin site itself.
        HexFov {
            f: f,
            stack: vec![Sector {
                            begin: PolarPoint::new(0.0, 1),
                            pt: PolarPoint::new(0.0, 1),
                            end: PolarPoint::new(6.0, 1),
                            group_value: init.clone(),
                        }],
            is_wall_f: Box::new(move |_, _| false),
            // The FOV algorithm will not generate the origin point, so we use
            // the side channel to explicitly add it in the beginning.
            side_channel: vec![(Point2D::new(0, 0), init)],
        }
    }

    /// Make wall tiles in acute corners visible when running the algorithm.
    ///
    /// This will ensure that a complete wall rectangle of fake-isometric rooms will appear
    /// in the FOV. Takes a function that queries if a location contains a wall-form tile.
    pub fn fake_isometric<G>(mut self, is_wall_f: G) -> HexFov<F, T>
        where G: Fn(Point2D<i32>, &T) -> bool + 'static
    {
        self.is_wall_f = Box::new(is_wall_f);
        self
    }
}

impl<F, T> Iterator for HexFov<F, T>
    where F: Fn(Point2D<i32>, &T) -> Option<T>,
          T: Eq + Clone
{
    type Item = (Point2D<i32>, T);
    fn next(&mut self) -> Option<(Point2D<i32>, T)> {
        if let Some(ret) = self.side_channel.pop() {
            return Some(ret);
        }

        if let Some(mut current) = self.stack.pop() {
            let current_value = (self.f)(current.pt.to_v2(), &current.group_value);

            if current.pt.is_below(current.end) && current_value.is_some() {
                let pos = current.pt.to_v2();
                let current_value = current_value.unwrap();

                if current_value != current.group_value {
                    if current.pt == current.begin {
                        // Oops, we've got a value mismatch on the very first item, so the entire
                        // sector was mistagged. This happens in the initial cycle when we
                        // guesstimate the value. Let's just quickly fix this before anyone
                        // notices.
                        current.group_value = current_value.clone();
                    } else {
                        // Terrain value changed, branch out.

                        // Add the rest of this sector with the new terrain value.
                        self.stack.push(Sector {
                            begin: current.pt,
                            pt: current.pt,
                            end: current.end,
                            group_value: current_value.clone(),
                        });

                        // Branch further if we still get values there.
                        if let Some(further_value) = (self.f)(current.begin.further().to_v2(),
                                                              &current_value) {
                            self.stack.push(Sector {
                                begin: current.begin.further(),
                                pt: current.begin.further(),
                                end: current.pt.further(),
                                group_value: further_value.clone(),
                            });
                        }
                        return self.next();
                    }
                }

                // Hack for making acute corner tiles of fake-isometric rooms visible.

                // We're moving along a vertical line on the hex circle, so there are side
                // points to check.
                if let Some(side_pos) = current.pt.side_point() {

                    let next = current.pt.next();
                    // If the next cell is within the current span and the current cell is
                    // wallform,
                    if next.is_below(current.end) &&
                       (self.is_wall_f)(current.pt.to_v2(), &current.group_value) {
                        // and if the next cell is visible,
                        if let Some(next_value) = (self.f)(next.to_v2(), &current.group_value) {
                            // and if the current and the next cell are in the same value group,
                            // both the next cell and the third corner point cell are
                            // wallforms, and the side point would not be otherwise
                            // visible:
                            if next_value == current.group_value &&
                               (self.is_wall_f)(next.to_v2(), &next_value) &&
                               (self.f)(side_pos, &current.group_value).is_none() &&
                               (self.is_wall_f)(side_pos, &current.group_value) {
                                // Add the side point to the side channel.
                                self.side_channel.push((side_pos, current.group_value.clone()));
                            }
                        }
                    }
                }

                // Proceed along the current sector.
                current.pt = current.pt.next();
                self.stack.push(current);
                return Some((pos, current_value));
            } else {
                // Hit the end of the sector.

                if let Some(group_value) = (self.f)(current.begin.further().to_v2(),
                                                    &current.group_value) {
                    // Branch out further if things are still visible there.
                    self.stack.push(Sector {
                        begin: current.begin.further(),
                        pt: current.begin.further(),
                        end: current.end.further(),
                        group_value: group_value,
                    });
                }

                self.next()
            }
        } else {
            None
        }
    }
}

struct Sector<T> {
    /// Start point of current sector.
    begin: PolarPoint,
    /// Point currently being processed.
    pt: PolarPoint,
    /// End point of current sector.
    end: PolarPoint,
    /// The user value for this group.
    group_value: T,
}

/// Points on a hex circle expressed in polar coordinates.
#[derive(Copy, Clone, PartialEq)]
struct PolarPoint {
    pos: f32,
    radius: u32,
}

impl PolarPoint {
    pub fn new(pos: f32, radius: u32) -> PolarPoint {
        PolarPoint {
            pos: pos,
            radius: radius,
        }
    }
    /// Index of the discrete hex cell along the circle that corresponds to this point.
    fn winding_index(self) -> i32 {
        (self.pos + 0.5).floor() as i32
    }

    pub fn is_below(self, other: PolarPoint) -> bool {
        self.winding_index() < other.end_index()
    }

    fn end_index(self) -> i32 {
        (self.pos + 0.5).ceil() as i32
    }

    pub fn to_v2(self) -> Point2D<i32> {
        if self.radius == 0 {
            return Point2D::new(0, 0);
        }
        let index = self.winding_index();
        let sector = index.mod_floor(&(self.radius as i32 * 6)) / self.radius as i32;
        let offset = index.mod_floor(&(self.radius as i32));

        let rod = Dir6::from_int(sector).to_v2();
        let tangent = Dir6::from_int((sector + 2) % 6).to_v2();

        rod * (self.radius as i32) + tangent * offset
    }

    /// If this point and the next point are adjacent vertically (along the xy
    /// axis), return the point outside of the circle between the two points.
    ///
    /// This is a helper function for the FOV special case where acute corners
    /// of fake isometric rooms are marked visible even though strict hex FOV
    /// logic would keep them unseen.
    pub fn side_point(self) -> Option<Point2D<i32>> {
        let next = self.next();
        let a = self.to_v2();
        let b = next.to_v2();

        if b.x == a.x + 1 && b.y == a.y + 1 {
            // Going down the right rim.
            Some(Point2D::new(a.x + 1, a.y))
        } else if b.x == a.x - 1 && b.y == a.y - 1 {
            // Going up the left rim.
            Some(Point2D::new(a.x - 1, a.y))
        } else {
            None
        }
    }

    /// The point corresponding to this one on the hex circle with radius +1.
    pub fn further(self) -> PolarPoint {
        PolarPoint::new(self.pos * (self.radius + 1) as f32 / self.radius as f32,
                        self.radius + 1)
    }

    /// The point next to this one along the hex circle.
    pub fn next(self) -> PolarPoint {
        PolarPoint::new((self.pos + 0.5).floor() + 0.5, self.radius)
    }
}
<commit_msg>Minimal unit test for fov<commit_after>use num::Integer;
use euclid::Point2D;
use hex::Dir6;

/// Field of view iterator for a hexagonal map.
///
/// Takes a function that maps cells into user-specified values and indicates when traversal should
/// stop.
pub struct HexFov<F, T> {
    /// Predicate for whether a given point will block the field of view.
    f: F,
    stack: Vec<Sector<T>>,
    /// Returns true if given cell is a wallform and should have fake isometric hack applied.
    is_wall_f: Box<Fn(Point2D<i32>, &T) -> bool>,
    /// Extra values generated by special cases.
    side_channel: Vec<(Point2D<i32>, T)>,
}

impl<F, T> HexFov<F, T>
    where F: Fn(Point2D<i32>, &T) -> Option<T>,
          T: Eq + Clone
{
    pub fn new(init: T, f: F) -> HexFov<F, T> {
        // We could run f for (0, 0) here, but the traditional way for the FOV to work is to only
        // consider your surroundings, not the origin site itself.
        HexFov {
            f: f,
            stack: vec![Sector {
                            begin: PolarPoint::new(0.0, 1),
                            pt: PolarPoint::new(0.0, 1),
                            end: PolarPoint::new(6.0, 1),
                            group_value: init.clone(),
                        }],
            is_wall_f: Box::new(move |_, _| false),
            // The FOV algorithm will not generate the origin point, so we use
            // the side channel to explicitly add it in the beginning.
            side_channel: vec![(Point2D::new(0, 0), init)],
        }
    }

    /// Make wall tiles in acute corners visible when running the algorithm.
    ///
    /// This will ensure that a complete wall rectangle of fake-isometric rooms will appear
    /// in the FOV. Takes a function that queries if a location contains a wall-form tile.
    pub fn fake_isometric<G>(mut self, is_wall_f: G) -> HexFov<F, T>
        where G: Fn(Point2D<i32>, &T) -> bool + 'static
    {
        self.is_wall_f = Box::new(is_wall_f);
        self
    }
}

impl<F, T> Iterator for HexFov<F, T>
    where F: Fn(Point2D<i32>, &T) -> Option<T>,
          T: Eq + Clone
{
    type Item = (Point2D<i32>, T);
    fn next(&mut self) -> Option<(Point2D<i32>, T)> {
        if let Some(ret) = self.side_channel.pop() {
            return Some(ret);
        }

        if let Some(mut current) = self.stack.pop() {
            let current_value = (self.f)(current.pt.to_v2(), &current.group_value);

            if current.pt.is_below(current.end) && current_value.is_some() {
                let pos = current.pt.to_v2();
                let current_value = current_value.unwrap();

                if current_value != current.group_value {
                    if current.pt == current.begin {
                        // Oops, we've got a value mismatch on the very first item, so the entire
                        // sector was mistagged. This happens in the initial cycle when we
                        // guesstimate the value. Let's just quickly fix this before anyone
                        // notices.
                        current.group_value = current_value.clone();
                    } else {
                        // Terrain value changed, branch out.

                        // Add the rest of this sector with the new terrain value.
                        self.stack.push(Sector {
                            begin: current.pt,
                            pt: current.pt,
                            end: current.end,
                            group_value: current_value.clone(),
                        });

                        // Branch further if we still get values there.
                        if let Some(further_value) = (self.f)(current.begin.further().to_v2(),
                                                              &current_value) {
                            self.stack.push(Sector {
                                begin: current.begin.further(),
                                pt: current.begin.further(),
                                end: current.pt.further(),
                                group_value: further_value.clone(),
                            });
                        }
                        return self.next();
                    }
                }

                // Hack for making acute corner tiles of fake-isometric rooms visible.

                // We're moving along a vertical line on the hex circle, so there are side
                // points to check.
                if let Some(side_pos) = current.pt.side_point() {

                    let next = current.pt.next();
                    // If the next cell is within the current span and the current cell is
                    // wallform,
                    if next.is_below(current.end) &&
                       (self.is_wall_f)(current.pt.to_v2(), &current.group_value) {
                        // and if the next cell is visible,
                        if let Some(next_value) = (self.f)(next.to_v2(), &current.group_value) {
                            // and if the current and the next cell are in the same value group,
                            // both the next cell and the third corner point cell are
                            // wallforms, and the side point would not be otherwise
                            // visible:
                            if next_value == current.group_value &&
                               (self.is_wall_f)(next.to_v2(), &next_value) &&
                               (self.f)(side_pos, &current.group_value).is_none() &&
                               (self.is_wall_f)(side_pos, &current.group_value) {
                                // Add the side point to the side channel.
                                self.side_channel.push((side_pos, current.group_value.clone()));
                            }
                        }
                    }
                }

                // Proceed along the current sector.
                current.pt = current.pt.next();
                self.stack.push(current);
                return Some((pos, current_value));
            } else {
                // Hit the end of the sector.

                if let Some(group_value) = (self.f)(current.begin.further().to_v2(),
                                                    &current.group_value) {
                    // Branch out further if things are still visible there.
                    self.stack.push(Sector {
                        begin: current.begin.further(),
                        pt: current.begin.further(),
                        end: current.end.further(),
                        group_value: group_value,
                    });
                }

                self.next()
            }
        } else {
            None
        }
    }
}

struct Sector<T> {
    /// Start point of current sector.
    begin: PolarPoint,
    /// Point currently being processed.
    pt: PolarPoint,
    /// End point of current sector.
    end: PolarPoint,
    /// The user value for this group.
    group_value: T,
}

/// Points on a hex circle expressed in polar coordinates.
#[derive(Copy, Clone, PartialEq)]
struct PolarPoint {
    pos: f32,
    radius: u32,
}

impl PolarPoint {
    pub fn new(pos: f32, radius: u32) -> PolarPoint {
        PolarPoint {
            pos: pos,
            radius: radius,
        }
    }
    /// Index of the discrete hex cell along the circle that corresponds to this point.
    fn winding_index(self) -> i32 {
        (self.pos + 0.5).floor() as i32
    }

    pub fn is_below(self, other: PolarPoint) -> bool {
        self.winding_index() < other.end_index()
    }

    fn end_index(self) -> i32 {
        (self.pos + 0.5).ceil() as i32
    }

    pub fn to_v2(self) -> Point2D<i32> {
        if self.radius == 0 {
            return Point2D::new(0, 0);
        }
        let index = self.winding_index();
        let sector = index.mod_floor(&(self.radius as i32 * 6)) / self.radius as i32;
        let offset = index.mod_floor(&(self.radius as i32));

        let rod = Dir6::from_int(sector).to_v2();
        let tangent = Dir6::from_int((sector + 2) % 6).to_v2();

        rod * (self.radius as i32) + tangent * offset
    }

    /// If this point and the next point are adjacent vertically (along the xy
    /// axis), return the point outside of the circle between the two points.
    ///
    /// This is a helper function for the FOV special case where acute corners
    /// of fake isometric rooms are marked visible even though strict hex FOV
    /// logic would keep them unseen.
    pub fn side_point(self) -> Option<Point2D<i32>> {
        let next = self.next();
        let a = self.to_v2();
        let b = next.to_v2();

        if b.x == a.x + 1 && b.y == a.y + 1 {
            // Going down the right rim.
            Some(Point2D::new(a.x + 1, a.y))
        } else if b.x == a.x - 1 && b.y == a.y - 1 {
            // Going up the left rim.
            Some(Point2D::new(a.x - 1, a.y))
        } else {
            None
        }
    }

    /// The point corresponding to this one on the hex circle with radius +1.
    pub fn further(self) -> PolarPoint {
        PolarPoint::new(self.pos * (self.radius + 1) as f32 / self.radius as f32,
                        self.radius + 1)
    }

    /// The point next to this one along the hex circle.
    pub fn next(self) -> PolarPoint {
        PolarPoint::new((self.pos + 0.5).floor() + 0.5, self.radius)
    }
}

#[cfg(test)]
mod test {
    use std::collections::HashMap;
    use std::iter::FromIterator;
    use euclid::Point2D;
    use super::HexFov;
    use hex::HexGeom;

    #[test]
    fn trivial_fov() {
        // Just draw a small circle.
        let fov = HexFov::new(0i32, |offset, &a| if offset.hex_dist() < 2 {
            Some(a + offset.x + offset.y)
        } else {
            None
        });

        let field: HashMap<Point2D<i32>, i32> = HashMap::from_iter(fov);

        assert!(field.contains_key(&Point2D::new(1, 0)));
        assert!(!field.contains_key(&Point2D::new(1, -1)));

        let fov = HexFov::new(0i32, |offset, &a| if offset.hex_dist() < 2 {
                      Some(a)
                  } else {
                      None
                  })
                      .fake_isometric(|_, _| true);
        let field: HashMap<Point2D<i32>, i32> = HashMap::from_iter(fov);

        assert!(field.contains_key(&Point2D::new(1, 0)));
        assert!(field.contains_key(&Point2D::new(1, -1)));
    }
}
<|endoftext|>


<commit_before>extern crate lancelot;
extern crate log;
extern crate chrono;
#[macro_use] extern crate clap;

use fern;
use log::{error, info, debug};
use failure::{Error};
use better_panic;

use lancelot::workspace::Workspace;

fn handle_functions(ws: &Workspace) -> Result<(), Error> {
    let mut functions = ws.get_functions().collect::<Vec<_>>();
    functions.sort();

    info!("found {} functions", functions.len());
    for rva in functions.iter() {
        if let Ok(basic_blocks) = ws.get_basic_blocks(**rva) {
            println!("{} with {} basic blocks", ws.va(**rva).unwrap(), basic_blocks.len());
        } else {
            println!("{}", rva);
        }
    }

    Ok(())
}

fn handle_layout(ws: &Workspace) -> Result<(), Error> {
    Ok(())
}

fn main() {
    better_panic::install();

    let matches = clap_app!(lancelot =>
        (author: "Willi Ballenthin <willi.ballenthin@gmail.com>")
        (about: "Binary analysis framework")
        (@arg verbose: -v --verbose +multiple "log verbose messages")
        (@arg quiet: -q --quiet "disable informational messages")
        (@subcommand functions =>
            (about: "find functions")
            (@arg input: +required "path to file to analyze"))
        (@subcommand layout =>
            (about: "show file sections")
            (@arg input: +required "path to file to analyze"))
        (@subcommand smoketest =>
            (about: "analyze file and exit on analysis failure")
            (@arg input: +required "path to file to analyze"))
    ).get_matches();

    // --quiet overrides --verbose
    let log_level = if matches.is_present("quiet") {
        log::LevelFilter::Error
    } else {
        match matches.occurrences_of("verbose") {
            0 => log::LevelFilter::Info,
            1 => log::LevelFilter::Debug,
            2 => log::LevelFilter::Trace,
            _ => log::LevelFilter::Trace,
        }
    };

    fern::Dispatch::new()
        .format(move |out, message, record| {
            out.finish(format_args!(
                "{} [{:5}] {} {}",
                chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
                record.level(),
                if log_level == log::LevelFilter::Trace {record.target()} else {""},
                message
            ))
        })
        .level(log_level)
        .chain(std::io::stderr())
        .filter(|metadata| {
            !metadata.target().starts_with("goblin::pe")
        })
        .apply()
        .expect("failed to configure logging");

    if let Some(matches) = matches.subcommand_matches("functions") {
        debug!("mode: find functions");

        let filename = matches.value_of("input").unwrap();
        debug!("input: {}", filename);

        let ws = Workspace::from_file(filename)
            .unwrap_or_else(|e| panic!("failed to load workspace: {}", e));

        let ws = ws.load()
            .unwrap_or_else(|e| panic!("failed to load workspace: {}", e));

        if let Err(e) = handle_functions(&ws) {
            error!("error: {}", e)
        }
    } else if let Some(matches) = matches.subcommand_matches("layout") {
        debug!("mode: layout");

        let filename = matches.value_of("input").unwrap();
        debug!("input: {}", filename);

        let ws = Workspace::from_file(filename)
            .unwrap_or_else(|e| panic!("failed to load workspace: {}", e))
            .disable_analysis()
            .load()
            .unwrap_or_else(|e| panic!("failed to load workspace: {}", e));

        if let Err(e) = handle_functions(&ws) {
            error!("error: {}", e)
        }
    } else if let Some(matches) = matches.subcommand_matches("smoketest") {
        debug!("mode: smoketest");

        let filename = matches.value_of("input").unwrap();
        debug!("input: {}", filename);

        match Workspace::from_file(filename)
            .unwrap_or_else(|e| panic!("failed to load workspace: {}", e))
            .enable_strict_mode()
            .load() {
            Err(e) => {
                println!("error");
                eprintln!("{:?}", e);
            },
            Ok(_) => println!("ok"),
        }
    };

    return;
}
<commit_msg>bin: remove 'layout' subcommand in favor of mapa<commit_after>extern crate lancelot;
extern crate log;
extern crate chrono;
#[macro_use] extern crate clap;

use fern;
use log::{error, info, debug};
use failure::{Error};
use better_panic;

use lancelot::workspace::Workspace;

fn handle_functions(ws: &Workspace) -> Result<(), Error> {
    let mut functions = ws.get_functions().collect::<Vec<_>>();
    functions.sort();

    info!("found {} functions", functions.len());
    for rva in functions.iter() {
        if let Ok(basic_blocks) = ws.get_basic_blocks(**rva) {
            println!("{} with {} basic blocks", ws.va(**rva).unwrap(), basic_blocks.len());
        } else {
            println!("{}", rva);
        }
    }

    Ok(())
}

fn main() {
    better_panic::install();

    let matches = clap_app!(lancelot =>
        (author: "Willi Ballenthin <willi.ballenthin@gmail.com>")
        (about: "Binary analysis framework")
        (@arg verbose: -v --verbose +multiple "log verbose messages")
        (@arg quiet: -q --quiet "disable informational messages")
        (@subcommand functions =>
            (about: "find functions")
            (@arg input: +required "path to file to analyze"))
        (@subcommand smoketest =>
            (about: "analyze file and exit on analysis failure")
            (@arg input: +required "path to file to analyze"))
    ).get_matches();

    // --quiet overrides --verbose
    let log_level = if matches.is_present("quiet") {
        log::LevelFilter::Error
    } else {
        match matches.occurrences_of("verbose") {
            0 => log::LevelFilter::Info,
            1 => log::LevelFilter::Debug,
            2 => log::LevelFilter::Trace,
            _ => log::LevelFilter::Trace,
        }
    };

    fern::Dispatch::new()
        .format(move |out, message, record| {
            out.finish(format_args!(
                "{} [{:5}] {} {}",
                chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
                record.level(),
                if log_level == log::LevelFilter::Trace {record.target()} else {""},
                message
            ))
        })
        .level(log_level)
        .chain(std::io::stderr())
        .filter(|metadata| {
            !metadata.target().starts_with("goblin::pe")
        })
        .apply()
        .expect("failed to configure logging");

    if let Some(matches) = matches.subcommand_matches("functions") {
        debug!("mode: find functions");

        let filename = matches.value_of("input").unwrap();
        debug!("input: {}", filename);

        let ws = Workspace::from_file(filename)
            .unwrap_or_else(|e| panic!("failed to load workspace: {}", e));

        let ws = ws.load()
            .unwrap_or_else(|e| panic!("failed to load workspace: {}", e));

        if let Err(e) = handle_functions(&ws) {
            error!("error: {}", e)
        }
    } else if let Some(matches) = matches.subcommand_matches("smoketest") {
        debug!("mode: smoketest");

        let filename = matches.value_of("input").unwrap();
        debug!("input: {}", filename);

        match Workspace::from_file(filename)
            .unwrap_or_else(|e| panic!("failed to load workspace: {}", e))
            .enable_strict_mode()
            .load() {
            Err(e) => {
                println!("error");
                eprintln!("{:?}", e);
            },
            Ok(_) => println!("ok"),
        }
    };

    return;
}
<|endoftext|>


<commit_before>//! Implements formating traits.
#![allow(unused)]

macro_rules! impl_hex_fmt {
    ($id:ident, $elem_ty:ident) => {
        impl ::fmt::LowerHex for $id {
            fn fmt(&self, f: &mut ::fmt::Formatter)
                   -> ::fmt::Result {
                use ::mem;
                write!(f, "{}(", stringify!($id))?;
                let n = mem::size_of_val(self)
                    / mem::size_of::<$elem_ty>();
                for i in 0..n {
                    if i > 0 {
                        write!(f, ", ")?;
                    }
                    self.extract(i).fmt(f)?;
                }
                write!(f, ")")
            }
        }
    }
}

#[cfg(test)]
macro_rules! test_hex_fmt_impl {
    ($id:ident, $elem_ty:ident, $($values:expr),+) => {
        #[test]
        fn hex_fmt() {
            use ::std::prelude::v1::*;
            use ::coresimd::simd::$id;
            for &i in [$($values),+].iter() {
                let vec = $id::splat(i as $elem_ty);

                let s = format!("{:#x}", vec);
                let beg = format!("{}(", stringify!($id));
                assert!(s.starts_with(&beg));
                assert!(s.ends_with(")"));
                let s: Vec<String> = s.replace(&beg, "").replace(")", "").split(",")
                    .map(|v| v.trim().to_string()).collect();
                assert_eq!(s.len(), $id::lanes());
                for (index, ss) in s.into_iter().enumerate() {
                    assert_eq!(ss, format!("{:#x}", vec.extract(index)));
                }
            }
        }
    }
}

#[cfg(test)]
macro_rules! test_hex_fmt {
    ($id:ident, $elem_ty:ident) => {
        test_hex_fmt_impl!($id, $elem_ty, 0 as $elem_ty, !(0 as $elem_ty), (1 as $elem_ty));
    }
}
<commit_msg>add missing fmt implementations (#380)<commit_after>//! Implements formating traits.
#![allow(unused)]

macro_rules! impl_hex_fmt {
    ($id:ident, $elem_ty:ident) => {
        impl ::fmt::LowerHex for $id {
            fn fmt(&self, f: &mut ::fmt::Formatter)
                   -> ::fmt::Result {
                use ::mem;
                write!(f, "{}(", stringify!($id))?;
                let n = mem::size_of_val(self)
                    / mem::size_of::<$elem_ty>();
                for i in 0..n {
                    if i > 0 {
                        write!(f, ", ")?;
                    }
                    self.extract(i).fmt(f)?;
                }
                write!(f, ")")
            }
        }
        impl ::fmt::UpperHex for $id {
            fn fmt(&self, f: &mut ::fmt::Formatter)
                   -> ::fmt::Result {
                write!(f, "{}(", stringify!($id))?;
                for i in 0..$id::lanes() {
                    if i > 0 {
                        write!(f, ", ")?;
                    }
                    self.extract(i).fmt(f)?;
                }
                write!(f, ")")
            }
        }
        impl ::fmt::Octal for $id {
            fn fmt(&self, f: &mut ::fmt::Formatter)
                   -> ::fmt::Result {
                write!(f, "{}(", stringify!($id))?;
                for i in 0..$id::lanes() {
                    if i > 0 {
                        write!(f, ", ")?;
                    }
                    self.extract(i).fmt(f)?;
                }
                write!(f, ")")
            }
        }
        impl ::fmt::Binary for $id {
            fn fmt(&self, f: &mut ::fmt::Formatter)
                   -> ::fmt::Result {
                write!(f, "{}(", stringify!($id))?;
                for i in 0..$id::lanes() {
                    if i > 0 {
                        write!(f, ", ")?;
                    }
                    self.extract(i).fmt(f)?;
                }
                write!(f, ")")
            }
        }
    }
}

#[cfg(test)]
macro_rules! test_hex_fmt_impl {
    ($id:ident, $elem_ty:ident, $($values:expr),+) => {
        #[test]
        fn hex_fmt() {
            use ::std::prelude::v1::*;
            use ::coresimd::simd::$id;
            for &i in [$($values),+].iter() {
                let vec = $id::splat(i as $elem_ty);

                let s = format!("{:#x}", vec);
                let beg = format!("{}(", stringify!($id));
                assert!(s.starts_with(&beg));
                assert!(s.ends_with(")"));
                let s: Vec<String> = s.replace(&beg, "").replace(")", "").split(",")
                    .map(|v| v.trim().to_string()).collect();
                assert_eq!(s.len(), $id::lanes());
                for (index, ss) in s.into_iter().enumerate() {
                    assert_eq!(ss, format!("{:#x}", vec.extract(index)));
                }
            }
        }
        #[test]
        fn upper_hex_fmt() {
            use ::std::prelude::v1::*;
            use ::coresimd::simd::$id;
            for &i in [$($values),+].iter() {
                let vec = $id::splat(i as $elem_ty);

                let s = format!("{:#X}", vec);
                let beg = format!("{}(", stringify!($id));
                assert!(s.starts_with(&beg));
                assert!(s.ends_with(")"));
                let s: Vec<String> = s.replace(&beg, "").replace(")", "").split(",")
                    .map(|v| v.trim().to_string()).collect();
                assert_eq!(s.len(), $id::lanes());
                for (index, ss) in s.into_iter().enumerate() {
                    assert_eq!(ss, format!("{:#X}", vec.extract(index)));
                }
            }
        }
        #[test]
        fn octal_fmt() {
            use ::std::prelude::v1::*;
            use ::coresimd::simd::$id;
            for &i in [$($values),+].iter() {
                let vec = $id::splat(i as $elem_ty);

                let s = format!("{:#o}", vec);
                let beg = format!("{}(", stringify!($id));
                assert!(s.starts_with(&beg));
                assert!(s.ends_with(")"));
                let s: Vec<String> = s.replace(&beg, "").replace(")", "").split(",")
                    .map(|v| v.trim().to_string()).collect();
                assert_eq!(s.len(), $id::lanes());
                for (index, ss) in s.into_iter().enumerate() {
                    assert_eq!(ss, format!("{:#o}", vec.extract(index)));
                }
            }
        }
        #[test]
        fn binary_fmt() {
            use ::std::prelude::v1::*;
            use ::coresimd::simd::$id;
            for &i in [$($values),+].iter() {
                let vec = $id::splat(i as $elem_ty);

                let s = format!("{:#b}", vec);
                let beg = format!("{}(", stringify!($id));
                assert!(s.starts_with(&beg));
                assert!(s.ends_with(")"));
                let s: Vec<String> = s.replace(&beg, "").replace(")", "").split(",")
                    .map(|v| v.trim().to_string()).collect();
                assert_eq!(s.len(), $id::lanes());
                for (index, ss) in s.into_iter().enumerate() {
                    assert_eq!(ss, format!("{:#b}", vec.extract(index)));
                }
            }
        }
    }
}

#[cfg(test)]
macro_rules! test_hex_fmt {
    ($id:ident, $elem_ty:ident) => {
        test_hex_fmt_impl!($id, $elem_ty, 0 as $elem_ty, !(0 as $elem_ty), (1 as $elem_ty));
    }
}
<|endoftext|>


<commit_before>//! Snapshot testing library, see
//! https://github.com/rust-analyzer/rust-analyzer/pull/5101
use std::{
    collections::HashMap,
    env, fmt, fs,
    ops::Range,
    path::{Path, PathBuf},
    sync::Mutex,
};

use once_cell::sync::Lazy;
use stdx::{lines_with_ends, trim_indent};

const HELP: &str = "
You can update all `expect![[]]` tests by:

    env UPDATE_EXPECT=1 cargo test

To update a single test, place the cursor on `expect` token and use `run` feature of rust-analyzer.
";

fn update_expect() -> bool {
    env::var("UPDATE_EXPECT").is_ok()
}

/// expect![[""]]
#[macro_export]
macro_rules! expect {
    [[$lit:literal]] => {$crate::Expect {
        file: file!(),
        line: line!(),
        column: column!(),
        data: $lit,
    }};
    [[]] => { $crate::expect![[""]] };
}

#[derive(Debug)]
pub struct Expect {
    pub file: &'static str,
    pub line: u32,
    pub column: u32,
    pub data: &'static str,
}

impl Expect {
    pub fn assert_eq(&self, actual: &str) {
        let trimmed = self.trimmed();
        if &trimmed == actual {
            return;
        }
        Runtime::fail(self, &trimmed, actual);
    }
    pub fn assert_debug_eq(&self, actual: &impl fmt::Debug) {
        let actual = format!("{:#?}\n", actual);
        self.assert_eq(&actual)
    }

    fn trimmed(&self) -> String {
        if !self.data.contains('\n') {
            return self.data.to_string();
        }
        trim_indent(self.data)
    }

    fn locate(&self, file: &str) -> Location {
        let mut target_line = None;
        let mut line_start = 0;
        for (i, line) in lines_with_ends(file).enumerate() {
            if i == self.line as usize - 1 {
                let pat = "expect![[";
                let offset = line.find(pat).unwrap();
                let literal_start = line_start + offset + pat.len();
                let indent = line.chars().take_while(|&it| it == ' ').count();
                target_line = Some((literal_start, indent));
                break;
            }
            line_start += line.len();
        }
        let (literal_start, line_indent) = target_line.unwrap();
        let literal_length = file[literal_start..].find("]]").unwrap();
        let literal_range = literal_start..literal_start + literal_length;
        Location { line_indent, literal_range }
    }
}

#[derive(Default)]
struct Runtime {
    help_printed: bool,
    per_file: HashMap<&'static str, FileRuntime>,
}
static RT: Lazy<Mutex<Runtime>> = Lazy::new(Default::default);

impl Runtime {
    fn fail(expect: &Expect, expected: &str, actual: &str) {
        let mut rt = RT.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
        let mut updated = "";
        if update_expect() {
            updated = " (updated)";
            rt.per_file
                .entry(expect.file)
                .or_insert_with(|| FileRuntime::new(expect))
                .update(expect, actual);
        }
        let print_help = !rt.help_printed && !update_expect();
        rt.help_printed = true;

        let help = if print_help { HELP } else { "" };
        println!(
            "\n
error: expect test failed{}
  --> {}:{}:{}
{}
Expect:
----
{}
----

Actual:
----
{}
----
",
            updated, expect.file, expect.line, expect.column, help, expected, actual
        );
        // Use resume_unwind instead of panic!() to prevent a backtrace, which is unnecessary noise.
        std::panic::resume_unwind(Box::new(()));
    }
}

struct FileRuntime {
    path: PathBuf,
    original_text: String,
    patchwork: Patchwork,
}

impl FileRuntime {
    fn new(expect: &Expect) -> FileRuntime {
        let path = workspace_root().join(expect.file);
        let original_text = fs::read_to_string(&path).unwrap();
        let patchwork = Patchwork::new(original_text.clone());
        FileRuntime { path, original_text, patchwork }
    }
    fn update(&mut self, expect: &Expect, actual: &str) {
        let loc = expect.locate(&self.original_text);
        let patch = format_patch(loc.line_indent.clone(), actual);
        self.patchwork.patch(loc.literal_range, &patch);
        fs::write(&self.path, &self.patchwork.text).unwrap()
    }
}

#[derive(Debug)]
struct Location {
    line_indent: usize,
    literal_range: Range<usize>,
}

#[derive(Debug)]
struct Patchwork {
    text: String,
    indels: Vec<(Range<usize>, usize)>,
}

impl Patchwork {
    fn new(text: String) -> Patchwork {
        Patchwork { text, indels: Vec::new() }
    }
    fn patch(&mut self, mut range: Range<usize>, patch: &str) {
        self.indels.push((range.clone(), patch.len()));
        self.indels.sort_by_key(|(delete, _insert)| delete.start);

        let (delete, insert) = self
            .indels
            .iter()
            .take_while(|(delete, _)| delete.start < range.start)
            .map(|(delete, insert)| (delete.end - delete.start, insert))
            .fold((0usize, 0usize), |(x1, y1), (x2, y2)| (x1 + x2, y1 + y2));

        for pos in &mut [&mut range.start, &mut range.end] {
            **pos += insert;
            **pos -= delete
        }

        self.text.replace_range(range, &patch);
    }
}

fn format_patch(line_indent: usize, patch: &str) -> String {
    let mut max_hashes = 0;
    let mut cur_hashes = 0;
    for byte in patch.bytes() {
        if byte != b'#' {
            cur_hashes = 0;
            continue;
        }
        cur_hashes += 1;
        max_hashes = max_hashes.max(cur_hashes);
    }
    let hashes = &"#".repeat(max_hashes + 1);
    let indent = &" ".repeat(line_indent);
    let is_multiline = patch.contains('\n');

    let mut buf = String::new();
    buf.push('r');
    buf.push_str(hashes);
    buf.push('"');
    if is_multiline {
        buf.push('\n');
    }
    let mut final_newline = false;
    for line in lines_with_ends(patch) {
        if is_multiline {
            buf.push_str(indent);
            buf.push_str("    ");
        }
        buf.push_str(line);
        final_newline = line.ends_with('\n');
    }
    if final_newline {
        buf.push_str(indent);
    }
    buf.push('"');
    buf.push_str(hashes);
    buf
}

fn workspace_root() -> PathBuf {
    Path::new(
        &env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()),
    )
    .ancestors()
    .nth(2)
    .unwrap()
    .to_path_buf()
}

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

    #[test]
    fn test_expect_macro() {
        let empty = expect![[]];
        expect![[r#"
            Expect {
                file: "crates/expect/src/lib.rs",
                line: 241,
                column: 21,
                data: "",
            }
        "#]]
        .assert_debug_eq(&empty);

        let expect = expect![["
            hello
            world
        "]];
        expect![[r#"
            Expect {
                file: "crates/expect/src/lib.rs",
                line: 252,
                column: 22,
                data: "\n            hello\n            world\n        ",
            }
        "#]]
        .assert_debug_eq(&expect);
    }

    #[test]
    fn test_format_patch() {
        let patch = format_patch(0, "hello\nworld\n");
        expect![[r##"
            r#"
                hello
                world
            "#"##]]
        .assert_eq(&patch);

        let patch = format_patch(4, "single line");
        expect![[r##"r#"single line"#"##]].assert_eq(&patch);
    }

    #[test]
    fn test_patchwork() {
        let mut patchwork = Patchwork::new("one two three".to_string());
        patchwork.patch(4..7, "zwei");
        patchwork.patch(0..3, "один");
        patchwork.patch(8..13, "3");
        expect![[r#"
            Patchwork {
                text: "один zwei 3",
                indels: [
                    (
                        0..3,
                        8,
                    ),
                    (
                        4..7,
                        4,
                    ),
                    (
                        8..13,
                        1,
                    ),
                ],
            }
        "#]]
        .assert_debug_eq(&patchwork);
    }
}
<commit_msg>Update crates/expect/src/lib.rs<commit_after>//! Snapshot testing library, see
//! https://github.com/rust-analyzer/rust-analyzer/pull/5101
use std::{
    collections::HashMap,
    env, fmt, fs,
    ops::Range,
    path::{Path, PathBuf},
    sync::Mutex,
};

use once_cell::sync::Lazy;
use stdx::{lines_with_ends, trim_indent};

const HELP: &str = "
You can update all `expect![[]]` tests by:

    env UPDATE_EXPECT=1 cargo test

To update a single test, place the cursor on `expect` token and use `run` feature of rust-analyzer.
";

fn update_expect() -> bool {
    env::var("UPDATE_EXPECT").is_ok()
}

/// expect![[""]]
#[macro_export]
macro_rules! expect {
    [[$lit:literal]] => {$crate::Expect {
        file: file!(),
        line: line!(),
        column: column!(),
        data: $lit,
    }};
    [[]] => { $crate::expect![[""]] };
}

#[derive(Debug)]
pub struct Expect {
    pub file: &'static str,
    pub line: u32,
    pub column: u32,
    pub data: &'static str,
}

impl Expect {
    pub fn assert_eq(&self, actual: &str) {
        let trimmed = self.trimmed();
        if &trimmed == actual {
            return;
        }
        Runtime::fail(self, &trimmed, actual);
    }
    pub fn assert_debug_eq(&self, actual: &impl fmt::Debug) {
        let actual = format!("{:#?}\n", actual);
        self.assert_eq(&actual)
    }

    fn trimmed(&self) -> String {
        if !self.data.contains('\n') {
            return self.data.to_string();
        }
        trim_indent(self.data)
    }

    fn locate(&self, file: &str) -> Location {
        let mut target_line = None;
        let mut line_start = 0;
        for (i, line) in lines_with_ends(file).enumerate() {
            if i == self.line as usize - 1 {
                let pat = "expect![[";
                let offset = line.find(pat).unwrap();
                let literal_start = line_start + offset + pat.len();
                let indent = line.chars().take_while(|&it| it == ' ').count();
                target_line = Some((literal_start, indent));
                break;
            }
            line_start += line.len();
        }
        let (literal_start, line_indent) = target_line.unwrap();
        let literal_length = file[literal_start..].find("]]").expect("Couldn't find matching `]]` for `expect![[`.");
        let literal_range = literal_start..literal_start + literal_length;
        Location { line_indent, literal_range }
    }
}

#[derive(Default)]
struct Runtime {
    help_printed: bool,
    per_file: HashMap<&'static str, FileRuntime>,
}
static RT: Lazy<Mutex<Runtime>> = Lazy::new(Default::default);

impl Runtime {
    fn fail(expect: &Expect, expected: &str, actual: &str) {
        let mut rt = RT.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
        let mut updated = "";
        if update_expect() {
            updated = " (updated)";
            rt.per_file
                .entry(expect.file)
                .or_insert_with(|| FileRuntime::new(expect))
                .update(expect, actual);
        }
        let print_help = !rt.help_printed && !update_expect();
        rt.help_printed = true;

        let help = if print_help { HELP } else { "" };
        println!(
            "\n
error: expect test failed{}
  --> {}:{}:{}
{}
Expect:
----
{}
----

Actual:
----
{}
----
",
            updated, expect.file, expect.line, expect.column, help, expected, actual
        );
        // Use resume_unwind instead of panic!() to prevent a backtrace, which is unnecessary noise.
        std::panic::resume_unwind(Box::new(()));
    }
}

struct FileRuntime {
    path: PathBuf,
    original_text: String,
    patchwork: Patchwork,
}

impl FileRuntime {
    fn new(expect: &Expect) -> FileRuntime {
        let path = workspace_root().join(expect.file);
        let original_text = fs::read_to_string(&path).unwrap();
        let patchwork = Patchwork::new(original_text.clone());
        FileRuntime { path, original_text, patchwork }
    }
    fn update(&mut self, expect: &Expect, actual: &str) {
        let loc = expect.locate(&self.original_text);
        let patch = format_patch(loc.line_indent.clone(), actual);
        self.patchwork.patch(loc.literal_range, &patch);
        fs::write(&self.path, &self.patchwork.text).unwrap()
    }
}

#[derive(Debug)]
struct Location {
    line_indent: usize,
    literal_range: Range<usize>,
}

#[derive(Debug)]
struct Patchwork {
    text: String,
    indels: Vec<(Range<usize>, usize)>,
}

impl Patchwork {
    fn new(text: String) -> Patchwork {
        Patchwork { text, indels: Vec::new() }
    }
    fn patch(&mut self, mut range: Range<usize>, patch: &str) {
        self.indels.push((range.clone(), patch.len()));
        self.indels.sort_by_key(|(delete, _insert)| delete.start);

        let (delete, insert) = self
            .indels
            .iter()
            .take_while(|(delete, _)| delete.start < range.start)
            .map(|(delete, insert)| (delete.end - delete.start, insert))
            .fold((0usize, 0usize), |(x1, y1), (x2, y2)| (x1 + x2, y1 + y2));

        for pos in &mut [&mut range.start, &mut range.end] {
            **pos += insert;
            **pos -= delete
        }

        self.text.replace_range(range, &patch);
    }
}

fn format_patch(line_indent: usize, patch: &str) -> String {
    let mut max_hashes = 0;
    let mut cur_hashes = 0;
    for byte in patch.bytes() {
        if byte != b'#' {
            cur_hashes = 0;
            continue;
        }
        cur_hashes += 1;
        max_hashes = max_hashes.max(cur_hashes);
    }
    let hashes = &"#".repeat(max_hashes + 1);
    let indent = &" ".repeat(line_indent);
    let is_multiline = patch.contains('\n');

    let mut buf = String::new();
    buf.push('r');
    buf.push_str(hashes);
    buf.push('"');
    if is_multiline {
        buf.push('\n');
    }
    let mut final_newline = false;
    for line in lines_with_ends(patch) {
        if is_multiline {
            buf.push_str(indent);
            buf.push_str("    ");
        }
        buf.push_str(line);
        final_newline = line.ends_with('\n');
    }
    if final_newline {
        buf.push_str(indent);
    }
    buf.push('"');
    buf.push_str(hashes);
    buf
}

fn workspace_root() -> PathBuf {
    Path::new(
        &env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()),
    )
    .ancestors()
    .nth(2)
    .unwrap()
    .to_path_buf()
}

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

    #[test]
    fn test_expect_macro() {
        let empty = expect![[]];
        expect![[r#"
            Expect {
                file: "crates/expect/src/lib.rs",
                line: 241,
                column: 21,
                data: "",
            }
        "#]]
        .assert_debug_eq(&empty);

        let expect = expect![["
            hello
            world
        "]];
        expect![[r#"
            Expect {
                file: "crates/expect/src/lib.rs",
                line: 252,
                column: 22,
                data: "\n            hello\n            world\n        ",
            }
        "#]]
        .assert_debug_eq(&expect);
    }

    #[test]
    fn test_format_patch() {
        let patch = format_patch(0, "hello\nworld\n");
        expect![[r##"
            r#"
                hello
                world
            "#"##]]
        .assert_eq(&patch);

        let patch = format_patch(4, "single line");
        expect![[r##"r#"single line"#"##]].assert_eq(&patch);
    }

    #[test]
    fn test_patchwork() {
        let mut patchwork = Patchwork::new("one two three".to_string());
        patchwork.patch(4..7, "zwei");
        patchwork.patch(0..3, "один");
        patchwork.patch(8..13, "3");
        expect![[r#"
            Patchwork {
                text: "один zwei 3",
                indels: [
                    (
                        0..3,
                        8,
                    ),
                    (
                        4..7,
                        4,
                    ),
                    (
                        8..13,
                        1,
                    ),
                ],
            }
        "#]]
        .assert_debug_eq(&patchwork);
    }
}
<|endoftext|>


<commit_before>//! Representation of a `TextEdit`.
//!
//! `rust-analyzer` never mutates text itself and only sends diffs to clients,
//! so `TextEdit` is the ultimate representation of the work done by
//! rust-analyzer.

pub use text_size::{TextRange, TextSize};

/// `InsertDelete` -- a single "atomic" change to text
///
/// Must not overlap with other `InDel`s
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Indel {
    pub insert: String,
    /// Refers to offsets in the original text
    pub delete: TextRange,
}

#[derive(Default, Debug, Clone)]
pub struct TextEdit {
    /// Invariant: disjoint and sorted by `delete`.
    indels: Vec<Indel>,
}

#[derive(Debug, Default, Clone)]
pub struct TextEditBuilder {
    indels: Vec<Indel>,
}

impl Indel {
    pub fn insert(offset: TextSize, text: String) -> Indel {
        Indel::replace(TextRange::empty(offset), text)
    }
    pub fn delete(range: TextRange) -> Indel {
        Indel::replace(range, String::new())
    }
    pub fn replace(range: TextRange, replace_with: String) -> Indel {
        Indel { delete: range, insert: replace_with }
    }

    pub fn apply(&self, text: &mut String) {
        let start: usize = self.delete.start().into();
        let end: usize = self.delete.end().into();
        text.replace_range(start..end, &self.insert);
    }
}

impl TextEdit {
    pub fn builder() -> TextEditBuilder {
        TextEditBuilder::default()
    }

    pub fn insert(offset: TextSize, text: String) -> TextEdit {
        let mut builder = TextEdit::builder();
        builder.insert(offset, text);
        builder.finish()
    }

    pub fn delete(range: TextRange) -> TextEdit {
        let mut builder = TextEdit::builder();
        builder.delete(range);
        builder.finish()
    }

    pub fn replace(range: TextRange, replace_with: String) -> TextEdit {
        let mut builder = TextEdit::builder();
        builder.replace(range, replace_with);
        builder.finish()
    }

    pub fn len(&self) -> usize {
        self.indels.len()
    }

    pub fn is_empty(&self) -> bool {
        self.indels.is_empty()
    }

    pub fn iter(&self) -> std::slice::Iter<'_, Indel> {
        self.into_iter()
    }

    pub fn apply(&self, text: &mut String) {
        match self.len() {
            0 => return,
            1 => {
                self.indels[0].apply(text);
                return;
            }
            _ => (),
        }

        let mut total_len = TextSize::of(&*text);
        for indel in &self.indels {
            total_len += TextSize::of(&indel.insert);
            total_len -= indel.delete.end() - indel.delete.start();
        }
        let mut buf = String::with_capacity(total_len.into());
        let mut prev = 0;
        for indel in &self.indels {
            let start: usize = indel.delete.start().into();
            let end: usize = indel.delete.end().into();
            if start > prev {
                buf.push_str(&text[prev..start]);
            }
            buf.push_str(&indel.insert);
            prev = end;
        }
        buf.push_str(&text[prev..text.len()]);
        assert_eq!(TextSize::of(&buf), total_len);

        // FIXME: figure out a way to mutate the text in-place or reuse the
        // memory in some other way
        *text = buf;
    }

    pub fn union(&mut self, other: TextEdit) -> Result<(), TextEdit> {
        // FIXME: can be done without allocating intermediate vector
        let mut all = self.iter().chain(other.iter()).collect::<Vec<_>>();
        if !check_disjoint_and_sort(&mut all) {
            return Err(other);
        }

        self.indels.extend(other.indels);
        check_disjoint_and_sort(&mut self.indels);
        // Only dedup deletions and replacements, keep all insertions
        self.indels.dedup_by(|a, b| a == b && !a.delete.is_empty());
        Ok(())
    }

    pub fn apply_to_offset(&self, offset: TextSize) -> Option<TextSize> {
        let mut res = offset;
        for indel in &self.indels {
            if indel.delete.start() >= offset {
                break;
            }
            if offset < indel.delete.end() {
                return None;
            }
            res += TextSize::of(&indel.insert);
            res -= indel.delete.len();
        }
        Some(res)
    }
}

impl IntoIterator for TextEdit {
    type Item = Indel;
    type IntoIter = std::vec::IntoIter<Indel>;

    fn into_iter(self) -> Self::IntoIter {
        self.indels.into_iter()
    }
}

impl<'a> IntoIterator for &'a TextEdit {
    type Item = &'a Indel;
    type IntoIter = std::slice::Iter<'a, Indel>;

    fn into_iter(self) -> Self::IntoIter {
        self.indels.iter()
    }
}

impl TextEditBuilder {
    pub fn is_empty(&self) -> bool {
        self.indels.is_empty()
    }
    pub fn replace(&mut self, range: TextRange, replace_with: String) {
        self.indel(Indel::replace(range, replace_with));
    }
    pub fn delete(&mut self, range: TextRange) {
        self.indel(Indel::delete(range));
    }
    pub fn insert(&mut self, offset: TextSize, text: String) {
        self.indel(Indel::insert(offset, text));
    }
    pub fn finish(self) -> TextEdit {
        let mut indels = self.indels;
        assert_disjoint_or_equal(&mut indels);
        TextEdit { indels }
    }
    pub fn invalidates_offset(&self, offset: TextSize) -> bool {
        self.indels.iter().any(|indel| indel.delete.contains_inclusive(offset))
    }
    fn indel(&mut self, indel: Indel) {
        self.indels.push(indel);
        if self.indels.len() <= 16 {
            assert_disjoint_or_equal(&mut self.indels);
        }
    }
}

fn assert_disjoint_or_equal(indels: &mut [Indel]) {
    assert!(check_disjoint_and_sort(indels));
}
// FIXME: Remove the impl Bound here, it shouldn't be needed
fn check_disjoint_and_sort(indels: &mut [impl std::borrow::Borrow<Indel>]) -> bool {
    indels.sort_by_key(|indel| (indel.borrow().delete.start(), indel.borrow().delete.end()));
    indels.iter().zip(indels.iter().skip(1)).all(|(l, r)| {
        let l = l.borrow();
        let r = r.borrow();
        l.delete.end() <= r.delete.start() || l == r
    })
}

#[test]
fn test_apply() {
    let mut text = "_11h1_2222_xx3333_4444_6666".to_string();
    let mut builder = TextEditBuilder::default();
    builder.replace(TextRange::new(3.into(), 4.into()), "1".to_string());
    builder.delete(TextRange::new(11.into(), 13.into()));
    builder.insert(22.into(), "_5555".to_string());

    let text_edit = builder.finish();
    text_edit.apply(&mut text);

    assert_eq!(text, "_1111_2222_3333_4444_5555_6666")
}<commit_msg>refact: edit text in place in TextEdit::apply<commit_after>//! Representation of a `TextEdit`.
//!
//! `rust-analyzer` never mutates text itself and only sends diffs to clients,
//! so `TextEdit` is the ultimate representation of the work done by
//! rust-analyzer.

pub use text_size::{TextRange, TextSize};

/// `InsertDelete` -- a single "atomic" change to text
///
/// Must not overlap with other `InDel`s
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Indel {
    pub insert: String,
    /// Refers to offsets in the original text
    pub delete: TextRange,
}

#[derive(Default, Debug, Clone)]
pub struct TextEdit {
    /// Invariant: disjoint and sorted by `delete`.
    indels: Vec<Indel>,
}

#[derive(Debug, Default, Clone)]
pub struct TextEditBuilder {
    indels: Vec<Indel>,
}

impl Indel {
    pub fn insert(offset: TextSize, text: String) -> Indel {
        Indel::replace(TextRange::empty(offset), text)
    }
    pub fn delete(range: TextRange) -> Indel {
        Indel::replace(range, String::new())
    }
    pub fn replace(range: TextRange, replace_with: String) -> Indel {
        Indel { delete: range, insert: replace_with }
    }

    pub fn apply(&self, text: &mut String) {
        let start: usize = self.delete.start().into();
        let end: usize = self.delete.end().into();
        text.replace_range(start..end, &self.insert);
    }
}

impl TextEdit {
    pub fn builder() -> TextEditBuilder {
        TextEditBuilder::default()
    }

    pub fn insert(offset: TextSize, text: String) -> TextEdit {
        let mut builder = TextEdit::builder();
        builder.insert(offset, text);
        builder.finish()
    }

    pub fn delete(range: TextRange) -> TextEdit {
        let mut builder = TextEdit::builder();
        builder.delete(range);
        builder.finish()
    }

    pub fn replace(range: TextRange, replace_with: String) -> TextEdit {
        let mut builder = TextEdit::builder();
        builder.replace(range, replace_with);
        builder.finish()
    }

    pub fn len(&self) -> usize {
        self.indels.len()
    }

    pub fn is_empty(&self) -> bool {
        self.indels.is_empty()
    }

    pub fn iter(&self) -> std::slice::Iter<'_, Indel> {
        self.into_iter()
    }

    pub fn apply(&self, text: &mut String) {
        match self.len() {
            0 => return,
            1 => {
                self.indels[0].apply(text);
                return;
            }
            _ => (),
        }

        let text_size = TextSize::of(&*text);
        let mut total_len = text_size.clone();
        for indel in &self.indels {
            total_len += TextSize::of(&indel.insert);
            total_len -= indel.delete.len();
        }

        if let Some(additional) = total_len.checked_sub(text_size.into()) {
            text.reserve(additional.into());
        }

        for indel in self.indels.iter().rev() {
            indel.apply(text);
        }

        assert_eq!(TextSize::of(&*text), total_len);
    }

    pub fn union(&mut self, other: TextEdit) -> Result<(), TextEdit> {
        // FIXME: can be done without allocating intermediate vector
        let mut all = self.iter().chain(other.iter()).collect::<Vec<_>>();
        if !check_disjoint_and_sort(&mut all) {
            return Err(other);
        }

        self.indels.extend(other.indels);
        check_disjoint_and_sort(&mut self.indels);
        // Only dedup deletions and replacements, keep all insertions
        self.indels.dedup_by(|a, b| a == b && !a.delete.is_empty());
        Ok(())
    }

    pub fn apply_to_offset(&self, offset: TextSize) -> Option<TextSize> {
        let mut res = offset;
        for indel in &self.indels {
            if indel.delete.start() >= offset {
                break;
            }
            if offset < indel.delete.end() {
                return None;
            }
            res += TextSize::of(&indel.insert);
            res -= indel.delete.len();
        }
        Some(res)
    }
}

impl IntoIterator for TextEdit {
    type Item = Indel;
    type IntoIter = std::vec::IntoIter<Indel>;

    fn into_iter(self) -> Self::IntoIter {
        self.indels.into_iter()
    }
}

impl<'a> IntoIterator for &'a TextEdit {
    type Item = &'a Indel;
    type IntoIter = std::slice::Iter<'a, Indel>;

    fn into_iter(self) -> Self::IntoIter {
        self.indels.iter()
    }
}

impl TextEditBuilder {
    pub fn is_empty(&self) -> bool {
        self.indels.is_empty()
    }
    pub fn replace(&mut self, range: TextRange, replace_with: String) {
        self.indel(Indel::replace(range, replace_with));
    }
    pub fn delete(&mut self, range: TextRange) {
        self.indel(Indel::delete(range));
    }
    pub fn insert(&mut self, offset: TextSize, text: String) {
        self.indel(Indel::insert(offset, text));
    }
    pub fn finish(self) -> TextEdit {
        let mut indels = self.indels;
        assert_disjoint_or_equal(&mut indels);
        TextEdit { indels }
    }
    pub fn invalidates_offset(&self, offset: TextSize) -> bool {
        self.indels.iter().any(|indel| indel.delete.contains_inclusive(offset))
    }
    fn indel(&mut self, indel: Indel) {
        self.indels.push(indel);
        if self.indels.len() <= 16 {
            assert_disjoint_or_equal(&mut self.indels);
        }
    }
}

fn assert_disjoint_or_equal(indels: &mut [Indel]) {
    assert!(check_disjoint_and_sort(indels));
}
// FIXME: Remove the impl Bound here, it shouldn't be needed
fn check_disjoint_and_sort(indels: &mut [impl std::borrow::Borrow<Indel>]) -> bool {
    indels.sort_by_key(|indel| (indel.borrow().delete.start(), indel.borrow().delete.end()));
    indels.iter().zip(indels.iter().skip(1)).all(|(l, r)| {
        let l = l.borrow();
        let r = r.borrow();
        l.delete.end() <= r.delete.start() || l == r
    })
}

#[test]
fn test_apply() {
    let mut text = "_11h1_2222_xx3333_4444_6666".to_string();
    let mut builder = TextEditBuilder::default();
    builder.replace(TextRange::new(3.into(), 4.into()), "1".to_string());
    builder.delete(TextRange::new(11.into(), 13.into()));
    builder.insert(22.into(), "_5555".to_string());

    let text_edit = builder.finish();
    text_edit.apply(&mut text);

    assert_eq!(text, "_1111_2222_3333_4444_5555_6666")
}<|endoftext|>


<commit_before>#[macro_use]
extern crate nom;

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

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

named!(category_bytes, take_until!("]"));
named!(value_bytes,    take_until_either!("\n;"));

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

named!(key_value    <&[u8],(&str,&str)>,
  chain!(
    key: map_res!(alphanumeric, from_utf8) ~
         space?                            ~
         tag!("=")                         ~
         space?                            ~
    val: map_res!(value_bytes, from_utf8)  ~
         space?                            ~
         chain!(
           tag!(";")        ~
           not_line_ending  ,
           ||{}
         ) ?                               ~
         multispace?                       ,
    ||{(key, val)}
  )
);

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

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

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

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

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

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

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

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

parameter=value
key = value2"[..];

  let ini_without_category = &b"parameter=value
key = value2"[..];

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

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

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

  let ini_without_key_value = &b"key = value2"[..];

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

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

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

  let ini_without_key_value = &b"key = value2"[..];

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

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

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

  let ini_without_key_value = &b"key = value2"[..];

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

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

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

key = value2

[category]"[..];

  let ini_without_key_value = &b"[category]"[..];

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

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

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

key = value2

[category]"[..];

  let ini_after_parser = &b"[category]"[..];

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

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

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

parameter=value;abc

key = value2

[category]
parameter3=value3
key4 = value4
"[..];

  let ini_after_parser = &b""[..];

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

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

<commit_msg>take_until* are now embeddable in other macros<commit_after>#[macro_use]
extern crate nom;

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

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

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

named!(key_value    <&[u8],(&str,&str)>,
  chain!(
    key: map_res!(alphanumeric, from_utf8) ~
         space?                            ~
         tag!("=")                         ~
         space?                            ~
    val: map_res!(
           take_until_either!("\n;"),
           from_utf8
         )                                 ~
         space?                            ~
         chain!(
           tag!(";")        ~
           not_line_ending  ,
           ||{}
         ) ?                               ~
         multispace?                       ,
    ||{(key, val)}
  )
);

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

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

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

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

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

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

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

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

parameter=value
key = value2"[..];

  let ini_without_category = &b"parameter=value
key = value2"[..];

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

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

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

  let ini_without_key_value = &b"key = value2"[..];

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

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

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

  let ini_without_key_value = &b"key = value2"[..];

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

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

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

  let ini_without_key_value = &b"key = value2"[..];

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

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

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

key = value2

[category]"[..];

  let ini_without_key_value = &b"[category]"[..];

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

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

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

key = value2

[category]"[..];

  let ini_after_parser = &b"[category]"[..];

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

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

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

parameter=value;abc

key = value2

[category]
parameter3=value3
key4 = value4
"[..];

  let ini_after_parser = &b""[..];

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

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

<|endoftext|>


<commit_before>#![feature(core)]
extern crate vobject;
use vobject::parse_component;
use std::borrow::ToOwned;

macro_rules! s(
    ($i:expr) => ($i.to_owned());
);

#[test]
fn test_vcard_basic() {
    let item = parse_component(
        "BEGIN:VCARD\n\
        VERSION:2.1\n\
        N:Mustermann;Erika\n\
        FN:Erika Mustermann\n\
        ORG:Wikipedia\n\
        TITLE:Oberleutnant\n\
        PHOTO;JPEG:http://commons.wikimedia.org/wiki/File:Erika_Mustermann_2010.jpg\n\
        TEL;WORK;VOICE:(0221) 9999123\n\n\n\
        TEL;HOME;VOICE:(0221) 1234567\n\
        ADR;HOME:;;Heidestrasse 17;Koeln;;51147;Deutschland\n\
        EMAIL;PREF;INTERNET:erika@mustermann.de\n\
        REV:20140301T221110Z\n\
        END:VCARD\n\r\n\n").unwrap();

    assert_eq!(item.single_prop("FN").unwrap().raw_value, s!("Erika Mustermann"));
    assert_eq!(item.single_prop("N").unwrap().raw_value,  s!("Mustermann;Erika"));

    let mut tel_values = item.all_props("TEL").iter().map(|x| x.raw_value.as_slice());
    assert_eq!(tel_values.next().unwrap(), s!("(0221) 9999123"));
    assert_eq!(tel_values.next().unwrap(), s!("(0221) 1234567"));
    assert!(tel_values.next().is_none());
}

#[test]
fn test_line_cont() {
    let item = parse_component(
        "BEGIN:VCARD\n\
        VERSION:2.1\n\
        N;ENCODING=QUOTED-PRINTABLE:Nikdo;Nikdo=\n\t\
        vic\n\
        NOTE:This ends with equal sign=\n\
        TEL;WORK:5555\n \
        4444\n\
        END:VCARD").unwrap();

    assert_eq!(item.name, s!("VCARD"));
    assert_eq!(item.single_prop("TEL").unwrap().raw_value, s!("55554444"));
    assert_eq!(item.single_prop("N").unwrap().raw_value, s!("Nikdo;Nikdo=vic"));
}

#[test]
fn test_icalendar_basic() {
    let item = parse_component(
            "BEGIN:VCALENDAR\n\
            VERSION:2.0\n\
            PRODID:http://www.example.com/calendarapplication/\n\
            METHOD:PUBLISH\n\
            BEGIN:VEVENT\n\
            UID:461092315540@example.com\n\
            ORGANIZER;CN=\"Alice Balder, Example Inc.\":MAILTO:alice@example.com\n\
            LOCATION:Somewhere\n\
            SUMMARY:Eine Kurzinfo\n\
            DESCRIPTION:Beschreibung des Termines\n\
            CLASS:PUBLIC\n\
            DTSTART:20060910T220000Z\n\
            DTEND:20060919T215900Z\n\
            DTSTAMP:20060812T125900Z\n\
            END:VEVENT\n\
            END:VCALENDAR\n").unwrap();

    assert_eq!(item.name, s!("VCALENDAR"));
    assert!(item.single_prop("LOCATION").is_none());
    assert!(item.single_prop("ORGANIZER").is_none());

    let event = &item.subcomponents[0];
    assert_eq!(event.name, s!("VEVENT"));
    assert!(event.single_prop("ORGANIZER").is_some());
    assert_eq!(event.single_prop("LOCATION").unwrap().raw_value, s!("Somewhere"));
}

#[test]
fn test_escaping() {
    let item = parse_component(
            "BEGIN:VCALENDAR\n\
            ORGANIZER;CN=\"Cott:n Eye Joe\":mailto:joe@joe.com\n\
            END:VCALENDAR\n").unwrap();
    assert_eq!(item.name, s!("VCALENDAR"));
    assert_eq!(item.single_prop("ORGANIZER").unwrap().raw_value, s!("mailto:joe@joe.com"));
}

#[test]
fn test_property_groups() {
    let item = parse_component(
            "BEGIN:VCARD\n\
            foo.EMAIL;TYPE=INTERNET:foo@example.com\n\
            foo.X-ACTUAL-TYPE:CUSTOM\n\
            END:VCARD\n").unwrap();
    assert_eq!(item.single_prop("EMAIL").unwrap().prop_group, Some("foo".to_owned()));

}
<commit_msg>Nightly fixes<commit_after>extern crate vobject;
use vobject::parse_component;
use std::borrow::ToOwned;

macro_rules! s(
    ($i:expr) => ($i.to_owned());
);

#[test]
fn test_vcard_basic() {
    let item = parse_component(
        "BEGIN:VCARD\n\
        VERSION:2.1\n\
        N:Mustermann;Erika\n\
        FN:Erika Mustermann\n\
        ORG:Wikipedia\n\
        TITLE:Oberleutnant\n\
        PHOTO;JPEG:http://commons.wikimedia.org/wiki/File:Erika_Mustermann_2010.jpg\n\
        TEL;WORK;VOICE:(0221) 9999123\n\n\n\
        TEL;HOME;VOICE:(0221) 1234567\n\
        ADR;HOME:;;Heidestrasse 17;Koeln;;51147;Deutschland\n\
        EMAIL;PREF;INTERNET:erika@mustermann.de\n\
        REV:20140301T221110Z\n\
        END:VCARD\n\r\n\n").unwrap();

    assert_eq!(item.single_prop("FN").unwrap().raw_value, s!("Erika Mustermann"));
    assert_eq!(item.single_prop("N").unwrap().raw_value,  s!("Mustermann;Erika"));

    let mut tel_values = item.all_props("TEL").iter().map(|x| &x.raw_value[..]);
    assert_eq!(tel_values.next().unwrap(), s!("(0221) 9999123"));
    assert_eq!(tel_values.next().unwrap(), s!("(0221) 1234567"));
    assert!(tel_values.next().is_none());
}

#[test]
fn test_line_cont() {
    let item = parse_component(
        "BEGIN:VCARD\n\
        VERSION:2.1\n\
        N;ENCODING=QUOTED-PRINTABLE:Nikdo;Nikdo=\n\t\
        vic\n\
        NOTE:This ends with equal sign=\n\
        TEL;WORK:5555\n \
        4444\n\
        END:VCARD").unwrap();

    assert_eq!(item.name, s!("VCARD"));
    assert_eq!(item.single_prop("TEL").unwrap().raw_value, s!("55554444"));
    assert_eq!(item.single_prop("N").unwrap().raw_value, s!("Nikdo;Nikdo=vic"));
}

#[test]
fn test_icalendar_basic() {
    let item = parse_component(
            "BEGIN:VCALENDAR\n\
            VERSION:2.0\n\
            PRODID:http://www.example.com/calendarapplication/\n\
            METHOD:PUBLISH\n\
            BEGIN:VEVENT\n\
            UID:461092315540@example.com\n\
            ORGANIZER;CN=\"Alice Balder, Example Inc.\":MAILTO:alice@example.com\n\
            LOCATION:Somewhere\n\
            SUMMARY:Eine Kurzinfo\n\
            DESCRIPTION:Beschreibung des Termines\n\
            CLASS:PUBLIC\n\
            DTSTART:20060910T220000Z\n\
            DTEND:20060919T215900Z\n\
            DTSTAMP:20060812T125900Z\n\
            END:VEVENT\n\
            END:VCALENDAR\n").unwrap();

    assert_eq!(item.name, s!("VCALENDAR"));
    assert!(item.single_prop("LOCATION").is_none());
    assert!(item.single_prop("ORGANIZER").is_none());

    let event = &item.subcomponents[0];
    assert_eq!(event.name, s!("VEVENT"));
    assert!(event.single_prop("ORGANIZER").is_some());
    assert_eq!(event.single_prop("LOCATION").unwrap().raw_value, s!("Somewhere"));
}

#[test]
fn test_escaping() {
    let item = parse_component(
            "BEGIN:VCALENDAR\n\
            ORGANIZER;CN=\"Cott:n Eye Joe\":mailto:joe@joe.com\n\
            END:VCALENDAR\n").unwrap();
    assert_eq!(item.name, s!("VCALENDAR"));
    assert_eq!(item.single_prop("ORGANIZER").unwrap().raw_value, s!("mailto:joe@joe.com"));
}

#[test]
fn test_property_groups() {
    let item = parse_component(
            "BEGIN:VCARD\n\
            foo.EMAIL;TYPE=INTERNET:foo@example.com\n\
            foo.X-ACTUAL-TYPE:CUSTOM\n\
            END:VCARD\n").unwrap();
    assert_eq!(item.single_prop("EMAIL").unwrap().prop_group, Some("foo".to_owned()));

}
<|endoftext|>


<commit_before>//! Utilities and parsers for dealing with ASCII data in `u8` format.

use conv::{NoError, ValueFrom};
use conv::errors::UnwrapOk;

use std::ops::{Add, Mul};

use {Input, U8Result};
use parsers::take_while1;
use parsers::satisfy;

/// Lowercase ASCII predicate.
#[inline]
pub fn is_lowercase(c: u8) -> bool {
    b'a' <= c && c <= b'z'
}

/// Uppercase ASCII character predicate.
#[inline]
pub fn is_uppercase(c: u8) -> bool {
    b'A' <= c && c <= b'Z'
}

/// ASCII whitespace predicate.
///
/// Includes:
///
/// * Horizontal tab (TAB)
/// * Line feed (LF)
/// * Vertical tab (VT)
/// * Form feed (FF)
/// * Carriage return (CR)
/// * Space
#[inline]
pub fn is_whitespace(c: u8) -> bool {
    9 <= c && c <= 13 || c == b' '
}

/// ASCII digit predicate.
#[inline]
pub fn is_digit(c: u8) -> bool {
    b'0' <= c && c <= b'9'
}

/// ASCII alphabetic predicate.
#[inline]
pub fn is_alpha(c: u8) -> bool {
    is_lowercase(c) || is_uppercase(c)
}

/// ASCII alphanumeric predicate.
#[inline]
pub fn is_alphanumeric(c: u8) -> bool {
    is_alpha(c) || is_digit(c)
}

/// Parses a single digit.
///
/// # Note
///
/// The result is the code of the digit, no conversion takes place.
///
/// # Example
///
/// ```
/// use chomp::Input;
/// use chomp::ascii::digit;
///
/// let i = Input::new(b"1");
///
/// let r = digit(i);
///
/// assert_eq!(r.unwrap(), b'1');
/// ```
#[inline]
pub fn digit(i: Input<u8>) -> U8Result<u8> {
    satisfy(i, is_digit)
}

/// Parses a series of digits and converts them to an integer.
///
/// # Note
///
/// The `T` type must be larger than `u8` if it is signed.
///
/// # Example
///
/// ```
/// use chomp::Input;
/// use chomp::ascii::decimal;
///
/// let i = Input::new(b"123");
///
/// let r = decimal::<u8>(i);
///
/// assert_eq!(r.unwrap(), 123u8);
/// ```
#[inline]
pub fn decimal<T: Copy + ValueFrom<u8, Err=NoError> + Add<Output=T> + Mul<Output=T>>(i: Input<u8>) -> U8Result<T> {
    take_while1(i, is_digit).bind(|i, buf| i.ret(to_decimal(buf)))
}

/// Internal function converting a `[u8]` to the given integer type `T`.
///
/// # Notes
///
/// * The slice must not contain any other characters besides 0 to 9.
/// * The `T` type must be larger than `u8` if it is signed.
#[inline]
fn to_decimal<T: Copy + ValueFrom<u8, Err=NoError> + Add<Output=T> + Mul<Output=T>>(buf: &[u8]) -> T {
    buf.iter().fold(T::value_from(0).unwrap_ok(), |a, n| a * T::value_from(10).unwrap_ok() + T::value_from(n - b'0').unwrap_ok())
}

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

    macro_rules! test_to_decimal {
        ( $($n:ty),+ ) => { $(
            assert_eq!(to_decimal::<$n>(b""), 0);
            assert_eq!(to_decimal::<$n>(b"0"), 0);
            assert_eq!(to_decimal::<$n>(b"1"), 1);
            assert_eq!(to_decimal::<$n>(b"2"), 2);
            assert_eq!(to_decimal::<$n>(b"10"), 10);
            assert_eq!(to_decimal::<$n>(b"20"), 20);
            assert_eq!(to_decimal::<$n>(b"25"), 25);
        )+ }
    }

    #[test]
    fn test_to_decimal_u8() {
        test_to_decimal!(u8, u16, u32, u64, i16, i32, i64);
    }
}
<commit_msg>Parser: ascii::skip_whitespace<commit_after>//! Utilities and parsers for dealing with ASCII data in `u8` format.

use conv::{NoError, ValueFrom};
use conv::errors::UnwrapOk;

use std::ops::{Add, Mul};

use {Input, U8Result};
use parsers::{take_while, take_while1, satisfy};

/// Lowercase ASCII predicate.
#[inline]
pub fn is_lowercase(c: u8) -> bool {
    b'a' <= c && c <= b'z'
}

/// Uppercase ASCII character predicate.
#[inline]
pub fn is_uppercase(c: u8) -> bool {
    b'A' <= c && c <= b'Z'
}

/// ASCII whitespace predicate.
///
/// Includes:
///
/// * Horizontal tab (TAB)
/// * Line feed (LF)
/// * Vertical tab (VT)
/// * Form feed (FF)
/// * Carriage return (CR)
/// * Space
#[inline]
pub fn is_whitespace(c: u8) -> bool {
    9 <= c && c <= 13 || c == b' '
}

/// ASCII digit predicate.
#[inline]
pub fn is_digit(c: u8) -> bool {
    b'0' <= c && c <= b'9'
}

/// ASCII alphabetic predicate.
#[inline]
pub fn is_alpha(c: u8) -> bool {
    is_lowercase(c) || is_uppercase(c)
}

/// ASCII alphanumeric predicate.
#[inline]
pub fn is_alphanumeric(c: u8) -> bool {
    is_alpha(c) || is_digit(c)
}

/// Skips over whitespace.
///
/// Matches zero-length.
///
/// # Example
///
/// ```
/// use chomp::Input;
/// use chomp::ascii::skip_whitespace;
///
/// let i = Input::new(b" \t ");
///
/// assert_eq!(skip_whitespace(i).unwrap(), ());
/// ```
#[inline]
pub fn skip_whitespace(i: Input<u8>) -> U8Result<()> {
    take_while(i, is_whitespace).map(|_| ())
}

/// Parses a single digit.
///
/// # Note
///
/// The result is the code of the digit, no conversion takes place.
///
/// # Example
///
/// ```
/// use chomp::Input;
/// use chomp::ascii::digit;
///
/// let i = Input::new(b"1");
///
/// let r = digit(i);
///
/// assert_eq!(r.unwrap(), b'1');
/// ```
#[inline]
pub fn digit(i: Input<u8>) -> U8Result<u8> {
    satisfy(i, is_digit)
}

/// Parses a series of digits and converts them to an integer.
///
/// # Note
///
/// The `T` type must be larger than `u8` if it is signed.
///
/// # Example
///
/// ```
/// use chomp::Input;
/// use chomp::ascii::decimal;
///
/// let i = Input::new(b"123");
///
/// let r = decimal::<u8>(i);
///
/// assert_eq!(r.unwrap(), 123u8);
/// ```
#[inline]
pub fn decimal<T: Copy + ValueFrom<u8, Err=NoError> + Add<Output=T> + Mul<Output=T>>(i: Input<u8>) -> U8Result<T> {
    take_while1(i, is_digit).bind(|i, buf| i.ret(to_decimal(buf)))
}

/// Internal function converting a `[u8]` to the given integer type `T`.
///
/// # Notes
///
/// * The slice must not contain any other characters besides 0 to 9.
/// * The `T` type must be larger than `u8` if it is signed.
#[inline]
fn to_decimal<T: Copy + ValueFrom<u8, Err=NoError> + Add<Output=T> + Mul<Output=T>>(buf: &[u8]) -> T {
    buf.iter().fold(T::value_from(0).unwrap_ok(), |a, n| a * T::value_from(10).unwrap_ok() + T::value_from(n - b'0').unwrap_ok())
}

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

    macro_rules! test_to_decimal {
        ( $($n:ty),+ ) => { $(
            assert_eq!(to_decimal::<$n>(b""), 0);
            assert_eq!(to_decimal::<$n>(b"0"), 0);
            assert_eq!(to_decimal::<$n>(b"1"), 1);
            assert_eq!(to_decimal::<$n>(b"2"), 2);
            assert_eq!(to_decimal::<$n>(b"10"), 10);
            assert_eq!(to_decimal::<$n>(b"20"), 20);
            assert_eq!(to_decimal::<$n>(b"25"), 25);
        )+ }
    }

    #[test]
    fn test_to_decimal_u8() {
        test_to_decimal!(u8, u16, u32, u64, i16, i32, i64);
    }
}
<|endoftext|>


<commit_before>extern crate portaudio;
use portaudio as pa;

use std::sync::mpsc::*;

pub const SAMPLE_RATE: f32 = 44100.0;
pub const FRAMES: usize = 512;

pub fn init() -> Result<pa::PortAudio, pa::Error> {
    pa::PortAudio::new()
}

pub fn get_device_list(pa: &pa::PortAudio) -> Result<Vec<(u32, String)>, pa::Error> {
    // This pa.devices gives a Result of devices, each of which is
    // also a Result. So a Result<Collection<Result<Device>>>. We
    // mould it into devices: a Vec<(index, DeviceInfo)>.
    let devices = try!(try!(pa.devices()).map(|device| {
        device.map(|(pa::DeviceIndex(idx), info)| (idx, info))
    }).collect::<Result<Vec<_>, _>>());

    let list = devices.iter().filter(|&&(_, ref info)| info.max_input_channels > 0)
        .map(|&(idx, ref info)| (idx, info.name.to_string()))
        .collect();
    Ok(list)
}

pub fn get_default_device(pa: &pa::PortAudio) -> Result<u32, pa::Error> {
    let pa::DeviceIndex(default_input_index) = pa.default_input_device()?;
    Ok(default_input_index)
}

pub fn start_listening_default(pa: &pa::PortAudio, sender: Sender<Vec<f32>>) -> Result<pa::Stream<pa::NonBlocking, pa::Input<f32>>, pa::Error> {
    let default = get_default_device(pa)?;
    start_listening(pa, default, sender)
}

pub fn start_listening(pa: &pa::PortAudio, device_index: u32,
                       sender: Sender<Vec<f32>>) -> Result<pa::Stream<pa::NonBlocking, pa::Input<f32>>, pa::Error> {
    let device_info = try!(pa.device_info(pa::DeviceIndex(device_index)));
    let latency = device_info.default_low_input_latency;

    // Construct the input stream parameters.

    let input_params = pa::StreamParameters::<f32>::new(pa::DeviceIndex(device_index), 1, true, latency);

    // Check that the stream format is supported.
    try!(pa.is_input_format_supported(input_params, SAMPLE_RATE as f64));

    // Construct the settings with which we'll open our stream.
    let stream_settings = pa::InputStreamSettings::new(input_params, SAMPLE_RATE as f64, FRAMES as u32);

    // This callback A callback to pass to the non-blocking stream.
    let callback = move |pa::InputStreamCallbackArgs { buffer, .. }| {
        match sender.send(Vec::from(buffer)) {
            Ok(_) => pa::Continue,
            Err(_) => pa::Complete //this happens when receiver is dropped
        }
    };

    let mut stream = try!(pa.open_non_blocking_stream(stream_settings, callback));
    try!(stream.start());

    Ok(stream)
}

#[test]
#[ignore] //ignored because TravisCI doesn't have any audio devices to test with
fn start_listening_returns_successfully() {
    // Just a note on unit tests here, portaudio doesn't seem to
    // respond well to being initialized many times, and starts
    // throwing errors.
    let pa = init().expect("Could not init portaudio");

    let devices = get_device_list(&pa).expect("Getting devices had an error");
    assert!(devices.len() > 0);

    let (sender, _) = channel();
    start_listening_default(&pa, sender).expect("Error starting listening to first channel");
}
<commit_msg>Removed unnecessary comment<commit_after>extern crate portaudio;
use portaudio as pa;

use std::sync::mpsc::*;

pub const SAMPLE_RATE: f32 = 44100.0;
pub const FRAMES: usize = 512;

pub fn init() -> Result<pa::PortAudio, pa::Error> {
    pa::PortAudio::new()
}

pub fn get_device_list(pa: &pa::PortAudio) -> Result<Vec<(u32, String)>, pa::Error> {
    // This pa.devices gives a Result of devices, each of which is
    // also a Result. So a Result<Collection<Result<Device>>>. We
    // mould it into devices: a Vec<(index, DeviceInfo)>.
    let devices = try!(try!(pa.devices()).map(|device| {
        device.map(|(pa::DeviceIndex(idx), info)| (idx, info))
    }).collect::<Result<Vec<_>, _>>());

    let list = devices.iter().filter(|&&(_, ref info)| info.max_input_channels > 0)
        .map(|&(idx, ref info)| (idx, info.name.to_string()))
        .collect();
    Ok(list)
}

pub fn get_default_device(pa: &pa::PortAudio) -> Result<u32, pa::Error> {
    let pa::DeviceIndex(default_input_index) = pa.default_input_device()?;
    Ok(default_input_index)
}

pub fn start_listening_default(pa: &pa::PortAudio, sender: Sender<Vec<f32>>) -> Result<pa::Stream<pa::NonBlocking, pa::Input<f32>>, pa::Error> {
    let default = get_default_device(pa)?;
    start_listening(pa, default, sender)
}

pub fn start_listening(pa: &pa::PortAudio, device_index: u32,
                       sender: Sender<Vec<f32>>) -> Result<pa::Stream<pa::NonBlocking, pa::Input<f32>>, pa::Error> {
    let device_info = try!(pa.device_info(pa::DeviceIndex(device_index)));
    let latency = device_info.default_low_input_latency;

    // Construct the input stream parameters.

    let input_params = pa::StreamParameters::<f32>::new(pa::DeviceIndex(device_index), 1, true, latency);

    // Check that the stream format is supported.
    try!(pa.is_input_format_supported(input_params, SAMPLE_RATE as f64));

    // Construct the settings with which we'll open our stream.
    let stream_settings = pa::InputStreamSettings::new(input_params, SAMPLE_RATE as f64, FRAMES as u32);

    // This callback A callback to pass to the non-blocking stream.
    let callback = move |pa::InputStreamCallbackArgs { buffer, .. }| {
        match sender.send(Vec::from(buffer)) {
            Ok(_) => pa::Continue,
            Err(_) => pa::Complete
        }
    };

    let mut stream = try!(pa.open_non_blocking_stream(stream_settings, callback));
    try!(stream.start());

    Ok(stream)
}

#[test]
#[ignore] //ignored because TravisCI doesn't have any audio devices to test with
fn start_listening_returns_successfully() {
    // Just a note on unit tests here, portaudio doesn't seem to
    // respond well to being initialized many times, and starts
    // throwing errors.
    let pa = init().expect("Could not init portaudio");

    let devices = get_device_list(&pa).expect("Getting devices had an error");
    assert!(devices.len() > 0);

    let (sender, _) = channel();
    start_listening_default(&pa, sender).expect("Error starting listening to first channel");
}
<|endoftext|>