<commit_before>extern crate chip8;
extern crate sdl2;

use sdl2::pixels::PixelFormatEnum;
use sdl2::rect::Rect;
use sdl2::Sdl;
use std::time::{Duration, Instant};
use std::thread::sleep;

pub struct Display<'a> {
    width: u32,
    height: u32,
    renderer: sdl2::render::Renderer<'a>,
    texture: sdl2::render::Texture,
    frame_duration: Duration,
    frame_last: Instant,
}

impl<'a> Display<'a> {
    pub fn new(context: &Sdl,
               title: &str,
               width: u32,
               height: u32,
               duration: Duration) -> Self {
        let video_subsystem = context.video().unwrap();
        let window = video_subsystem.window(title, width, height)
            .position_centered()
            .opengl()
            .build()
            .unwrap();
        let mut renderer = window.renderer().build().unwrap();
        let mut texture = renderer.create_texture_streaming(
            PixelFormatEnum::RGB24, 64, 32).unwrap();

        Display {
            width: width,
            height: height,
            renderer: renderer,
            texture: texture,
            frame_duration: duration,
            frame_last: Instant::now(),
        }
    }

    pub fn draw_frame(&mut self, bitmap: &[u8; chip8::NPIXELS]) {
        // Keep timing okay
        let prev_duration = Instant::now().duration_since(self.frame_last);
        debug!("Last frame duration: {:?}", prev_duration);
        if prev_duration < self.frame_duration {
            let diff = self.frame_duration - prev_duration;
            debug!("Sleeping for: {:?}", diff);
            sleep(diff);
        }
        self.frame_last = Instant::now();

        self.texture.with_lock(None, |buffer: &mut [u8], pitch: usize| {
            for y in 0..32 {
                for x in 0..64 {
                    let offset = y*pitch + x*3;
                    let value = if 0 != bitmap[y * 64 + x] { 255 } else { 0 };
                    buffer[offset + 0] = value as u8;
                    buffer[offset + 1] = value as u8;
                    buffer[offset + 2] = 0;
                }
            }

        }).unwrap();
        self.renderer.clear();
        self.renderer.copy(&self.texture,
                           None,
                           Some(Rect::new(0, 0, self.width, self.height)));
        self.renderer.present();
    }
}

<commit_msg>Fixed compiler warnings<commit_after>extern crate chip8;
extern crate sdl2;

use sdl2::pixels::PixelFormatEnum;
use sdl2::rect::Rect;
use sdl2::Sdl;
use std::time::{Duration, Instant};
use std::thread::sleep;

pub struct Display<'a> {
    width: u32,
    height: u32,
    renderer: sdl2::render::Renderer<'a>,
    texture: sdl2::render::Texture,
    frame_duration: Duration,
    frame_last: Instant,
}

impl<'a> Display<'a> {
    pub fn new(context: &Sdl,
               title: &str,
               width: u32,
               height: u32,
               duration: Duration) -> Self {
        let video_subsystem = context.video().unwrap();
        let window = video_subsystem.window(title, width, height)
            .position_centered()
            .opengl()
            .build()
            .unwrap();
        let renderer = window.renderer().build().unwrap();
        let texture = renderer.create_texture_streaming(
            PixelFormatEnum::RGB24, 64, 32).unwrap();

        Display {
            width: width,
            height: height,
            renderer: renderer,
            texture: texture,
            frame_duration: duration,
            frame_last: Instant::now(),
        }
    }

    pub fn draw_frame(&mut self, bitmap: &[u8; chip8::NPIXELS]) {
        // Keep timing okay
        let prev_duration = Instant::now().duration_since(self.frame_last);
        debug!("Last frame duration: {:?}", prev_duration);
        if prev_duration < self.frame_duration {
            let diff = self.frame_duration - prev_duration;
            debug!("Sleeping for: {:?}", diff);
            sleep(diff);
        }
        self.frame_last = Instant::now();

        self.texture.with_lock(None, |buffer: &mut [u8], pitch: usize| {
            for y in 0..32 {
                for x in 0..64 {
                    let offset = y*pitch + x*3;
                    let value = if 0 != bitmap[y * 64 + x] { 255 } else { 0 };
                    buffer[offset + 0] = value as u8;
                    buffer[offset + 1] = value as u8;
                    buffer[offset + 2] = 0;
                }
            }

        }).unwrap();
        self.renderer.clear();
        self.renderer.copy(&self.texture,
                           None,
                           Some(Rect::new(0, 0, self.width, self.height)));
        self.renderer.present();
    }
}

<|endoftext|>


<commit_before>use futures::channel::{mpsc, oneshot};
use futures::{SinkExt, StreamExt};
use std::convert::TryInto;
use std::time::Duration;
use zeromq::prelude::*;

#[tokio::test]
async fn test_pub_sub_sockets() {
    let (server_stop_sender, mut server_stop) = oneshot::channel::<()>();
    let (results_sender, results) = mpsc::channel(100);
    tokio::spawn(async move {
        let mut pub_socket = zeromq::PubSocket::new();
        pub_socket
            .bind("127.0.0.1:5556")
            .await
            .expect("Failed to bind socket");

        loop {
            if let Ok(Some(_)) = server_stop.try_recv() {
                break;
            }
            pub_socket
                .send(chrono::Utc::now().to_rfc2822().into())
                .expect("Failed to send");
            tokio::time::delay_for(Duration::from_millis(100)).await;
        }
    });

    for _ in 0..10 {
        let mut client_sender = results_sender.clone();
        tokio::spawn(async move {
            let mut sub_socket = zeromq::SubSocket::new();
            sub_socket
                .connect("127.0.0.1:5556")
                .await
                .expect("Failed to connect");

            sub_socket.subscribe("").await.expect("Failed to subscribe");

            for _ in 0..10i32 {
                let repl: String = sub_socket
                    .recv()
                    .await
                    .expect("Failed to recv")
                    .try_into()
                    .expect("Malformed string");
                assert_eq!(chrono::Utc::now().to_rfc2822(), repl);
                client_sender.send(true).await.unwrap();
            }
        });
    }
    drop(results_sender);

    let res_vec: Vec<bool> = results.collect().await;
    server_stop_sender.send(()).unwrap();
    assert_eq!(100, res_vec.len());
}
<commit_msg>Refactored pub_sub test to use helper<commit_after>use futures::channel::{mpsc, oneshot};
use futures::{SinkExt, StreamExt};
use std::convert::TryInto;
use std::time::Duration;
use zeromq::prelude::*;

#[tokio::test]
async fn test_pub_sub_sockets() {
    async fn helper(bind_addr: &'static str) {
        let (server_stop_sender, mut server_stop) = oneshot::channel::<()>();
        let (results_sender, results) = mpsc::channel(100);
        tokio::spawn(async move {
            let mut pub_socket = zeromq::PubSocket::new();
            pub_socket
                .bind(bind_addr)
                .await
                .expect("Failed to bind socket");

            loop {
                if let Ok(Some(_)) = server_stop.try_recv() {
                    break;
                }
                pub_socket
                    .send(chrono::Utc::now().to_rfc2822().into())
                    .expect("Failed to send");
                tokio::time::delay_for(Duration::from_millis(100)).await;
            }
        });

        for _ in 0..10 {
            let mut client_sender = results_sender.clone();
            tokio::spawn(async move {
                let mut sub_socket = zeromq::SubSocket::new();
                sub_socket
                    .connect(bind_addr)
                    .await
                    .expect("Failed to connect");

                sub_socket.subscribe("").await.expect("Failed to subscribe");

                for _ in 0..10i32 {
                    let repl: String = sub_socket
                        .recv()
                        .await
                        .expect("Failed to recv")
                        .try_into()
                        .expect("Malformed string");
                    assert_eq!(chrono::Utc::now().to_rfc2822(), repl);
                    client_sender.send(true).await.unwrap();
                }
            });
        }
        drop(results_sender);

        let res_vec: Vec<bool> = results.collect().await;
        server_stop_sender.send(()).unwrap();
        assert_eq!(100, res_vec.len());
    }
    let addrs = vec!["127.0.0.1:5556", "localhost:5555"];
    futures::future::join_all(addrs.into_iter().map(helper)).await;
}
<|endoftext|>


<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![deny(unused_imports)]
#![deny(unused_variables)]

extern crate png;
extern crate test;
extern crate regex;
extern crate url;

use std::ascii::AsciiExt;
use std::io;
use std::io::{File, Reader, Command};
use std::io::process::ExitStatus;
use std::io::fs::PathExtensions;
use std::os;
use std::path::Path;
use test::{AutoColor, DynTestName, DynTestFn, TestDesc, TestOpts, TestDescAndFn};
use test::run_tests_console;
use regex::Regex;
use url::Url;

bitflags!(
    flags RenderMode: u32 {
        const CPU_RENDERING  = 0x00000001,
        const GPU_RENDERING  = 0x00000010,
        const LINUX_TARGET   = 0x00000100,
        const MACOS_TARGET   = 0x00001000,
        const ANDROID_TARGET = 0x00010000
    }
)

fn main() {
    let args = os::args();
    let mut parts = args.tail().split(|e| "--" == e.as_slice());

    let harness_args = parts.next().unwrap();  // .split() is never empty
    let servo_args = parts.next().unwrap_or(&[]);

    let (render_mode_string, base_path, testname) = match harness_args {
        [] | [_] => panic!("USAGE: cpu|gpu base_path [testname regex]"),
        [ref render_mode_string, ref base_path] => (render_mode_string, base_path, None),
        [ref render_mode_string, ref base_path, ref testname, ..] => (render_mode_string, base_path, Some(Regex::new(testname.as_slice()).unwrap())),
    };

    let mut render_mode = match render_mode_string.as_slice() {
        "cpu" => CPU_RENDERING,
        "gpu" => GPU_RENDERING,
        _ => panic!("First argument must specify cpu or gpu as rendering mode")
    };
    if cfg!(target_os = "linux") {
        render_mode.insert(LINUX_TARGET);
    }
    if cfg!(target_os = "macos") {
        render_mode.insert(MACOS_TARGET);
    }
    if cfg!(target_os = "android") {
        render_mode.insert(ANDROID_TARGET);
    }

    let mut all_tests = vec!();
    println!("Scanning {} for manifests\n", base_path);

    for file in io::fs::walk_dir(&Path::new(base_path.as_slice())).unwrap() {
        let maybe_extension = file.extension_str();
        match maybe_extension {
            Some(extension) => {
                if extension.to_ascii_lower().as_slice() == "list" && file.is_file() {
                    let tests = parse_lists(&file, servo_args, render_mode, all_tests.len());
                    println!("\t{} [{} tests]", file.display(), tests.len());
                    all_tests.extend(tests.into_iter());
                }
            }
            _ => {}
        }
    }

    let test_opts = TestOpts {
        filter: testname,
        run_ignored: false,
        logfile: None,
        run_tests: true,
        run_benchmarks: false,
        ratchet_noise_percent: None,
        ratchet_metrics: None,
        save_metrics: None,
        test_shard: None,
        nocapture: false,
        color: AutoColor
    };

    match run_tests_console(&test_opts, all_tests) {
        Ok(false) => os::set_exit_status(1), // tests failed
        Err(_) => os::set_exit_status(2),    // I/O-related failure
        _ => (),
    }
}

#[deriving(PartialEq)]
enum ReftestKind {
    Same,
    Different,
}

struct Reftest {
    name: String,
    kind: ReftestKind,
    files: [Path, ..2],
    id: uint,
    servo_args: Vec<String>,
    render_mode: RenderMode,
    is_flaky: bool,
    experimental: bool,
    fragment_identifier: Option<String>,
}

struct TestLine<'a> {
    conditions: &'a str,
    kind: &'a str,
    file_left: &'a str,
    file_right: &'a str,
}

fn parse_lists(file: &Path, servo_args: &[String], render_mode: RenderMode, id_offset: uint) -> Vec<TestDescAndFn> {
    let mut tests = Vec::new();
    let contents = File::open_mode(file, io::Open, io::Read)
                       .and_then(|mut f| f.read_to_string())
                       .ok().expect("Could not read file");

    for line in contents.as_slice().lines() {
        // ignore comments or empty lines
        if line.starts_with("#") || line.is_empty() {
            continue;
        }

        let parts: Vec<&str> = line.split(' ').filter(|p| !p.is_empty()).collect();

        let test_line = match parts.len() {
            3 => TestLine {
                conditions: "",
                kind: parts[0],
                file_left: parts[1],
                file_right: parts[2],
            },
            4 => TestLine {
                conditions: parts[0],
                kind: parts[1],
                file_left: parts[2],
                file_right: parts[3],
            },
            _ => panic!("reftest line: '{:s}' doesn't match '[CONDITIONS] KIND LEFT RIGHT'", line),
        };

        let kind = match test_line.kind {
            "==" => Same,
            "!=" => Different,
            part => panic!("reftest line: '{:s}' has invalid kind '{:s}'", line, part)
        };

        // If we're running this directly, file.dir_path() might be relative.
        // (see issue #3521)
        let base = match file.dir_path().is_relative() {
            true  => os::getcwd().join(file.dir_path()),
            false => file.dir_path()
        };

        let file_left =  base.join(test_line.file_left);
        let file_right = base.join(test_line.file_right);

        let mut conditions_list = test_line.conditions.split(',');
        let mut flakiness = RenderMode::empty();
        let mut experimental = false;
        let mut fragment_identifier = None;
        for condition in conditions_list {
            match condition {
                "flaky_cpu" => flakiness.insert(CPU_RENDERING),
                "flaky_gpu" => flakiness.insert(GPU_RENDERING),
                "flaky_linux" => flakiness.insert(LINUX_TARGET),
                "flaky_macos" => flakiness.insert(MACOS_TARGET),
                "experimental" => experimental = true,
                _ => (),
            }
            if condition.starts_with("fragment=") {
                fragment_identifier = Some(condition.slice_from("fragment=".len()).to_string());
            }
        }

        let reftest = Reftest {
            name: format!("{} {} {}", test_line.file_left, test_line.kind, test_line.file_right),
            kind: kind,
            files: [file_left, file_right],
            id: id_offset + tests.len(),
            render_mode: render_mode,
            servo_args: servo_args.iter().map(|x| x.clone()).collect(),
            is_flaky: render_mode.intersects(flakiness),
            experimental: experimental,
            fragment_identifier: fragment_identifier,
        };

        tests.push(make_test(reftest));
    }
    tests
}

fn make_test(reftest: Reftest) -> TestDescAndFn {
    let name = reftest.name.clone();
    TestDescAndFn {
        desc: TestDesc {
            name: DynTestName(name),
            ignore: false,
            should_fail: false,
        },
        testfn: DynTestFn(proc() {
            check_reftest(reftest);
        }),
    }
}

fn capture(reftest: &Reftest, side: uint) -> (u32, u32, Vec<u8>) {
    let png_filename = format!("/tmp/servo-reftest-{:06u}-{:u}.png", reftest.id, side);
    let mut command = Command::new("target/servo");
    command
        .args(reftest.servo_args.as_slice())
        // Allows pixel perfect rendering of Ahem font for reftests.
        .arg("-Z")
        .arg("disable-text-aa")
        .args(["-f", "-o"])
        .arg(png_filename.as_slice())
        .arg({
            let mut url = Url::from_file_path(&reftest.files[side]).unwrap();
            url.fragment = reftest.fragment_identifier.clone();
            url.to_string()
        });
    // CPU rendering is the default
    if reftest.render_mode.contains(CPU_RENDERING) {
        command.arg("-c");
    }
    if reftest.render_mode.contains(GPU_RENDERING) {
        command.arg("-g");
    }
    if reftest.experimental {
        command.arg("--experimental");
    }
    let retval = match command.status() {
        Ok(status) => status,
        Err(e) => panic!("failed to execute process: {}", e),
    };
    assert_eq!(retval, ExitStatus(0));

    let image = png::load_png(&from_str::<Path>(png_filename.as_slice()).unwrap()).unwrap();
    let rgba8_bytes = match image.pixels {
        png::RGBA8(pixels) => pixels,
        _ => panic!(),
    };
    (image.width, image.height, rgba8_bytes)
}

fn check_reftest(reftest: Reftest) {
    let (left_width, left_height, left_bytes) = capture(&reftest, 0);
    let (right_width, right_height, right_bytes) = capture(&reftest, 1);

    assert_eq!(left_width, right_width);
    assert_eq!(left_height, right_height);

    let left_all_white = left_bytes.iter().all(|&p| p == 255);
    let right_all_white = right_bytes.iter().all(|&p| p == 255);

    if left_all_white && right_all_white {
        panic!("Both renderings are empty")
    }

    let pixels = left_bytes.iter().zip(right_bytes.iter()).map(|(&a, &b)| {
        if a as i8 - b as i8 == 0 {
            // White for correct
            0xFF
        } else {
            // "1100" in the RGBA channel with an error for an incorrect value
            // This results in some number of C0 and FFs, which is much more
            // readable (and distinguishable) than the previous difference-wise
            // scaling but does not require reconstructing the actual RGBA pixel.
            0xC0
        }
    }).collect::<Vec<u8>>();

    if pixels.iter().any(|&a| a < 255) {
        let output_str = format!("/tmp/servo-reftest-{:06u}-diff.png", reftest.id);
        let output = from_str::<Path>(output_str.as_slice()).unwrap();

        let mut img = png::Image {
            width: left_width,
            height: left_height,
            pixels: png::RGBA8(pixels),
        };
        let res = png::store_png(&mut img, &output);
        assert!(res.is_ok());

        match (reftest.kind, reftest.is_flaky) {
            (Same, true) => println!("flaky test - rendering difference: {}", output_str),
            (Same, false) => panic!("rendering difference: {}", output_str),
            (Different, _) => {}   // Result was different and that's what was expected
        }
    } else {
        assert!(reftest.is_flaky || reftest.kind == Same);
    }
}
<commit_msg>Verify that reftest passes valid servo args<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![deny(unused_imports)]
#![deny(unused_variables)]

extern crate png;
extern crate test;
extern crate regex;
extern crate url;

use std::ascii::AsciiExt;
use std::io;
use std::io::{File, Reader, Command, IoResult};
use std::io::process::ExitStatus;
use std::io::fs::PathExtensions;
use std::os;
use std::path::Path;
use test::{AutoColor, DynTestName, DynTestFn, TestDesc, TestOpts, TestDescAndFn};
use test::run_tests_console;
use regex::Regex;
use url::Url;

bitflags!(
    flags RenderMode: u32 {
        const CPU_RENDERING  = 0x00000001,
        const GPU_RENDERING  = 0x00000010,
        const LINUX_TARGET   = 0x00000100,
        const MACOS_TARGET   = 0x00001000,
        const ANDROID_TARGET = 0x00010000
    }
)

fn main() {
    let args = os::args();
    let mut parts = args.tail().split(|e| "--" == e.as_slice());

    let harness_args = parts.next().unwrap();  // .split() is never empty
    let servo_args = parts.next().unwrap_or(&[]);

    let (render_mode_string, base_path, testname) = match harness_args {
        [] | [_] => panic!("USAGE: cpu|gpu base_path [testname regex]"),
        [ref render_mode_string, ref base_path] => (render_mode_string, base_path, None),
        [ref render_mode_string, ref base_path, ref testname, ..] => (render_mode_string, base_path, Some(Regex::new(testname.as_slice()).unwrap())),
    };

    let mut render_mode = match render_mode_string.as_slice() {
        "cpu" => CPU_RENDERING,
        "gpu" => GPU_RENDERING,
        _ => panic!("First argument must specify cpu or gpu as rendering mode")
    };
    if cfg!(target_os = "linux") {
        render_mode.insert(LINUX_TARGET);
    }
    if cfg!(target_os = "macos") {
        render_mode.insert(MACOS_TARGET);
    }
    if cfg!(target_os = "android") {
        render_mode.insert(ANDROID_TARGET);
    }

    let mut all_tests = vec!();
    println!("Scanning {} for manifests\n", base_path);

    for file in io::fs::walk_dir(&Path::new(base_path.as_slice())).unwrap() {
        let maybe_extension = file.extension_str();
        match maybe_extension {
            Some(extension) => {
                if extension.to_ascii_lower().as_slice() == "list" && file.is_file() {
                    let tests = parse_lists(&file, servo_args, render_mode, all_tests.len());
                    println!("\t{} [{} tests]", file.display(), tests.len());
                    all_tests.extend(tests.into_iter());
                }
            }
            _ => {}
        }
    }

    let test_opts = TestOpts {
        filter: testname,
        run_ignored: false,
        logfile: None,
        run_tests: true,
        run_benchmarks: false,
        ratchet_noise_percent: None,
        ratchet_metrics: None,
        save_metrics: None,
        test_shard: None,
        nocapture: false,
        color: AutoColor
    };

    match run(test_opts,
              all_tests,
              servo_args.iter().map(|x| x.clone()).collect()) {
        Ok(false) => os::set_exit_status(1), // tests failed
        Err(_) => os::set_exit_status(2),    // I/O-related failure
        _ => (),
    }
}

fn run(test_opts: TestOpts, all_tests: Vec<TestDescAndFn>,
       servo_args: Vec<String>) -> IoResult<bool> {
    // Verify that we're passing in valid servo arguments. Otherwise, servo
    // will exit before we've run any tests, and it will appear to us as if
    // all the tests are failing.
    let mut command = match Command::new("target/servo").args(servo_args.as_slice()).spawn() {
        Ok(p) => p,
        Err(e) => panic!("failed to execute process: {}", e),
    };
    let stderr = command.stderr.as_mut().unwrap().read_to_string().unwrap();

    if stderr.as_slice().contains("Unrecognized") {
        println!("Servo: {}", stderr.as_slice());
        return Ok(false);
    }

    run_tests_console(&test_opts, all_tests)
}

#[deriving(PartialEq)]
enum ReftestKind {
    Same,
    Different,
}

struct Reftest {
    name: String,
    kind: ReftestKind,
    files: [Path, ..2],
    id: uint,
    servo_args: Vec<String>,
    render_mode: RenderMode,
    is_flaky: bool,
    experimental: bool,
    fragment_identifier: Option<String>,
}

struct TestLine<'a> {
    conditions: &'a str,
    kind: &'a str,
    file_left: &'a str,
    file_right: &'a str,
}

fn parse_lists(file: &Path, servo_args: &[String], render_mode: RenderMode, id_offset: uint) -> Vec<TestDescAndFn> {
    let mut tests = Vec::new();
    let contents = File::open_mode(file, io::Open, io::Read)
                       .and_then(|mut f| f.read_to_string())
                       .ok().expect("Could not read file");

    for line in contents.as_slice().lines() {
        // ignore comments or empty lines
        if line.starts_with("#") || line.is_empty() {
            continue;
        }

        let parts: Vec<&str> = line.split(' ').filter(|p| !p.is_empty()).collect();

        let test_line = match parts.len() {
            3 => TestLine {
                conditions: "",
                kind: parts[0],
                file_left: parts[1],
                file_right: parts[2],
            },
            4 => TestLine {
                conditions: parts[0],
                kind: parts[1],
                file_left: parts[2],
                file_right: parts[3],
            },
            _ => panic!("reftest line: '{:s}' doesn't match '[CONDITIONS] KIND LEFT RIGHT'", line),
        };

        let kind = match test_line.kind {
            "==" => Same,
            "!=" => Different,
            part => panic!("reftest line: '{:s}' has invalid kind '{:s}'", line, part)
        };

        // If we're running this directly, file.dir_path() might be relative.
        // (see issue #3521)
        let base = match file.dir_path().is_relative() {
            true  => os::getcwd().join(file.dir_path()),
            false => file.dir_path()
        };

        let file_left =  base.join(test_line.file_left);
        let file_right = base.join(test_line.file_right);

        let mut conditions_list = test_line.conditions.split(',');
        let mut flakiness = RenderMode::empty();
        let mut experimental = false;
        let mut fragment_identifier = None;
        for condition in conditions_list {
            match condition {
                "flaky_cpu" => flakiness.insert(CPU_RENDERING),
                "flaky_gpu" => flakiness.insert(GPU_RENDERING),
                "flaky_linux" => flakiness.insert(LINUX_TARGET),
                "flaky_macos" => flakiness.insert(MACOS_TARGET),
                "experimental" => experimental = true,
                _ => (),
            }
            if condition.starts_with("fragment=") {
                fragment_identifier = Some(condition.slice_from("fragment=".len()).to_string());
            }
        }

        let reftest = Reftest {
            name: format!("{} {} {}", test_line.file_left, test_line.kind, test_line.file_right),
            kind: kind,
            files: [file_left, file_right],
            id: id_offset + tests.len(),
            render_mode: render_mode,
            servo_args: servo_args.iter().map(|x| x.clone()).collect(),
            is_flaky: render_mode.intersects(flakiness),
            experimental: experimental,
            fragment_identifier: fragment_identifier,
        };

        tests.push(make_test(reftest));
    }
    tests
}

fn make_test(reftest: Reftest) -> TestDescAndFn {
    let name = reftest.name.clone();
    TestDescAndFn {
        desc: TestDesc {
            name: DynTestName(name),
            ignore: false,
            should_fail: false,
        },
        testfn: DynTestFn(proc() {
            check_reftest(reftest);
        }),
    }
}

fn capture(reftest: &Reftest, side: uint) -> (u32, u32, Vec<u8>) {
    let png_filename = format!("/tmp/servo-reftest-{:06u}-{:u}.png", reftest.id, side);
    let mut command = Command::new("target/servo");
    command
        .args(reftest.servo_args.as_slice())
        // Allows pixel perfect rendering of Ahem font for reftests.
        .arg("-Z")
        .arg("disable-text-aa")
        .args(["-f", "-o"])
        .arg(png_filename.as_slice())
        .arg({
            let mut url = Url::from_file_path(&reftest.files[side]).unwrap();
            url.fragment = reftest.fragment_identifier.clone();
            url.to_string()
        });
    // CPU rendering is the default
    if reftest.render_mode.contains(CPU_RENDERING) {
        command.arg("-c");
    }
    if reftest.render_mode.contains(GPU_RENDERING) {
        command.arg("-g");
    }
    if reftest.experimental {
        command.arg("--experimental");
    }
    let retval = match command.status() {
        Ok(status) => status,
        Err(e) => panic!("failed to execute process: {}", e),
    };
    assert_eq!(retval, ExitStatus(0));

    let image = png::load_png(&from_str::<Path>(png_filename.as_slice()).unwrap()).unwrap();
    let rgba8_bytes = match image.pixels {
        png::RGBA8(pixels) => pixels,
        _ => panic!(),
    };
    (image.width, image.height, rgba8_bytes)
}

fn check_reftest(reftest: Reftest) {
    let (left_width, left_height, left_bytes) = capture(&reftest, 0);
    let (right_width, right_height, right_bytes) = capture(&reftest, 1);

    assert_eq!(left_width, right_width);
    assert_eq!(left_height, right_height);

    let left_all_white = left_bytes.iter().all(|&p| p == 255);
    let right_all_white = right_bytes.iter().all(|&p| p == 255);

    if left_all_white && right_all_white {
        panic!("Both renderings are empty")
    }

    let pixels = left_bytes.iter().zip(right_bytes.iter()).map(|(&a, &b)| {
        if a as i8 - b as i8 == 0 {
            // White for correct
            0xFF
        } else {
            // "1100" in the RGBA channel with an error for an incorrect value
            // This results in some number of C0 and FFs, which is much more
            // readable (and distinguishable) than the previous difference-wise
            // scaling but does not require reconstructing the actual RGBA pixel.
            0xC0
        }
    }).collect::<Vec<u8>>();

    if pixels.iter().any(|&a| a < 255) {
        let output_str = format!("/tmp/servo-reftest-{:06u}-diff.png", reftest.id);
        let output = from_str::<Path>(output_str.as_slice()).unwrap();

        let mut img = png::Image {
            width: left_width,
            height: left_height,
            pixels: png::RGBA8(pixels),
        };
        let res = png::store_png(&mut img, &output);
        assert!(res.is_ok());

        match (reftest.kind, reftest.is_flaky) {
            (Same, true) => println!("flaky test - rendering difference: {}", output_str),
            (Same, false) => panic!("rendering difference: {}", output_str),
            (Different, _) => {}   // Result was different and that's what was expected
        }
    } else {
        assert!(reftest.is_flaky || reftest.kind == Same);
    }
}
<|endoftext|>


<commit_before>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io

extern crate bragi;
extern crate iron_test;
extern crate serde_json;
use super::BragiHandler;
use super::count_types;
use super::get_types;
use super::get_value;
use super::get_values;

pub fn bragi_osm_test(es_wrapper: ::ElasticSearchWrapper) {
    let bragi = BragiHandler::new(format!("{}/munin", es_wrapper.host()));

    // *********************************
    // We load the OSM dataset (including ways)
    // *********************************
    let osm2mimir = concat!(env!("OUT_DIR"), "/../../../osm2mimir");
    ::launch_and_assert(
        osm2mimir,
        vec![
            "--input=./tests/fixtures/osm_fixture.osm.pbf".into(),
            "--import-way".into(),
            "--level=8".into(),
            format!("--connection-string={}", es_wrapper.host()),
        ],
        &es_wrapper,
    );

    zip_code_test(&bragi);
    zip_code_street_test(&bragi);
    zip_code_admin_test(&bragi);
    city_admin_test(&bragi);
}

fn zip_code_test(bragi: &BragiHandler) {
    let all_20 = bragi.get("/autocomplete?q=77000");
    assert_eq!(all_20.len(), 10);
    for postcodes in get_values(&all_20, "postcode") {
        assert!(postcodes.split(';').any(|p| p == "77000"));
    }
    assert!(
        get_values(&all_20, "postcode")
            .iter()
            .any(|r| *r == "77000;77003;77008;CP77001")
    );

    let types = get_types(&all_20);
    let count = count_types(&types, "street");
    assert_eq!(count, 7);

    let count = count_types(&types, "city");
    assert_eq!(count, 3);

    let count = count_types(&types, "house");
    assert_eq!(count, 0);
}

fn zip_code_street_test(bragi: &BragiHandler) {
    let res = bragi.get("/autocomplete?q=77000 Lotissement le Clos de Givry");
    assert_eq!(res.len(), 1);
    let le_clos = &res[0];
    assert_eq!(le_clos["postcode"], "77000");
    assert_eq!(
        le_clos["label"],
        "Lotissement le Clos de Givry (Livry-sur-Seine)"
    );
    assert_eq!(le_clos["name"], "Lotissement le Clos de Givry");
    assert_eq!(le_clos["street"], "Lotissement le Clos de Givry");

    let boundary = le_clos["administrative_regions"].pointer("/0/boundary");
    assert_eq!(boundary, None);

    assert_eq!(le_clos["type"], "street");
    assert_eq!(le_clos["citycode"], "77255");
}

fn zip_code_admin_test(bragi: &BragiHandler) {
    let all_20 = bragi.get("/autocomplete?q=77000 Vaux-le-Pénil");
    assert_eq!(all_20.len(), 4);
    assert!(
        get_values(&all_20, "postcode")
            .iter()
            .all(|r| *r == "77000",)
    );
    let types = get_types(&all_20);
    let count = count_types(&types, "street");
    assert_eq!(count, 3);

    let count = count_types(&types, "city");
    assert_eq!(count, 1);
    let first_city = all_20.iter().find(|e| get_value(e, "type") == "city");
    assert_eq!(get_value(first_city.unwrap(), "citycode"), "77487");

    let count = count_types(&types, "house");
    assert_eq!(count, 0);
}

fn city_admin_test(bragi: &BragiHandler) {
    let all_melun = bragi.get("/autocomplete?q=Melun Rp");
    let types = get_types(&all_melun);
    let count = count_types(&types, "city");
    assert!(count > 0);
}
<commit_msg>Test administrative_region type from Bragi<commit_after>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io

extern crate bragi;
extern crate iron_test;
extern crate serde_json;
use super::BragiHandler;
use super::count_types;
use super::get_types;
use super::get_value;
use super::get_values;

pub fn bragi_osm_test(es_wrapper: ::ElasticSearchWrapper) {
    let bragi = BragiHandler::new(format!("{}/munin", es_wrapper.host()));

    // *********************************
    // We load the OSM dataset (including ways)
    // *********************************
    let osm2mimir = concat!(env!("OUT_DIR"), "/../../../osm2mimir");
    ::launch_and_assert(
        osm2mimir,
        vec![
            "--input=./tests/fixtures/osm_fixture.osm.pbf".into(),
            "--import-way".into(),
            "--level=8".into(),
            "--level=7".into(),
            format!("--connection-string={}", es_wrapper.host()),
        ],
        &es_wrapper,
    );

    zip_code_test(&bragi);
    zip_code_street_test(&bragi);
    zip_code_admin_test(&bragi);
    city_admin_test(&bragi);
    administrative_region_test(&bragi);
}

fn zip_code_test(bragi: &BragiHandler) {
    let all_20 = bragi.get("/autocomplete?q=77000");
    assert_eq!(all_20.len(), 10);
    for postcodes in get_values(&all_20, "postcode") {
        assert!(postcodes.split(';').any(|p| p == "77000"));
    }
    assert!(
        get_values(&all_20, "postcode")
            .iter()
            .any(|r| *r == "77000;77003;77008;CP77001")
    );

    let types = get_types(&all_20);
    let count = count_types(&types, "street");
    assert_eq!(count, 7);

    let count = count_types(&types, "city");
    assert_eq!(count, 3);

    let count = count_types(&types, "house");
    assert_eq!(count, 0);
}

fn zip_code_street_test(bragi: &BragiHandler) {
    let res = bragi.get("/autocomplete?q=77000 Lotissement le Clos de Givry");
    assert_eq!(res.len(), 1);
    let le_clos = &res[0];
    assert_eq!(le_clos["postcode"], "77000");
    assert_eq!(
        le_clos["label"],
        "Lotissement le Clos de Givry (Livry-sur-Seine)"
    );
    assert_eq!(le_clos["name"], "Lotissement le Clos de Givry");
    assert_eq!(le_clos["street"], "Lotissement le Clos de Givry");

    let boundary = le_clos["administrative_regions"].pointer("/0/boundary");
    assert_eq!(boundary, None);

    assert_eq!(le_clos["type"], "street");
    assert_eq!(le_clos["citycode"], "77255");
}

fn zip_code_admin_test(bragi: &BragiHandler) {
    let all_20 = bragi.get("/autocomplete?q=77000 Vaux-le-Pénil");
    assert_eq!(all_20.len(), 4);
    assert!(
        get_values(&all_20, "postcode")
            .iter()
            .all(|r| *r == "77000",)
    );
    let types = get_types(&all_20);
    let count = count_types(&types, "street");
    assert_eq!(count, 3);

    let count = count_types(&types, "city");
    assert_eq!(count, 1);
    let first_city = all_20.iter().find(|e| get_value(e, "type") == "city");
    assert_eq!(get_value(first_city.unwrap(), "citycode"), "77487");

    let count = count_types(&types, "house");
    assert_eq!(count, 0);
}

fn city_admin_test(bragi: &BragiHandler) {
    let all_melun = bragi.get("/autocomplete?q=Melun Rp");
    let types = get_types(&all_melun);
    let count = count_types(&types, "city");
    assert!(count > 0);
}

fn administrative_region_test(bragi: &BragiHandler) {
    let all_creteil = bragi.get("/autocomplete?q=Créteil");
    let types = get_types(&all_creteil);
    let count = count_types(&types, "administrative_region");
    assert_eq!(count, 1);
}
<|endoftext|>


<commit_before>use crate::syntax::namespace::Namespace;
use crate::syntax::ExternFn;

pub fn extern_fn(namespace: &Namespace, efn: &ExternFn) -> String {
    let receiver_type = match &efn.receiver {
        Some(receiver) => receiver.ident.to_string(),
        None => "_".to_string(),
    };
    format!("{}cxxbridge02${}${}", namespace, receiver_type, efn.ident)
}
<commit_msg>Remove '_' segment from symbol of non-associated functions<commit_after>use crate::syntax::namespace::Namespace;
use crate::syntax::ExternFn;

pub fn extern_fn(namespace: &Namespace, efn: &ExternFn) -> String {
    let receiver = match &efn.receiver {
        Some(receiver) => receiver.ident.to_string() + "$",
        None => String::new(),
    };
    format!("{}cxxbridge02${}{}", namespace, receiver, efn.ident)
}
<|endoftext|>


<commit_before>extern crate ctest;
extern crate cc;

use std::env;
use std::str;

fn main() {
    let mut cfg = ctest::TestGenerator::new();

    let mut build = cc::Build::new();
    build.file("version_detect.c");
    if let Ok(out) = env::var("DEP_CURL_INCLUDE") {
        cfg.include(&out);
        build.include(&out);
    }
    let version = build.expand();
    let version = str::from_utf8(&version).unwrap();
    let version = version.lines()
        .filter(|l| !l.is_empty() && !l.starts_with("#"))
        .next()
        .and_then(|s| s.parse::<u32>().ok())
        .unwrap_or(10000);

    if env::var("TARGET").unwrap().contains("msvc") {
        cfg.flag("/wd4574"); // did you mean to use '#if INCL_WINSOCK_API_TYPEDEFS'
    }

    cfg.header("curl/curl.h");
    cfg.define("CURL_STATICLIB", None);
    cfg.field_name(|s, field| {
        if s == "curl_fileinfo" {
            field.replace("strings_", "strings.")
        } else if s == "CURLMsg" && field == "data" {
            "data.whatever".to_string()
        } else {
            field.to_string()
        }
    });
    cfg.type_name(|s, is_struct, _is_union| {
        match s {
            "CURL" |
            "CURLM" |
            "CURLSH" |
            "curl_version_info_data" => s.to_string(),
            "curl_khtype" |
            "curl_khstat" |
            "curl_khmatch" => format!("enum {}", s),
            s if is_struct => format!("struct {}", s),
            "sockaddr" => format!("struct sockaddr"),
            s => s.to_string(),
        }
    });
    // cfg.fn_cname(|s, l| l.unwrap_or(s).to_string());
    cfg.skip_type(|n| n == "__enum_ty");
    cfg.skip_signededness(|s| {
        s.ends_with("callback") || s.ends_with("function")
    });

    cfg.skip_struct(move |s| {
        if version < 60 {
            match s {
                "curl_version_info_data" => return true,
                _ => {}
            }
        }

        false
    });

    cfg.skip_const(move |s| {
        if version < 61 {
            match s {
                "CURLOPT_PIPEWAIT" => return true,
                "CURLE_PEER_FAILED_VERIFICATION" => return true,
                _ => {}
            }
        }
        if version < 60 {
            match s {
                "CURLVERSION_FIFTH" |
                "CURLVERSION_NOW" => return true,
                _ => {}
            }
        }

        if version < 49 {
            if s.starts_with("CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE") {
                return true
            }
        }

        if version < 47 {
            if s.starts_with("CURL_HTTP_VERSION_2") {
                return true
            }
        }

        if version < 43 {
            if s.starts_with("CURLPIPE_") {
                return true
            }
        }

        // OSX doesn't have this yet
        s == "CURLSSLOPT_NO_REVOKE" ||

        // A lot of curl versions doesn't support unix sockets
        s == "CURLOPT_UNIX_SOCKET_PATH" || s == "CURL_VERSION_UNIX_SOCKETS"
    });

    if cfg!(target_env = "msvc") {
        cfg.skip_fn_ptrcheck(|s| s.starts_with("curl_"));
    }

    cfg.generate("../curl-sys/lib.rs", "all.rs");
}
<commit_msg>Skip verifying `CURL_SSLVERSION_TLSv1_3` when not available<commit_after>extern crate ctest;
extern crate cc;

use std::env;
use std::str;

fn main() {
    let mut cfg = ctest::TestGenerator::new();

    let mut build = cc::Build::new();
    build.file("version_detect.c");
    if let Ok(out) = env::var("DEP_CURL_INCLUDE") {
        cfg.include(&out);
        build.include(&out);
    }
    let version = build.expand();
    let version = str::from_utf8(&version).unwrap();
    let version = version.lines()
        .filter(|l| !l.is_empty() && !l.starts_with("#"))
        .next()
        .and_then(|s| s.parse::<u32>().ok())
        .unwrap_or(10000);

    if env::var("TARGET").unwrap().contains("msvc") {
        cfg.flag("/wd4574"); // did you mean to use '#if INCL_WINSOCK_API_TYPEDEFS'
    }

    cfg.header("curl/curl.h");
    cfg.define("CURL_STATICLIB", None);
    cfg.field_name(|s, field| {
        if s == "curl_fileinfo" {
            field.replace("strings_", "strings.")
        } else if s == "CURLMsg" && field == "data" {
            "data.whatever".to_string()
        } else {
            field.to_string()
        }
    });
    cfg.type_name(|s, is_struct, _is_union| {
        match s {
            "CURL" |
            "CURLM" |
            "CURLSH" |
            "curl_version_info_data" => s.to_string(),
            "curl_khtype" |
            "curl_khstat" |
            "curl_khmatch" => format!("enum {}", s),
            s if is_struct => format!("struct {}", s),
            "sockaddr" => format!("struct sockaddr"),
            s => s.to_string(),
        }
    });
    // cfg.fn_cname(|s, l| l.unwrap_or(s).to_string());
    cfg.skip_type(|n| n == "__enum_ty");
    cfg.skip_signededness(|s| {
        s.ends_with("callback") || s.ends_with("function")
    });

    cfg.skip_struct(move |s| {
        if version < 60 {
            match s {
                "curl_version_info_data" => return true,
                _ => {}
            }
        }

        false
    });

    cfg.skip_const(move |s| {
        if version < 61 {
            match s {
                "CURLOPT_PIPEWAIT" => return true,
                "CURLE_PEER_FAILED_VERIFICATION" => return true,
                _ => {}
            }
        }
        if version < 60 {
            match s {
                "CURLVERSION_FIFTH" |
                "CURLVERSION_NOW" => return true,
                _ => {}
            }
        }
        if version < 54 {
            match s {
                "CURL_SSLVERSION_TLSv1_3" => return true,
                _ => {}
            }
        }

        if version < 49 {
            if s.starts_with("CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE") {
                return true
            }
        }

        if version < 47 {
            if s.starts_with("CURL_HTTP_VERSION_2") {
                return true
            }
        }

        if version < 43 {
            if s.starts_with("CURLPIPE_") {
                return true
            }
        }

        // OSX doesn't have this yet
        s == "CURLSSLOPT_NO_REVOKE" ||

        // A lot of curl versions doesn't support unix sockets
        s == "CURLOPT_UNIX_SOCKET_PATH" || s == "CURL_VERSION_UNIX_SOCKETS"
    });

    if cfg!(target_env = "msvc") {
        cfg.skip_fn_ptrcheck(|s| s.starts_with("curl_"));
    }

    cfg.generate("../curl-sys/lib.rs", "all.rs");
}
<|endoftext|>


<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Alex Lyon <arcterus@mail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) RFILE refsize rfilename fsize tsize

#[macro_use]
extern crate uucore;

use clap::{App, Arg};
use std::fs::{metadata, OpenOptions};
use std::io::ErrorKind;
use std::path::Path;

#[derive(Eq, PartialEq)]
enum TruncateMode {
    Absolute,
    Reference,
    Extend,
    Reduce,
    AtMost,
    AtLeast,
    RoundDown,
    RoundUp,
}

static ABOUT: &str = "Shrink or extend the size of each file to the specified size.";
static VERSION: &str = env!("CARGO_PKG_VERSION");

pub mod options {
    pub static IO_BLOCKS: &str = "io-blocks";
    pub static NO_CREATE: &str = "no-create";
    pub static REFERENCE: &str = "reference";
    pub static SIZE: &str = "size";
}

static ARG_FILES: &str = "files";

fn get_usage() -> String {
    format!("{0} [OPTION]... [FILE]...", executable!())
}

fn get_long_usage() -> String {
    String::from(
        "
    SIZE is an integer with an optional prefix and optional unit.
    The available units (K, M, G, T, P, E, Z, and Y) use the following format:
        'KB' =>           1000 (kilobytes)
        'K'  =>           1024 (kibibytes)
        'MB' =>      1000*1000 (megabytes)
        'M'  =>      1024*1024 (mebibytes)
        'GB' => 1000*1000*1000 (gigabytes)
        'G'  => 1024*1024*1024 (gibibytes)
    SIZE may also be prefixed by one of the following to adjust the size of each
    file based on its current size:
        '+'  => extend by
        '-'  => reduce by
        '<'  => at most
        '>'  => at least
        '/'  => round down to multiple of
        '%'  => round up to multiple of",
    )
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let usage = get_usage();
    let long_usage = get_long_usage();

    let matches = App::new(executable!())
        .version(VERSION)
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(&long_usage[..])
        .arg(
            Arg::with_name(options::IO_BLOCKS)
            .short("o")
            .long(options::IO_BLOCKS)
            .help("treat SIZE as the number of I/O blocks of the file rather than bytes (NOT IMPLEMENTED)")
        )
        .arg(
            Arg::with_name(options::NO_CREATE)
            .short("c")
            .long(options::NO_CREATE)
            .help("do not create files that do not exist")
        )
        .arg(
            Arg::with_name(options::REFERENCE)
            .short("r")
            .long(options::REFERENCE)
            .help("base the size of each file on the size of RFILE")
            .value_name("RFILE")
        )
        .arg(
            Arg::with_name(options::SIZE)
            .short("s")
            .long("size")
            .help("set or adjust the size of each file according to SIZE, which is in bytes unless --io-blocks is specified")
            .value_name("SIZE")
        )
        .arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true).min_values(1))
        .get_matches_from(args);

    let files: Vec<String> = matches
        .values_of(ARG_FILES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    if files.is_empty() {
        show_error!("Missing an argument");
        return 1;
    } else {
        let io_blocks = matches.is_present(options::IO_BLOCKS);
        let no_create = matches.is_present(options::NO_CREATE);
        let reference = matches.value_of(options::REFERENCE).map(String::from);
        let size = matches.value_of(options::SIZE).map(String::from);
        if reference.is_none() && size.is_none() {
            crash!(1, "you must specify either --reference or --size");
        } else {
            truncate(no_create, io_blocks, reference, size, files);
        }
    }

    0
}

fn truncate(
    no_create: bool,
    _: bool,
    reference: Option<String>,
    size: Option<String>,
    filenames: Vec<String>,
) {
    let (modsize, mode) = match size {
        Some(size_string) => {
            // Trim any whitespace.
            let size_string = size_string.trim();

            // Get the modifier character from the size string, if any. For
            // example, if the argument is "+123", then the modifier is '+'.
            let c = size_string.chars().next().unwrap();

            let mode = match c {
                '+' => TruncateMode::Extend,
                '-' => TruncateMode::Reduce,
                '<' => TruncateMode::AtMost,
                '>' => TruncateMode::AtLeast,
                '/' => TruncateMode::RoundDown,
                '%' => TruncateMode::RoundUp,
                _ => TruncateMode::Absolute, /* assume that the size is just a number */
            };

            // If there was a modifier character, strip it.
            let size_string = match mode {
                TruncateMode::Absolute => size_string,
                _ => &size_string[1..],
            };
            let num_bytes = match parse_size(size_string) {
                Ok(b) => b,
                Err(_) => crash!(1, "Invalid number: ‘{}’", size_string),
            };
            (num_bytes, mode)
        }
        None => (0, TruncateMode::Reference),
    };

    let refsize = match reference {
        Some(ref rfilename) => {
            match mode {
                // Only Some modes work with a reference
                TruncateMode::Reference => (), //No --size was given
                TruncateMode::Extend => (),
                TruncateMode::Reduce => (),
                _ => crash!(1, "you must specify a relative ‘--size’ with ‘--reference’"),
            };
            match metadata(rfilename) {
                Ok(meta) => meta.len(),
                Err(f) => match f.kind() {
                    ErrorKind::NotFound => {
                        crash!(1, "cannot stat '{}': No such file or directory", rfilename)
                    }
                    _ => crash!(1, "{}", f.to_string()),
                },
            }
        }
        None => 0,
    };
    for filename in &filenames {
        let path = Path::new(filename);
        match OpenOptions::new().write(true).create(!no_create).open(path) {
            Ok(file) => {
                let fsize = match reference {
                    Some(_) => refsize,
                    None => match metadata(filename) {
                        Ok(meta) => meta.len(),
                        Err(f) => {
                            show_warning!("{}", f.to_string());
                            continue;
                        }
                    },
                };
                let tsize: u64 = match mode {
                    TruncateMode::Absolute => modsize,
                    TruncateMode::Reference => fsize,
                    TruncateMode::Extend => fsize + modsize,
                    TruncateMode::Reduce => fsize - modsize,
                    TruncateMode::AtMost => fsize.min(modsize),
                    TruncateMode::AtLeast => fsize.max(modsize),
                    TruncateMode::RoundDown => fsize - fsize % modsize,
                    TruncateMode::RoundUp => fsize + fsize % modsize,
                };
                match file.set_len(tsize) {
                    Ok(_) => {}
                    Err(f) => crash!(1, "{}", f.to_string()),
                };
            }
            Err(f) => crash!(1, "{}", f.to_string()),
        }
    }
}

/// Parse a size string into a number of bytes.
///
/// A size string comprises an integer and an optional unit. The unit
/// may be K, M, G, T, P, E, Z, or Y (powers of 1024) or KB, MB,
/// etc. (powers of 1000).
///
/// # Errors
///
/// This function returns an error if the string does not begin with a
/// numeral, or if the unit is not one of the supported units described
/// in the preceding section.
///
/// # Examples
///
/// ```rust,ignore
/// assert_eq!(parse_size("123").unwrap(), 123);
/// assert_eq!(parse_size("123K").unwrap(), 123 * 1024);
/// assert_eq!(parse_size("123KB").unwrap(), 123 * 1000);
/// ```
fn parse_size(size: &str) -> Result<u64, ()> {
    // Get the numeric part of the size argument. For example, if the
    // argument is "123K", then the numeric part is "123".
    let numeric_string: String = size.chars().take_while(|c| c.is_digit(10)).collect();
    let number: u64 = match numeric_string.parse() {
        Ok(n) => n,
        Err(_) => return Err(()),
    };

    // Get the alphabetic units part of the size argument and compute
    // the factor it represents. For example, if the argument is "123K",
    // then the unit part is "K" and the factor is 1024. This may be the
    // empty string, in which case, the factor is 1.
    let n = numeric_string.len();
    let (base, exponent): (u64, u32) = match &size[n..] {
        "" => (1, 0),
        "K" | "k" => (1024, 1),
        "M" | "m" => (1024, 2),
        "G" | "g" => (1024, 3),
        "T" | "t" => (1024, 4),
        "P" | "p" => (1024, 5),
        "E" | "e" => (1024, 6),
        "Z" | "z" => (1024, 7),
        "Y" | "y" => (1024, 8),
        "KB" | "kB" => (1000, 1),
        "MB" | "mB" => (1000, 2),
        "GB" | "gB" => (1000, 3),
        "TB" | "tB" => (1000, 4),
        "PB" | "pB" => (1000, 5),
        "EB" | "eB" => (1000, 6),
        "ZB" | "zB" => (1000, 7),
        "YB" | "yB" => (1000, 8),
        _ => return Err(()),
    };
    let factor = base.pow(exponent);
    Ok(number * factor)
}

#[cfg(test)]
mod tests {
    use crate::parse_size;

    #[test]
    fn test_parse_size_zero() {
        assert_eq!(parse_size("0").unwrap(), 0);
        assert_eq!(parse_size("0K").unwrap(), 0);
        assert_eq!(parse_size("0KB").unwrap(), 0);
    }

    #[test]
    fn test_parse_size_without_factor() {
        assert_eq!(parse_size("123").unwrap(), 123);
    }

    #[test]
    fn test_parse_size_kilobytes() {
        assert_eq!(parse_size("123K").unwrap(), 123 * 1024);
        assert_eq!(parse_size("123KB").unwrap(), 123 * 1000);
    }

    #[test]
    fn test_parse_size_megabytes() {
        assert_eq!(parse_size("123").unwrap(), 123);
        assert_eq!(parse_size("123M").unwrap(), 123 * 1024 * 1024);
        assert_eq!(parse_size("123MB").unwrap(), 123 * 1000 * 1000);
    }
}
<commit_msg>truncate: add parse_mode_and_size() helper func<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Alex Lyon <arcterus@mail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) RFILE refsize rfilename fsize tsize

#[macro_use]
extern crate uucore;

use clap::{App, Arg};
use std::fs::{metadata, OpenOptions};
use std::io::ErrorKind;
use std::path::Path;

#[derive(Debug, Eq, PartialEq)]
enum TruncateMode {
    Reference(u64),
    Absolute(u64),
    Extend(u64),
    Reduce(u64),
    AtMost(u64),
    AtLeast(u64),
    RoundDown(u64),
    RoundUp(u64),
}

static ABOUT: &str = "Shrink or extend the size of each file to the specified size.";
static VERSION: &str = env!("CARGO_PKG_VERSION");

pub mod options {
    pub static IO_BLOCKS: &str = "io-blocks";
    pub static NO_CREATE: &str = "no-create";
    pub static REFERENCE: &str = "reference";
    pub static SIZE: &str = "size";
}

static ARG_FILES: &str = "files";

fn get_usage() -> String {
    format!("{0} [OPTION]... [FILE]...", executable!())
}

fn get_long_usage() -> String {
    String::from(
        "
    SIZE is an integer with an optional prefix and optional unit.
    The available units (K, M, G, T, P, E, Z, and Y) use the following format:
        'KB' =>           1000 (kilobytes)
        'K'  =>           1024 (kibibytes)
        'MB' =>      1000*1000 (megabytes)
        'M'  =>      1024*1024 (mebibytes)
        'GB' => 1000*1000*1000 (gigabytes)
        'G'  => 1024*1024*1024 (gibibytes)
    SIZE may also be prefixed by one of the following to adjust the size of each
    file based on its current size:
        '+'  => extend by
        '-'  => reduce by
        '<'  => at most
        '>'  => at least
        '/'  => round down to multiple of
        '%'  => round up to multiple of",
    )
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let usage = get_usage();
    let long_usage = get_long_usage();

    let matches = App::new(executable!())
        .version(VERSION)
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(&long_usage[..])
        .arg(
            Arg::with_name(options::IO_BLOCKS)
            .short("o")
            .long(options::IO_BLOCKS)
            .help("treat SIZE as the number of I/O blocks of the file rather than bytes (NOT IMPLEMENTED)")
        )
        .arg(
            Arg::with_name(options::NO_CREATE)
            .short("c")
            .long(options::NO_CREATE)
            .help("do not create files that do not exist")
        )
        .arg(
            Arg::with_name(options::REFERENCE)
            .short("r")
            .long(options::REFERENCE)
            .help("base the size of each file on the size of RFILE")
            .value_name("RFILE")
        )
        .arg(
            Arg::with_name(options::SIZE)
            .short("s")
            .long("size")
            .help("set or adjust the size of each file according to SIZE, which is in bytes unless --io-blocks is specified")
            .value_name("SIZE")
        )
        .arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true).min_values(1))
        .get_matches_from(args);

    let files: Vec<String> = matches
        .values_of(ARG_FILES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    if files.is_empty() {
        show_error!("Missing an argument");
        return 1;
    } else {
        let io_blocks = matches.is_present(options::IO_BLOCKS);
        let no_create = matches.is_present(options::NO_CREATE);
        let reference = matches.value_of(options::REFERENCE).map(String::from);
        let size = matches.value_of(options::SIZE).map(String::from);
        if reference.is_none() && size.is_none() {
            crash!(1, "you must specify either --reference or --size");
        } else {
            truncate(no_create, io_blocks, reference, size, files);
        }
    }

    0
}

fn truncate(
    no_create: bool,
    _: bool,
    reference: Option<String>,
    size: Option<String>,
    filenames: Vec<String>,
) {
    let mode = match size {
        Some(size_string) => match parse_mode_and_size(&size_string) {
            Ok(m) => m,
            Err(_) => crash!(1, "Invalid number: ‘{}’", size_string),
        },
        None => TruncateMode::Reference(0),
    };

    let refsize = match reference {
        Some(ref rfilename) => {
            match mode {
                // Only Some modes work with a reference
                TruncateMode::Reference(_) => (), //No --size was given
                TruncateMode::Extend(_) => (),
                TruncateMode::Reduce(_) => (),
                _ => crash!(1, "you must specify a relative ‘--size’ with ‘--reference’"),
            };
            match metadata(rfilename) {
                Ok(meta) => meta.len(),
                Err(f) => match f.kind() {
                    ErrorKind::NotFound => {
                        crash!(1, "cannot stat '{}': No such file or directory", rfilename)
                    }
                    _ => crash!(1, "{}", f.to_string()),
                },
            }
        }
        None => 0,
    };
    for filename in &filenames {
        let path = Path::new(filename);
        match OpenOptions::new().write(true).create(!no_create).open(path) {
            Ok(file) => {
                let fsize = match reference {
                    Some(_) => refsize,
                    None => match metadata(filename) {
                        Ok(meta) => meta.len(),
                        Err(f) => {
                            show_warning!("{}", f.to_string());
                            continue;
                        }
                    },
                };
                let tsize: u64 = match mode {
                    TruncateMode::Absolute(modsize) => modsize,
                    TruncateMode::Reference(_) => fsize,
                    TruncateMode::Extend(modsize) => fsize + modsize,
                    TruncateMode::Reduce(modsize) => fsize - modsize,
                    TruncateMode::AtMost(modsize) => fsize.min(modsize),
                    TruncateMode::AtLeast(modsize) => fsize.max(modsize),
                    TruncateMode::RoundDown(modsize) => fsize - fsize % modsize,
                    TruncateMode::RoundUp(modsize) => fsize + fsize % modsize,
                };
                match file.set_len(tsize) {
                    Ok(_) => {}
                    Err(f) => crash!(1, "{}", f.to_string()),
                };
            }
            Err(f) => crash!(1, "{}", f.to_string()),
        }
    }
}

/// Decide whether a character is one of the size modifiers, like '+' or '<'.
fn is_modifier(c: char) -> bool {
    c == '+' || c == '-' || c == '<' || c == '>' || c == '/' || c == '%'
}

/// Parse a size string with optional modifier symbol as its first character.
///
/// A size string is as described in [`parse_size`]. The first character
/// of `size_string` might be a modifier symbol, like `'+'` or
/// `'<'`. The first element of the pair returned by this function
/// indicates which modifier symbol was present, or
/// [`TruncateMode::Absolute`] if none.
///
/// # Panics
///
/// If `size_string` is empty, or if no number could be parsed from the
/// given string (for example, if the string were `"abc"`).
///
/// # Examples
///
/// ```rust,ignore
/// assert_eq!(parse_mode_and_size("+123"), (TruncateMode::Extend, 123));
/// ```
fn parse_mode_and_size(size_string: &str) -> Result<TruncateMode, ()> {
    // Trim any whitespace.
    let size_string = size_string.trim();

    // Get the modifier character from the size string, if any. For
    // example, if the argument is "+123", then the modifier is '+'.
    let c = size_string.chars().next().unwrap();
    let size_string = if is_modifier(c) {
        &size_string[1..]
    } else {
        size_string
    };
    parse_size(size_string).map(match c {
        '+' => TruncateMode::Extend,
        '-' => TruncateMode::Reduce,
        '<' => TruncateMode::AtMost,
        '>' => TruncateMode::AtLeast,
        '/' => TruncateMode::RoundDown,
        '%' => TruncateMode::RoundUp,
        _ => TruncateMode::Absolute,
    })
}

/// Parse a size string into a number of bytes.
///
/// A size string comprises an integer and an optional unit. The unit
/// may be K, M, G, T, P, E, Z, or Y (powers of 1024) or KB, MB,
/// etc. (powers of 1000).
///
/// # Errors
///
/// This function returns an error if the string does not begin with a
/// numeral, or if the unit is not one of the supported units described
/// in the preceding section.
///
/// # Examples
///
/// ```rust,ignore
/// assert_eq!(parse_size("123").unwrap(), 123);
/// assert_eq!(parse_size("123K").unwrap(), 123 * 1024);
/// assert_eq!(parse_size("123KB").unwrap(), 123 * 1000);
/// ```
fn parse_size(size: &str) -> Result<u64, ()> {
    // Get the numeric part of the size argument. For example, if the
    // argument is "123K", then the numeric part is "123".
    let numeric_string: String = size.chars().take_while(|c| c.is_digit(10)).collect();
    let number: u64 = match numeric_string.parse() {
        Ok(n) => n,
        Err(_) => return Err(()),
    };

    // Get the alphabetic units part of the size argument and compute
    // the factor it represents. For example, if the argument is "123K",
    // then the unit part is "K" and the factor is 1024. This may be the
    // empty string, in which case, the factor is 1.
    let n = numeric_string.len();
    let (base, exponent): (u64, u32) = match &size[n..] {
        "" => (1, 0),
        "K" | "k" => (1024, 1),
        "M" | "m" => (1024, 2),
        "G" | "g" => (1024, 3),
        "T" | "t" => (1024, 4),
        "P" | "p" => (1024, 5),
        "E" | "e" => (1024, 6),
        "Z" | "z" => (1024, 7),
        "Y" | "y" => (1024, 8),
        "KB" | "kB" => (1000, 1),
        "MB" | "mB" => (1000, 2),
        "GB" | "gB" => (1000, 3),
        "TB" | "tB" => (1000, 4),
        "PB" | "pB" => (1000, 5),
        "EB" | "eB" => (1000, 6),
        "ZB" | "zB" => (1000, 7),
        "YB" | "yB" => (1000, 8),
        _ => return Err(()),
    };
    let factor = base.pow(exponent);
    Ok(number * factor)
}

#[cfg(test)]
mod tests {
    use crate::parse_mode_and_size;
    use crate::parse_size;
    use crate::TruncateMode;

    #[test]
    fn test_parse_size_zero() {
        assert_eq!(parse_size("0").unwrap(), 0);
        assert_eq!(parse_size("0K").unwrap(), 0);
        assert_eq!(parse_size("0KB").unwrap(), 0);
    }

    #[test]
    fn test_parse_size_without_factor() {
        assert_eq!(parse_size("123").unwrap(), 123);
    }

    #[test]
    fn test_parse_size_kilobytes() {
        assert_eq!(parse_size("123K").unwrap(), 123 * 1024);
        assert_eq!(parse_size("123KB").unwrap(), 123 * 1000);
    }

    #[test]
    fn test_parse_size_megabytes() {
        assert_eq!(parse_size("123").unwrap(), 123);
        assert_eq!(parse_size("123M").unwrap(), 123 * 1024 * 1024);
        assert_eq!(parse_size("123MB").unwrap(), 123 * 1000 * 1000);
    }

    #[test]
    fn test_parse_mode_and_size() {
        assert_eq!(parse_mode_and_size("10"), Ok(TruncateMode::Absolute(10)));
        assert_eq!(parse_mode_and_size("+10"), Ok(TruncateMode::Extend(10)));
        assert_eq!(parse_mode_and_size("-10"), Ok(TruncateMode::Reduce(10)));
        assert_eq!(parse_mode_and_size("<10"), Ok(TruncateMode::AtMost(10)));
        assert_eq!(parse_mode_and_size(">10"), Ok(TruncateMode::AtLeast(10)));
        assert_eq!(parse_mode_and_size("/10"), Ok(TruncateMode::RoundDown(10)));
        assert_eq!(parse_mode_and_size("%10"), Ok(TruncateMode::RoundUp(10)));
    }
}
<|endoftext|>


<commit_before>use super::text_image::TextImage;
use super::image::{DynamicImage, GenericImage, Pixel};
use character_set::CharacterSet;

/// Converts an image to a TextImage using the given character set
pub fn image_to_text(img: DynamicImage, char_set: CharacterSet, target_width: u32) -> TextImage {
    let (img_width, img_height) = img.dimensions();
    let width_interval = closest_interval(img_width / target_width, img_width);
    let height_interval = width_interval * 2;
    let width = img_width / width_interval;
    let height = img_height / height_interval;

    let mut text_img = TextImage::new(width as usize, height as usize);

    // Iterate through each width_interval x height_interval chunk and calculate brightness
    for height_index in 0..height {
        for width_index in 0..width {
            let mut total_brightness: u32 = 0;
            for y in height_interval * height_index..height_interval * (height_index + 1) {
                for x in width_interval * width_index..width_interval * (width_index + 1) {
                    let rgb  = img.get_pixel(x, y).to_rgb().data;
                    let (r, g, b) = (rgb[0] as u32, rgb[1] as u32, rgb[2] as u32);
                    total_brightness += (r + g + b) / 3;
                }
            }
            let area = width_interval * height_interval;
            let brightness = total_brightness as f32 / area as f32;
            let c = char_set.get(brightness as i32);
            text_img.set_char(width_index as usize, height_index as usize, c);
        }
    }
    text_img
}

// Calculates the interval closest to target_interval such that img_size % target_interval == 0
fn closest_interval(target_interval: u32, img_size: u32) -> u32 {
    // Naive approach
    let mut upper_bound = target_interval;
    let mut lower_bound = target_interval;
    let res: u32;
    loop {
        if img_size % upper_bound == 0 {
            res = upper_bound;
            break;
        }
        else if img_size % lower_bound == 0 {
            res = lower_bound;
            break;
        }
        upper_bound += 1;
        lower_bound -= 1;
    }
    res
}
<commit_msg>Additional docs for image_to_text<commit_after>use super::text_image::TextImage;
use super::image::{DynamicImage, GenericImage, Pixel};
use character_set::CharacterSet;

/// Converts an image to a TextImage using the given character set
///
/// `target_width` is a goal width - the actual width will be a value close to
/// `target_width` that preserves the image's aspect ratio
pub fn image_to_text(img: DynamicImage, char_set: CharacterSet, target_width: u32) -> TextImage {
    let (img_width, img_height) = img.dimensions();
    let width_interval = closest_interval(img_width / target_width, img_width);
    let height_interval = width_interval * 2;
    let width = img_width / width_interval;
    let height = img_height / height_interval;

    let mut text_img = TextImage::new(width as usize, height as usize);

    // Iterate through each width_interval x height_interval chunk and calculate brightness
    for height_index in 0..height {
        for width_index in 0..width {
            let mut total_brightness: u32 = 0;
            for y in height_interval * height_index..height_interval * (height_index + 1) {
                for x in width_interval * width_index..width_interval * (width_index + 1) {
                    let rgb  = img.get_pixel(x, y).to_rgb().data;
                    let (r, g, b) = (rgb[0] as u32, rgb[1] as u32, rgb[2] as u32);
                    total_brightness += (r + g + b) / 3;
                }
            }
            let area = width_interval * height_interval;
            let brightness = total_brightness as f32 / area as f32;
            let c = char_set.get(brightness as i32);
            text_img.set_char(width_index as usize, height_index as usize, c);
        }
    }
    text_img
}

// Calculates the interval closest to target_interval such that img_size % target_interval == 0
fn closest_interval(target_interval: u32, img_size: u32) -> u32 {
    // Naive approach
    let mut upper_bound = target_interval;
    let mut lower_bound = target_interval;
    let res: u32;
    loop {
        if img_size % upper_bound == 0 {
            res = upper_bound;
            break;
        }
        else if img_size % lower_bound == 0 {
            res = lower_bound;
            break;
        }
        upper_bound += 1;
        lower_bound -= 1;
    }
    res
}
<|endoftext|>


<commit_before>extern mod sdl;

use std::rand::Rng;
use std::rand;

#[main]
pub fn main() {
    sdl::init([sdl::InitVideo]);
    sdl::wm::set_caption("rust-sdl demo - video", "rust-sdl");

    let mut rng = rand::rng();
    let screen = match sdl::video::set_video_mode(800, 600, 32, [sdl::video::HWSurface],
                                                                [sdl::video::DoubleBuf]) {
        Ok(screen) => screen,
        Err(err) => fail!("failed to set video mode: {}", err)
    };

    // Note: You'll want to put this and the flip call inside the main loop
    // but we don't as to not startle epileptics
    for i in range(0, 10) {
        for j in range(0, 10) {
            screen.fill_rect(Some(sdl::Rect {
                x: (i as i16) * 800 / 10,
                y: (j as i16) * 600 / 10,
                w: 800 / 10,
                h: 600 / 10
            }), rng.gen::<sdl::video::Color>());
        }
    }

    screen.flip();

    'main : loop {
        'event : loop {
            match sdl::event::poll_event() {
                sdl::event::QuitEvent => break 'main,
                sdl::event::NoEvent => break 'event,
                sdl::event::KeyEvent(k, _, _, _)
                    if k == sdl::event::EscapeKey
                        => break 'main,
                _ => {}
            }
        }
    }

    sdl::quit();
}
<commit_msg>updated sdl-demo for the language change.<commit_after>extern crate sdl;

use std::rand::Rng;
use std::rand;

#[main]
pub fn main() {
    sdl::init([sdl::InitVideo]);
    sdl::wm::set_caption("rust-sdl demo - video", "rust-sdl");

    let mut rng = rand::rng();
    let screen = match sdl::video::set_video_mode(800, 600, 32, [sdl::video::HWSurface],
                                                                [sdl::video::DoubleBuf]) {
        Ok(screen) => screen,
        Err(err) => fail!("failed to set video mode: {}", err)
    };

    // Note: You'll want to put this and the flip call inside the main loop
    // but we don't as to not startle epileptics
    for i in range(0, 10) {
        for j in range(0, 10) {
            screen.fill_rect(Some(sdl::Rect {
                x: (i as i16) * 800 / 10,
                y: (j as i16) * 600 / 10,
                w: 800 / 10,
                h: 600 / 10
            }), rng.gen::<sdl::video::Color>());
        }
    }

    screen.flip();

    'main : loop {
        'event : loop {
            match sdl::event::poll_event() {
                sdl::event::QuitEvent => break 'main,
                sdl::event::NoEvent => break 'event,
                sdl::event::KeyEvent(k, _, _, _)
                    if k == sdl::event::EscapeKey
                        => break 'main,
                _ => {}
            }
        }
    }

    sdl::quit();
}
<|endoftext|>


<commit_before>#![warn(non_snake_case)]

use std::collections::HashMap;
use std::str::FromStr;

use serde::de::Error;
use serde::{Deserialize, Deserializer};
use std::ops::Deref;
use std::ops::Range;

#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
#[allow(unused)]
struct SpeedTreeRaw {
    version_major: u8,
    version_minor: u8,
    user_data: String,
    source: String,
    wind: Wind,
    objects: Objects,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
#[allow(unused)]
struct Wind {
    bend: f32,
    branch_amplitude: f32,
    leaf_amplitude: f32,
    detail_frequency: f32,
    global_height: f32,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
#[allow(unused)]
struct Objects {
    count: u32,
    lod_near: f32,
    lod_far: f32,
    bounds_min_x: f32,
    bounds_min_y: f32,
    bounds_min_z: f32,
    bounds_max_x: f32,
    bounds_max_y: f32,
    bounds_max_z: f32,
    #[serde(rename = "Object", default)]
    objects: Vec<Object>,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
struct Object {
    #[serde(rename = "ID")]
    id: u32,
    name: String,
    #[serde(rename = "ParentID")]
    parent_id: Option<u32>,
    points: Option<Points>,
    vertices: Option<Vertices>,
    triangles: Option<Triangles>,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
struct Points {
    x: List<f32>,
    y: List<f32>,
    z: List<f32>,
    lod_x: List<f32>,
    lod_y: List<f32>,
    lod_z: List<f32>,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
#[allow(unused)]
struct Vertices {
    count: u32,
    normal_x: List<f32>,
    normal_y: List<f32>,
    normal_z: List<f32>,
    binormal_x: List<f32>,
    binormal_y: List<f32>,
    binormal_z: List<f32>,
    tangent_x: List<f32>,
    tangent_y: List<f32>,
    tangent_z: List<f32>,
    texcoord_u: List<f32>,
    texcoord_v: List<f32>,
    #[serde(rename = "AO")]
    ao: List<f32>,
    vertex_color_r: List<f32>,
    vertex_color_g: List<f32>,
    vertex_color_b: List<f32>,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
struct Triangles {
    count: u32,
    #[allow(unused)]
    material: u32,
    point_indices: List<u32>,
    vertex_indices: List<u32>,
}

#[derive(Debug)]
struct List<T>(Vec<T>);
impl<'de, T: Deserialize<'de> + FromStr> serde::Deserialize<'de> for List<T> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;

        match s.split(" ").map(T::from_str).collect() {
            Err(_) => Err(D::Error::custom("Failed to parse space seperated list")),
            Ok(values) => Ok(Self(values)),
        }
    }
}
impl<T> Deref for List<T> {
    type Target = Vec<T>;
    fn deref(&self) -> &Vec<T> {
        &self.0
    }
}

#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub(crate) struct Vertex {
    position: [f32; 3],
    ao: f32,

    lod_position: [f32; 3],
    color: u32,
    normal: [f32; 3],
    texcoord_u: f32,
    binormal: [f32; 3],
    texcoord_v: f32,
}
unsafe impl bytemuck::Pod for Vertex {}
unsafe impl bytemuck::Zeroable for Vertex {}

pub(crate) struct SpeedTreeModel {
    pub vertices: Vec<Vertex>,
    pub indices: Vec<u32>,
    pub lods: Vec<Range<u32>>,
}

pub(crate) fn parse_xml(contents: &str) -> Result<SpeedTreeModel, anyhow::Error> {
    let t: SpeedTreeRaw = quick_xml::de::from_str(contents)?;

    let mut object_names = HashMap::new();
    let mut merged_objects = HashMap::new();
    for object in &t.objects.objects {
        if let Some(parent_id) = object.parent_id {
            let points = object.points.as_ref().unwrap();
            let vertices = object.vertices.as_ref().unwrap();
            let triangles = object.triangles.as_ref().unwrap();

            let (output_vertices, output_indices) =
                merged_objects.entry(parent_id).or_insert((Vec::new(), Vec::new()));

            assert_eq!(triangles.count * 3, triangles.point_indices.len() as u32);

            let mut ids_to_index = HashMap::new();
            for tri_index in 0..(triangles.count * 3) as usize {
                let pid = triangles.point_indices[tri_index] as usize;
                let vid = triangles.vertex_indices[tri_index] as usize;

                if let Some(index) = ids_to_index.get(&(pid, vid)) {
                    output_indices.push(*index);
                    continue;
                }
                let index = output_vertices.len() as u32;
                ids_to_index.insert((pid, vid), index);
                output_indices.push(index);
                output_vertices.push(Vertex {
                    position: [points.x[pid], points.z[pid], points.y[pid]],
                    lod_position: [points.lod_x[pid], points.lod_z[pid], points.lod_y[pid]],
                    normal: [
                        vertices.normal_x[vid],
                        vertices.normal_z[vid],
                        vertices.normal_y[vid],
                    ],
                    binormal: [
                        vertices.binormal_x[vid],
                        vertices.binormal_z[vid],
                        vertices.binormal_y[vid],
                    ],
                    texcoord_u: vertices.texcoord_u[vid],
                    texcoord_v: vertices.texcoord_v[vid],
                    ao: vertices.ao[vid],
                    color: ((vertices.vertex_color_r[vid].min(1.0).max(0.0) * 255.0) as u32)
                        | ((vertices.vertex_color_g[vid].min(1.0).max(0.0) * 255.0) as u32) << 8
                        | ((vertices.vertex_color_b[vid].min(1.0).max(0.0) * 255.0) as u32) << 16,
                })
            }
        } else {
            object_names.insert(object.name.clone(), object.id);
        }
    }

    let mut lods = Vec::new();
    let mut vertices = Vec::new();
    let mut indices = Vec::new();
    for name in ["LOD0", "LOD1", "LOD2", "LOD3"] {
        if let Some(id) = object_names.get(name) {
            let mut merged = merged_objects.remove(id).unwrap();

            let base_vertex = vertices.len() as u32;
            let start = indices.len() as u32;
            let end = (indices.len() + merged.1.len()) as u32;

            for index in merged.1 {
                indices.push(index + base_vertex);
            }
            vertices.append(&mut merged.0);
            lods.push(start..end);
        }
    }

    Ok(SpeedTreeModel { vertices, indices, lods })
}

#[cfg(test)]
mod tests {
    #[test]
    fn vertex_size() {
        assert_eq!(std::mem::size_of::<super::Vertex>(), 64);
    }
    #[test]
    fn parse_xml() {
        let t: super::SpeedTreeRaw =
            quick_xml::de::from_str(include_str!("../assets/Tree/Oak_English_Sapling.xml"))
                .unwrap();
    }
}
<commit_msg>Comment out xml parsing test<commit_after>#![warn(non_snake_case)]

use std::collections::HashMap;
use std::str::FromStr;

use serde::de::Error;
use serde::{Deserialize, Deserializer};
use std::ops::Deref;
use std::ops::Range;

#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
#[allow(unused)]
struct SpeedTreeRaw {
    version_major: u8,
    version_minor: u8,
    user_data: String,
    source: String,
    wind: Wind,
    objects: Objects,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
#[allow(unused)]
struct Wind {
    bend: f32,
    branch_amplitude: f32,
    leaf_amplitude: f32,
    detail_frequency: f32,
    global_height: f32,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
#[allow(unused)]
struct Objects {
    count: u32,
    lod_near: f32,
    lod_far: f32,
    bounds_min_x: f32,
    bounds_min_y: f32,
    bounds_min_z: f32,
    bounds_max_x: f32,
    bounds_max_y: f32,
    bounds_max_z: f32,
    #[serde(rename = "Object", default)]
    objects: Vec<Object>,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
struct Object {
    #[serde(rename = "ID")]
    id: u32,
    name: String,
    #[serde(rename = "ParentID")]
    parent_id: Option<u32>,
    points: Option<Points>,
    vertices: Option<Vertices>,
    triangles: Option<Triangles>,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
struct Points {
    x: List<f32>,
    y: List<f32>,
    z: List<f32>,
    lod_x: List<f32>,
    lod_y: List<f32>,
    lod_z: List<f32>,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
#[allow(unused)]
struct Vertices {
    count: u32,
    normal_x: List<f32>,
    normal_y: List<f32>,
    normal_z: List<f32>,
    binormal_x: List<f32>,
    binormal_y: List<f32>,
    binormal_z: List<f32>,
    tangent_x: List<f32>,
    tangent_y: List<f32>,
    tangent_z: List<f32>,
    texcoord_u: List<f32>,
    texcoord_v: List<f32>,
    #[serde(rename = "AO")]
    ao: List<f32>,
    vertex_color_r: List<f32>,
    vertex_color_g: List<f32>,
    vertex_color_b: List<f32>,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
struct Triangles {
    count: u32,
    #[allow(unused)]
    material: u32,
    point_indices: List<u32>,
    vertex_indices: List<u32>,
}

#[derive(Debug)]
struct List<T>(Vec<T>);
impl<'de, T: Deserialize<'de> + FromStr> serde::Deserialize<'de> for List<T> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;

        match s.split(" ").map(T::from_str).collect() {
            Err(_) => Err(D::Error::custom("Failed to parse space seperated list")),
            Ok(values) => Ok(Self(values)),
        }
    }
}
impl<T> Deref for List<T> {
    type Target = Vec<T>;
    fn deref(&self) -> &Vec<T> {
        &self.0
    }
}

#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub(crate) struct Vertex {
    position: [f32; 3],
    ao: f32,

    lod_position: [f32; 3],
    color: u32,
    normal: [f32; 3],
    texcoord_u: f32,
    binormal: [f32; 3],
    texcoord_v: f32,
}
unsafe impl bytemuck::Pod for Vertex {}
unsafe impl bytemuck::Zeroable for Vertex {}

pub(crate) struct SpeedTreeModel {
    pub vertices: Vec<Vertex>,
    pub indices: Vec<u32>,
    pub lods: Vec<Range<u32>>,
}

pub(crate) fn parse_xml(contents: &str) -> Result<SpeedTreeModel, anyhow::Error> {
    let t: SpeedTreeRaw = quick_xml::de::from_str(contents)?;

    let mut object_names = HashMap::new();
    let mut merged_objects = HashMap::new();
    for object in &t.objects.objects {
        if let Some(parent_id) = object.parent_id {
            let points = object.points.as_ref().unwrap();
            let vertices = object.vertices.as_ref().unwrap();
            let triangles = object.triangles.as_ref().unwrap();

            let (output_vertices, output_indices) =
                merged_objects.entry(parent_id).or_insert((Vec::new(), Vec::new()));

            assert_eq!(triangles.count * 3, triangles.point_indices.len() as u32);

            let mut ids_to_index = HashMap::new();
            for tri_index in 0..(triangles.count * 3) as usize {
                let pid = triangles.point_indices[tri_index] as usize;
                let vid = triangles.vertex_indices[tri_index] as usize;

                if let Some(index) = ids_to_index.get(&(pid, vid)) {
                    output_indices.push(*index);
                    continue;
                }
                let index = output_vertices.len() as u32;
                ids_to_index.insert((pid, vid), index);
                output_indices.push(index);
                output_vertices.push(Vertex {
                    position: [points.x[pid], points.z[pid], points.y[pid]],
                    lod_position: [points.lod_x[pid], points.lod_z[pid], points.lod_y[pid]],
                    normal: [
                        vertices.normal_x[vid],
                        vertices.normal_z[vid],
                        vertices.normal_y[vid],
                    ],
                    binormal: [
                        vertices.binormal_x[vid],
                        vertices.binormal_z[vid],
                        vertices.binormal_y[vid],
                    ],
                    texcoord_u: vertices.texcoord_u[vid],
                    texcoord_v: vertices.texcoord_v[vid],
                    ao: vertices.ao[vid],
                    color: ((vertices.vertex_color_r[vid].min(1.0).max(0.0) * 255.0) as u32)
                        | ((vertices.vertex_color_g[vid].min(1.0).max(0.0) * 255.0) as u32) << 8
                        | ((vertices.vertex_color_b[vid].min(1.0).max(0.0) * 255.0) as u32) << 16,
                })
            }
        } else {
            object_names.insert(object.name.clone(), object.id);
        }
    }

    let mut lods = Vec::new();
    let mut vertices = Vec::new();
    let mut indices = Vec::new();
    for name in ["LOD0", "LOD1", "LOD2", "LOD3"] {
        if let Some(id) = object_names.get(name) {
            let mut merged = merged_objects.remove(id).unwrap();

            let base_vertex = vertices.len() as u32;
            let start = indices.len() as u32;
            let end = (indices.len() + merged.1.len()) as u32;

            for index in merged.1 {
                indices.push(index + base_vertex);
            }
            vertices.append(&mut merged.0);
            lods.push(start..end);
        }
    }

    Ok(SpeedTreeModel { vertices, indices, lods })
}

#[cfg(test)]
mod tests {
    #[test]
    fn vertex_size() {
        assert_eq!(std::mem::size_of::<super::Vertex>(), 64);
    }

    // #[test]
    // fn parse_xml() {
    //     let t: super::SpeedTreeRaw =
    //         quick_xml::de::from_str(include_str!("../assets/Tree/Oak_English_Sapling.xml"))
    //             .unwrap();
    // }
}
<|endoftext|>