<commit_before>extern crate image;
extern crate bspline;

use std::ops::{Mul, Add, Index, IndexMut};
use std::iter;

#[derive(Copy, Clone, Debug)]
struct Point {
    x: f32,
    y: f32,
}
impl Point {
    fn new(x: f32, y: f32) -> Point {
        Point { x: x, y: y }
    }
}
impl Mul<f32> for Point {
    type Output = Point;
    fn mul(self, rhs: f32) -> Point {
        Point { x: self.x * rhs, y: self.y * rhs }
    }
}
impl Add for Point {
    type Output = Point;
    fn add(self, rhs: Point) -> Point {
        Point { x: self.x + rhs.x, y: self.y + rhs.y }
    }
}

/// Clamp `x` to be between `min` and `max`
pub fn clamp<T: PartialOrd>(x: T, min: T, max: T) -> T {
    if x < min { min } else if x > max { max } else { x }
}

#[derive(Debug, Copy, Clone)]
pub struct Colorf {
    pub r: f32,
    pub g: f32,
    pub b: f32,
}

impl Colorf {
    /// Create an RGB color
    pub fn new(r: f32, g: f32, b: f32) -> Colorf {
        Colorf { r: r, g: g, b: b }
    }
    pub fn broadcast(x: f32) -> Colorf {
        Colorf { r: x, g: x, b: x }
    }
    /// Clamp the color values between [0, 1]
    pub fn clamp(&self) -> Colorf {
        Colorf { r: clamp(self.r, 0.0, 1.0),
                 g: clamp(self.g, 0.0, 1.0),
                 b: clamp(self.b, 0.0, 1.0) }
    }
    /// Convert the linear RGB color to sRGB
    pub fn to_srgb(&self) -> Colorf {
        let a = 0.055f32;
        let b = 1f32 / 2.4;
        let mut srgb = Colorf::broadcast(0.0);
        for i in 0..3 {
            if self[i] <= 0.0031308 {
                srgb[i] = 12.92 * self[i];
            } else {
                srgb[i] = (1.0 + a) * f32::powf(self[i], b) - a;
            }
        }
        srgb
    }
}
impl Add for Colorf {
    type Output = Colorf;
    /// Add two colors together
    fn add(self, rhs: Colorf) -> Colorf {
        Colorf { r: self.r + rhs.r, g: self.g + rhs.g, b: self.b + rhs.b }
    }
}
impl Mul<f32> for Colorf {
    type Output = Colorf;
    /// Scale the color by the float
    fn mul(self, rhs: f32) -> Colorf {
        Colorf { r: self.r * rhs, g: self.g * rhs, b: self.b * rhs }
    }
}
impl Index<usize> for Colorf {
    type Output = f32;
    /// Access the channels by index
    ///
    /// - 0 = r
    /// - 1 = g
    /// - 2 = b
    fn index(&self, i: usize) -> &f32 {
        match i {
            0 => &self.r,
            1 => &self.g,
            2 => &self.b,
            _ => panic!("Invalid index into color"),
        }
    }
}
impl IndexMut<usize> for Colorf {
    /// Access the channels by index
    ///
    /// - 0 = r
    /// - 1 = g
    /// - 2 = b
    fn index_mut(&mut self, i: usize) -> &mut f32 {
        match i {
            0 => &mut self.r,
            1 => &mut self.g,
            2 => &mut self.b,
            _ => panic!("Invalid index into color"),
        }
    }
}

/// Evaluate the B-spline and plot it to the image buffer passed. The colors and points splines
/// should have the same t range.
fn plot_2d(spline: &bspline::BSpline<Point>, colors: &bspline::BSpline<Colorf>, plot: &mut [u8],
           plot_dim: (usize, usize), scale: (f32, f32), offset: (f32, f32), t_range: (f32, f32),
           show_control_pts: bool) {
    let step_size = 0.001;
    let steps = ((t_range.1 - t_range.0) / step_size) as usize;
    for s in 0..steps {
        let t = step_size * s as f32 + t_range.0;
        let pt = spline.point(t);
        let color = colors.point(t).to_srgb();
        let ix = ((pt.x + offset.0) * scale.0) as isize;
        let iy = ((pt.y + offset.1) * scale.1) as isize;
        for y in iy - 1..iy + 1 {
            for x in ix - 1..ix + 1 {
                if y >= 0 && y < plot_dim.1 as isize && x >= 0 && x < plot_dim.0 as isize {
                    let px = (plot_dim.1 - 1 - y as usize) * plot_dim.0 * 3 + x as usize * 3;
                    for i in 0..3 {
                        plot[px + i] = (color[i] * 255.0) as u8;
                    }
                }
            }
        }
    }
    // Draw the control points
    if show_control_pts {
        for pt in spline.control_points() {
            let ix = ((pt.x + offset.0) * scale.0) as isize;
            let iy = ((pt.y + offset.1) * scale.1) as isize;
            // Plot a 4x4 red marker for each control point
            for y in iy - 2..iy + 2 {
                for x in ix - 2..ix + 2 {
                    if y >= 0 && y < plot_dim.1 as isize && x >= 0 && x < plot_dim.0 as isize {
                        let px = (plot_dim.1 - 1 - y as usize) * plot_dim.0 * 3 + x as usize * 3;
                        plot[px] = 0;
                        plot[px + 1] = 0;
                        plot[px + 2] = 0;
                    }
                }
            }
        }
    }
}

/// Plot the text 'bspline' to create the logo for the library
fn main() {
    let points = vec![// Draw the b
                      Point::new(-4.0, 4.0), Point::new(-4.0, -1.0), Point::new(-4.0, -1.0),
                      Point::new(-2.0, 0.0), Point::new(-4.0, 1.35), Point::new(-4.0, 1.35),
                      // Draw the s
                      Point::new(-1.0, 1.5), Point::new(-1.0, 1.5), Point::new(-2.8, 1.0),
                      Point::new(-0.5, 0.0), Point::new(-2.5, -0.5), Point::new(-2.5, -0.5),
                      // Draw the p
                      Point::new(0.0, -1.0), Point::new(0.0, 1.8), Point::new(0.0, 1.8),
                      Point::new(0.0, 1.8), Point::new(-0.2, -1.8), Point::new(-0.2, -1.8),
                      Point::new(-0.2, -1.8), Point::new(0.25, 2.5), Point::new(1.5, 1.2),
                      Point::new(0.0, -0.5), Point::new(0.0, -0.5), Point::new(1.0, -0.3),
                      Point::new(1.0, -0.3), Point::new(2.5, 1.5),
                      // Draw the l
                      Point::new(2.0, 3.0), Point::new(2.0, 3.0), Point::new(1.5, 1.5),
                      Point::new(2.5, -0.5), Point::new(2.5, -0.5),
                      // Draw the i
                      Point::new(2.8, 1.3), Point::new(2.8, 1.3), Point::new(2.8, 1.3),
                      // Draw the n
                      Point::new(3.1, -0.5), Point::new(3.1, -0.5), Point::new(3.4, 1.2),
                      Point::new(3.4, 1.2), Point::new(3.4, 1.2), Point::new(3.4, -0.5),
                      Point::new(3.4, -0.5), Point::new(3.4, -0.5), Point::new(4.0, 2.3),
                      Point::new(4.2, -0.5), Point::new(4.2, -0.5),
                      // Draw the e
                      Point::new(5.5, 0.8), Point::new(5.5, 0.8), Point::new(4.5, 1.3), Point::new(4.5, -0.3),
                      Point::new(6.5, -0.3)];
    let knots = vec![0.0, 0.0, 0.0, 0.0, 1.0, 2.0, 3.0, 4.0, 4.0, 5.0, 6.0, 7.0, 7.0,
                     8.0, 9.0, 9.0, 9.0, 10.0, 10.0, 10.0, 11.0, 12.0, 13.0, 13.0,
                     14.0, 14.0, 15.0, 16.0, 16.0, 17.0, 18.0, 18.0, 19.0, 19.0, 19.0,
                     20.0, 20.0, 21.0, 21.0, 21.0, 22.0, 22.0, 22.0, 23.0, 24.0, 24.0,
                     25.0, 25.0, 26.0, 27.0, 28.0, 28.0, 28.0, 28.0];

    let colors = vec![Colorf::new(1.0, 0.0, 0.0), Colorf::new(1.0, 0.0, 0.0),
                      Colorf::new(0.0, 0.0, 1.0), Colorf::new(0.0, 1.0, 0.0),
                      Colorf::new(0.0, 1.0, 0.0)];
    let color_knots = vec![0.0, 0.0, 0.0, 14.0, 28.0, 28.0, 28.0];

    let t_start = knots[0];
    let t_end = knots[knots.len() - 1];

    let plot_dim = (720, 540);
    let scale = (plot_dim.0 as f32 / 14.0, plot_dim.1 as f32 / 10.0);
    let offset = (6.0, 5.0);

    let mut plot: Vec<_> = iter::repeat(255u8).take(plot_dim.0 * plot_dim.1 * 3).collect();

    let spline = bspline::BSpline::new(3, points, knots);
    let color_spline = bspline::BSpline::new(2, colors, color_knots);

    plot_2d(&spline, &color_spline, &mut plot[..], plot_dim, scale, offset, (t_start, t_end), false);
    match image::save_buffer("logo.png", &plot[..], plot_dim.0 as u32, plot_dim.1 as u32, image::RGB(8)) {
        Ok(_) => println!("B-spline logo saved to logo.png"),
        Err(e) => println!("Error saving logo.png,  {}", e),
    }
}

<commit_msg>Thicker stroke in the logo looks so much nicer<commit_after>extern crate image;
extern crate bspline;

use std::ops::{Mul, Add, Index, IndexMut};
use std::iter;

#[derive(Copy, Clone, Debug)]
struct Point {
    x: f32,
    y: f32,
}
impl Point {
    fn new(x: f32, y: f32) -> Point {
        Point { x: x, y: y }
    }
}
impl Mul<f32> for Point {
    type Output = Point;
    fn mul(self, rhs: f32) -> Point {
        Point { x: self.x * rhs, y: self.y * rhs }
    }
}
impl Add for Point {
    type Output = Point;
    fn add(self, rhs: Point) -> Point {
        Point { x: self.x + rhs.x, y: self.y + rhs.y }
    }
}

/// Clamp `x` to be between `min` and `max`
pub fn clamp<T: PartialOrd>(x: T, min: T, max: T) -> T {
    if x < min { min } else if x > max { max } else { x }
}

#[derive(Debug, Copy, Clone)]
pub struct Colorf {
    pub r: f32,
    pub g: f32,
    pub b: f32,
}

impl Colorf {
    /// Create an RGB color
    pub fn new(r: f32, g: f32, b: f32) -> Colorf {
        Colorf { r: r, g: g, b: b }
    }
    pub fn broadcast(x: f32) -> Colorf {
        Colorf { r: x, g: x, b: x }
    }
    /// Clamp the color values between [0, 1]
    pub fn clamp(&self) -> Colorf {
        Colorf { r: clamp(self.r, 0.0, 1.0),
                 g: clamp(self.g, 0.0, 1.0),
                 b: clamp(self.b, 0.0, 1.0) }
    }
    /// Convert the linear RGB color to sRGB
    pub fn to_srgb(&self) -> Colorf {
        let a = 0.055f32;
        let b = 1f32 / 2.4;
        let mut srgb = Colorf::broadcast(0.0);
        for i in 0..3 {
            if self[i] <= 0.0031308 {
                srgb[i] = 12.92 * self[i];
            } else {
                srgb[i] = (1.0 + a) * f32::powf(self[i], b) - a;
            }
        }
        srgb
    }
}
impl Add for Colorf {
    type Output = Colorf;
    /// Add two colors together
    fn add(self, rhs: Colorf) -> Colorf {
        Colorf { r: self.r + rhs.r, g: self.g + rhs.g, b: self.b + rhs.b }
    }
}
impl Mul<f32> for Colorf {
    type Output = Colorf;
    /// Scale the color by the float
    fn mul(self, rhs: f32) -> Colorf {
        Colorf { r: self.r * rhs, g: self.g * rhs, b: self.b * rhs }
    }
}
impl Index<usize> for Colorf {
    type Output = f32;
    /// Access the channels by index
    ///
    /// - 0 = r
    /// - 1 = g
    /// - 2 = b
    fn index(&self, i: usize) -> &f32 {
        match i {
            0 => &self.r,
            1 => &self.g,
            2 => &self.b,
            _ => panic!("Invalid index into color"),
        }
    }
}
impl IndexMut<usize> for Colorf {
    /// Access the channels by index
    ///
    /// - 0 = r
    /// - 1 = g
    /// - 2 = b
    fn index_mut(&mut self, i: usize) -> &mut f32 {
        match i {
            0 => &mut self.r,
            1 => &mut self.g,
            2 => &mut self.b,
            _ => panic!("Invalid index into color"),
        }
    }
}

/// Evaluate the B-spline and plot it to the image buffer passed. The colors and points splines
/// should have the same t range.
fn plot_2d(spline: &bspline::BSpline<Point>, colors: &bspline::BSpline<Colorf>, plot: &mut [u8],
           plot_dim: (usize, usize), scale: (f32, f32), offset: (f32, f32), t_range: (f32, f32),
           show_control_pts: bool) {
    let step_size = 0.001;
    let steps = ((t_range.1 - t_range.0) / step_size) as usize;
    for s in 0..steps {
        let t = step_size * s as f32 + t_range.0;
        let pt = spline.point(t);
        let color = colors.point(t).to_srgb();
        let ix = ((pt.x + offset.0) * scale.0) as isize;
        let iy = ((pt.y + offset.1) * scale.1) as isize;
        for y in iy - 4..iy + 4 {
            for x in ix - 4..ix + 4 {
                if y >= 0 && y < plot_dim.1 as isize && x >= 0 && x < plot_dim.0 as isize {
                    let px = (plot_dim.1 - 1 - y as usize) * plot_dim.0 * 3 + x as usize * 3;
                    for i in 0..3 {
                        plot[px + i] = (color[i] * 255.0) as u8;
                    }
                }
            }
        }
    }
    // Draw the control points
    if show_control_pts {
        for pt in spline.control_points() {
            let ix = ((pt.x + offset.0) * scale.0) as isize;
            let iy = ((pt.y + offset.1) * scale.1) as isize;
            // Plot a black marker for each control point
            for y in iy - 3..iy + 3 {
                for x in ix - 3..ix + 3 {
                    if y >= 0 && y < plot_dim.1 as isize && x >= 0 && x < plot_dim.0 as isize {
                        let px = (plot_dim.1 - 1 - y as usize) * plot_dim.0 * 3 + x as usize * 3;
                        plot[px] = 0;
                        plot[px + 1] = 0;
                        plot[px + 2] = 0;
                    }
                }
            }
        }
    }
}

/// Plot the text 'bspline' to create the logo for the library
fn main() {
    let points = vec![// Draw the b
                      Point::new(-4.0, 4.0), Point::new(-4.0, -1.0), Point::new(-4.0, -1.0),
                      Point::new(-2.0, 0.0), Point::new(-4.0, 1.35), Point::new(-4.0, 1.35),
                      // Draw the s
                      Point::new(-1.0, 1.5), Point::new(-1.0, 1.5), Point::new(-2.8, 1.0),
                      Point::new(-0.5, 0.0), Point::new(-2.5, -0.5), Point::new(-2.5, -0.5),
                      // Draw the p
                      Point::new(0.0, -1.0), Point::new(0.0, 1.8), Point::new(0.0, 1.8),
                      Point::new(0.0, 1.8), Point::new(-0.2, -1.8), Point::new(-0.2, -1.8),
                      Point::new(-0.2, -1.8), Point::new(0.25, 2.5), Point::new(1.5, 1.2),
                      Point::new(0.0, -0.5), Point::new(0.0, -0.5), Point::new(1.0, -0.3),
                      Point::new(1.0, -0.3), Point::new(2.5, 1.5),
                      // Draw the l
                      Point::new(2.0, 3.0), Point::new(2.0, 3.0), Point::new(1.5, 1.5),
                      Point::new(2.5, -0.5), Point::new(2.5, -0.5),
                      // Draw the i
                      Point::new(2.8, 1.3), Point::new(2.8, 1.3), Point::new(2.8, 1.3),
                      // Draw the n
                      Point::new(3.1, -0.5), Point::new(3.1, -0.5), Point::new(3.4, 1.2),
                      Point::new(3.4, 1.2), Point::new(3.4, 1.2), Point::new(3.4, -0.5),
                      Point::new(3.4, -0.5), Point::new(3.4, -0.5), Point::new(4.0, 2.3),
                      Point::new(4.2, -0.5), Point::new(4.2, -0.5),
                      // Draw the e
                      Point::new(5.5, 0.8), Point::new(5.5, 0.8), Point::new(4.5, 1.3), Point::new(4.5, -0.3),
                      Point::new(6.5, -0.3)];
    let knots = vec![0.0, 0.0, 0.0, 0.0, 1.0, 2.0, 3.0, 4.0, 4.0, 5.0, 6.0, 7.0, 7.0,
                     8.0, 9.0, 9.0, 9.0, 10.0, 10.0, 10.0, 11.0, 12.0, 13.0, 13.0,
                     14.0, 14.0, 15.0, 16.0, 16.0, 17.0, 18.0, 18.0, 19.0, 19.0, 19.0,
                     20.0, 20.0, 21.0, 21.0, 21.0, 22.0, 22.0, 22.0, 23.0, 24.0, 24.0,
                     25.0, 25.0, 26.0, 27.0, 28.0, 28.0, 28.0, 28.0];

    let colors = vec![Colorf::new(1.0, 0.0, 0.0), Colorf::new(1.0, 0.0, 0.0),
                      Colorf::new(0.0, 0.0, 1.0), Colorf::new(0.0, 1.0, 0.0),
                      Colorf::new(0.0, 1.0, 0.0)];
    let color_knots = vec![0.0, 0.0, 0.0, 14.0, 28.0, 28.0, 28.0];

    let t_start = knots[0];
    let t_end = knots[knots.len() - 1];

    let plot_dim = (720, 540);
    let scale = (plot_dim.0 as f32 / 14.0, plot_dim.1 as f32 / 10.0);
    let offset = (6.0, 5.0);

    let mut plot: Vec<_> = iter::repeat(255u8).take(plot_dim.0 * plot_dim.1 * 3).collect();

    let spline = bspline::BSpline::new(3, points, knots);
    let color_spline = bspline::BSpline::new(2, colors, color_knots);

    plot_2d(&spline, &color_spline, &mut plot[..], plot_dim, scale, offset, (t_start, t_end), false);
    match image::save_buffer("logo.png", &plot[..], plot_dim.0 as u32, plot_dim.1 as u32, image::RGB(8)) {
        Ok(_) => println!("B-spline logo saved to logo.png"),
        Err(e) => println!("Error saving logo.png,  {}", e),
    }
}

<|endoftext|>


<commit_before>// normalize-stderr-test "std::option::Option<\[u32; \d+\]>" -> "TYPE"
// normalize-stderr-test "\[u32; \d+\]" -> "TYPE"

#[cfg(target_pointer_width = "32")]
fn main() {
    let big: Option<[u32; (1<<29)-1]> = None;
}

#[cfg(target_pointer_width = "64")]
fn main() {
    let big: Option<[u32; (1<<45)-1]> = None;
}
<commit_msg>huge-enum also exhibits this error<commit_after>// normalize-stderr-test "std::option::Option<\[u32; \d+\]>" -> "TYPE"
// normalize-stderr-test "\[u32; \d+\]" -> "TYPE"

// FIXME https://github.com/rust-lang/rust/issues/59774
// normalize-stderr-test "thread.*panicked.*Metadata module not compiled.*\n" -> ""
// normalize-stderr-test "note:.*RUST_BACKTRACE=1.*\n" -> ""

#[cfg(target_pointer_width = "32")]
fn main() {
    let big: Option<[u32; (1<<29)-1]> = None;
}

#[cfg(target_pointer_width = "64")]
fn main() {
    let big: Option<[u32; (1<<45)-1]> = None;
}
<|endoftext|>


<commit_before>use std::fmt::Write;

/* For branch name dedup */
use std::collections::HashSet;

use util::*;

use std::process;

use timesheet::traits::HasHTML;

#[derive(Serialize, Deserialize, Debug)]
pub enum EventType {
    Pause,
    Resume,
    Note,
    Commit { hash: String },
}

#[derive(Serialize, Deserialize, Debug)]
struct Event {
    timestamp : u64,
    note      : Option<String>,
    ty        : EventType
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Session {
    pub start    : u64,
    pub end      : u64,
    running  : bool,
    branches : HashSet<String>,
    events   : Vec<Event>,
}

impl Session {
    pub fn new(timestamp: Option<u64>) -> Session {
        let timestamp = match timestamp {
            Some(timestamp) => timestamp,
            None => get_seconds(),
        };
        Session {
            start    : timestamp,
            end      : timestamp + 1,
            running  : true,
            branches : HashSet::<String>::new(),
            events   : Vec::<Event>::new(),
        }
    }

    pub fn is_running(&self) -> bool {
        self.running
    }

    pub fn is_paused(&self) -> bool {
        match self.events.len() {
            0 => false,
            n => {
                match self.events[n - 1].ty {
                    EventType::Pause => true,
                    _ => false,
                }
            }
        }
    }

    pub fn update_end(&mut self) {
        self.end = match self.events.len() {
            0 => self.end,
            n => &self.events[n - 1].timestamp + 1,
        }
    }

    pub fn finalize(&mut self, timestamp: Option<u64>) {
        let timestamp = match timestamp {
            None => get_seconds(),
            Some(timestamp) => {
                let is_valid_ts = match self.events.len() {
                    0 => timestamp > self.start,
                    n => {
                        let last_ev = &self.events[n - 1];
                        timestamp > last_ev.timestamp
                    }
                };
                if is_valid_ts {
                    timestamp
                } else {
                    println!("That is not a valid timestamp!");
                    process::exit(0);
                }
            }
        };
        if self.is_running() {
            if self.is_paused() {
                self.push_event(Some(timestamp), None, EventType::Resume);
            }
            self.running = false;
            self.end = timestamp + 1;
        }
    }

    pub fn push_event(&mut self,
                  timestamp : Option<u64>,
                  note      : Option<String>,
                  type_of_event : EventType)
                  -> bool {
        /* Cannot push if session is already finalized. */
        if !self.is_running() {
            println!("Already finalized, cannot push event.");
            return false;
        }

        let timestamp = match timestamp {
            None => {
                let now = get_seconds();
                self.end = now;
                now
            }
            Some(timestamp) => {
                let is_valid_ts = match self.events.len() {
                    0 => timestamp > self.start,
                    n => timestamp > self.events[n - 1].timestamp,
                };
                if is_valid_ts {
                    self.end = timestamp + 1;
                    timestamp
                } else {
                    println!("That timestamp is before the last event.");
                    return false;
                }
            }
        };
        /* TODO: improve logic */
        match type_of_event {
            // TODO: fix this, so both note and ago work...
            EventType::Pause => {
                if self.is_paused() {
                    println!("Already paused.");
                    false
                } else {
                    self.events
                        .push(Event {
                                  timestamp : timestamp,
                                  note      : note,
                                  ty      : EventType::Pause,
                              });
                    true
                }
            }
            EventType::Resume => {
                if !self.is_paused() {
                    println!("Currently not paused.");
                    false
                } else {
                    self.events
                        .push(Event {
                                  timestamp : timestamp,
                                  note      : note,
                                  ty        : EventType::Resume,
                              });
                    true
                }
            }
            EventType::Note => {
                if self.is_paused() {
                    /* Add note to previous pause (last of events vec) */
                    /* If self.is_paused(), then self.len() is always at least 1 */
                    let len = self.events.len();
                    let pause = &mut self.events[len - 1];
                    match pause.note {
                        Some(ref mut already) => {
                            // TODO: handle long strings (also in other types)
                            // TODO: there must be another way other than

                            already.push_str("
");
                            already.push_str(&note.unwrap());
                        }
                        None => pause.note = note,
                    }
                } else {
                    self.events
                        .push(Event {
                                  timestamp : timestamp,
                                  note      : note,
                                  ty        : EventType::Note,
                              })
                };
                true
            }
            /* Commit adding possible only in present */
            EventType::Commit { hash } => {
                if self.is_paused() {
                    self.push_event(None, None, EventType::Resume);
                }
                /* Commit message must be provided */
                if note.is_none() {
                    println!("No commit message found for commit {}.", hash);
                }
                self.events
                    .push(Event {
                              timestamp : get_seconds(),
                              note      : note,
                              ty        : EventType::Commit { hash },
                          });
                true
            }
        }
    }

    pub fn pause_time(&self) -> u64 {
        let mut pause_time = 0;
        let mut last_pause_ts = 0;
        for event in &self.events {
            match event.ty {
                EventType::Pause => last_pause_ts = event.timestamp,
                EventType::Resume => pause_time += event.timestamp - last_pause_ts,
                _ => {}
            }
        }
        pause_time
    }

    pub fn working_time(&self) -> u64 {
        let pause_time = self.pause_time();
        self.end - self.start - pause_time
    }

    pub fn add_branch(&mut self, name: String) {
        if self.is_running() {
            self.branches.insert(name);
        }
    }

    pub fn status(&self) -> String {
        let mut status = String::new();
        write!(&mut status,
               r#"Session running since {}.
"#,
               sec_to_hms_string(get_seconds() - self.start))
                .unwrap();
        if self.is_paused() {
            write!(&mut status,
                   r#"    Paused since {}.
"#,
                   sec_to_hms_string(get_seconds() - self.events[self.events.len() - 1].timestamp))
                    .unwrap();
        } else {
            match self.events.len() {
                0 => write!(&mut status, "    No events in this session yet!\n").unwrap(),
                n => {
                    write!(&mut status,
                           "    Last event: {:?}, {} ago.\n",
                           &self.events[n - 1].ty,
                           sec_to_hms_string(get_seconds() - self.events[n - 1].timestamp))
                            .unwrap()
                }
            }
        }
        let branch_str = match self.branches.len() {
            0 => String::new(),
            n => {
                self.branches.iter().fold(
                    format!("Worked on {} branches: ", n),
                    |res, s| res + s + " "
                )
            }
        };
        status.push_str(&branch_str);
        status
    }
}

impl HasHTML for Event {
    fn to_html(&self) -> String {
        match self.ty {
            EventType::Pause => {
                match self.note {
                    Some(ref info) => {
                        format!(r#"
{}: Started a pause

{}

"#,
                                ts_to_date(self.timestamp),
                                info.clone())
                    }
                    None => {
                        format!(r#"
{}: Started a pause

"#,
                                ts_to_date(self.timestamp))
                    }
                }
            }
            EventType::Resume => {
                format!(r#"
{}: Resumed work

"#,
                        ts_to_date(self.timestamp))
            }
            /* An EventType::Note note is a Some because it's
             * 'constructor' function takes a String
             * (and not Option<String>)
             */
            EventType::Note => {
                match self.note {
                    Some(ref text) => {
                        format!(r#"
{}: Note: {
}

"#,
                                ts_to_date(self.timestamp),
                                text)
                    }
                    None => unreachable!(),
                }
            }
            /* It is safe to unwrap an EventType::Commit note because if
             * a commit has no message something went really wrong
             * (like parsing the output of `git log` in git_commit_message()
             */
            EventType::Commit { ref hash } => {
                match self.note {
                    Some(ref text) => {
                        format!(r#"
{}: Commit id: {}

message: {}

"#,
                                ts_to_date(self.timestamp),
                                hash,
                                text)
                    }
                    None => unreachable!(),
                }
            }
        }
    }
}

impl HasHTML for Session {
    fn to_html(&self) -> String {
        let mut html = format!(r#"

Session on {}
"#,
                               ts_to_date(self.start));

        for event in &self.events {
            html.push_str(&event.to_html());
        }

        write!(&mut html,
               r#"
Ended on {}
"#,
               ts_to_date(self.end))
                .unwrap();

        let mut branch_str = String::new();
        match self.branches.len() {
            0 => {}
            n => {
                write!(&mut branch_str, "Worked on {} branches: ", n).unwrap();
                for branch in &self.branches {
                    write!(&mut branch_str, "{} ", branch).unwrap();
                }
            }
        };

        write!(&mut html,
               r#"

{}

Worked for {}

Paused for {}

"#,
               branch_str,
               sec_to_hms_string(self.working_time()),
               sec_to_hms_string(self.pause_time()))
                .unwrap();

        write!(&mut html, "
").unwrap();
        html
    }
}<commit_msg>simplify string interpolation<commit_after>use std::fmt::Write;

/* For branch name dedup */
use std::collections::HashSet;

use util::*;

use std::process;

use timesheet::traits::HasHTML;

#[derive(Serialize, Deserialize, Debug)]
pub enum EventType {
    Pause,
    Resume,
    Note,
    Commit { hash: String },
}

#[derive(Serialize, Deserialize, Debug)]
struct Event {
    timestamp : u64,
    note      : Option<String>,
    ty        : EventType
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Session {
    pub start    : u64,
    pub end      : u64,
    running  : bool,
    branches : HashSet<String>,
    events   : Vec<Event>,
}

impl Session {
    pub fn new(timestamp: Option<u64>) -> Session {
        let timestamp = match timestamp {
            Some(timestamp) => timestamp,
            None => get_seconds(),
        };
        Session {
            start    : timestamp,
            end      : timestamp + 1,
            running  : true,
            branches : HashSet::<String>::new(),
            events   : Vec::<Event>::new(),
        }
    }

    pub fn is_running(&self) -> bool {
        self.running
    }

    pub fn is_paused(&self) -> bool {
        match self.events.len() {
            0 => false,
            n => {
                match self.events[n - 1].ty {
                    EventType::Pause => true,
                    _ => false,
                }
            }
        }
    }

    pub fn update_end(&mut self) {
        self.end = match self.events.len() {
            0 => self.end,
            n => &self.events[n - 1].timestamp + 1,
        }
    }

    pub fn finalize(&mut self, timestamp: Option<u64>) {
        let timestamp = match timestamp {
            None => get_seconds(),
            Some(timestamp) => {
                let is_valid_ts = match self.events.len() {
                    0 => timestamp > self.start,
                    n => {
                        let last_ev = &self.events[n - 1];
                        timestamp > last_ev.timestamp
                    }
                };
                if is_valid_ts {
                    timestamp
                } else {
                    println!("That is not a valid timestamp!");
                    process::exit(0);
                }
            }
        };
        if self.is_running() {
            if self.is_paused() {
                self.push_event(Some(timestamp), None, EventType::Resume);
            }
            self.running = false;
            self.end = timestamp + 1;
        }
    }

    pub fn push_event(&mut self,
                  timestamp : Option<u64>,
                  note      : Option<String>,
                  type_of_event : EventType)
                  -> bool {
        /* Cannot push if session is already finalized. */
        if !self.is_running() {
            println!("Already finalized, cannot push event.");
            return false;
        }

        let timestamp = match timestamp {
            None => {
                let now = get_seconds();
                self.end = now;
                now
            }
            Some(timestamp) => {
                let is_valid_ts = match self.events.len() {
                    0 => timestamp > self.start,
                    n => timestamp > self.events[n - 1].timestamp,
                };
                if is_valid_ts {
                    self.end = timestamp + 1;
                    timestamp
                } else {
                    println!("That timestamp is before the last event.");
                    return false;
                }
            }
        };
        /* TODO: improve logic */
        match type_of_event {
            // TODO: fix this, so both note and ago work...
            EventType::Pause => {
                if self.is_paused() {
                    println!("Already paused.");
                    false
                } else {
                    self.events
                        .push(Event {
                                  timestamp : timestamp,
                                  note      : note,
                                  ty      : EventType::Pause,
                              });
                    true
                }
            }
            EventType::Resume => {
                if !self.is_paused() {
                    println!("Currently not paused.");
                    false
                } else {
                    self.events
                        .push(Event {
                                  timestamp : timestamp,
                                  note      : note,
                                  ty        : EventType::Resume,
                              });
                    true
                }
            }
            EventType::Note => {
                if self.is_paused() {
                    /* Add note to previous pause (last of events vec) */
                    /* If self.is_paused(), then self.len() is always at least 1 */
                    let len = self.events.len();
                    let pause = &mut self.events[len - 1];
                    match pause.note {
                        Some(ref mut already) => {
                            // TODO: handle long strings (also in other types)
                            // TODO: there must be another way other than

                            already.push_str("
");
                            already.push_str(&note.unwrap());
                        }
                        None => pause.note = note,
                    }
                } else {
                    self.events
                        .push(Event {
                                  timestamp : timestamp,
                                  note      : note,
                                  ty        : EventType::Note,
                              })
                };
                true
            }
            /* Commit adding possible only in present */
            EventType::Commit { hash } => {
                if self.is_paused() {
                    self.push_event(None, None, EventType::Resume);
                }
                /* Commit message must be provided */
                if note.is_none() {
                    println!("No commit message found for commit {}.", hash);
                }
                self.events
                    .push(Event {
                              timestamp : get_seconds(),
                              note      : note,
                              ty        : EventType::Commit { hash },
                          });
                true
            }
        }
    }

    pub fn pause_time(&self) -> u64 {
        let mut pause_time = 0;
        let mut last_pause_ts = 0;
        for event in &self.events {
            match event.ty {
                EventType::Pause => last_pause_ts = event.timestamp,
                EventType::Resume => pause_time += event.timestamp - last_pause_ts,
                _ => {}
            }
        }
        pause_time
    }

    pub fn working_time(&self) -> u64 {
        let pause_time = self.pause_time();
        self.end - self.start - pause_time
    }

    pub fn add_branch(&mut self, name: String) {
        if self.is_running() {
            self.branches.insert(name);
        }
    }

    pub fn status(&self) -> String {
        let mut status = String::new();
        write!(&mut status, "Session running since {}.\n",
               sec_to_hms_string(get_seconds() - self.start))
                .unwrap();
        if self.is_paused() {
            write!(&mut status, "    Paused since {}.\n",
                   sec_to_hms_string(get_seconds() - self.events[self.events.len() - 1].timestamp))
                    .unwrap();
        } else {
            match self.events.len() {
                0 => write!(&mut status, "    No events in this session yet!\n").unwrap(),
                n => {
                    write!(&mut status,
                           "    Last event: {:?}, {} ago.\n",
                           &self.events[n - 1].ty,
                           sec_to_hms_string(get_seconds() - self.events[n - 1].timestamp))
                            .unwrap()
                }
            }
        }
        let branch_str = match self.branches.len() {
            0 => String::new(),
            n => {
                self.branches.iter().fold(
                    format!("Worked on {} branches: ", n),
                    |res, s| res + s + " "
                )
            }
        };
        status.push_str(&branch_str);
        status
    }
}

impl HasHTML for Event {
    fn to_html(&self) -> String {
        match self.ty {
            EventType::Pause => {
                match self.note {
                    Some(ref info) => {
                        format!(r#"
{}: Started a pause

{}

"#,
                                ts_to_date(self.timestamp),
                                info.clone())
                    }
                    None => {
                        format!(r#"
{}: Started a pause

"#,
                                ts_to_date(self.timestamp))
                    }
                }
            }
            EventType::Resume => {
                format!(r#"
{}: Resumed work

"#,
                        ts_to_date(self.timestamp))
            }
            /* An EventType::Note note is a Some because it's
             * 'constructor' function takes a String
             * (and not Option<String>)
             */
            EventType::Note => {
                match self.note {
                    Some(ref text) => {
                        format!(r#"
{}: Note: {
}

"#,
                                ts_to_date(self.timestamp),
                                text)
                    }
                    None => unreachable!(),
                }
            }
            /* It is safe to unwrap an EventType::Commit note because if
             * a commit has no message something went really wrong
             * (like parsing the output of `git log` in git_commit_message()
             */
            EventType::Commit { ref hash } => {
                match self.note {
                    Some(ref text) => {
                        format!(r#"
{}: Commit id: {}

message: {}

"#,
                                ts_to_date(self.timestamp),
                                hash,
                                text)
                    }
                    None => unreachable!(),
                }
            }
        }
    }
}

impl HasHTML for Session {
    fn to_html(&self) -> String {
        let mut html = format!(r#"

Session on {}
"#,
                               ts_to_date(self.start));

        for event in &self.events {
            html.push_str(&event.to_html());
        }

        write!(&mut html,
               r#"
Ended on {}
"#,
               ts_to_date(self.end))
                .unwrap();

        let mut branch_str = String::new();
        match self.branches.len() {
            0 => {}
            n => {
                write!(&mut branch_str, "Worked on {} branches: ", n).unwrap();
                for branch in &self.branches {
                    write!(&mut branch_str, "{} ", branch).unwrap();
                }
            }
        };

        write!(&mut html,
               r#"

{}

Worked for {}

Paused for {}

"#,
               branch_str,
               sec_to_hms_string(self.working_time()),
               sec_to_hms_string(self.pause_time()))
                .unwrap();

        write!(&mut html, "
").unwrap();
        html
    }
}<|endoftext|>


<commit_before>use crate::errors::incompatible_language;

use super::{Kernel, KernelTrait};
use async_trait::async_trait;
use eyre::{bail, Result};
use once_cell::sync::Lazy;
use regex::Regex;
use schemars::JsonSchema;
use serde::Serialize;
use std::collections::BTreeMap;
use stencila_schema::Node;

#[derive(Debug, Clone, Default, JsonSchema, Serialize)]
#[schemars(deny_unknown_fields)]
pub struct CalcKernel {
    #[serde(skip)]
    symbols: BTreeMap<String, f64>,
}

impl CalcKernel {
    pub fn create() -> Kernel {
        Kernel::Calc(CalcKernel::default())
    }
}

#[async_trait]
impl KernelTrait for CalcKernel {
    fn language(&self, language: Option<String>) -> Result<String> {
        let canonical = Ok("calc".to_string());
        match language.as_deref() {
            Some("calc") => canonical,
            Some(language) => bail!(incompatible_language::<Self>(language)),
            None => canonical,
        }
    }

    async fn get(&self, name: &str) -> Result<Node> {
        match self.symbols.get(name) {
            Some(number) => Ok(Node::Number(*number)),
            None => bail!("Symbol `{}` does not exist in this kernel", name),
        }
    }

    async fn set(&mut self, name: &str, value: Node) -> Result<()> {
        let value = match value {
            Node::Number(number) => number,
            _ => bail!("Unable to convert node to a number"),
        };
        self.symbols.insert(name.to_string(), value);
        Ok(())
    }

    async fn exec(&mut self, code: &str) -> Result<Vec<Node>> {
        static STATEMENTS_REGEX: Lazy<Regex> =
            Lazy::new(|| Regex::new(r"\r?\n|;").expect("Unable to create regex"));
        static ASSIGN_REGEX: Lazy<Regex> = Lazy::new(|| {
            Regex::new(r"\s*([a-zA-Z_][a-zA-Z_0-9]*)\s*=(.*)").expect("Unable to create regex")
        });

        let mut outputs = Vec::new();
        for statement in STATEMENTS_REGEX.split(code) {
            let statement = statement.trim();

            // Skip the statement if it is blank or is a comment
            if statement.is_empty() || statement.starts_with('#') {
                continue;
            }

            // Get the name of any assigned variable, and the expression to be evaluated
            let (symbol, expr) = if let Some(captures) = ASSIGN_REGEX.captures(statement) {
                let symbol = captures.get(1).expect("Should always have group 1");
                let expr = captures.get(2).expect("Should always have group 2");
                (Some(symbol.as_str()), expr.as_str())
            } else {
                (None, statement)
            };

            // Evaluate the expression, and assign it or add it to outputs
            let num = fasteval::ez_eval(expr, &mut self.symbols)?;
            if let Some(symbol) = symbol {
                self.symbols.insert(symbol.to_string(), num);
            } else {
                outputs.push(Node::Number(num))
            }
        }
        Ok(outputs)
    }
}
<commit_msg>feat(Calc kernel): Improve error messages<commit_after>use super::{Kernel, KernelTrait};
use crate::errors::incompatible_language;
use async_trait::async_trait;
use eyre::{bail, Result};
use fasteval::{ez_eval, Error};
use once_cell::sync::Lazy;
use regex::Regex;
use schemars::JsonSchema;
use serde::Serialize;
use std::collections::BTreeMap;
use stencila_schema::Node;

#[derive(Debug, Clone, Default, JsonSchema, Serialize)]
#[schemars(deny_unknown_fields)]
pub struct CalcKernel {
    #[serde(skip)]
    symbols: BTreeMap<String, f64>,
}

impl CalcKernel {
    pub fn create() -> Kernel {
        Kernel::Calc(CalcKernel::default())
    }
}

#[async_trait]
impl KernelTrait for CalcKernel {
    fn language(&self, language: Option<String>) -> Result<String> {
        let canonical = Ok("calc".to_string());
        match language.as_deref() {
            Some("calc") => canonical,
            Some(language) => bail!(incompatible_language::<Self>(language)),
            None => canonical,
        }
    }

    async fn get(&self, name: &str) -> Result<Node> {
        match self.symbols.get(name) {
            Some(number) => Ok(Node::Number(*number)),
            None => bail!("Symbol `{}` does not exist in this kernel", name),
        }
    }

    async fn set(&mut self, name: &str, value: Node) -> Result<()> {
        let value = match value {
            Node::Number(number) => number,
            _ => bail!("Unable to convert node to a number"),
        };
        self.symbols.insert(name.to_string(), value);
        Ok(())
    }

    async fn exec(&mut self, code: &str) -> Result<Vec<Node>> {
        static STATEMENTS_REGEX: Lazy<Regex> =
            Lazy::new(|| Regex::new(r"\r?\n|;").expect("Unable to create regex"));
        static ASSIGN_REGEX: Lazy<Regex> = Lazy::new(|| {
            Regex::new(r"\s*([a-zA-Z_][a-zA-Z_0-9]*)\s*=(.*)").expect("Unable to create regex")
        });

        let mut outputs = Vec::new();
        for statement in STATEMENTS_REGEX.split(code) {
            let statement = statement.trim();

            // Skip the statement if it is blank or is a comment
            if statement.is_empty() || statement.starts_with('#') {
                continue;
            }

            // Get the name of any assigned variable, and the expression to be evaluated
            let (symbol, expr) = if let Some(captures) = ASSIGN_REGEX.captures(statement) {
                let symbol = captures.get(1).expect("Should always have group 1");
                let expr = captures.get(2).expect("Should always have group 2");
                (Some(symbol.as_str()), expr.as_str())
            } else {
                (None, statement)
            };

            // Evaluate the expression
            let num = match ez_eval(expr, &mut self.symbols) {
                Ok(num) => num,
                Err(error) => {
                    let error = match error {
                        // Custom error strings for common errors
                        Error::EOF | Error::EofWhileParsing(..) => {
                            "Unexpected end of Calc expression".to_string()
                        }
                        Error::Undefined(name) => {
                            format!("Undefined variable or function: {}", name)
                        }
                        Error::WrongArgs(msg) => {
                            format!("Function called with wrong number of arguments: {}", msg)
                        }
                        Error::InvalidValue => "Unexpected value in expression".to_string(),
                        Error::TooLong => "Calc expression was too long".to_string(),
                        Error::TooDeep => "Calc expression was too recursive".to_string(),
                        // Use the debug string for others
                        _ => format!("Could not execute Calc expression: {:?}", error),
                    };
                    bail!(error)
                }
            };

            // Either assign it, or add it to outputs
            if let Some(symbol) = symbol {
                self.symbols.insert(symbol.to_string(), num);
            } else {
                outputs.push(Node::Number(num))
            }
        }
        Ok(outputs)
    }
}
<|endoftext|>


<commit_before>use std::collections::hash_map::Entry::{Occupied, Vacant};

use ggez::nalgebra::{Point2, Vector2};
use ggez::{graphics, Context, GameResult};
use specs::{Join, World, WorldExt};
use warmy::SimpleKey;

use crate::animation::{Image, ImageType};
use crate::combat::components::{Draw, Palette, Position};
use crate::components::RenderOrder;
use crate::game::Game;
use crate::objects::TextureAtlas;
use crate::piv::Colour;

pub fn draw_entities(
    specs_world: &World,
    default_palette: &[Colour],
    background: Option<&graphics::Canvas>,
    game: &mut Game,
    ctx: &mut Context,
) -> GameResult<()> {
    let render_order_storage = specs_world.read_storage::<RenderOrder>();
    let position_storage = specs_world.read_storage::<Position>();
    let draw_storage = specs_world.read_storage::<Draw>();
    let entities = specs_world.entities();

    let palette_storage = specs_world.read_storage::<Palette>();

    let mut storage = (
        &render_order_storage,
        &position_storage,
        &draw_storage,
        &entities,
    )
        .join()
        .collect::<Vec<_>>();
    storage.sort_by(|&a, &b| a.0.depth.cmp(&b.0.depth));

    for (_, position, draw, entity) in storage {
        let images: Vec<&Image> = if game.gore_on {
            draw.frame.images.iter().collect()
        } else {
            draw.frame.images.iter().filter(|i| !i.is_blood()).collect()
        };
        for image in images {
            let atlas = game
                .store
                .get::<TextureAtlas>(&SimpleKey::from(image.sheet.as_str()), ctx)
                // TODO fix error handling, make this ?
                .expect("error loading texture atlas when drawing");

            let atlas_dimension = atlas.borrow().image.width as u32;
            // TODO: change with palettes
            let palette: Option<&Palette> = palette_storage.get(entity);
            let ggez_image = match palette {
                None => match game.images.entry(image.sheet.clone()) {
                    Occupied(i) => i.into_mut(),
                    Vacant(i) => i.insert(
                        graphics::Image::from_rgba8(
                            ctx,
                            atlas_dimension as u16,
                            atlas_dimension as u16,
                            &atlas.borrow().image.to_rgba8(&default_palette),
                        )
                        .unwrap(),
                    ),
                },
                Some(palette) => {
                    let image_name = [image.sheet.clone(), palette.name.clone()].join("-");
                    match game.images.entry(image_name) {
                        Occupied(i) => i.into_mut(),
                        Vacant(i) => i.insert(
                            graphics::Image::from_rgba8(
                                ctx,
                                atlas_dimension as u16,
                                atlas_dimension as u16,
                                &atlas.borrow().image.to_rgba8(&palette.palette),
                            )
                            .unwrap(),
                        ),
                    }
                }
            };

            let rect = atlas.borrow().rects[image.image];
            let texture_size = atlas.borrow().image.width as f32;
            let draw_params = graphics::DrawParam::default()
                .src(graphics::Rect {
                    x: rect.x as f32 / texture_size,
                    y: rect.y as f32 / texture_size,
                    w: rect.w as f32 / texture_size,
                    h: rect.h as f32 / texture_size,
                })
                .dest(Point2::new(
                    (position.x as i32 + (draw.direction as i32 * image.x)) as f32,
                    (position.y as i32 + image.y) as f32,
                ))
                .scale(Vector2::new(draw.direction as i32 as f32, 1.0));
            // .scale(Vector2::new((draw.direction as i32 * 3) as f32, 3.0));
            // .dest(Point2::new(
            //     (position.x as i32 + (draw.direction as i32 * image.x)) as f32 * 3.0,
            //     (position.y as i32 + image.y) as f32 * 3.0,
            // ))
            // .scale(Vector2::new((draw.direction as i32 * 3) as f32, 3.0));
            graphics::draw(ctx, ggez_image, draw_params)?;
            if let ImageType::BloodStain = image.image_type {
                if let Some(b) = background {
                    graphics::pop_transform(ctx);
                    graphics::apply_transformations(ctx);

                    graphics::set_canvas(ctx, Some(&*b));
                    graphics::draw(ctx, ggez_image, draw_params)?;
                    graphics::set_canvas(ctx, None);

                    let scale_matrix = graphics::DrawParam::default()
                        .scale(game.screen_scale)
                        .to_matrix();
                    graphics::push_transform(ctx, Some(scale_matrix));
                    graphics::apply_transformations(ctx);
                }
            }
        }
    }
    Ok(())
}
<commit_msg>minor rendering refactor<commit_after>use std::collections::hash_map::Entry::{Occupied, Vacant};

use ggez::nalgebra::{Point2, Vector2};
use ggez::{graphics, Context, GameResult};
use specs::{Join, World, WorldExt};
use warmy::SimpleKey;

use crate::animation::{Image, ImageType};
use crate::combat::components::{Draw, Palette, Position};
use crate::components::RenderOrder;
use crate::game::Game;
use crate::objects::TextureAtlas;
use crate::piv::Colour;

pub fn draw_entities(
    specs_world: &World,
    default_palette: &[Colour],
    background: Option<&graphics::Canvas>,
    game: &mut Game,
    ctx: &mut Context,
) -> GameResult<()> {
    let render_order_storage = specs_world.read_storage::<RenderOrder>();
    let position_storage = specs_world.read_storage::<Position>();
    let draw_storage = specs_world.read_storage::<Draw>();
    let entities = specs_world.entities();

    let palette_storage = specs_world.read_storage::<Palette>();

    let mut storage = (
        &render_order_storage,
        &position_storage,
        &draw_storage,
        &entities,
    )
        .join()
        .collect::<Vec<_>>();
    storage.sort_by(|&a, &b| a.0.depth.cmp(&b.0.depth));

    for (_, position, draw, entity) in storage {
        let images: Vec<&Image> = if game.gore_on {
            draw.frame.images.iter().collect()
        } else {
            draw.frame.images.iter().filter(|i| !i.is_blood()).collect()
        };
        for image in images {
            let atlas = game
                .store
                .get::<TextureAtlas>(&SimpleKey::from(image.sheet.as_str()), ctx)
                // TODO fix error handling, make this ?
                .expect("error loading texture atlas when drawing");

            let atlas_dimension = atlas.borrow().image.width as u32;

            let palette: Option<&Palette> = palette_storage.get(entity);
            let image_name = match palette {
                None => image.sheet.clone(),
                Some(palette) => format!("{}-{}", &image.sheet, &palette.name),
            };

            let p = palette.map_or(default_palette, |p| &p.palette[..]);
            let ggez_image = match game.images.entry(image_name) {
                Occupied(i) => i.into_mut(),
                Vacant(i) => i.insert(
                    graphics::Image::from_rgba8(
                        ctx,
                        atlas_dimension as u16,
                        atlas_dimension as u16,
                        &atlas.borrow().image.to_rgba8(p),
                    )
                    .unwrap(),
                ),
            };

            let rect = atlas.borrow().rects[image.image];
            let texture_size = atlas.borrow().image.width as f32;
            let draw_params = graphics::DrawParam::default()
                .src(graphics::Rect {
                    x: rect.x as f32 / texture_size,
                    y: rect.y as f32 / texture_size,
                    w: rect.w as f32 / texture_size,
                    h: rect.h as f32 / texture_size,
                })
                .dest(Point2::new(
                    (position.x as i32 + (draw.direction as i32 * image.x)) as f32,
                    (position.y as i32 + image.y) as f32,
                ))
                .scale(Vector2::new(draw.direction as i32 as f32, 1.0));

            graphics::draw(ctx, ggez_image, draw_params)?;
            if let ImageType::BloodStain = image.image_type {
                if let Some(b) = background {
                    graphics::pop_transform(ctx);
                    graphics::apply_transformations(ctx);

                    graphics::set_canvas(ctx, Some(&*b));
                    graphics::draw(ctx, ggez_image, draw_params)?;
                    graphics::set_canvas(ctx, None);

                    let scale_matrix = graphics::DrawParam::default()
                        .scale(game.screen_scale)
                        .to_matrix();
                    graphics::push_transform(ctx, Some(scale_matrix));
                    graphics::apply_transformations(ctx);
                }
            }
        }
    }
    Ok(())
}
<|endoftext|>


<commit_before>//! Configuration for the zwreec compiler.
//!
//! Uses the `getopts` library to append a `getopts::Options` with compiler
//! specific flags and then creates a `Config` struct from `getopts::Matches`.
//!
//! # Usage
//!
//! This module depends on the [getopts](http://doc.rust-lang.org/getopts/getopts/index.html)
//! crate. To use it, you need to add `getopts` to your project. Read getopts'
//! [Usage](http://doc.rust-lang.org/getopts/getopts/index.html#usage) for more information.
//!
//! # Example
//!
//! The following example shows how to generate a `Config` struct from an empty
//! `getopts::Options`.
//!
//! ```
//! extern crate zwreec;
//! extern crate getopts;
//!
//! use zwreec::config;
//! use zwreec::config::Config;
//!
//! fn main() {
//!     let args: Vec<String> = std::env::args().collect();
//!     let opts = config::zwreec_options(getopts::Options::new());
//!
//!     let matches = match opts.parse(&args[1..]) {
//!         Ok(m) => m,
//!         Err(f) => { panic!(f.to_string()) }
//!     }
//!
//!     let cfg = Config::from_matches(&matches);
//! }
//! ```
//!
//! # Appending this module (Development)
//!
//! To add new compiler flags for the compiler, you need to change three parts
//! in this module.
//!
//! 1. Appending the Struct
//!
//!     To store your new setting, you need to add it as a field to the `Config` struct. Here we
//!     add a new boolean `pub italics: bool` to control *italics*. Always add a field-comment.
//!
//!     ```
//! pub struct Config {
//!     /// Add easter egg to compiler
//!     pub easter_egg: bool,
//!     /// Instruct compiler to run these test-cases
//!     pub test_cases: Vec<TestCase>,
//!     /// Enables or disalbes Italics
//!     pub italics: bool,
//! }
//!     ```
//!
//!     Now you also need to change `Config::default_config()` to provide the
//!     default value for your new field:
//!
//!     ```
//! pub fn default_config() -> Config {
//!     Config{
//!         easter_egg: true,
//!         test_cases: Vec::new(),
//!         italics: true,
//!     }
//! }
//!     ```
//!
//! 2. Parse your flag in `from_matches()`
//!
//!     `from_matches` uses the Options `-F` and `-N` to enable or disable boolean flags.
//!     Therefore, if you added a new boolean flag, you can now simply add it to the match
//!     statement.
//!
//!     ```
//!     for s in matches.opt_strs("F") {
//!         match s.as_ref() {
//!             "easter-egg" => {
//!                  cfg.easter_egg = true;
//!                  debug!("enabled easter-egg");
//!             },
//!             "italics" => {
//!                 cfg.italics = true;
//!                 debug!("enabbled italics");
//!             },
//!             _ => {
//!                 error!("Cannot enable feature {} - feature not known.", s);
//!             }
//!         }
//!     }
//!
//!     for s in matches.opt_strs("N") {
//!         match s.as_ref() {
//!             "easter-egg" => {
//!                 cfg.easter_egg = false;
//!                 debug!("disabled easter-egg");
//!             },
//!             "italics" => {
//!                 cfg.italics = false;
//!                 debug:("disalbed italics");
//!             }
//!             _ => {
//!                 error!("Cannot disable feature {} - feature not known.", s);
//!             }
//!         }
//!     }
//!     ```
//!
//! 3. Adding something that is not a boolean
//!
//!     If you need to add an option that is *not* a boolean, you will need to append the struct
//!     and `default_config()` accordingly (e.g. `pub notaflag: String,`). Then you need to add a new
//!     `getopts::Option` to `zwreec_options()`:
//!
//!     ```
//! pub fn zwreec_options(mut opts: getopts::Options) -> getopts::Options {
//!     opts.optmulti("F", "feature", "", "FEAT");
//!     opts.optmulti("N", "no-feature", "enable or disable a feature (can occur multiple times).
//!                         List of supported features (default):
//!                             easter-egg (enabled)", "FEAT");
//!     opts.optflag("e", "generate-sample-zcode", "writes out a sample zcode file, input file is not used and can be omitted");
//!
//!     opts.optopt("n", "notaflag", "notaflag", "SOMETHING");
//!
//!     opts
//! }
//!     ```
//!
//!     Now you can append `Config::from_matches()` to analyse the provided matches for your new
//!     option and set the `Config` accordingly:
//!
//!     ```
//!     if let s = matches.opt_str("n") {
//!         cfg.notaflag = s;
//!     }
//!     ```
//!
//! 4. Now you can use the new flag inside the compiler.
use getopts;

use std::vec::Vec;

/// Represents the configuration for the compiler.
///
/// This struct is created using either `config::default_config()` or
/// `config::parse_matches(matches: &getopts::Matches)`. It contains a series
/// of fields that can be used to control the behaviour of the compiler.
///
/// # Example Usage inside the compiler
///
/// ```
/// let cfg = Config::default_config();
///
/// if cfg.easter_egg {
///     println!("Egg!");
/// }
/// ```
pub struct Config {
    /// Add easter egg to compiler
    pub easter_egg: bool,
    /// Instruct compiler to run these test-cases
    pub test_cases: Vec<TestCase>,
}

impl Config {
    /// Returns a `Config` struct with preset defaults
    pub fn default_config() -> Config {
        Config{
            easter_egg: true,
            test_cases: Vec::new(),
        }
    }

    /// Returns a `Config` struct by using `getopts::Matches` to set the fields.
    ///
    /// This method analyses a `getopts::Matches` for fields provided by
    /// `zwreec_options()`.
    ///
    /// # Example
    ///
    /// ```
    /// let args: Vec<String> = std::env::args().collect();
    /// let opts = config::zwreec_options(getopts::Options::new());
    ///
    /// let matches = match opts.parse(&args[1..]) {
    ///     Ok(m) => m,
    ///     Err(f) => { panic!(f.to_string()) }
    /// }
    ///
    /// let cfg = Config::from_matches(&matches);
    /// ```
    pub fn from_matches(matches: &getopts::Matches) -> Config {
        // load defaults
        let mut cfg = Config::default_config();

        if matches.opt_present("generate-sample-zcode") {
            cfg.test_cases.push(TestCase::ZcodeBackend);
        }

        // TODO: Find a way to make these two loops somewhat less.. repetitive
        for s in matches.opt_strs("F") {
            match s.as_ref() {
                "easter-egg" => {
                     cfg.easter_egg = true;
                     debug!("enabled easter-egg");
                },
                _ => {
                    error!("Cannot enable feature {} - feature not known.", s);
                }
            }
        }

        for s in matches.opt_strs("N") {
            match s.as_ref() {
                "easter-egg" => {
                    cfg.easter_egg = false;
                    debug!("disabled easter-egg");
                },
                _ => {
                    error!("Cannot disable feature {} - feature not known.", s);
                }
            }
        }

        cfg
    }
}

// TODO: If this stays only one Test Case, enum should be removed
/// The Type used to define backend tests for the compiler
pub enum TestCase {
    /// Skips the normal compiler chain and builds an example zcode file by
    /// using every opcode.
    ZcodeBackend,
}

/// Appends a `getopts::Options` with compiler specific flags
///
/// The method `Config::from_matches()` looks for very specific `getopts::Matches`.
/// This function takes an `getopts::Options` to append it with Options required
/// by `from_matches`. It currently adds three fields:
///
/// ```
/// # let mut opts = getopts::Options::new();
/// opts.optmulti("F", "feature", "", "FEAT");
/// opts.optmulti("N", "no-feature", "enable or disable a feature (can occur multiple times).
///                     List of supported features (default):
///                         easter-egg (enabled)", "FEAT");
/// opts.optflag("e", "generate-sample-zcode", "writes out a sample zcode file, input file is not used and can be omitted");
/// ```
///
/// # Example
///
/// You can use this function to append your `getopts::Options`.
///
/// ```
/// let mut opts = getopts::Options::new();
/// opts.optflag("h", "help", "print this message");
///
/// let opts = config::zwreec_options(opts);
/// ```
///
/// Another useful example is to use it to gernerate a more compact usage by
/// having a function that only returns your options.
///
/// ```
/// fn options() -> getopts::Options {
///     let mut opts = getopts::Options::new();
///     opts.optflag("h", "help", "display this help and exit");
///     opts.optflag("V", "version", "display version");
///
///     opts
/// }
///
/// fn print_usage(program: &str, verbose: bool) {
///     let brief = format!("Usage: {} [options]", program)
///
///     let opts = if verbose {
///         config::zwreec_options(options());
///     } else {
///         options();
///     }
///
///     print!("{}", opts.usage(&brief);
/// }
/// ```
/// As you can see, `options()` returns your own command line options, which are then conditionally
/// expanded by using `zwreec_options()`.
pub fn zwreec_options(mut opts: getopts::Options) -> getopts::Options {
    opts.optmulti("F", "feature", "", "FEAT");
    opts.optmulti("N", "no-feature", "enable or disable a feature (can occur multiple times).
                        List of supported features (default):
                            easter-egg (enabled)", "FEAT");
    opts.optflag("e", "generate-sample-zcode", "writes out a sample zcode file, input file is not used and can be omitted");

    opts
}
<commit_msg>Add Tests for Config module<commit_after>//! Configuration for the zwreec compiler.
//!
//! Uses the `getopts` library to append a `getopts::Options` with compiler
//! specific flags and then creates a `Config` struct from `getopts::Matches`.
//!
//! # Usage
//!
//! This module depends on the [getopts](http://doc.rust-lang.org/getopts/getopts/index.html)
//! crate. To use it, you need to add `getopts` to your project. Read getopts'
//! [Usage](http://doc.rust-lang.org/getopts/getopts/index.html#usage) for more information.
//!
//! # Example
//!
//! The following example shows how to generate a `Config` struct from an empty
//! `getopts::Options`.
//!
//! ```
//! extern crate zwreec;
//! extern crate getopts;
//!
//! use zwreec::config;
//! use zwreec::config::Config;
//!
//! fn main() {
//!     let args: Vec<String> = std::env::args().collect();
//!     let opts = config::zwreec_options(getopts::Options::new());
//!
//!     let matches = match opts.parse(&args[1..]) {
//!         Ok(m) => m,
//!         Err(f) => { panic!(f.to_string()) }
//!     }
//!
//!     let cfg = Config::from_matches(&matches);
//! }
//! ```
//!
//! # Appending this module (Development)
//!
//! To add new compiler flags for the compiler, you need to change three parts
//! in this module.
//!
//! 1. Appending the Struct
//!
//!     To store your new setting, you need to add it as a field to the `Config` struct. Here we
//!     add a new boolean `pub italics: bool` to control *italics*. Always add a field-comment.
//!
//!     ```
//! pub struct Config {
//!     /// Add easter egg to compiler
//!     pub easter_egg: bool,
//!     /// Instruct compiler to run these test-cases
//!     pub test_cases: Vec<TestCase>,
//!     /// Enables or disalbes Italics
//!     pub italics: bool,
//! }
//!     ```
//!
//!     Now you also need to change `Config::default_config()` to provide the
//!     default value for your new field:
//!
//!     ```
//! pub fn default_config() -> Config {
//!     Config{
//!         easter_egg: true,
//!         test_cases: Vec::new(),
//!         italics: true,
//!     }
//! }
//!     ```
//!
//! 2. Parse your flag in `from_matches()`
//!
//!     `from_matches` uses the Options `-F` and `-N` to enable or disable boolean flags.
//!     Therefore, if you added a new boolean flag, you can now simply add it to the match
//!     statement.
//!
//!     ```
//!     for s in matches.opt_strs("F") {
//!         match s.as_ref() {
//!             "easter-egg" => {
//!                  cfg.easter_egg = true;
//!                  debug!("enabled easter-egg");
//!             },
//!             "italics" => {
//!                 cfg.italics = true;
//!                 debug!("enabbled italics");
//!             },
//!             _ => {
//!                 error!("Cannot enable feature {} - feature not known.", s);
//!             }
//!         }
//!     }
//!
//!     for s in matches.opt_strs("N") {
//!         match s.as_ref() {
//!             "easter-egg" => {
//!                 cfg.easter_egg = false;
//!                 debug!("disabled easter-egg");
//!             },
//!             "italics" => {
//!                 cfg.italics = false;
//!                 debug:("disalbed italics");
//!             }
//!             _ => {
//!                 error!("Cannot disable feature {} - feature not known.", s);
//!             }
//!         }
//!     }
//!     ```
//!
//! 3. Adding something that is not a boolean
//!
//!     If you need to add an option that is *not* a boolean, you will need to append the struct
//!     and `default_config()` accordingly (e.g. `pub notaflag: String,`). Then you need to add a new
//!     `getopts::Option` to `zwreec_options()`:
//!
//!     ```
//! pub fn zwreec_options(mut opts: getopts::Options) -> getopts::Options {
//!     opts.optmulti("F", "feature", "", "FEAT");
//!     opts.optmulti("N", "no-feature", "enable or disable a feature (can occur multiple times).
//!                         List of supported features (default):
//!                             easter-egg (enabled)", "FEAT");
//!     opts.optflag("e", "generate-sample-zcode", "writes out a sample zcode file, input file is not used and can be omitted");
//!
//!     opts.optopt("n", "notaflag", "notaflag", "SOMETHING");
//!
//!     opts
//! }
//!     ```
//!
//!     Now you can append `Config::from_matches()` to analyse the provided matches for your new
//!     option and set the `Config` accordingly:
//!
//!     ```
//!     if let s = matches.opt_str("n") {
//!         cfg.notaflag = s;
//!     }
//!     ```
//!
//! 4. Now you can use the new flag inside the compiler.
use getopts;

use std::vec::Vec;

/// Represents the configuration for the compiler.
///
/// This struct is created using either `config::default_config()` or
/// `config::parse_matches(matches: &getopts::Matches)`. It contains a series
/// of fields that can be used to control the behaviour of the compiler.
///
/// # Example Usage inside the compiler
///
/// ```
/// let cfg = Config::default_config();
///
/// if cfg.easter_egg {
///     println!("Egg!");
/// }
/// ```
pub struct Config {
    /// Add easter egg to compiler
    pub easter_egg: bool,
    /// Instruct compiler to run these test-cases
    pub test_cases: Vec<TestCase>,
}

impl Config {
    /// Returns a `Config` struct with preset defaults
    pub fn default_config() -> Config {
        Config{
            easter_egg: true,
            test_cases: Vec::new(),
        }
    }

    /// Returns a `Config` struct by using `getopts::Matches` to set the fields.
    ///
    /// This method analyses a `getopts::Matches` for fields provided by
    /// `zwreec_options()`.
    ///
    /// # Example
    ///
    /// ```
    /// let args: Vec<String> = std::env::args().collect();
    /// let opts = config::zwreec_options(getopts::Options::new());
    ///
    /// let matches = match opts.parse(&args[1..]) {
    ///     Ok(m) => m,
    ///     Err(f) => { panic!(f.to_string()) }
    /// }
    ///
    /// let cfg = Config::from_matches(&matches);
    /// ```
    pub fn from_matches(matches: &getopts::Matches) -> Config {
        // load defaults
        let mut cfg = Config::default_config();

        if matches.opt_present("generate-sample-zcode") {
            cfg.test_cases.push(TestCase::ZcodeBackend);
        }

        // TODO: Find a way to make these two loops somewhat less.. repetitive
        for s in matches.opt_strs("F") {
            match s.as_ref() {
                "easter-egg" => {
                     cfg.easter_egg = true;
                     debug!("enabled easter-egg");
                },
                _ => {
                    error!("Cannot enable feature {} - feature not known.", s);
                }
            }
        }

        for s in matches.opt_strs("N") {
            match s.as_ref() {
                "easter-egg" => {
                    cfg.easter_egg = false;
                    debug!("disabled easter-egg");
                },
                _ => {
                    error!("Cannot disable feature {} - feature not known.", s);
                }
            }
        }

        cfg
    }
}

// TODO: If this stays only one Test Case, enum should be removed
/// The Type used to define backend tests for the compiler
#[derive(PartialEq)]
pub enum TestCase {
    /// Skips the normal compiler chain and builds an example zcode file by
    /// using every opcode.
    ZcodeBackend,
}

/// Appends a `getopts::Options` with compiler specific flags
///
/// The method `Config::from_matches()` looks for very specific `getopts::Matches`.
/// This function takes an `getopts::Options` to append it with Options required
/// by `from_matches`. It currently adds three fields:
///
/// ```
/// # let mut opts = getopts::Options::new();
/// opts.optmulti("F", "feature", "", "FEAT");
/// opts.optmulti("N", "no-feature", "enable or disable a feature (can occur multiple times).
///                     List of supported features (default):
///                         easter-egg (enabled)", "FEAT");
/// opts.optflag("e", "generate-sample-zcode", "writes out a sample zcode file, input file is not used and can be omitted");
/// ```
///
/// # Example
///
/// You can use this function to append your `getopts::Options`.
///
/// ```
/// let mut opts = getopts::Options::new();
/// opts.optflag("h", "help", "print this message");
///
/// let opts = config::zwreec_options(opts);
/// ```
///
/// Another useful example is to use it to gernerate a more compact usage by
/// having a function that only returns your options.
///
/// ```
/// fn options() -> getopts::Options {
///     let mut opts = getopts::Options::new();
///     opts.optflag("h", "help", "display this help and exit");
///     opts.optflag("V", "version", "display version");
///
///     opts
/// }
///
/// fn print_usage(program: &str, verbose: bool) {
///     let brief = format!("Usage: {} [options]", program)
///
///     let opts = if verbose {
///         config::zwreec_options(options());
///     } else {
///         options();
///     }
///
///     print!("{}", opts.usage(&brief);
/// }
/// ```
/// As you can see, `options()` returns your own command line options, which are then conditionally
/// expanded by using `zwreec_options()`.
pub fn zwreec_options(mut opts: getopts::Options) -> getopts::Options {
    opts.optmulti("F", "feature", "", "FEAT");
    opts.optmulti("N", "no-feature", "enable or disable a feature (can occur multiple times).
                        List of supported features (default):
                            easter-egg (enabled)", "FEAT");
    opts.optflag("e", "generate-sample-zcode", "writes out a sample zcode file, input file is not used and can be omitted");

    opts
}

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

    fn config_from_args(args: Vec<String>) -> Config {
        let opts = zwreec_options(getopts::Options::new());

        match opts.parse(&args) {
            Ok(m) => Config::from_matches(&m),
            Err(f) => { panic!(f.to_string()) }
        }
    }

    #[test]
    fn test_feature_easter_egg_true() {
        let cfg = config_from_args(vec!["-F".to_string(), "easter-egg".to_string()]);

        assert_eq!(cfg.easter_egg, true);
    }

    #[test]
    fn test_feature_easter_egg_false() {
        let cfg = config_from_args(vec!["-N".to_string(), "easter-egg".to_string()]);

        assert_eq!(cfg.easter_egg, false);
    }

    #[test]
    fn test_feature_easter_egg_both() {
        let cfg = config_from_args(vec![
                                   "-N".to_string(),
                                   "easter-egg".to_string(),
                                   "-F".to_string(),
                                   "easter-egg".to_string()]);

        assert_eq!(cfg.easter_egg, false);
    }

    #[test]
    fn test_generate_sample_zcode() {
        let cfg = config_from_args(vec!["-e".to_string()]);

        assert_eq!(cfg.test_cases.is_empty(), false);

        let mut contains = false;
        for tc in cfg.test_cases {
            if tc == TestCase::ZcodeBackend {
                contains = true;
            }
        }

        assert!(contains);
    }
}
<|endoftext|>


<commit_before>extern crate twig;

use twig::node::{ Expr, ExprValue };
use twig::Environment;
use twig::Lexer;
use twig::node::Module;
use twig::parser::{ Context, Parse };

#[test]
fn test_string_expression() {
    for (template, expected) in get_tests_for_string() {
        let env = Environment::default().init_all();
        let lexer = Lexer::default(&env.lexing);
        let mut tokens = lexer.tokens(&template);
        let mut context = Context::new(&env.parsing, &mut tokens);
        let module = match Module::parse(&mut context) {
            Ok(m) => m,
            Err(e) => panic!("parsing error: {:?}", e),
        };
        assert_eq!(module.body.expect_print(), &expected);
    }
}

fn get_tests_for_string<'r>() -> Vec<(&'static str, Expr<'r>)> {
    vec![
        (r#"{{ "foo" }}"#, Expr::new_str_constant("foo", 1)),
        (r#"{{ "foo #{bar}" }}"#, Expr::new_at(ExprValue::Concat {
            left: Box::new(Expr::new_str_constant("foo ", 1)),
            right: Box::new(Expr::new_name("bar", 1)),
        }, 1)),
        (r#"{{ "foo #{bar} baz" }}"#, Expr::new_at(ExprValue::Concat {
            left: Box::new(Expr::new_at(ExprValue::Concat {
                left: Box::new(Expr::new_str_constant("foo ", 1)),
                right: Box::new(Expr::new_name("bar", 1)),
            }, 1)),
            right: Box::new(Expr::new_str_constant(" baz", 1)),
        }, 1)),
        (r#"{{ "foo #{"foo #{bar} baz"} baz" }}"#, Expr::new_at(ExprValue::Concat {
            left: Box::new(Expr::new_at(ExprValue::Concat {
                left: Box::new(Expr::new_str_constant("foo ", 1)),
                right: Box::new(Expr::new_at(ExprValue::Concat {
                    left: Box::new(Expr::new_at(ExprValue::Concat {
                        left: Box::new(Expr::new_str_constant("foo ", 1)),
                        right: Box::new(Expr::new_name("bar", 1)),
                    }, 1)),
                    right: Box::new(Expr::new_str_constant(" baz", 1)),
                }, 1)),
            }, 1)),
            right: Box::new(Expr::new_str_constant(" baz", 1)),
        }, 1)),
    ]
}
<commit_msg>Pass concatenate_two_consecutive_strings test.<commit_after>extern crate twig;

use twig::node::{ Expr, ExprValue };
use twig::Environment;
use twig::Lexer;
use twig::node::Module;
use twig::parser::{ Context, Parse };
use twig::Result;

#[test]
#[should_panic(
    expected = r#"Unexpected token "string" of value "b" ("end of print statement" expected)"#
)]
fn test_string_expression_does_not_concatenate_two_consecutive_strings() {
    maybe_parsed(r#"{{ "a" "b" }}"#).unwrap();
}

#[test]
fn test_string_expression() {
    for (template, expected) in get_tests_for_string() {
        let module = expect_parsed(template);
        assert_eq!(module.body.expect_print(), &expected);
    }
}

fn get_tests_for_string<'r>() -> Vec<(&'static str, Expr<'r>)> {
    vec![
        (r#"{{ "foo" }}"#, Expr::new_str_constant("foo", 1)),
        (r#"{{ "foo #{bar}" }}"#, Expr::new_at(ExprValue::Concat {
            left: Box::new(Expr::new_str_constant("foo ", 1)),
            right: Box::new(Expr::new_name("bar", 1)),
        }, 1)),
        (r#"{{ "foo #{bar} baz" }}"#, Expr::new_at(ExprValue::Concat {
            left: Box::new(Expr::new_at(ExprValue::Concat {
                left: Box::new(Expr::new_str_constant("foo ", 1)),
                right: Box::new(Expr::new_name("bar", 1)),
            }, 1)),
            right: Box::new(Expr::new_str_constant(" baz", 1)),
        }, 1)),
        (r#"{{ "foo #{"foo #{bar} baz"} baz" }}"#, Expr::new_at(ExprValue::Concat {
            left: Box::new(Expr::new_at(ExprValue::Concat {
                left: Box::new(Expr::new_str_constant("foo ", 1)),
                right: Box::new(Expr::new_at(ExprValue::Concat {
                    left: Box::new(Expr::new_at(ExprValue::Concat {
                        left: Box::new(Expr::new_str_constant("foo ", 1)),
                        right: Box::new(Expr::new_name("bar", 1)),
                    }, 1)),
                    right: Box::new(Expr::new_str_constant(" baz", 1)),
                }, 1)),
            }, 1)),
            right: Box::new(Expr::new_str_constant(" baz", 1)),
        }, 1)),
    ]
}

fn maybe_parsed(template: &'static str) -> Result<Module> {
    let env = Environment::default().init_all();
    let lexer = Lexer::default(&env.lexing);
    let mut tokens = lexer.tokens(template);
    let mut context = Context::new(&env.parsing, &mut tokens);
    Module::parse(&mut context)
}

fn expect_parsed(template: &'static str) -> Module {
    match maybe_parsed(template) {
        Ok(m) => m,
        Err(e) => panic!("parsing error: {:?}", e),
    }
}
<|endoftext|>