<commit_before>use application::AppRunError;
use image::{DynamicImage, FilterType, GenericImage, ImageBuffer, imageops, Luma, Rgba, RgbaImage};
use imageproc::{drawing, edges};
use imageproc::rect::Rect;
use rusttype::{Font, Scale};

pub struct AnnotationCollection(Vec<Annotation>);

impl AnnotationCollection {
    pub fn new(annotations: Vec<Annotation>) -> AnnotationCollection {
        AnnotationCollection(annotations)
    }

    pub fn render<'a>(&'a self, pixels: &'a mut DynamicImage, font: &'a Font<'a>, scale_factor: f32) -> Result<DynamicImage, RenderError> {
        fn top_position(width: u32, _height: u32, text_width: u32, text_height: u32) -> (u32, u32) {
            let x = (width / 2) - (text_width / 2);
            let y = {
                let text_height = text_height as f32;
                (text_height * 0.2) as u32
            };

            (x, y)
        }

        fn middle_position(width: u32, height: u32, text_width: u32, text_height: u32) -> (u32, u32) {
            let x = (width / 2) - (text_width / 2);
            let y = (height / 2) - (text_height / 2);

            (x, y)
        }

        fn bottom_position(width: u32, height: u32, text_width: u32, text_height: u32) -> (u32, u32) {
            let x = (width / 2) - (text_width / 2);
            let y = {
                let height = height as f32;
                let text_height = text_height as f32;
                (height - (text_height * 1.2)) as u32
            };

            (x, y)
        }

        let (width, height) = pixels.dimensions();
        let mut text_rendering = DynamicImage::ImageRgba8(RgbaImage::new(width * 4, height * 4));

        for annotation in self.0.iter() {
            match *annotation {
                Annotation::Top(ref text) => render_text(text, &mut text_rendering, font, scale_factor, width, height, top_position)?,
                Annotation::Middle(ref text) => render_text(text, &mut text_rendering, font, scale_factor, width, height, middle_position)?,
                Annotation::Bottom(ref text) => render_text(text, &mut text_rendering, font, scale_factor, width, height, bottom_position)?,
            };
        }

        let downsampled_text = imageops::resize(&text_rendering, width, height, FilterType::CatmullRom);
        imageops::overlay(pixels, &mut DynamicImage::ImageRgba8(downsampled_text), 0, 0);
        Ok(text_rendering)
    }
}

pub enum Annotation {
    Top(String),
    Middle(String),
    Bottom(String),
}

// Apparently, rendering cannot produce any errors?
pub struct RenderError;

impl From<RenderError> for AppRunError {
    fn from(_: RenderError) -> Self {
        unimplemented!()
    }
}

// c_width and c_height refer to the dimensions of the canvas onto which all of this will
// be drawn in the end--that is, the original image.
fn render_text<'a>(text: &'a str,
                   pixels: &'a mut DynamicImage,
                   font: &'a Font<'a>,
                   scale_factor: f32,
                   c_width: u32,
                   c_height: u32,
                   position: fn(w: u32, h: u32, tw: u32, th: u32) -> (u32, u32))
                   -> Result<(), RenderError> {
    // The final value in the array here is the *opacity* of the pixel. Not the transparency.
    // Apparently, this is not CSS...
    let white_pixel = Rgba([255, 255, 255, 255]);
    let black_pixel = Rgba([0, 0, 0, 255]);

    let scale = Scale::uniform(scale_factor);
    let scale_4x = Scale::uniform(scale_factor * 4.0);
    let (text_width, text_height) = text_size(&text, font, scale);

    // To reduce the janky jagginess of the black border around each letter, we want to render the
    // words themselves at 4x resolution and then paste that on top of the existing image.
    let (x, y) = position(c_width, c_height, text_width, text_height);
    let x = x * 4;
    let y = y * 4;

    let mut edge_rendering = ImageBuffer::from_pixel(text_width * 4, text_height * 4, black_pixel);
    drawing::draw_text_with_font_mut(&mut edge_rendering, white_pixel, 0, 0, scale_4x, &font, &text);

    let edge_rendering = edges::canny(&imageops::grayscale(&edge_rendering), 245.0, 245.0);
    let edge_pixels = edge_rendering.pixels().enumerate()
        .filter(|&(_, &px)| Luma([255u8]) == px)
        .map(|(idx, _)| {
            let idx = idx as u32;
            let x = idx % (text_width * 4) + x;
            let y = idx / (text_width * 4) + y;
            (x, y)
        });

    // I wonder how long this ends up taking. Seems like this would just have to be the slowest
    // part of the process. Would be great to parallelize this somehow, but it would probably be
    // pretty difficult to allow multiple mutable aliases, too...
    let rect_size = (0.1 * scale_factor * 2.2) as u32;
    let offset = (rect_size / 2) as i32;
    for (x, y) in edge_pixels {
        let rect = Rect::at(x as i32 - offset, y as i32 - offset).of_size(rect_size, rect_size);
        drawing::draw_filled_rect_mut(pixels, rect, black_pixel);
    }

    drawing::draw_text_with_font_mut(pixels, white_pixel, x, y, scale_4x, &font, &text);
    Ok(())
}

/// Calculate the dimensions of the bounding box for a given string, font, and scale.
///
/// This works by summing the "advance width" of each glyph in the text, entirely ignoring
/// kerning as each character is considered in isolation. Because this is used just to center
/// text in the image, it's close enough for government work.
fn text_size<'a>(s: &'a str, font: &'a Font<'a>, scale: Scale) -> (u32, u32) {
    use rusttype::VMetrics;

    let text_width = font.glyphs_for(s.chars())
        .map(|glyph| glyph.scaled(scale).h_metrics().advance_width)
        .sum::<f32>();

    // The "v-metrics" for any given letter in a font are the same for a given scale, so we don't
    // need to check this for each glyph.
    let text_height = {
        let VMetrics { ascent, descent, ..} = font.v_metrics(scale);
        (ascent - descent) as u32
    };

    // I know I'm truncating the length and this is probably wrong, but it's not wrong by enough
    // to be noticeable when you print it to an image.
    //
    // The padding you see below is added to aid in edge detection, specifically because the
    // exclamation point doesn't seem to have enough advance width. -.-
    (text_width as u32 + 2, text_height)
}
<commit_msg>Create edge rendering as grayscale<commit_after>use application::AppRunError;
use image::{DynamicImage, FilterType, GenericImage, ImageBuffer, imageops, Luma, Rgba, RgbaImage};
use imageproc::{drawing, edges};
use imageproc::rect::Rect;
use rusttype::{Font, Scale};

pub struct AnnotationCollection(Vec<Annotation>);

impl AnnotationCollection {
    pub fn new(annotations: Vec<Annotation>) -> AnnotationCollection {
        AnnotationCollection(annotations)
    }

    pub fn render<'a>(&'a self, pixels: &'a mut DynamicImage, font: &'a Font<'a>, scale_factor: f32) -> Result<DynamicImage, RenderError> {
        fn top_position(width: u32, _height: u32, text_width: u32, text_height: u32) -> (u32, u32) {
            let x = (width / 2) - (text_width / 2);
            let y = {
                let text_height = text_height as f32;
                (text_height * 0.2) as u32
            };

            (x, y)
        }

        fn middle_position(width: u32, height: u32, text_width: u32, text_height: u32) -> (u32, u32) {
            let x = (width / 2) - (text_width / 2);
            let y = (height / 2) - (text_height / 2);

            (x, y)
        }

        fn bottom_position(width: u32, height: u32, text_width: u32, text_height: u32) -> (u32, u32) {
            let x = (width / 2) - (text_width / 2);
            let y = {
                let height = height as f32;
                let text_height = text_height as f32;
                (height - (text_height * 1.2)) as u32
            };

            (x, y)
        }

        let (width, height) = pixels.dimensions();
        let mut text_rendering = DynamicImage::ImageRgba8(RgbaImage::new(width * 4, height * 4));

        for annotation in self.0.iter() {
            match *annotation {
                Annotation::Top(ref text) => render_text(text, &mut text_rendering, font, scale_factor, width, height, top_position)?,
                Annotation::Middle(ref text) => render_text(text, &mut text_rendering, font, scale_factor, width, height, middle_position)?,
                Annotation::Bottom(ref text) => render_text(text, &mut text_rendering, font, scale_factor, width, height, bottom_position)?,
            };
        }

        let downsampled_text = imageops::resize(&text_rendering, width, height, FilterType::CatmullRom);
        imageops::overlay(pixels, &mut DynamicImage::ImageRgba8(downsampled_text), 0, 0);
        Ok(text_rendering)
    }
}

pub enum Annotation {
    Top(String),
    Middle(String),
    Bottom(String),
}

// Apparently, rendering cannot produce any errors?
pub struct RenderError;

impl From<RenderError> for AppRunError {
    fn from(_: RenderError) -> Self {
        unimplemented!()
    }
}

// c_width and c_height refer to the dimensions of the canvas onto which all of this will
// be drawn in the end--that is, the original image.
fn render_text<'a>(text: &'a str,
                   pixels: &'a mut DynamicImage,
                   font: &'a Font<'a>,
                   scale_factor: f32,
                   c_width: u32,
                   c_height: u32,
                   position: fn(w: u32, h: u32, tw: u32, th: u32) -> (u32, u32))
                   -> Result<(), RenderError> {
    // The final value in the array here is the *opacity* of the pixel. Not the transparency.
    // Apparently, this is not CSS...
    let white_pixel = Rgba([255, 255, 255, 255]);
    let black_pixel = Rgba([0, 0, 0, 255]);

    let scale = Scale::uniform(scale_factor);
    let scale_4x = Scale::uniform(scale_factor * 4.0);
    let (text_width, text_height) = text_size(&text, font, scale);

    // To reduce the janky jagginess of the black border around each letter, we want to render the
    // words themselves at 4x resolution and then paste that on top of the existing image.
    let (x, y) = position(c_width, c_height, text_width, text_height);
    let x = x * 4;
    let y = y * 4;

    let mut edge_rendering = ImageBuffer::from_pixel(text_width * 4, text_height * 4, Luma([0u8]));
    drawing::draw_text_with_font_mut(&mut edge_rendering, Luma([255u8]), 0, 0, scale_4x, &font, &text);

    let edge_rendering = edges::canny(&edge_rendering, 245.0, 245.0);
    let edge_pixels = edge_rendering.pixels().enumerate()
        .filter(|&(_, &px)| Luma([255u8]) == px)
        .map(|(idx, _)| {
            let idx = idx as u32;
            let x = idx % (text_width * 4) + x;
            let y = idx / (text_width * 4) + y;
            (x, y)
        });

    // I wonder how long this ends up taking. Seems like this would just have to be the slowest
    // part of the process. Would be great to parallelize this somehow, but it would probably be
    // pretty difficult to allow multiple mutable aliases, too...
    let rect_size = (0.1 * scale_factor * 2.2) as u32;
    let offset = (rect_size / 2) as i32;
    for (x, y) in edge_pixels {
        let rect = Rect::at(x as i32 - offset, y as i32 - offset).of_size(rect_size, rect_size);
        drawing::draw_filled_rect_mut(pixels, rect, black_pixel);
    }

    drawing::draw_text_with_font_mut(pixels, white_pixel, x, y, scale_4x, &font, &text);
    Ok(())
}

/// Calculate the dimensions of the bounding box for a given string, font, and scale.
///
/// This works by summing the "advance width" of each glyph in the text, entirely ignoring
/// kerning as each character is considered in isolation. Because this is used just to center
/// text in the image, it's close enough for government work.
fn text_size<'a>(s: &'a str, font: &'a Font<'a>, scale: Scale) -> (u32, u32) {
    use rusttype::VMetrics;

    let text_width = font.glyphs_for(s.chars())
        .map(|glyph| glyph.scaled(scale).h_metrics().advance_width)
        .sum::<f32>();

    // The "v-metrics" for any given letter in a font are the same for a given scale, so we don't
    // need to check this for each glyph.
    let text_height = {
        let VMetrics { ascent, descent, ..} = font.v_metrics(scale);
        (ascent - descent) as u32
    };

    // I know I'm truncating the length and this is probably wrong, but it's not wrong by enough
    // to be noticeable when you print it to an image.
    //
    // The padding you see below is added to aid in edge detection, specifically because the
    // exclamation point doesn't seem to have enough advance width. -.-
    (text_width as u32 + 2, text_height)
}
<|endoftext|>


<commit_before>extern crate zwreec;
extern crate getopts;
#[macro_use] extern crate log;
extern crate time;
extern crate term;

use std::env;
use std::vec::Vec;
use std::error::Error;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::process::exit;

use zwreec::config;

mod logger;

// found in:
// http://stackoverflow.com/a/27590832
macro_rules! print_stderr(
    ($($arg:tt)*) => (
        match write!(&mut ::std::io::stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
    )
);

/// Prints usage information
/// The verbose flag signals if all options should be shown.
/// NOTE: This is similar to librustc_driver's usage function.
fn usage(verbose: bool) {
    let options = if verbose {
        config::zwreec_options()
    } else {
        config::zwreec_short_options()
    };

    let brief = format!("Usage: zwreec [-hV] [-vq] [-l [LOGFILE]] [-o OUTPUT] INPUT");

    println!("{}\n
Additional help:
    --help -v           Print the full set of options zwreec accepts",
        options.usage(&brief));
}

/// NOTE: This is similar to librustc_driver's handle_options function.
fn parse_arguments(args: Vec<String>) -> getopts::Matches {
    let mut loggers: Vec<Box<logger::SharedLogger>> = vec![];

    if args.is_empty() {
        // No options provided, print usage and exit
        usage(false);
        exit(1);
    }

    let opts = config::zwreec_options();

    let matches = match opts.parse(&args[1..]) {
        Ok(m)  => m,
        Err(f) => {
            // parsing error
            // display usage and return
            print_stderr!("{}\n", f.to_string());
            exit(1);
        }
    };

    if matches.opt_present("help") {
        usage(matches.opt_present("verbose"));
        exit(1);
    }

    if matches.opt_present("version") {
        println!("zwreec {}", match option_env!("CFG_VERSION") {
            Some(s) => s.to_string(),
            None => format!("{}.{}.{}{}",
                            env!("CARGO_PKG_VERSION_MAJOR"),
                            env!("CARGO_PKG_VERSION_MINOR"),
                            env!("CARGO_PKG_VERSION_PATCH"),
                            option_env!("CARGO_PKG_VERSION_PRE").unwrap_or(""))
        });
        exit(0);
    }

    if matches.opt_present("verbose") {
        // set log level to verbose
        loggers.push(logger::TermLogger::new(
                match matches.opt_count("v") {
                    1 => logger::LogLevelFilter::Info,
                    2 => logger::LogLevelFilter::Debug,
                    _ => logger::LogLevelFilter::Trace,
                }));
    } else if matches.opt_present("quiet") {
        // set log level to error
        loggers.push(logger::TermLogger::new(logger::LogLevelFilter::Error));
    } else {
        // set log level to warn
        loggers.push(logger::TermLogger::new(logger::LogLevelFilter::Warn));
    }

    if matches.opt_present("logfile") {
        // sets a logger to output to logfile
        let name = if let Some(n) = matches.opt_str("logfile") {
            n
        } else {
            "zwreec.log".to_string()
        };
        loggers.push(logger::FileLogger::new(
                        logger::LogLevelFilter::Trace,
                        File::create(name).unwrap())
            );
    }

    // TODO: This might not belong in a function called parse_arguments
    // activate logger
    let _ = logger::CombinedLogger::init(loggers);

    matches
}

fn main() {
    // handle command line parameters
    let matches = parse_arguments(env::args().collect());

    // TODO: Everything below this should be analysed inside the config module

    let mut infile = if matches.free.len() == 1 {
        let path = Path::new(&matches.free[0]);
        match File::open(path) {
            Err(why) => {
                panic!("Couldn't open {}: {}",
                               path.display(), Error::description(&why))
            },
            Ok(file) => {
                info!("Opened input: {}", path.display());
                file
            }
        }
    } else {
        // TODO: check if STDOUT is a tty
        print_stderr!("Input file name missing\n");
        usage(matches.opt_present("verbose"));
        exit(1);
    };

    let mut outfile = if let Some(file) = matches.opt_str("o") {
        // try to open FILE
        let path = Path::new(&file);
        match File::create(path) {
            Err(why) => {
                panic!("Couldn't open {}: {}",
                       path.display(), Error::description(&why))
            },
            Ok(file) => {
                info!("Opened output: {}", path.display());
                file
            }
        }
    } else {
        let path = Path::new("a.z8");
        match File::create(path) {
            Err(why) => {
                panic!("Couldn't open {}: {}",
                       path.display(), Error::description(&why))
            },
            Ok(file) => {
                debug!("No output file specified, assuming default");
                info!("Opened output: {}", path.display());
                file
            }
        }
    };

    debug!("Parsed command line options");
    info!("Main started");

    // call library
    zwreec::compile(&mut infile, &mut outfile);

    info!("Main finished");
}
<commit_msg>Move further parsing to parse_argument<commit_after>extern crate zwreec;
extern crate getopts;
#[macro_use] extern crate log;
extern crate time;
extern crate term;

use std::env;
use std::vec::Vec;
use std::error::Error;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::process::exit;

use zwreec::config;

mod logger;

// found in:
// http://stackoverflow.com/a/27590832
macro_rules! print_stderr(
    ($($arg:tt)*) => (
        match write!(&mut ::std::io::stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
    )
);

/// Prints usage information
/// The verbose flag signals if all options should be shown.
/// NOTE: This is similar to librustc_driver's usage function.
fn usage(verbose: bool) {
    let options = if verbose {
        config::zwreec_options()
    } else {
        config::zwreec_short_options()
    };

    let brief = format!("Usage: zwreec [-hV] [-vq] [-l [LOGFILE]] [-o OUTPUT] INPUT");

    println!("{}\n
Additional help:
    --help -v           Print the full set of options zwreec accepts",
        options.usage(&brief));
}

/// Parse command line arguments
///
/// Parses command line arguments to set up the logger and extract input and
/// output parameters. Will display the usage and exit depending on arguments.
///
/// Returns `getopts::Matches` and a `std::fs::File` for the input and output
/// file. The `getopts::Matches` can be used to
///
/// # Examples
///
/// ```
/// let mut opts = getopts::Options::new();
/// opts.optflag("h", "help", "display this help and exit");
///
/// let (matches, mut input, mut output) = parse_arguments(
///     env::args().collect(),
///     opts
/// );
/// ```
///
/// # Failures
///
/// Depending on encountered arguments or parsing errors this function will
/// print the usage and/or call `exit(1)`.
///
/// NOTE: This is similar to librustc_driver's handle_options function.
fn parse_arguments(args: Vec<String>, opts: getopts::Options) -> (getopts::Matches, File, File) {
    let mut loggers: Vec<Box<logger::SharedLogger>> = vec![];

    if args.is_empty() {
        // No options provided, print usage and exit
        usage(false);
        exit(1);
    }

    let matches = match opts.parse(&args[1..]) {
        Ok(m)  => m,
        Err(f) => {
            // parsing error
            // display usage and return
            print_stderr!("{}\n", f.to_string());
            exit(1);
        }
    };

    if matches.opt_present("help") {
        usage(matches.opt_present("verbose"));
        exit(1);
    }

    if matches.opt_present("version") {
        println!("zwreec {}", match option_env!("CFG_VERSION") {
            Some(s) => s.to_string(),
            None => format!("{}.{}.{}{}",
                            env!("CARGO_PKG_VERSION_MAJOR"),
                            env!("CARGO_PKG_VERSION_MINOR"),
                            env!("CARGO_PKG_VERSION_PATCH"),
                            option_env!("CARGO_PKG_VERSION_PRE").unwrap_or(""))
        });
        exit(0);
    }

    if matches.opt_present("verbose") {
        // set log level to verbose
        loggers.push(logger::TermLogger::new(
                match matches.opt_count("v") {
                    1 => logger::LogLevelFilter::Info,
                    2 => logger::LogLevelFilter::Debug,
                    _ => logger::LogLevelFilter::Trace,
                }));
    } else if matches.opt_present("quiet") {
        // set log level to error
        loggers.push(logger::TermLogger::new(logger::LogLevelFilter::Error));
    } else {
        // set log level to warn
        loggers.push(logger::TermLogger::new(logger::LogLevelFilter::Warn));
    }

    if matches.opt_present("logfile") {
        // sets a logger to output to logfile
        let name = if let Some(n) = matches.opt_str("logfile") {
            n
        } else {
            "zwreec.log".to_string()
        };
        loggers.push(logger::FileLogger::new(
                        logger::LogLevelFilter::Trace,
                        File::create(name).unwrap())
            );
    }

    // TODO: This might not belong in a function called parse_arguments
    // activate logger
    let _ = logger::CombinedLogger::init(loggers);

    let infile = if matches.free.len() == 1 {
        let path = Path::new(&matches.free[0]);
        match File::open(path) {
            Err(why) => {
                panic!("Couldn't open {}: {}",
                       path.display(), Error::description(&why))
            },
            Ok(file) => {
                info!("Opened input: {}", path.display());
                file
            }
        }
    } else {
        // TODO: check if STDOUT is a tty
        print_stderr!("Input file name missing\n");
        usage(matches.opt_present("verbose"));
        exit(1);
    };

    let outfile = if let Some(file) = matches.opt_str("o") {
        // try to open FILE
        let path = Path::new(&file);
        match File::create(path) {
            Err(why) => {
                panic!("Couldn't open {}: {}",
                       path.display(), Error::description(&why))
            },
            Ok(file) => {
                info!("Opened output: {}", path.display());
                file
            }
        }
    } else {
        let path = Path::new("a.z8");
        match File::create(path) {
            Err(why) => {
                panic!("Couldn't open {}: {}",
                       path.display(), Error::description(&why))
            },
            Ok(file) => {
                debug!("No output file specified, assuming default");
                info!("Opened output: {}", path.display());
                file
            }
        }
    };

    (matches, infile, outfile)
}

fn main() {
    // handle command line parameters
    let (matches, mut input, mut output) = parse_arguments(
        env::args().collect(),
        config::zwreec_options()
    );

    debug!("Parsed command line options");
    info!("Main started");

    // call library
    zwreec::compile(&mut input, &mut output);

    info!("Main finished");
}
<|endoftext|>


<commit_before>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the
// terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not
// distributed with this file, You can
// obtain one at
// http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

use std::{str};
use std::ffi::{CStr, CString};
use std::thread;
use std::net::TcpStream;
use std::sync::mpsc::{channel, Sender, Receiver};

use super::libc::{c_int, c_char};
use super::simple_stream::bstream::Bstream;

/// This client's writer channel
static mut writer_tx: *mut Sender<Vec<u8>> = 0 as *mut Sender<Vec<u8>>;

/// This client's kill channel
static mut kill_tx: *mut Sender<()> = 0 as *mut Sender<()>;

#[no_mangle]
pub extern "C" fn start(address: *const c_char,
    data_handler: extern fn(*const c_char),
    on_connect_handler: extern fn(),
    on_disconnect_handler: extern fn()) -> c_int {

    // TODO - adjust this to accept a log level adjustable by whoever is running
    // the application
    super::init();

    trace!("Rust - start()");

    let mut r_address;
    unsafe {
        r_address = CStr::from_ptr(address);
    }
    let s_address = r_address.to_bytes();
    let host_address = match str::from_utf8(s_address) {
        Ok(safe_str) => safe_str,
        Err(_) => {
            error!("Invalid host address");
            return -1 as c_int;
        }
    };
    //
    // // Create and register a way to kill this client
    // let (k_tx, kill_rx): (Sender<()>, Receiver<()>) = channel();
    // unsafe { *kill_tx = k_tx.clone(); }
    //
    // // Writer thread's channel
    // let (w_tx, writer_rx): (Sender<Vec<u8>>, Receiver<Vec<u8>>) = channel();
    // unsafe { *writer_tx = w_tx.clone(); }
    //
    // debug!("Attempting connect to: {}", host_address);
    //
    // let result = TcpStream::connect(host_address);
    // if result.is_err() {
    //     error!("Error connecting to {} - {}", host_address, result.unwrap_err());
    //     return -1 as c_int;
    // }
    // debug!("Connected");
    // on_connect_handler();
    //
    // let stream = result.unwrap();
    // let client = Bstream::new(stream);
    // let r_client = client.clone();
    // let w_client = client.clone();
    //
    // // Start the reader thread
    // thread::Builder::new()
    //     .name("ReaderThread".to_string())
    //     .spawn(move||{
    //         reader_thread(r_client, data_handler)
    //     }).unwrap();
    //
    // // Start the writer thread
    // thread::Builder::new()
    //     .name("WriterThread".to_string())
    //     .spawn(move||{
    //         writer_thread(writer_rx, w_client)
    //     }).unwrap();
    //
    // // Wait for the kill signal
    // match kill_rx.recv() {
    //     Ok(_) => { }
    //     Err(e) => {
    //         error!("Error on kill channel: {}", e);
    //         on_disconnect_handler();
    //         return -1 as c_int;
    //     }
    // };
    // on_disconnect_handler();

    // Exit out in standard C fashion
    0 as c_int
}

/// Writes the complete contents of buffer to the server
/// Returns -1 on error
#[no_mangle]
pub extern "C" fn hydrogen_write(buffer: *const c_char) -> c_int {
    trace!("Rust.write");

    let mut buf_as_cstr;
    unsafe {
        buf_as_cstr = CStr::from_ptr(buffer);
    }
    let buf_as_slice = buf_as_cstr.to_bytes();

    let mut n_buffer = Vec::<u8>::with_capacity(buf_as_slice.len());
    for byte in buf_as_slice.iter() {
        n_buffer.push(*byte);
    }

    unsafe {
        match (*writer_tx).send(n_buffer) {
            Ok(_) => { }
            Err(e) => {
                warn!("Error sending buffer: {}", e);
                let _ = (*kill_tx).send(());
                return -1 as c_int;
            }
        };
    }

    0 as c_int
}

/// Forever listens to incoming data and when a complete message is received,
/// the passed callback is hit
fn reader_thread(client: Bstream, handler: extern fn(*const c_char)) {
    trace!("Rust.reader_thread started");

    let mut reader = client.clone();
    loop {
        match reader.read() {
            Ok(buffer) => {
                // Launch the handler in a new thread
                thread::Builder::new()
                    .name("Reader-Worker".to_string())
                    .spawn(move||{
                        let slice = &buffer[..];
                        let c_buffer = CString::new(slice).unwrap();
                        handler(c_buffer.as_ptr());
                    }).unwrap();
            }
            Err(e) => {
                error!("Error: {}", e);
                break;
            }
        };
    }
    debug!("Rust.reader_thread finished");
    unsafe { let _ = (*kill_tx).send(()); }
}

/// Forever listens to Receiver<Vec<u8>> waiting on messages to come in
/// Once available, blocks until the entire message has been written
fn writer_thread(rx: Receiver<Vec<u8>>, client: Bstream) {
    trace!("Rust.writer_thread started");

    let mut writer = client.clone();
    loop {
        match rx.recv() {
            Ok(ref mut buffer) => {
                match writer.write(buffer) {
                    Ok(_) => { }
                    Err(e) => {
                        error!("Error: {}", e);
                        break;
                    }
                };
            }
            Err(e) => {
                error!("Error: {}", e);
                break;
            }
        };
    }

    debug!("Rust.writer_thread finished");
    unsafe { let _ = (*kill_tx).send(()); }
}

/// Drops the current connection and kills all current threads
#[no_mangle]
pub extern "C" fn kill() {
    unsafe { let _ = (*kill_tx).send(()); }
}
<commit_msg>Debugging<commit_after>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the
// terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not
// distributed with this file, You can
// obtain one at
// http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

use std::{str};
use std::ffi::{CStr, CString};
use std::thread;
use std::net::TcpStream;
use std::sync::mpsc::{channel, Sender, Receiver};

use super::libc::{c_int, c_char};
use super::simple_stream::bstream::Bstream;

/// This client's writer channel
static mut writer_tx: *mut Sender<Vec<u8>> = 0 as *mut Sender<Vec<u8>>;

/// This client's kill channel
static mut kill_tx: *mut Sender<()> = 0 as *mut Sender<()>;

#[no_mangle]
pub extern "C" fn hydrogen_start(address: *const c_char,
    data_handler: extern fn(*const c_char),
    on_connect_handler: extern fn(),
    on_disconnect_handler: extern fn()) -> c_int {

    // TODO - adjust this to accept a log level adjustable by whoever is running
    // the application
    super::init();

    trace!("Rust - start()");

    let mut r_address;
    unsafe {
        r_address = CStr::from_ptr(address);
    }
    let s_address = r_address.to_bytes();
    let host_address = match str::from_utf8(s_address) {
        Ok(safe_str) => safe_str,
        Err(_) => {
            error!("Invalid host address");
            return -1 as c_int;
        }
    };
    trace!("Address: ");

    // Create and register a way to kill this client
    let (k_tx, kill_rx): (Sender<()>, Receiver<()>) = channel();
    unsafe { *kill_tx = k_tx.clone(); }

    // // Writer thread's channel
    // let (w_tx, writer_rx): (Sender<Vec<u8>>, Receiver<Vec<u8>>) = channel();
    // unsafe { *writer_tx = w_tx.clone(); }
    //
    // debug!("Attempting connect to: {}", host_address);
    //
    // let result = TcpStream::connect(host_address);
    // if result.is_err() {
    //     error!("Error connecting to {} - {}", host_address, result.unwrap_err());
    //     return -1 as c_int;
    // }
    // debug!("Connected");
    // on_connect_handler();
    //
    // let stream = result.unwrap();
    // let client = Bstream::new(stream);
    // let r_client = client.clone();
    // let w_client = client.clone();
    //
    // // Start the reader thread
    // thread::Builder::new()
    //     .name("ReaderThread".to_string())
    //     .spawn(move||{
    //         reader_thread(r_client, data_handler)
    //     }).unwrap();
    //
    // // Start the writer thread
    // thread::Builder::new()
    //     .name("WriterThread".to_string())
    //     .spawn(move||{
    //         writer_thread(writer_rx, w_client)
    //     }).unwrap();
    //
    // // Wait for the kill signal
    // match kill_rx.recv() {
    //     Ok(_) => { }
    //     Err(e) => {
    //         error!("Error on kill channel: {}", e);
    //         on_disconnect_handler();
    //         return -1 as c_int;
    //     }
    // };
    // on_disconnect_handler();

    // Exit out in standard C fashion
    0 as c_int
}

/// Writes the complete contents of buffer to the server
/// Returns -1 on error
#[no_mangle]
pub extern "C" fn hydrogen_write(buffer: *const c_char) -> c_int {
    trace!("Rust.write");

    let mut buf_as_cstr;
    unsafe {
        buf_as_cstr = CStr::from_ptr(buffer);
    }
    let buf_as_slice = buf_as_cstr.to_bytes();

    let mut n_buffer = Vec::<u8>::with_capacity(buf_as_slice.len());
    for byte in buf_as_slice.iter() {
        n_buffer.push(*byte);
    }

    unsafe {
        match (*writer_tx).send(n_buffer) {
            Ok(_) => { }
            Err(e) => {
                warn!("Error sending buffer: {}", e);
                let _ = (*kill_tx).send(());
                return -1 as c_int;
            }
        };
    }

    0 as c_int
}

/// Forever listens to incoming data and when a complete message is received,
/// the passed callback is hit
fn reader_thread(client: Bstream, handler: extern fn(*const c_char)) {
    trace!("Rust.reader_thread started");

    let mut reader = client.clone();
    loop {
        match reader.read() {
            Ok(buffer) => {
                // Launch the handler in a new thread
                thread::Builder::new()
                    .name("Reader-Worker".to_string())
                    .spawn(move||{
                        let slice = &buffer[..];
                        let c_buffer = CString::new(slice).unwrap();
                        handler(c_buffer.as_ptr());
                    }).unwrap();
            }
            Err(e) => {
                error!("Error: {}", e);
                break;
            }
        };
    }
    debug!("Rust.reader_thread finished");
    unsafe { let _ = (*kill_tx).send(()); }
}

/// Forever listens to Receiver<Vec<u8>> waiting on messages to come in
/// Once available, blocks until the entire message has been written
fn writer_thread(rx: Receiver<Vec<u8>>, client: Bstream) {
    trace!("Rust.writer_thread started");

    let mut writer = client.clone();
    loop {
        match rx.recv() {
            Ok(ref mut buffer) => {
                match writer.write(buffer) {
                    Ok(_) => { }
                    Err(e) => {
                        error!("Error: {}", e);
                        break;
                    }
                };
            }
            Err(e) => {
                error!("Error: {}", e);
                break;
            }
        };
    }

    debug!("Rust.writer_thread finished");
    unsafe { let _ = (*kill_tx).send(()); }
}

/// Drops the current connection and kills all current threads
#[no_mangle]
pub extern "C" fn kill() {
    unsafe { let _ = (*kill_tx).send(()); }
}
<|endoftext|>


<commit_before><commit_msg>Simplify based on eddyb's comment<commit_after><|endoftext|>


<commit_before>// Copied from rustc-objc-foundation project by SSheldon, examples/custom_class.rs
// https://github.com/SSheldon/rust-objc-foundation/blob/master/examples/custom_class.rs
// Covered by MIT License: https://en.wikipedia.org/wiki/MIT_License

#[macro_use]
extern crate objc;
extern crate objc_foundation;

use std::sync::{Once, ONCE_INIT};

use objc::Message;
use objc::declare::ClassDecl;
use objc::runtime::{Class, Object, Sel};
use objc_foundation::{INSObject, NSObject};

pub enum RustNSObject { }

impl RustNSObject {
    fn number(&self) -> u32 {
        unsafe {
            let obj = &*(self as *const _ as *const Object);
            *obj.get_ivar("_number")
        }
    }

    fn set_number(&mut self, number: u32) {
        unsafe {
            let obj =  &mut *(self as *mut _ as *mut Object);
            obj.set_ivar("_number", number);
        }
    }
}

unsafe impl Message for RustNSObject { }

static RUSTNSOBJECT_REGISTER_CLASS: Once = ONCE_INIT;

impl INSObject for RustNSObject {
    fn class() -> &'static Class {
        RUSTNSOBJECT_REGISTER_CLASS.call_once(|| {
            let superclass = NSObject::class();
            let mut decl = ClassDecl::new("RustNSObject", superclass).unwrap();
            decl.add_ivar::<u32>("_number");

            // Add ObjC methods for getting and setting the number
            extern fn my_object_set_number(this: &mut Object, _cmd: Sel, number: u32) {
                unsafe { this.set_ivar("_number", number); }
            }

            extern fn my_object_get_number(this: &Object, _cmd: Sel) -> u32 {
                unsafe { *this.get_ivar("_number") }
            }

            unsafe {
                let set_number: extern fn(&mut Object, Sel, u32) = my_object_set_number;
                decl.add_method(sel!(setNumber:), set_number);
                let get_number: extern fn(&Object, Sel) -> u32 = my_object_get_number;
                decl.add_method(sel!(number), get_number);
            }

            decl.register();
        });

        Class::get("RustNSObject").unwrap()
    }
}

fn main() {
    let mut obj = RustNSObject::new();

    obj.set_number(7);
    println!("Number: {}", unsafe {
        let number: u32 = msg_send![obj, number];
        number
    });

    unsafe {
        let _: () = msg_send![obj, setNumber:12u32];
    }
    println!("Number: {}", obj.number());
}
<commit_msg>Removed unused OSX file<commit_after><|endoftext|>


<commit_before>use iron::prelude::*;
use iron::Handler;
use iron::status::{self, Status};

use hyper::header::ContentType;
use hyper::mime;

use controller::Reservation;
use controller::User;

// Re-export Logger and Router so you can use redstart::Router instead of redstart::router::Router.
pub use self::logger::Logger;
pub use self::urlparser::{URLParser, URL};
pub use self::cookieparser::CookieParser;
pub use self::configreader::ConfigReader;
pub use self::authentication::Authentication as Auth;
pub use self::session::{Session, Store, SessionStore};
//pub use self::authentication::Authentication::API as AuthAPI;

mod logger;
mod urlparser;
mod cookieparser;
mod cookiesetter;
mod permission;
mod authentication;
pub mod session;
// End Re-export

pub struct RedStart
{
    sessionstore: Store,
}

impl RedStart
{
    pub fn new(sessionstore: Store) -> RedStart
    {
        RedStart { sessionstore: sessionstore }
    }
}

#[allow(unused_variables)]
impl Handler for RedStart
{
    fn handle(&self, req: &mut Request) -> IronResult<Response>
    {
        let controller_string: String;
        let model_string: String;
        {
            let ext_url: &mut [String] = req.extensions.get_mut::<URL>().unwrap(); // If this panics, URLParser has a bug! :D
            controller_string = ext_url[0].clone();
            model_string = ext_url[1].clone();
        }
        let controller: &str = controller_string.as_slice();
        let model: &str = model_string.as_slice();

        let session_key: Option<String>;
        if req.extensions.contains::<Session>()
        {
            let ext_session: &mut String = req.extensions.get_mut::<Session>().unwrap();
            session_key = Some(ext_session.clone());
        }
        else
        {
            session_key = None;
        }

        let reservation = Reservation::new();
        let user = User::new(self.sessionstore.clone());

        let status: Status;
        let body: Box<Reader + Send>;
        let (status, body) = match controller
        {
            "reservation" => { reservation.call(model, req) },
            "user" => { user.call(model, req) },
            _ =>
            {
                (status::NotFound, "".to_string())
            },
        };

        let mime: mime::Mime = "application/json".parse().unwrap();
        let mut res = Response::new();
        res.headers.set(ContentType(mime));
        Ok(res.set(status).set(body))
    }
}

<commit_msg>Made compile<commit_after>use iron::prelude::*;
use iron::Handler;
use iron::status::{self, Status};

use hyper::header::ContentType;
use hyper::mime;

use controller::Reservation;
use controller::User;

// Re-export Logger and Router so you can use redstart::Router instead of redstart::router::Router.
pub use self::logger::Logger;
pub use self::urlparser::{URLParser, URL};
pub use self::cookieparser::CookieParser;
pub use self::cookiesetter::CookieSetter;
pub use self::authentication::Authentication as Auth;
pub use self::session::{Session, Store, SessionStore};
//pub use self::authentication::Authentication::API as AuthAPI;

mod logger;
mod urlparser;
mod cookieparser;
mod cookiesetter;
mod permission;
mod authentication;
pub mod session;
// End Re-export

pub struct RedStart
{
    sessionstore: Store,
}

impl RedStart
{
    pub fn new(sessionstore: Store) -> RedStart
    {
        RedStart { sessionstore: sessionstore }
    }
}

#[allow(unused_variables)]
impl Handler for RedStart
{
    fn handle(&self, req: &mut Request) -> IronResult<Response>
    {
        let controller_string: String;
        let model_string: String;
        {
            let ext_url: &mut [String] = req.extensions.get_mut::<URL>().unwrap(); // If this panics, URLParser has a bug! :D
            controller_string = ext_url[0].clone();
            model_string = ext_url[1].clone();
        }
        let controller: &str = controller_string.as_slice();
        let model: &str = model_string.as_slice();

        let session_key: Option<String>;
        if req.extensions.contains::<Session>()
        {
            let ext_session: &mut String = req.extensions.get_mut::<Session>().unwrap();
            session_key = Some(ext_session.clone());
        }
        else
        {
            session_key = None;
        }

        let reservation = Reservation::new();
        let user = User::new(self.sessionstore.clone());

        let status: Status;
        let body: Box<Reader + Send>;
        let (status, body) = match controller
        {
            "reservation" => { reservation.call(model, req) },
            "user" => { user.call(model, req) },
            _ =>
            {
                (status::NotFound, "".to_string())
            },
        };

        let mime: mime::Mime = "application/json".parse().unwrap();
        let mut res = Response::new();
        res.headers.set(ContentType(mime));
        Ok(res.set(status).set(body))
    }
}

<|endoftext|>


<commit_before>use gl;
use gl::types::*;

pub struct Renderbuffer {
    gl_id: GLuint
}

impl Renderbuffer {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl Drop for Renderbuffer {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteRenderbuffers(1, &self.gl_id as *const GLuint);
        }
    }
}
<commit_msg>Add `Context.gen_renderbuffer()`<commit_after>use gl;
use gl::types::*;
use context::Context;
use types::GLError;

pub struct Renderbuffer {
    gl_id: GLuint
}

impl Renderbuffer {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl Drop for Renderbuffer {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteRenderbuffers(1, &self.gl_id as *const GLuint);
        }
    }
}

impl Context {
    pub fn gen_renderbuffer(&self) -> Renderbuffer {
        unsafe {
            let mut id : GLuint = 0;

            gl::GenRenderbuffers(1, &mut id as *mut GLuint);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`n` is negative",
                _ => "Unknown error"
            }

            Renderbuffer {
                gl_id: id
            }
        }
    }
}
<|endoftext|>


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

use app_units::Au;
use device::TextureFilter;
use euclid::{TypedPoint2D, UnknownUnit};
use fnv::FnvHasher;
use profiler::BackendProfileCounters;
use std::collections::{HashMap, HashSet};
use std::f32;
use std::hash::BuildHasherDefault;
use std::{i32, usize};
use std::path::PathBuf;
use std::sync::Arc;
use tiling;
use renderer::BlendMode;
use webrender_traits::{Epoch, ColorF, PipelineId};
use webrender_traits::{ImageFormat, NativeFontHandle};
use webrender_traits::{ExternalImageData, ExternalImageId, ScrollLayerId};
use webrender_traits::{ImageData};
use webrender_traits::{DeviceUintRect};

// An ID for a texture that is owned by the
// texture cache module. This can include atlases
// or standalone textures allocated via the
// texture cache (e.g. if an image is too large
// to be added to an atlas). The texture cache
// manages the allocation and freeing of these
// IDs, and the rendering thread maintains a
// map from cache texture ID to native texture.

#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct CacheTextureId(pub usize);

// Represents the source for a texture.
// These are passed from throughout the
// pipeline until they reach the rendering
// thread, where they are resolved to a
// native texture ID.

#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum SourceTexture {
    Invalid,
    TextureCache(CacheTextureId),
    External(ExternalImageData),
    #[cfg_attr(not(feature = "webgl"), allow(dead_code))]
    /// This is actually a gl::GLuint, with the shared texture id between the
    /// main context and the WebGL context.
    WebGL(u32),
}

const COLOR_FLOAT_TO_FIXED: f32 = 255.0;
const COLOR_FLOAT_TO_FIXED_WIDE: f32 = 65535.0;
pub const ANGLE_FLOAT_TO_FIXED: f32 = 65535.0;

pub const ORTHO_NEAR_PLANE: f32 = -1000000.0;
pub const ORTHO_FAR_PLANE: f32 = 1000000.0;

#[derive(Clone)]
pub enum FontTemplate {
    Raw(Arc<Vec<u8>>),
    Native(NativeFontHandle),
}

#[derive(Debug, PartialEq, Eq)]
pub enum TextureSampler {
    Color0,
    Color1,
    Color2,
    Mask,
    CacheA8,
    CacheRGBA8,
    Data16,
    Data32,
    Data64,
    Data128,
    Layers,
    RenderTasks,
    Geometry,
    ResourceRects,
    Gradients,
    Dither,
}

impl TextureSampler {
    pub fn color(n: usize) -> TextureSampler {
        match n {
            0 => TextureSampler::Color0,
            1 => TextureSampler::Color1,
            2 => TextureSampler::Color2,
            _ => {
                panic!("There are only 3 color samplers.");
            }
        }
    }
}

/// Optional textures that can be used as a source in the shaders.
/// Textures that are not used by the batch are equal to TextureId::invalid().
#[derive(Copy, Clone, Debug)]
pub struct BatchTextures {
    pub colors: [SourceTexture; 3],
}

impl BatchTextures {
    pub fn no_texture() -> Self {
        BatchTextures {
            colors: [SourceTexture::Invalid; 3],
        }
    }
}

// In some places we need to temporarily bind a texture to any slot.
pub const DEFAULT_TEXTURE: TextureSampler = TextureSampler::Color0;

#[derive(Clone, Copy, Debug)]
pub enum VertexAttribute {
    // vertex-frequency basic attributes
    Position,
    Color,
    ColorTexCoord,
    // instance-frequency primitive attributes
    GlobalPrimId,
    PrimitiveAddress,
    TaskIndex,
    ClipTaskIndex,
    LayerIndex,
    ElementIndex,
    UserData,
    ZIndex,
}

#[derive(Clone, Copy, Debug)]
pub enum ClearAttribute {
    // vertex frequency
    Position,
    // instance frequency
    Rectangle,
}

#[derive(Clone, Copy, Debug)]
pub enum BlurAttribute {
    // vertex frequency
    Position,
    // instance frequency
    RenderTaskIndex,
    SourceTaskIndex,
    Direction,
}

#[derive(Clone, Copy, Debug)]
pub enum ClipAttribute {
    // vertex frequency
    Position,
    // instance frequency
    RenderTaskIndex,
    LayerIndex,
    DataIndex,
    SegmentIndex,
}

// A packed RGBA8 color ordered for vertex data or similar.
// Use PackedTexel instead if intending to upload to a texture.

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct PackedColor {
    pub r: u8,
    pub g: u8,
    pub b: u8,
    pub a: u8,
}

impl PackedColor {
    pub fn from_color(color: &ColorF) -> PackedColor {
        PackedColor {
            r: (0.5 + color.r * COLOR_FLOAT_TO_FIXED).floor() as u8,
            g: (0.5 + color.g * COLOR_FLOAT_TO_FIXED).floor() as u8,
            b: (0.5 + color.b * COLOR_FLOAT_TO_FIXED).floor() as u8,
            a: (0.5 + color.a * COLOR_FLOAT_TO_FIXED).floor() as u8,
        }
    }
}

// RGBA8 textures currently pack texels in BGRA format for upload.
// PackedTexel abstracts away this difference from PackedColor.

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct PackedTexel {
    pub b: u8,
    pub g: u8,
    pub r: u8,
    pub a: u8,
}

impl PackedTexel {
    pub fn high_bytes(color: &ColorF) -> PackedTexel {
        Self::extract_bytes(color, COLOR_FLOAT_TO_FIXED)
    }

    pub fn low_bytes(color: &ColorF) -> PackedTexel {
        Self::extract_bytes(color, COLOR_FLOAT_TO_FIXED_WIDE)
    }

    fn extract_bytes(color: &ColorF, multiplier: f32) -> PackedTexel {
        PackedTexel {
            b: ((0.5 + color.b * multiplier).floor() as u32 & 0xff) as u8,
            g: ((0.5 + color.g * multiplier).floor() as u32 & 0xff) as u8,
            r: ((0.5 + color.r * multiplier).floor() as u32 & 0xff) as u8,
            a: ((0.5 + color.a * multiplier).floor() as u32 & 0xff) as u8,
        }
    }
}

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct PackedVertex {
    pub pos: [f32; 2],
}

#[derive(Debug)]
#[repr(C)]
pub struct DebugFontVertex {
    pub x: f32,
    pub y: f32,
    pub color: PackedColor,
    pub u: f32,
    pub v: f32,
}

impl DebugFontVertex {
    pub fn new(x: f32, y: f32, u: f32, v: f32, color: PackedColor) -> DebugFontVertex {
        DebugFontVertex {
            x: x,
            y: y,
            color: color,
            u: u,
            v: v,
        }
    }
}

#[repr(C)]
pub struct DebugColorVertex {
    pub x: f32,
    pub y: f32,
    pub color: PackedColor,
}

impl DebugColorVertex {
    pub fn new(x: f32, y: f32, color: PackedColor) -> DebugColorVertex {
        DebugColorVertex {
            x: x,
            y: y,
            color: color,
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum RenderTargetMode {
    None,
    SimpleRenderTarget,
    LayerRenderTarget(i32),      // Number of texture layers
}

pub enum TextureUpdateOp {
    Create {
      width: u32,
      height: u32,
      format: ImageFormat,
      filter: TextureFilter,
      mode: RenderTargetMode,
      data: Option<ImageData>,
    },
    Update {
        page_pos_x: u32,    // the texture page position which we want to upload
        page_pos_y: u32,
        width: u32,
        height: u32,
        data: Arc<Vec<u8>>,
        stride: Option<u32>,
        offset: u32,
    },
    UpdateForExternalBuffer {
        rect: DeviceUintRect,
        id: ExternalImageId,
        stride: Option<u32>,
    },
    Grow {
        width: u32,
        height: u32,
        format: ImageFormat,
        filter: TextureFilter,
        mode: RenderTargetMode,
    },
    Free,
}

pub type ExternalImageUpdateList = Vec<ExternalImageId>;

pub struct TextureUpdate {
    pub id: CacheTextureId,
    pub op: TextureUpdateOp,
}

pub struct TextureUpdateList {
    pub updates: Vec<TextureUpdate>,
}

impl TextureUpdateList {
    pub fn new() -> TextureUpdateList {
        TextureUpdateList {
            updates: Vec::new(),
        }
    }

    #[inline]
    pub fn push(&mut self, update: TextureUpdate) {
        self.updates.push(update);
    }
}

/// Mostly wraps a tiling::Frame, adding a bit of extra information.
pub struct RendererFrame {
    /// The last rendered epoch for each pipeline present in the frame.
    /// This information is used to know if a certain transformation on the layout has
    /// been rendered, which is necessary for reftests.
    pub pipeline_epoch_map: HashMap<PipelineId, Epoch, BuildHasherDefault<FnvHasher>>,
    /// The layers that are currently affected by the over-scrolling animation.
    pub layers_bouncing_back: HashSet<ScrollLayerId, BuildHasherDefault<FnvHasher>>,

    pub frame: Option<tiling::Frame>,
}

impl RendererFrame {
    pub fn new(pipeline_epoch_map: HashMap<PipelineId, Epoch, BuildHasherDefault<FnvHasher>>,
               layers_bouncing_back: HashSet<ScrollLayerId, BuildHasherDefault<FnvHasher>>,
               frame: Option<tiling::Frame>)
               -> RendererFrame {
        RendererFrame {
            pipeline_epoch_map: pipeline_epoch_map,
            layers_bouncing_back: layers_bouncing_back,
            frame: frame,
        }
    }
}

pub enum ResultMsg {
    RefreshShader(PathBuf),
    NewFrame(RendererFrame, TextureUpdateList, ExternalImageUpdateList, BackendProfileCounters),
}

#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AxisDirection {
    Horizontal,
    Vertical,
}

#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
pub struct StackingContextIndex(pub usize);

#[derive(Clone, Copy, Debug)]
pub struct RectUv<T, U = UnknownUnit> {
    pub top_left: TypedPoint2D<T, U>,
    pub top_right: TypedPoint2D<T, U>,
    pub bottom_left: TypedPoint2D<T, U>,
    pub bottom_right: TypedPoint2D<T, U>,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum LowLevelFilterOp {
    Blur(Au, AxisDirection),
    Brightness(Au),
    Contrast(Au),
    Grayscale(Au),
    /// Fixed-point in `ANGLE_FLOAT_TO_FIXED` units.
    HueRotate(i32),
    Invert(Au),
    Opacity(Au),
    Saturate(Au),
    Sepia(Au),
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum HardwareCompositeOp {
    PremultipliedAlpha,
}

impl HardwareCompositeOp {
    pub fn to_blend_mode(&self) -> BlendMode {
        match self {
            &HardwareCompositeOp::PremultipliedAlpha => BlendMode::PremultipliedAlpha,
        }
    }
}
<commit_msg>Doug's extract_bytes() fix<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use app_units::Au;
use device::TextureFilter;
use euclid::{TypedPoint2D, UnknownUnit};
use fnv::FnvHasher;
use profiler::BackendProfileCounters;
use std::collections::{HashMap, HashSet};
use std::f32;
use std::hash::BuildHasherDefault;
use std::{i32, usize};
use std::path::PathBuf;
use std::sync::Arc;
use tiling;
use renderer::BlendMode;
use webrender_traits::{Epoch, ColorF, PipelineId};
use webrender_traits::{ImageFormat, NativeFontHandle};
use webrender_traits::{ExternalImageData, ExternalImageId, ScrollLayerId};
use webrender_traits::{ImageData};
use webrender_traits::{DeviceUintRect};

// An ID for a texture that is owned by the
// texture cache module. This can include atlases
// or standalone textures allocated via the
// texture cache (e.g. if an image is too large
// to be added to an atlas). The texture cache
// manages the allocation and freeing of these
// IDs, and the rendering thread maintains a
// map from cache texture ID to native texture.

#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct CacheTextureId(pub usize);

// Represents the source for a texture.
// These are passed from throughout the
// pipeline until they reach the rendering
// thread, where they are resolved to a
// native texture ID.

#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum SourceTexture {
    Invalid,
    TextureCache(CacheTextureId),
    External(ExternalImageData),
    #[cfg_attr(not(feature = "webgl"), allow(dead_code))]
    /// This is actually a gl::GLuint, with the shared texture id between the
    /// main context and the WebGL context.
    WebGL(u32),
}

const COLOR_FLOAT_TO_FIXED: f32 = 255.0;
const COLOR_FLOAT_TO_FIXED_WIDE: f32 = 65535.0;
pub const ANGLE_FLOAT_TO_FIXED: f32 = 65535.0;

pub const ORTHO_NEAR_PLANE: f32 = -1000000.0;
pub const ORTHO_FAR_PLANE: f32 = 1000000.0;

#[derive(Clone)]
pub enum FontTemplate {
    Raw(Arc<Vec<u8>>),
    Native(NativeFontHandle),
}

#[derive(Debug, PartialEq, Eq)]
pub enum TextureSampler {
    Color0,
    Color1,
    Color2,
    Mask,
    CacheA8,
    CacheRGBA8,
    Data16,
    Data32,
    Data64,
    Data128,
    Layers,
    RenderTasks,
    Geometry,
    ResourceRects,
    Gradients,
    Dither,
}

impl TextureSampler {
    pub fn color(n: usize) -> TextureSampler {
        match n {
            0 => TextureSampler::Color0,
            1 => TextureSampler::Color1,
            2 => TextureSampler::Color2,
            _ => {
                panic!("There are only 3 color samplers.");
            }
        }
    }
}

/// Optional textures that can be used as a source in the shaders.
/// Textures that are not used by the batch are equal to TextureId::invalid().
#[derive(Copy, Clone, Debug)]
pub struct BatchTextures {
    pub colors: [SourceTexture; 3],
}

impl BatchTextures {
    pub fn no_texture() -> Self {
        BatchTextures {
            colors: [SourceTexture::Invalid; 3],
        }
    }
}

// In some places we need to temporarily bind a texture to any slot.
pub const DEFAULT_TEXTURE: TextureSampler = TextureSampler::Color0;

#[derive(Clone, Copy, Debug)]
pub enum VertexAttribute {
    // vertex-frequency basic attributes
    Position,
    Color,
    ColorTexCoord,
    // instance-frequency primitive attributes
    GlobalPrimId,
    PrimitiveAddress,
    TaskIndex,
    ClipTaskIndex,
    LayerIndex,
    ElementIndex,
    UserData,
    ZIndex,
}

#[derive(Clone, Copy, Debug)]
pub enum ClearAttribute {
    // vertex frequency
    Position,
    // instance frequency
    Rectangle,
}

#[derive(Clone, Copy, Debug)]
pub enum BlurAttribute {
    // vertex frequency
    Position,
    // instance frequency
    RenderTaskIndex,
    SourceTaskIndex,
    Direction,
}

#[derive(Clone, Copy, Debug)]
pub enum ClipAttribute {
    // vertex frequency
    Position,
    // instance frequency
    RenderTaskIndex,
    LayerIndex,
    DataIndex,
    SegmentIndex,
}

// A packed RGBA8 color ordered for vertex data or similar.
// Use PackedTexel instead if intending to upload to a texture.

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct PackedColor {
    pub r: u8,
    pub g: u8,
    pub b: u8,
    pub a: u8,
}

impl PackedColor {
    pub fn from_color(color: &ColorF) -> PackedColor {
        PackedColor {
            r: (0.5 + color.r * COLOR_FLOAT_TO_FIXED).floor() as u8,
            g: (0.5 + color.g * COLOR_FLOAT_TO_FIXED).floor() as u8,
            b: (0.5 + color.b * COLOR_FLOAT_TO_FIXED).floor() as u8,
            a: (0.5 + color.a * COLOR_FLOAT_TO_FIXED).floor() as u8,
        }
    }
}

// RGBA8 textures currently pack texels in BGRA format for upload.
// PackedTexel abstracts away this difference from PackedColor.

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct PackedTexel {
    pub b: u8,
    pub g: u8,
    pub r: u8,
    pub a: u8,
}

impl PackedTexel {
    pub fn high_bytes(color: &ColorF) -> PackedTexel {
        Self::extract_bytes(color, 8)
    }

    pub fn low_bytes(color: &ColorF) -> PackedTexel {
        Self::extract_bytes(color, 0)
    }

    fn extract_bytes(color: &ColorF, shift_by: i32) -> PackedTexel {
        PackedTexel {
            b: ((0.5 + color.b * COLOR_FLOAT_TO_FIXED_WIDE).floor() as u32 >> shift_by & 0xff) as u8,
            g: ((0.5 + color.g * COLOR_FLOAT_TO_FIXED_WIDE).floor() as u32 >> shift_by & 0xff) as u8,
            r: ((0.5 + color.r * COLOR_FLOAT_TO_FIXED_WIDE).floor() as u32 >> shift_by & 0xff) as u8,
            a: ((0.5 + color.a * COLOR_FLOAT_TO_FIXED_WIDE).floor() as u32 >> shift_by & 0xff) as u8,
        }
    }
}

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct PackedVertex {
    pub pos: [f32; 2],
}

#[derive(Debug)]
#[repr(C)]
pub struct DebugFontVertex {
    pub x: f32,
    pub y: f32,
    pub color: PackedColor,
    pub u: f32,
    pub v: f32,
}

impl DebugFontVertex {
    pub fn new(x: f32, y: f32, u: f32, v: f32, color: PackedColor) -> DebugFontVertex {
        DebugFontVertex {
            x: x,
            y: y,
            color: color,
            u: u,
            v: v,
        }
    }
}

#[repr(C)]
pub struct DebugColorVertex {
    pub x: f32,
    pub y: f32,
    pub color: PackedColor,
}

impl DebugColorVertex {
    pub fn new(x: f32, y: f32, color: PackedColor) -> DebugColorVertex {
        DebugColorVertex {
            x: x,
            y: y,
            color: color,
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum RenderTargetMode {
    None,
    SimpleRenderTarget,
    LayerRenderTarget(i32),      // Number of texture layers
}

pub enum TextureUpdateOp {
    Create {
      width: u32,
      height: u32,
      format: ImageFormat,
      filter: TextureFilter,
      mode: RenderTargetMode,
      data: Option<ImageData>,
    },
    Update {
        page_pos_x: u32,    // the texture page position which we want to upload
        page_pos_y: u32,
        width: u32,
        height: u32,
        data: Arc<Vec<u8>>,
        stride: Option<u32>,
        offset: u32,
    },
    UpdateForExternalBuffer {
        rect: DeviceUintRect,
        id: ExternalImageId,
        stride: Option<u32>,
    },
    Grow {
        width: u32,
        height: u32,
        format: ImageFormat,
        filter: TextureFilter,
        mode: RenderTargetMode,
    },
    Free,
}

pub type ExternalImageUpdateList = Vec<ExternalImageId>;

pub struct TextureUpdate {
    pub id: CacheTextureId,
    pub op: TextureUpdateOp,
}

pub struct TextureUpdateList {
    pub updates: Vec<TextureUpdate>,
}

impl TextureUpdateList {
    pub fn new() -> TextureUpdateList {
        TextureUpdateList {
            updates: Vec::new(),
        }
    }

    #[inline]
    pub fn push(&mut self, update: TextureUpdate) {
        self.updates.push(update);
    }
}

/// Mostly wraps a tiling::Frame, adding a bit of extra information.
pub struct RendererFrame {
    /// The last rendered epoch for each pipeline present in the frame.
    /// This information is used to know if a certain transformation on the layout has
    /// been rendered, which is necessary for reftests.
    pub pipeline_epoch_map: HashMap<PipelineId, Epoch, BuildHasherDefault<FnvHasher>>,
    /// The layers that are currently affected by the over-scrolling animation.
    pub layers_bouncing_back: HashSet<ScrollLayerId, BuildHasherDefault<FnvHasher>>,

    pub frame: Option<tiling::Frame>,
}

impl RendererFrame {
    pub fn new(pipeline_epoch_map: HashMap<PipelineId, Epoch, BuildHasherDefault<FnvHasher>>,
               layers_bouncing_back: HashSet<ScrollLayerId, BuildHasherDefault<FnvHasher>>,
               frame: Option<tiling::Frame>)
               -> RendererFrame {
        RendererFrame {
            pipeline_epoch_map: pipeline_epoch_map,
            layers_bouncing_back: layers_bouncing_back,
            frame: frame,
        }
    }
}

pub enum ResultMsg {
    RefreshShader(PathBuf),
    NewFrame(RendererFrame, TextureUpdateList, ExternalImageUpdateList, BackendProfileCounters),
}

#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AxisDirection {
    Horizontal,
    Vertical,
}

#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
pub struct StackingContextIndex(pub usize);

#[derive(Clone, Copy, Debug)]
pub struct RectUv<T, U = UnknownUnit> {
    pub top_left: TypedPoint2D<T, U>,
    pub top_right: TypedPoint2D<T, U>,
    pub bottom_left: TypedPoint2D<T, U>,
    pub bottom_right: TypedPoint2D<T, U>,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum LowLevelFilterOp {
    Blur(Au, AxisDirection),
    Brightness(Au),
    Contrast(Au),
    Grayscale(Au),
    /// Fixed-point in `ANGLE_FLOAT_TO_FIXED` units.
    HueRotate(i32),
    Invert(Au),
    Opacity(Au),
    Saturate(Au),
    Sepia(Au),
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum HardwareCompositeOp {
    PremultipliedAlpha,
}

impl HardwareCompositeOp {
    pub fn to_blend_mode(&self) -> BlendMode {
        match self {
            &HardwareCompositeOp::PremultipliedAlpha => BlendMode::PremultipliedAlpha,
        }
    }
}
<|endoftext|>


<commit_before>use syntax::{
    ast::{self, make},
    ted, AstNode,
};

use crate::{
    assist_context::{AssistContext, Assists},
    AssistId, AssistKind,
};

// Assist: pull_assignment_up
//
// Extracts variable assignment to outside an if or match statement.
//
// ```
// fn main() {
//     let mut foo = 6;
//
//     if true {
//         $0foo = 5;
//     } else {
//         foo = 4;
//     }
// }
// ```
// ->
// ```
// fn main() {
//     let mut foo = 6;
//
//     foo = if true {
//         5
//     } else {
//         4
//     };
// }
// ```
pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let assign_expr = ctx.find_node_at_offset::<ast::BinExpr>()?;

    let op_kind = assign_expr.op_kind()?;
    if op_kind != ast::BinOp::Assignment {
        cov_mark::hit!(test_cant_pull_non_assignments);
        return None;
    }

    let mut collector = AssignmentsCollector {
        sema: &ctx.sema,
        common_lhs: assign_expr.lhs()?,
        assignments: Vec::new(),
    };

    let tgt: ast::Expr = if let Some(if_expr) = ctx.find_node_at_offset::<ast::IfExpr>() {
        collector.collect_if(&if_expr)?;
        if_expr.into()
    } else if let Some(match_expr) = ctx.find_node_at_offset::<ast::MatchExpr>() {
        collector.collect_match(&match_expr)?;
        match_expr.into()
    } else {
        return None;
    };

    acc.add(
        AssistId("pull_assignment_up", AssistKind::RefactorExtract),
        "Pull assignment up",
        tgt.syntax().text_range(),
        move |edit| {
            let assignments: Vec<_> = collector
                .assignments
                .into_iter()
                .map(|(stmt, rhs)| (edit.make_ast_mut(stmt), rhs.clone_for_update()))
                .collect();

            let tgt = edit.make_ast_mut(tgt);

            for (stmt, rhs) in assignments {
                ted::replace(stmt.syntax(), rhs.syntax());
            }
            let assign_expr = make::expr_assignment(collector.common_lhs, tgt.clone());
            let assign_stmt = make::expr_stmt(assign_expr);

            ted::replace(tgt.syntax(), assign_stmt.syntax().clone_for_update());
        },
    )
}

struct AssignmentsCollector<'a> {
    sema: &'a hir::Semantics<'a, ide_db::RootDatabase>,
    common_lhs: ast::Expr,
    assignments: Vec<(ast::ExprStmt, ast::Expr)>,
}

impl<'a> AssignmentsCollector<'a> {
    fn collect_match(&mut self, match_expr: &ast::MatchExpr) -> Option<()> {
        for arm in match_expr.match_arm_list()?.arms() {
            match arm.expr()? {
                ast::Expr::BlockExpr(block) => self.collect_block(&block)?,
                // TODO: Handle this while we are at it?
                _ => return None,
            }
        }

        Some(())
    }
    fn collect_if(&mut self, if_expr: &ast::IfExpr) -> Option<()> {
        let then_branch = if_expr.then_branch()?;
        self.collect_block(&then_branch)?;

        match if_expr.else_branch()? {
            ast::ElseBranch::Block(block) => self.collect_block(&block),
            ast::ElseBranch::IfExpr(expr) => {
                cov_mark::hit!(test_pull_assignment_up_chained_if);
                self.collect_if(&expr)
            }
        }
    }
    fn collect_block(&mut self, block: &ast::BlockExpr) -> Option<()> {
        if block.tail_expr().is_some() {
            return None;
        }

        let last_stmt = block.statements().last()?;
        if let ast::Stmt::ExprStmt(stmt) = last_stmt {
            if let ast::Expr::BinExpr(expr) = stmt.expr()? {
                if expr.op_kind()? == ast::BinOp::Assignment
                    && is_equivalent(self.sema, &expr.lhs()?, &self.common_lhs)
                {
                    self.assignments.push((stmt, expr.rhs()?));
                    return Some(());
                }
            }
        }

        None
    }
}

fn is_equivalent(
    sema: &hir::Semantics<ide_db::RootDatabase>,
    expr0: &ast::Expr,
    expr1: &ast::Expr,
) -> bool {
    match (expr0, expr1) {
        (ast::Expr::FieldExpr(field_expr0), ast::Expr::FieldExpr(field_expr1)) => {
            cov_mark::hit!(test_pull_assignment_up_field_assignment);
            sema.resolve_field(field_expr0) == sema.resolve_field(field_expr1)
        }
        (ast::Expr::PathExpr(path0), ast::Expr::PathExpr(path1)) => {
            let path0 = path0.path();
            let path1 = path1.path();
            if let (Some(path0), Some(path1)) = (path0, path1) {
                sema.resolve_path(&path0) == sema.resolve_path(&path1)
            } else {
                false
            }
        }
        (ast::Expr::PrefixExpr(prefix0), ast::Expr::PrefixExpr(prefix1))
            if prefix0.op_kind() == Some(ast::PrefixOp::Deref)
                && prefix1.op_kind() == Some(ast::PrefixOp::Deref) =>
        {
            cov_mark::hit!(test_pull_assignment_up_deref);
            if let (Some(prefix0), Some(prefix1)) = (prefix0.expr(), prefix1.expr()) {
                is_equivalent(sema, &prefix0, &prefix1)
            } else {
                false
            }
        }
        _ => false,
    }
}

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

    use crate::tests::{check_assist, check_assist_not_applicable};

    #[test]
    fn test_pull_assignment_up_if() {
        check_assist(
            pull_assignment_up,
            r#"
fn foo() {
    let mut a = 1;

    if true {
        $0a = 2;
    } else {
        a = 3;
    }
}"#,
            r#"
fn foo() {
    let mut a = 1;

    a = if true {
        2
    } else {
        3
    };
}"#,
        );
    }

    #[test]
    fn test_pull_assignment_up_match() {
        check_assist(
            pull_assignment_up,
            r#"
fn foo() {
    let mut a = 1;

    match 1 {
        1 => {
            $0a = 2;
        },
        2 => {
            a = 3;
        },
        3 => {
            a = 4;
        }
    }
}"#,
            r#"
fn foo() {
    let mut a = 1;

    a = match 1 {
        1 => {
            2
        },
        2 => {
            3
        },
        3 => {
            4
        }
    };
}"#,
        );
    }

    #[test]
    fn test_pull_assignment_up_not_last_not_applicable() {
        check_assist_not_applicable(
            pull_assignment_up,
            r#"
fn foo() {
    let mut a = 1;

    if true {
        $0a = 2;
        b = a;
    } else {
        a = 3;
    }
}"#,
        )
    }

    #[test]
    fn test_pull_assignment_up_chained_if() {
        cov_mark::check!(test_pull_assignment_up_chained_if);
        check_assist(
            pull_assignment_up,
            r#"
fn foo() {
    let mut a = 1;

    if true {
        $0a = 2;
    } else if false {
        a = 3;
    } else {
        a = 4;
    }
}"#,
            r#"
fn foo() {
    let mut a = 1;

    a = if true {
        2
    } else if false {
        3
    } else {
        4
    };
}"#,
        );
    }

    #[test]
    fn test_pull_assignment_up_retains_stmts() {
        check_assist(
            pull_assignment_up,
            r#"
fn foo() {
    let mut a = 1;

    if true {
        let b = 2;
        $0a = 2;
    } else {
        let b = 3;
        a = 3;
    }
}"#,
            r#"
fn foo() {
    let mut a = 1;

    a = if true {
        let b = 2;
        2
    } else {
        let b = 3;
        3
    };
}"#,
        )
    }

    #[test]
    fn pull_assignment_up_let_stmt_not_applicable() {
        check_assist_not_applicable(
            pull_assignment_up,
            r#"
fn foo() {
    let mut a = 1;

    let b = if true {
        $0a = 2
    } else {
        a = 3
    };
}"#,
        )
    }

    #[test]
    fn pull_assignment_up_if_missing_assigment_not_applicable() {
        check_assist_not_applicable(
            pull_assignment_up,
            r#"
fn foo() {
    let mut a = 1;

    if true {
        $0a = 2;
    } else {}
}"#,
        )
    }

    #[test]
    fn pull_assignment_up_match_missing_assigment_not_applicable() {
        check_assist_not_applicable(
            pull_assignment_up,
            r#"
fn foo() {
    let mut a = 1;

    match 1 {
        1 => {
            $0a = 2;
        },
        2 => {
            a = 3;
        },
        3 => {},
    }
}"#,
        )
    }

    #[test]
    fn test_pull_assignment_up_field_assignment() {
        cov_mark::check!(test_pull_assignment_up_field_assignment);
        check_assist(
            pull_assignment_up,
            r#"
struct A(usize);

fn foo() {
    let mut a = A(1);

    if true {
        $0a.0 = 2;
    } else {
        a.0 = 3;
    }
}"#,
            r#"
struct A(usize);

fn foo() {
    let mut a = A(1);

    a.0 = if true {
        2
    } else {
        3
    };
}"#,
        )
    }

    #[test]
    fn test_pull_assignment_up_deref() {
        cov_mark::check!(test_pull_assignment_up_deref);
        check_assist(
            pull_assignment_up,
            r#"
fn foo() {
    let mut a = 1;
    let b = &mut a;

    if true {
        $0*b = 2;
    } else {
        *b = 3;
    }
}
"#,
            r#"
fn foo() {
    let mut a = 1;
    let b = &mut a;

    *b = if true {
        2
    } else {
        3
    };
}
"#,
        )
    }

    #[test]
    fn test_cant_pull_non_assignments() {
        cov_mark::check!(test_cant_pull_non_assignments);
        check_assist_not_applicable(
            pull_assignment_up,
            r#"
fn foo() {
    let mut a = 1;
    let b = &mut a;

    if true {
        $0*b + 2;
    } else {
        *b + 3;
    }
}
"#,
        )
    }
}
<commit_msg>feat: add "mentoring instructions" test for pull up assist<commit_after>use syntax::{
    ast::{self, make},
    ted, AstNode,
};

use crate::{
    assist_context::{AssistContext, Assists},
    AssistId, AssistKind,
};

// Assist: pull_assignment_up
//
// Extracts variable assignment to outside an if or match statement.
//
// ```
// fn main() {
//     let mut foo = 6;
//
//     if true {
//         $0foo = 5;
//     } else {
//         foo = 4;
//     }
// }
// ```
// ->
// ```
// fn main() {
//     let mut foo = 6;
//
//     foo = if true {
//         5
//     } else {
//         4
//     };
// }
// ```
pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let assign_expr = ctx.find_node_at_offset::<ast::BinExpr>()?;

    let op_kind = assign_expr.op_kind()?;
    if op_kind != ast::BinOp::Assignment {
        cov_mark::hit!(test_cant_pull_non_assignments);
        return None;
    }

    let mut collector = AssignmentsCollector {
        sema: &ctx.sema,
        common_lhs: assign_expr.lhs()?,
        assignments: Vec::new(),
    };

    let tgt: ast::Expr = if let Some(if_expr) = ctx.find_node_at_offset::<ast::IfExpr>() {
        collector.collect_if(&if_expr)?;
        if_expr.into()
    } else if let Some(match_expr) = ctx.find_node_at_offset::<ast::MatchExpr>() {
        collector.collect_match(&match_expr)?;
        match_expr.into()
    } else {
        return None;
    };

    acc.add(
        AssistId("pull_assignment_up", AssistKind::RefactorExtract),
        "Pull assignment up",
        tgt.syntax().text_range(),
        move |edit| {
            let assignments: Vec<_> = collector
                .assignments
                .into_iter()
                .map(|(stmt, rhs)| (edit.make_ast_mut(stmt), rhs.clone_for_update()))
                .collect();

            let tgt = edit.make_ast_mut(tgt);

            for (stmt, rhs) in assignments {
                ted::replace(stmt.syntax(), rhs.syntax());
            }
            let assign_expr = make::expr_assignment(collector.common_lhs, tgt.clone());
            let assign_stmt = make::expr_stmt(assign_expr);

            ted::replace(tgt.syntax(), assign_stmt.syntax().clone_for_update());
        },
    )
}

struct AssignmentsCollector<'a> {
    sema: &'a hir::Semantics<'a, ide_db::RootDatabase>,
    common_lhs: ast::Expr,
    assignments: Vec<(ast::ExprStmt, ast::Expr)>,
}

impl<'a> AssignmentsCollector<'a> {
    fn collect_match(&mut self, match_expr: &ast::MatchExpr) -> Option<()> {
        for arm in match_expr.match_arm_list()?.arms() {
            match arm.expr()? {
                ast::Expr::BlockExpr(block) => self.collect_block(&block)?,
                _ => return None,
            }
        }

        Some(())
    }
    fn collect_if(&mut self, if_expr: &ast::IfExpr) -> Option<()> {
        let then_branch = if_expr.then_branch()?;
        self.collect_block(&then_branch)?;

        match if_expr.else_branch()? {
            ast::ElseBranch::Block(block) => self.collect_block(&block),
            ast::ElseBranch::IfExpr(expr) => {
                cov_mark::hit!(test_pull_assignment_up_chained_if);
                self.collect_if(&expr)
            }
        }
    }
    fn collect_block(&mut self, block: &ast::BlockExpr) -> Option<()> {
        if block.tail_expr().is_some() {
            return None;
        }

        let last_stmt = block.statements().last()?;
        if let ast::Stmt::ExprStmt(stmt) = last_stmt {
            if let ast::Expr::BinExpr(expr) = stmt.expr()? {
                if expr.op_kind()? == ast::BinOp::Assignment
                    && is_equivalent(self.sema, &expr.lhs()?, &self.common_lhs)
                {
                    self.assignments.push((stmt, expr.rhs()?));
                    return Some(());
                }
            }
        }

        None
    }
}

fn is_equivalent(
    sema: &hir::Semantics<ide_db::RootDatabase>,
    expr0: &ast::Expr,
    expr1: &ast::Expr,
) -> bool {
    match (expr0, expr1) {
        (ast::Expr::FieldExpr(field_expr0), ast::Expr::FieldExpr(field_expr1)) => {
            cov_mark::hit!(test_pull_assignment_up_field_assignment);
            sema.resolve_field(field_expr0) == sema.resolve_field(field_expr1)
        }
        (ast::Expr::PathExpr(path0), ast::Expr::PathExpr(path1)) => {
            let path0 = path0.path();
            let path1 = path1.path();
            if let (Some(path0), Some(path1)) = (path0, path1) {
                sema.resolve_path(&path0) == sema.resolve_path(&path1)
            } else {
                false
            }
        }
        (ast::Expr::PrefixExpr(prefix0), ast::Expr::PrefixExpr(prefix1))
            if prefix0.op_kind() == Some(ast::PrefixOp::Deref)
                && prefix1.op_kind() == Some(ast::PrefixOp::Deref) =>
        {
            cov_mark::hit!(test_pull_assignment_up_deref);
            if let (Some(prefix0), Some(prefix1)) = (prefix0.expr(), prefix1.expr()) {
                is_equivalent(sema, &prefix0, &prefix1)
            } else {
                false
            }
        }
        _ => false,
    }
}

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

    use crate::tests::{check_assist, check_assist_not_applicable};

    #[test]
    fn test_pull_assignment_up_if() {
        check_assist(
            pull_assignment_up,
            r#"
fn foo() {
    let mut a = 1;

    if true {
        $0a = 2;
    } else {
        a = 3;
    }
}"#,
            r#"
fn foo() {
    let mut a = 1;

    a = if true {
        2
    } else {
        3
    };
}"#,
        );
    }

    #[test]
    fn test_pull_assignment_up_match() {
        check_assist(
            pull_assignment_up,
            r#"
fn foo() {
    let mut a = 1;

    match 1 {
        1 => {
            $0a = 2;
        },
        2 => {
            a = 3;
        },
        3 => {
            a = 4;
        }
    }
}"#,
            r#"
fn foo() {
    let mut a = 1;

    a = match 1 {
        1 => {
            2
        },
        2 => {
            3
        },
        3 => {
            4
        }
    };
}"#,
        );
    }

    #[test]
    #[ignore]
    fn test_pull_assignment_up_assignment_expressions() {
        check_assist(
            pull_assignment_up,
            r#"
fn foo() {
    let mut a = 1;

    match 1 {
        1 => { $0a = 2; },
        2 => a = 3,
        3 => {
            a = 4
        }
    }
}"#,
            r#"
fn foo() {
    let mut a = 1;

    a = match 1 {
        1 => { 2 },
        2 => 3,
        3 => {
            4
        }
    };
}"#,
        );
    }

    #[test]
    fn test_pull_assignment_up_not_last_not_applicable() {
        check_assist_not_applicable(
            pull_assignment_up,
            r#"
fn foo() {
    let mut a = 1;

    if true {
        $0a = 2;
        b = a;
    } else {
        a = 3;
    }
}"#,
        )
    }

    #[test]
    fn test_pull_assignment_up_chained_if() {
        cov_mark::check!(test_pull_assignment_up_chained_if);
        check_assist(
            pull_assignment_up,
            r#"
fn foo() {
    let mut a = 1;

    if true {
        $0a = 2;
    } else if false {
        a = 3;
    } else {
        a = 4;
    }
}"#,
            r#"
fn foo() {
    let mut a = 1;

    a = if true {
        2
    } else if false {
        3
    } else {
        4
    };
}"#,
        );
    }

    #[test]
    fn test_pull_assignment_up_retains_stmts() {
        check_assist(
            pull_assignment_up,
            r#"
fn foo() {
    let mut a = 1;

    if true {
        let b = 2;
        $0a = 2;
    } else {
        let b = 3;
        a = 3;
    }
}"#,
            r#"
fn foo() {
    let mut a = 1;

    a = if true {
        let b = 2;
        2
    } else {
        let b = 3;
        3
    };
}"#,
        )
    }

    #[test]
    fn pull_assignment_up_let_stmt_not_applicable() {
        check_assist_not_applicable(
            pull_assignment_up,
            r#"
fn foo() {
    let mut a = 1;

    let b = if true {
        $0a = 2
    } else {
        a = 3
    };
}"#,
        )
    }

    #[test]
    fn pull_assignment_up_if_missing_assigment_not_applicable() {
        check_assist_not_applicable(
            pull_assignment_up,
            r#"
fn foo() {
    let mut a = 1;

    if true {
        $0a = 2;
    } else {}
}"#,
        )
    }

    #[test]
    fn pull_assignment_up_match_missing_assigment_not_applicable() {
        check_assist_not_applicable(
            pull_assignment_up,
            r#"
fn foo() {
    let mut a = 1;

    match 1 {
        1 => {
            $0a = 2;
        },
        2 => {
            a = 3;
        },
        3 => {},
    }
}"#,
        )
    }

    #[test]
    fn test_pull_assignment_up_field_assignment() {
        cov_mark::check!(test_pull_assignment_up_field_assignment);
        check_assist(
            pull_assignment_up,
            r#"
struct A(usize);

fn foo() {
    let mut a = A(1);

    if true {
        $0a.0 = 2;
    } else {
        a.0 = 3;
    }
}"#,
            r#"
struct A(usize);

fn foo() {
    let mut a = A(1);

    a.0 = if true {
        2
    } else {
        3
    };
}"#,
        )
    }

    #[test]
    fn test_pull_assignment_up_deref() {
        cov_mark::check!(test_pull_assignment_up_deref);
        check_assist(
            pull_assignment_up,
            r#"
fn foo() {
    let mut a = 1;
    let b = &mut a;

    if true {
        $0*b = 2;
    } else {
        *b = 3;
    }
}
"#,
            r#"
fn foo() {
    let mut a = 1;
    let b = &mut a;

    *b = if true {
        2
    } else {
        3
    };
}
"#,
        )
    }

    #[test]
    fn test_cant_pull_non_assignments() {
        cov_mark::check!(test_cant_pull_non_assignments);
        check_assist_not_applicable(
            pull_assignment_up,
            r#"
fn foo() {
    let mut a = 1;
    let b = &mut a;

    if true {
        $0*b + 2;
    } else {
        *b + 3;
    }
}
"#,
        )
    }
}
<|endoftext|>


<commit_before>use crate::completion::{CompletionItem, Completions, CompletionKind, CompletionItemKind, CompletionContext, completion_item::Builder};

fn snippet(label: &str, snippet: &str) -> Builder {
    CompletionItem::new(CompletionKind::Snippet, label)
        .snippet(snippet)
        .kind(CompletionItemKind::Keyword)
}

pub(super) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
    if !(ctx.is_trivial_path && ctx.enclosing_fn.is_some()) {
        return;
    }
    snippet("pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
    snippet("ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
}

pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
    if !ctx.is_new_item {
        return;
    }
    snippet(
        "Test function",
        "\
#[test]
fn ${1:feature}() {
    $0
}",
    )
    .lookup_by("tfn")
    .add_to(acc);

    snippet("pub(crate)", "pub(crate) $0").add_to(acc);
}

#[cfg(test)]
mod tests {
    use crate::completion::{CompletionKind, check_completion};
    fn check_snippet_completion(code: &str, expected_completions: &str) {
        check_completion(code, expected_completions, CompletionKind::Snippet);
    }

    #[test]
    fn completes_snippets_in_expressions() {
        check_snippet_completion(
            r"fn foo(x: i32) { <|> }",
            r##"
            pd "eprintln!(\"$0 = {:?}\", $0);"
            ppd "eprintln!(\"$0 = {:#?}\", $0);"
            "##,
        );
    }

    #[test]
    fn completes_snippets_in_items() {
        // check_snippet_completion(r"
        //     <|>
        //     ",
        //     r##"[CompletionItem { label: "Test function", lookup: None, snippet: Some("#[test]\nfn test_${1:feature}() {\n$0\n}"##,
        // );
        check_snippet_completion(
            r"
            #[cfg(test)]
            mod tests {
                <|>
            }
            ",
            r##"
            tfn "Test function" "#[test]\nfn ${1:feature}() {\n    $0\n}"
            pub(crate) "pub(crate) $0"
            "##,
        );
    }
}
<commit_msg>fix snippet icon<commit_after>use crate::completion::{CompletionItem, Completions, CompletionKind, CompletionItemKind, CompletionContext, completion_item::Builder};

fn snippet(label: &str, snippet: &str) -> Builder {
    CompletionItem::new(CompletionKind::Snippet, label)
        .snippet(snippet)
        .kind(CompletionItemKind::Snippet)
}

pub(super) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
    if !(ctx.is_trivial_path && ctx.enclosing_fn.is_some()) {
        return;
    }
    snippet("pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
    snippet("ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
}

pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
    if !ctx.is_new_item {
        return;
    }
    snippet(
        "Test function",
        "\
#[test]
fn ${1:feature}() {
    $0
}",
    )
    .lookup_by("tfn")
    .add_to(acc);

    snippet("pub(crate)", "pub(crate) $0").add_to(acc);
}

#[cfg(test)]
mod tests {
    use crate::completion::{CompletionKind, check_completion};
    fn check_snippet_completion(code: &str, expected_completions: &str) {
        check_completion(code, expected_completions, CompletionKind::Snippet);
    }

    #[test]
    fn completes_snippets_in_expressions() {
        check_snippet_completion(
            r"fn foo(x: i32) { <|> }",
            r##"
            pd "eprintln!(\"$0 = {:?}\", $0);"
            ppd "eprintln!(\"$0 = {:#?}\", $0);"
            "##,
        );
    }

    #[test]
    fn completes_snippets_in_items() {
        // check_snippet_completion(r"
        //     <|>
        //     ",
        //     r##"[CompletionItem { label: "Test function", lookup: None, snippet: Some("#[test]\nfn test_${1:feature}() {\n$0\n}"##,
        // );
        check_snippet_completion(
            r"
            #[cfg(test)]
            mod tests {
                <|>
            }
            ",
            r##"
            tfn "Test function" "#[test]\nfn ${1:feature}() {\n    $0\n}"
            pub(crate) "pub(crate) $0"
            "##,
        );
    }
}
<|endoftext|>


<commit_before>use std::ascii::AsciiExt;
use std::collections::HashMap;
use std::collections::LinkedList;
use std::str::FromStr;

use Term::*;

pub type Value = i32;
pub type ForthResult = Result<(), Error>;
type StackResult<T> = Result<T, Error>;

type Stack = LinkedList<Value>;
type Code = LinkedList<Term>;
type Definitions = HashMap<String, Code>;

pub struct Forth {
    code: Code,
    defs: Definitions,
    stack: Stack,
}

#[derive(Debug, PartialEq)]
pub enum Error {
    DivisionByZero,
    StackUnderflow,
    UnknownWord,
    InvalidWord,
}

#[derive(Debug, Clone)]
enum Term {
    Number(Value),
    Word(String),
    StartDefinition,
    EndDefinition,
}

impl FromStr for Term {
    type Err = ();

    fn from_str(s: &str) -> Result<Term, ()> {
        match s {
            ":" => Ok(StartDefinition),
            ";" => Ok(EndDefinition),
            _   => Err(())
        }
        .or_else(|_| Value::from_str(s).map(Number))
        .or_else(|_| Ok(Word(s.to_ascii_lowercase())))
    }
}

impl Forth {
    pub fn new() -> Forth {
        Forth {
            code: LinkedList::new(),
            defs: HashMap::new(),
            stack: LinkedList::new(),
        }
    }

    pub fn format_stack(&self) -> String {
        let mut s = self.stack.iter().fold(String::new(), |s, v| {
            s + &v.to_string() + " "
        });
        s.pop();
        s
    }

    pub fn eval(&mut self, input: &str) -> ForthResult {
        let mut new_code = Forth::into_code(input);
        self.code.append(&mut new_code);
        self.run()
    }

    fn run(&mut self) -> ForthResult {
        loop {
            match self.code.pop_front() {
                Some(term) => try!(self.step_term(term)),
                None       => break,
            }
        }

        Forth::ok()
    }

    fn step_term(&mut self, term: Term) -> ForthResult {
        match term {
            Number(value)   => self.push(value),
            Word(word)      => self.step_word(word),
            StartDefinition => self.store_definition(),
            EndDefinition   => Err(Error::InvalidWord),
        }
    }

    fn step_word(&mut self, word: String) -> ForthResult {
        self.defs.get(&word)
            .ok_or(Error::UnknownWord)
            .map(Clone::clone)
            .map(|mut code| self.code.append(&mut code))
            .or_else(|_| self.step_built_in(&word))
    }

    fn step_built_in(&mut self, word: &String) -> ForthResult {
        match word.as_ref() {
            "+" =>
                self.bin_op(|(a, b)| Ok(a + b)),
            "-" =>
                self.bin_op(|(a, b)| Ok(a - b)),
            "*" =>
                self.bin_op(|(a, b)| Ok(a * b)),
            "/" =>
                self.bin_op(|(a, b)| {
                    a.checked_div(b).ok_or(Error::DivisionByZero)
                }),
            "dup" =>
                self.pop().and_then(|a| {
                    self.push(a).and(self.push(a))
                }),
            "drop" =>
                self.pop().and(Forth::ok()),
            "swap" =>
                self.pop_two().and_then(|(a, b)| {
                    self.push(b).and(self.push(a))
                }),
            "over" =>
                self.pop_two().and_then(|(a, b)| {
                    self.push(a).and(self.push(b)).and(self.push(a))
                }),
            _ =>
                Err(Error::UnknownWord)
        }
    }

    fn store_definition(&mut self) -> ForthResult {
        let mut def = LinkedList::new();

        loop {
            match self.code.pop_front() {
                Some(EndDefinition) => break,
                Some(term)          => def.push_back(term),
                None                => return Err(Error::InvalidWord),
            }
        }

        if let Some(Word(name)) = def.pop_front() {
            self.store_word(name, def)
        } else {
            Err(Error::InvalidWord)
        }
    }

    fn push(&mut self, value: Value) -> ForthResult {
        self.stack.push_back(value);
        Forth::ok()
    }

    fn pop(&mut self) -> StackResult<Value> {
        self.stack.pop_back()
            .ok_or(Error::StackUnderflow)
    }

    fn pop_two(&mut self) -> StackResult<(Value, Value)> {
        self.pop()
            .and_then(|b| {
                self.pop()
                    .and_then(|a| Ok((a, b)))
            })
    }

    fn bin_op<F>(&mut self, op: F) -> ForthResult
        where F: FnOnce((Value, Value)) -> StackResult<Value> {
        self.pop_two()
            .and_then(op)
            .and_then(|value| self.push(value))
    }

    fn store_word(&mut self, name: String, code: Code) -> ForthResult {
        self.defs.insert(name, code);
        Forth::ok()
    }

    fn into_code(input: &str) -> LinkedList<Term> {
        input
            .split(|c: char| c.is_whitespace() || c.is_control())
            .map(Term::from_str)
            .filter(Result::is_ok)
            .map(Result::unwrap)
            .collect()
    }

    fn ok() -> ForthResult { Ok(()) }
}
<commit_msg>forth: Use while let<commit_after>use std::ascii::AsciiExt;
use std::collections::HashMap;
use std::collections::LinkedList;
use std::str::FromStr;

use Term::*;

pub type Value = i32;
pub type ForthResult = Result<(), Error>;
type StackResult<T> = Result<T, Error>;

type Stack = LinkedList<Value>;
type Code = LinkedList<Term>;
type Definitions = HashMap<String, Code>;

pub struct Forth {
    code: Code,
    defs: Definitions,
    stack: Stack,
}

#[derive(Debug, PartialEq)]
pub enum Error {
    DivisionByZero,
    StackUnderflow,
    UnknownWord,
    InvalidWord,
}

#[derive(Debug, Clone)]
enum Term {
    Number(Value),
    Word(String),
    StartDefinition,
    EndDefinition,
}

impl FromStr for Term {
    type Err = ();

    fn from_str(s: &str) -> Result<Term, ()> {
        match s {
            ":" => Ok(StartDefinition),
            ";" => Ok(EndDefinition),
            _   => Err(())
        }
        .or_else(|_| Value::from_str(s).map(Number))
        .or_else(|_| Ok(Word(s.to_ascii_lowercase())))
    }
}

impl Forth {
    pub fn new() -> Forth {
        Forth {
            code: LinkedList::new(),
            defs: HashMap::new(),
            stack: LinkedList::new(),
        }
    }

    pub fn format_stack(&self) -> String {
        let mut s = self.stack.iter().fold(String::new(), |s, v| {
            s + &v.to_string() + " "
        });
        s.pop();
        s
    }

    pub fn eval(&mut self, input: &str) -> ForthResult {
        let mut new_code = Forth::into_code(input);
        self.code.append(&mut new_code);
        self.run()
    }

    fn run(&mut self) -> ForthResult {
        while let Some(term) = self.code.pop_front() {
            try!(self.step_term(term))
        }

        Forth::ok()
    }

    fn step_term(&mut self, term: Term) -> ForthResult {
        match term {
            Number(value)   => self.push(value),
            Word(word)      => self.step_word(word),
            StartDefinition => self.store_definition(),
            EndDefinition   => Err(Error::InvalidWord),
        }
    }

    fn step_word(&mut self, word: String) -> ForthResult {
        self.defs.get(&word)
            .ok_or(Error::UnknownWord)
            .map(Clone::clone)
            .map(|mut code| self.code.append(&mut code))
            .or_else(|_| self.step_built_in(&word))
    }

    fn step_built_in(&mut self, word: &String) -> ForthResult {
        match word.as_ref() {
            "+" =>
                self.bin_op(|(a, b)| Ok(a + b)),
            "-" =>
                self.bin_op(|(a, b)| Ok(a - b)),
            "*" =>
                self.bin_op(|(a, b)| Ok(a * b)),
            "/" =>
                self.bin_op(|(a, b)| {
                    a.checked_div(b).ok_or(Error::DivisionByZero)
                }),
            "dup" =>
                self.pop().and_then(|a| {
                    self.push(a).and(self.push(a))
                }),
            "drop" =>
                self.pop().and(Forth::ok()),
            "swap" =>
                self.pop_two().and_then(|(a, b)| {
                    self.push(b).and(self.push(a))
                }),
            "over" =>
                self.pop_two().and_then(|(a, b)| {
                    self.push(a).and(self.push(b)).and(self.push(a))
                }),
            _ =>
                Err(Error::UnknownWord)
        }
    }

    fn store_definition(&mut self) -> ForthResult {
        let mut def = LinkedList::new();

        loop {
            match self.code.pop_front() {
                Some(EndDefinition) => break,
                Some(term)          => def.push_back(term),
                None                => return Err(Error::InvalidWord),
            }
        }

        if let Some(Word(name)) = def.pop_front() {
            self.store_word(name, def)
        } else {
            Err(Error::InvalidWord)
        }
    }

    fn push(&mut self, value: Value) -> ForthResult {
        self.stack.push_back(value);
        Forth::ok()
    }

    fn pop(&mut self) -> StackResult<Value> {
        self.stack.pop_back()
            .ok_or(Error::StackUnderflow)
    }

    fn pop_two(&mut self) -> StackResult<(Value, Value)> {
        self.pop()
            .and_then(|b| {
                self.pop()
                    .and_then(|a| Ok((a, b)))
            })
    }

    fn bin_op<F>(&mut self, op: F) -> ForthResult
        where F: FnOnce((Value, Value)) -> StackResult<Value> {
        self.pop_two()
            .and_then(op)
            .and_then(|value| self.push(value))
    }

    fn store_word(&mut self, name: String, code: Code) -> ForthResult {
        self.defs.insert(name, code);
        Forth::ok()
    }

    fn into_code(input: &str) -> LinkedList<Term> {
        input
            .split(|c: char| c.is_whitespace() || c.is_control())
            .map(Term::from_str)
            .filter(Result::is_ok)
            .map(Result::unwrap)
            .collect()
    }

    fn ok() -> ForthResult { Ok(()) }
}
<|endoftext|>