<commit_before>pub mod project;

use self::project::*;
use assert_cmd::prelude::*;
use predicates::prelude::*;
use std::fs;
use std::path::Path;
use std::process::Command;

fn cargo_fuzz() -> Command {
    Command::cargo_bin("cargo-fuzz").unwrap()
}

#[test]
fn help() {
    cargo_fuzz().arg("help").assert().success();
}

#[test]
fn init() {
    let project = project("init").build();
    project.cargo_fuzz().arg("init").assert().success();
    assert!(project.fuzz_dir().is_dir());
    assert!(project.fuzz_cargo_toml().is_file());
    assert!(project.fuzz_targets_dir().is_dir());
    assert!(project.fuzz_target_path("fuzz_target_1").is_file());
    project
        .cargo_fuzz()
        .arg("run")
        .arg("fuzz_target_1")
        .arg("--")
        .arg("-runs=1")
        .assert()
        .success();
}

#[test]
fn init_with_target() {
    let project = project("init_with_target").build();
    project
        .cargo_fuzz()
        .arg("init")
        .arg("-t")
        .arg("custom_target_name")
        .assert()
        .success();
    assert!(project.fuzz_dir().is_dir());
    assert!(project.fuzz_cargo_toml().is_file());
    assert!(project.fuzz_targets_dir().is_dir());
    assert!(project.fuzz_target_path("custom_target_name").is_file());
    project
        .cargo_fuzz()
        .arg("run")
        .arg("custom_target_name")
        .arg("--")
        .arg("-runs=1")
        .assert()
        .success();
}

#[test]
fn init_twice() {
    let project = project("init_twice").build();

    // First init should succeed and make all the things.
    project.cargo_fuzz().arg("init").assert().success();
    assert!(project.fuzz_dir().is_dir());
    assert!(project.fuzz_cargo_toml().is_file());
    assert!(project.fuzz_targets_dir().is_dir());
    assert!(project.fuzz_target_path("fuzz_target_1").is_file());

    // Second init should fail.
    project
        .cargo_fuzz()
        .arg("init")
        .assert()
        .stderr(predicates::str::contains("File exists (os error 17)").and(
            predicates::str::contains(format!(
                "failed to create directory {}",
                project.fuzz_dir().display()
            )),
        ))
        .failure();
}

#[test]
fn init_finds_parent_project() {
    let project = project("init_finds_parent_project").build();
    project
        .cargo_fuzz()
        .current_dir(project.root().join("src"))
        .arg("init")
        .assert()
        .success();
    assert!(project.fuzz_dir().is_dir());
    assert!(project.fuzz_cargo_toml().is_file());
    assert!(project.fuzz_targets_dir().is_dir());
    assert!(project.fuzz_target_path("fuzz_target_1").is_file());
}

#[test]
fn add() {
    let project = project("add").with_fuzz().build();
    project
        .cargo_fuzz()
        .arg("add")
        .arg("new_fuzz_target")
        .assert()
        .success();
    assert!(project.fuzz_target_path("new_fuzz_target").is_file());
    project
        .cargo_fuzz()
        .arg("run")
        .arg("new_fuzz_target")
        .arg("--")
        .arg("-runs=1")
        .assert()
        .success();
}

#[test]
fn add_twice() {
    let project = project("add").with_fuzz().build();
    project
        .cargo_fuzz()
        .arg("add")
        .arg("new_fuzz_target")
        .assert()
        .success();
    assert!(project.fuzz_target_path("new_fuzz_target").is_file());
    project
        .cargo_fuzz()
        .arg("add")
        .arg("new_fuzz_target")
        .assert()
        .stderr(
            predicate::str::contains("could not add target")
                .and(predicate::str::contains("File exists (os error 17)")),
        )
        .failure();
}

#[test]
fn list() {
    let project = project("add").with_fuzz().build();

    // Create some targets.
    project.cargo_fuzz().arg("add").arg("c").assert().success();
    project.cargo_fuzz().arg("add").arg("b").assert().success();
    project.cargo_fuzz().arg("add").arg("a").assert().success();

    // Make sure that we can list our targets, and that they're always sorted.
    project
        .cargo_fuzz()
        .arg("list")
        .assert()
        .stdout("a\nb\nc\n")
        .success();
}

#[test]
fn run_no_crash() {
    let project = project("run_no_crash")
        .with_fuzz()
        .fuzz_target(
            "no_crash",
            r#"
                #![no_main]
                use libfuzzer_sys::fuzz_target;

                fuzz_target!(|data: &[u8]| {
                    run_no_crash::pass_fuzzing(data);
                });
            "#,
        )
        .build();

    project
        .cargo_fuzz()
        .arg("run")
        .arg("no_crash")
        .arg("--")
        .arg("-runs=1000")
        .assert()
        .stderr(predicate::str::contains("Done 1000 runs"))
        .success();
}

#[test]
fn run_with_crash() {
    let project = project("run_with_crash")
        .with_fuzz()
        .fuzz_target(
            "yes_crash",
            r#"
                #![no_main]
                use libfuzzer_sys::fuzz_target;

                fuzz_target!(|data: &[u8]| {
                    run_with_crash::fail_fuzzing(data);
                });
            "#,
        )
        .build();

    project
        .cargo_fuzz()
        .arg("run")
        .arg("yes_crash")
        .arg("--")
        .arg("-runs=1000")
        .env("RUST_BACKTRACE", "1")
        .assert()
        .stderr(
            predicate::str::contains("panicked at 'I'm afraid of number 7'")
                .and(predicate::str::contains("ERROR: libFuzzer: deadly signal"))
                .and(predicate::str::contains("run_with_crash::fail_fuzzing"))
                .and(predicate::str::contains("fuzz/artifacts/yes_crash/crash-")),
        )
        .failure();
}

#[test]
fn cmin() {
    let corpus = Path::new("fuzz").join("corpus").join("foo");
    let project = project("cmin")
        .with_fuzz()
        .fuzz_target(
            "foo",
            r#"
                #![no_main]
                use libfuzzer_sys::fuzz_target;

                fuzz_target!(|data: &[u8]| {
                    let _ = data;
                });
            "#,
        )
        .file(corpus.join("0"), "")
        .file(corpus.join("1"), "a")
        .file(corpus.join("2"), "ab")
        .file(corpus.join("3"), "abc")
        .file(corpus.join("4"), "abcd")
        .build();

    let corpus_count = || {
        fs::read_dir(project.root().join("fuzz").join("corpus").join("foo"))
            .unwrap()
            .map(|e| e.unwrap())
            .count()
    };
    assert_eq!(corpus_count(), 5);

    project
        .cargo_fuzz()
        .arg("cmin")
        .arg("foo")
        .assert()
        .success();
    assert_eq!(corpus_count(), 1);
}

#[test]
fn tmin() {
    let corpus = Path::new("fuzz").join("corpus").join("i_hate_zed");
    let test_case = corpus.join("test-case");
    let project = project("tmin")
        .with_fuzz()
        .fuzz_target(
            "i_hate_zed",
            r#"
                #![no_main]
                use libfuzzer_sys::fuzz_target;

                fuzz_target!(|data: &[u8]| {
                    let s = String::from_utf8_lossy(data);
                    if s.contains('z') {
                        panic!("nooooooooo");
                    }
                });
            "#,
        )
        .file(&test_case, "pack my box with five dozen liquor jugs")
        .build();
    let test_case = project.root().join(test_case);
    project
        .cargo_fuzz()
        .arg("tmin")
        .arg("i_hate_zed")
        .arg(&test_case)
        .assert()
        .stderr(
            predicates::str::contains("CRASH_MIN: minimizing crash input: ")
                .and(predicate::str::contains("(1 bytes) caused a crash")),
        )
        .success();
}

#[test]
fn build_all() {
    let project = project("build_all").with_fuzz().build();

    // Create some targets.
    project
        .cargo_fuzz()
        .arg("add")
        .arg("build_all_a")
        .assert()
        .success();
    project
        .cargo_fuzz()
        .arg("add")
        .arg("build_all_b")
        .assert()
        .success();

    // Build to ensure that the build directory is created and
    // `fuzz_build_dir()` won't panic.
    project.cargo_fuzz().arg("build").assert().success();

    let build_dir = project.fuzz_build_dir().join("debug");

    let a_bin = build_dir.join("build_all_a");
    let b_bin = build_dir.join("build_all_b");

    // Remove the files we just built.
    fs::remove_file(&a_bin).unwrap();
    fs::remove_file(&b_bin).unwrap();

    assert!(!a_bin.is_file());
    assert!(!b_bin.is_file());

    // Test that building all fuzz targets does in fact recreate the files.
    project.cargo_fuzz().arg("build").assert().success();

    assert!(a_bin.is_file());
    assert!(b_bin.is_file());
}

#[test]
fn build_one() {
    let project = project("build_one").with_fuzz().build();

    // Create some targets.
    project
        .cargo_fuzz()
        .arg("add")
        .arg("build_one_a")
        .assert()
        .success();
    project
        .cargo_fuzz()
        .arg("add")
        .arg("build_one_b")
        .assert()
        .success();

    // Build to ensure that the build directory is created and
    // `fuzz_build_dir()` won't panic.
    project.cargo_fuzz().arg("build").assert().success();

    let build_dir = project.fuzz_build_dir().join("debug");
    let a_bin = build_dir.join("build_one_a");
    let b_bin = build_dir.join("build_one_b");

    // Remove the files we just built.
    fs::remove_file(&a_bin).unwrap();
    fs::remove_file(&b_bin).unwrap();

    assert!(!a_bin.is_file());
    assert!(!b_bin.is_file());

    // Test that we can build one and not the other.
    project
        .cargo_fuzz()
        .arg("build")
        .arg("build_one_a")
        .assert()
        .success();

    assert!(a_bin.is_file());
    assert!(!b_bin.is_file());
}
<commit_msg>Add tests for `cargo fuzz run <file>...`<commit_after>pub mod project;

use self::project::*;
use assert_cmd::prelude::*;
use predicates::prelude::*;
use std::fs;
use std::path::Path;
use std::process::Command;

fn cargo_fuzz() -> Command {
    Command::cargo_bin("cargo-fuzz").unwrap()
}

#[test]
fn help() {
    cargo_fuzz().arg("help").assert().success();
}

#[test]
fn init() {
    let project = project("init").build();
    project.cargo_fuzz().arg("init").assert().success();
    assert!(project.fuzz_dir().is_dir());
    assert!(project.fuzz_cargo_toml().is_file());
    assert!(project.fuzz_targets_dir().is_dir());
    assert!(project.fuzz_target_path("fuzz_target_1").is_file());
    project
        .cargo_fuzz()
        .arg("run")
        .arg("fuzz_target_1")
        .arg("--")
        .arg("-runs=1")
        .assert()
        .success();
}

#[test]
fn init_with_target() {
    let project = project("init_with_target").build();
    project
        .cargo_fuzz()
        .arg("init")
        .arg("-t")
        .arg("custom_target_name")
        .assert()
        .success();
    assert!(project.fuzz_dir().is_dir());
    assert!(project.fuzz_cargo_toml().is_file());
    assert!(project.fuzz_targets_dir().is_dir());
    assert!(project.fuzz_target_path("custom_target_name").is_file());
    project
        .cargo_fuzz()
        .arg("run")
        .arg("custom_target_name")
        .arg("--")
        .arg("-runs=1")
        .assert()
        .success();
}

#[test]
fn init_twice() {
    let project = project("init_twice").build();

    // First init should succeed and make all the things.
    project.cargo_fuzz().arg("init").assert().success();
    assert!(project.fuzz_dir().is_dir());
    assert!(project.fuzz_cargo_toml().is_file());
    assert!(project.fuzz_targets_dir().is_dir());
    assert!(project.fuzz_target_path("fuzz_target_1").is_file());

    // Second init should fail.
    project
        .cargo_fuzz()
        .arg("init")
        .assert()
        .stderr(predicates::str::contains("File exists (os error 17)").and(
            predicates::str::contains(format!(
                "failed to create directory {}",
                project.fuzz_dir().display()
            )),
        ))
        .failure();
}

#[test]
fn init_finds_parent_project() {
    let project = project("init_finds_parent_project").build();
    project
        .cargo_fuzz()
        .current_dir(project.root().join("src"))
        .arg("init")
        .assert()
        .success();
    assert!(project.fuzz_dir().is_dir());
    assert!(project.fuzz_cargo_toml().is_file());
    assert!(project.fuzz_targets_dir().is_dir());
    assert!(project.fuzz_target_path("fuzz_target_1").is_file());
}

#[test]
fn add() {
    let project = project("add").with_fuzz().build();
    project
        .cargo_fuzz()
        .arg("add")
        .arg("new_fuzz_target")
        .assert()
        .success();
    assert!(project.fuzz_target_path("new_fuzz_target").is_file());
    project
        .cargo_fuzz()
        .arg("run")
        .arg("new_fuzz_target")
        .arg("--")
        .arg("-runs=1")
        .assert()
        .success();
}

#[test]
fn add_twice() {
    let project = project("add").with_fuzz().build();
    project
        .cargo_fuzz()
        .arg("add")
        .arg("new_fuzz_target")
        .assert()
        .success();
    assert!(project.fuzz_target_path("new_fuzz_target").is_file());
    project
        .cargo_fuzz()
        .arg("add")
        .arg("new_fuzz_target")
        .assert()
        .stderr(
            predicate::str::contains("could not add target")
                .and(predicate::str::contains("File exists (os error 17)")),
        )
        .failure();
}

#[test]
fn list() {
    let project = project("add").with_fuzz().build();

    // Create some targets.
    project.cargo_fuzz().arg("add").arg("c").assert().success();
    project.cargo_fuzz().arg("add").arg("b").assert().success();
    project.cargo_fuzz().arg("add").arg("a").assert().success();

    // Make sure that we can list our targets, and that they're always sorted.
    project
        .cargo_fuzz()
        .arg("list")
        .assert()
        .stdout("a\nb\nc\n")
        .success();
}

#[test]
fn run_no_crash() {
    let project = project("run_no_crash")
        .with_fuzz()
        .fuzz_target(
            "no_crash",
            r#"
                #![no_main]
                use libfuzzer_sys::fuzz_target;

                fuzz_target!(|data: &[u8]| {
                    run_no_crash::pass_fuzzing(data);
                });
            "#,
        )
        .build();

    project
        .cargo_fuzz()
        .arg("run")
        .arg("no_crash")
        .arg("--")
        .arg("-runs=1000")
        .assert()
        .stderr(predicate::str::contains("Done 1000 runs"))
        .success();
}

#[test]
fn run_with_crash() {
    let project = project("run_with_crash")
        .with_fuzz()
        .fuzz_target(
            "yes_crash",
            r#"
                #![no_main]
                use libfuzzer_sys::fuzz_target;

                fuzz_target!(|data: &[u8]| {
                    run_with_crash::fail_fuzzing(data);
                });
            "#,
        )
        .build();

    project
        .cargo_fuzz()
        .arg("run")
        .arg("yes_crash")
        .arg("--")
        .arg("-runs=1000")
        .env("RUST_BACKTRACE", "1")
        .assert()
        .stderr(
            predicate::str::contains("panicked at 'I'm afraid of number 7'")
                .and(predicate::str::contains("ERROR: libFuzzer: deadly signal"))
                .and(predicate::str::contains("run_with_crash::fail_fuzzing"))
                .and(predicate::str::contains("fuzz/artifacts/yes_crash/crash-")),
        )
        .failure();
}

#[test]
fn run_one_input() {
    let corpus = Path::new("fuzz").join("corpus").join("run_one");

    let project = project("run_one_input")
        .with_fuzz()
        .fuzz_target(
            "run_one",
            r#"
                #![no_main]
                use libfuzzer_sys::fuzz_target;

                fuzz_target!(|data: &[u8]| {
                    assert!(data.is_empty());
                });
            "#,
        )
        .file(corpus.join("pass"), "")
        .file(corpus.join("fail"), "not empty")
        .build();

    project
        .cargo_fuzz()
        .arg("run")
        .arg("run_one")
        .arg(corpus.join("pass"))
        .assert()
        .stderr(
            predicate::str::contains("Running 1 inputs 1 time(s) each.").and(
                predicate::str::contains("Running: fuzz/corpus/run_one/pass"),
            ),
        )
        .success();
}

#[test]
fn run_a_few_inputs() {
    let corpus = Path::new("fuzz").join("corpus").join("run_few");

    let project = project("run_a_few_inputs")
        .with_fuzz()
        .fuzz_target(
            "run_few",
            r#"
                #![no_main]
                use libfuzzer_sys::fuzz_target;

                fuzz_target!(|data: &[u8]| {
                    assert!(data.len() != 4);
                });
            "#,
        )
        .file(corpus.join("pass-0"), "")
        .file(corpus.join("pass-1"), "1")
        .file(corpus.join("pass-2"), "12")
        .file(corpus.join("pass-3"), "123")
        .file(corpus.join("fail"), "fail")
        .build();

    project
        .cargo_fuzz()
        .arg("run")
        .arg("run_few")
        .arg(corpus.join("pass-0"))
        .arg(corpus.join("pass-1"))
        .arg(corpus.join("pass-2"))
        .arg(corpus.join("pass-3"))
        .assert()
        .stderr(
            predicate::str::contains("Running 4 inputs 1 time(s) each.").and(
                predicate::str::contains("Running: fuzz/corpus/run_few/pass"),
            ),
        )
        .success();
}

#[test]
fn run_alt_corpus() {
    let corpus = Path::new("fuzz").join("corpus").join("run_alt");
    let alt_corpus = Path::new("fuzz").join("alt-corpus").join("run_alt");

    let project = project("run_alt_corpus")
        .with_fuzz()
        .fuzz_target(
            "run_alt",
            r#"
                #![no_main]
                use libfuzzer_sys::fuzz_target;

                fuzz_target!(|data: &[u8]| {
                    assert!(data.len() <= 1);
                });
            "#,
        )
        .file(corpus.join("fail"), "fail")
        .file(alt_corpus.join("pass-0"), "0")
        .file(alt_corpus.join("pass-1"), "1")
        .file(alt_corpus.join("pass-2"), "2")
        .build();

    project
        .cargo_fuzz()
        .arg("run")
        .arg("run_alt")
        .arg(&alt_corpus)
        .arg("--")
        .arg("-runs=0")
        .assert()
        .stderr(
            predicate::str::contains("3 files found in fuzz/alt-corpus/run_alt")
                .and(predicate::str::contains("fuzz/corpus/run_alt").not())
                // libFuzzer will always test the empty input, so the number of
                // runs performed is always one more than the number of files in
                // the corpus.
                .and(predicate::str::contains("Done 4 runs in")),
        )
        .success();
}

#[test]
fn cmin() {
    let corpus = Path::new("fuzz").join("corpus").join("foo");
    let project = project("cmin")
        .with_fuzz()
        .fuzz_target(
            "foo",
            r#"
                #![no_main]
                use libfuzzer_sys::fuzz_target;

                fuzz_target!(|data: &[u8]| {
                    let _ = data;
                });
            "#,
        )
        .file(corpus.join("0"), "")
        .file(corpus.join("1"), "a")
        .file(corpus.join("2"), "ab")
        .file(corpus.join("3"), "abc")
        .file(corpus.join("4"), "abcd")
        .build();

    let corpus_count = || {
        fs::read_dir(project.root().join("fuzz").join("corpus").join("foo"))
            .unwrap()
            .map(|e| e.unwrap())
            .count()
    };
    assert_eq!(corpus_count(), 5);

    project
        .cargo_fuzz()
        .arg("cmin")
        .arg("foo")
        .assert()
        .success();
    assert_eq!(corpus_count(), 1);
}

#[test]
fn tmin() {
    let corpus = Path::new("fuzz").join("corpus").join("i_hate_zed");
    let test_case = corpus.join("test-case");
    let project = project("tmin")
        .with_fuzz()
        .fuzz_target(
            "i_hate_zed",
            r#"
                #![no_main]
                use libfuzzer_sys::fuzz_target;

                fuzz_target!(|data: &[u8]| {
                    let s = String::from_utf8_lossy(data);
                    if s.contains('z') {
                        panic!("nooooooooo");
                    }
                });
            "#,
        )
        .file(&test_case, "pack my box with five dozen liquor jugs")
        .build();
    let test_case = project.root().join(test_case);
    project
        .cargo_fuzz()
        .arg("tmin")
        .arg("i_hate_zed")
        .arg(&test_case)
        .assert()
        .stderr(
            predicates::str::contains("CRASH_MIN: minimizing crash input: ")
                .and(predicate::str::contains("(1 bytes) caused a crash")),
        )
        .success();
}

#[test]
fn build_all() {
    let project = project("build_all").with_fuzz().build();

    // Create some targets.
    project
        .cargo_fuzz()
        .arg("add")
        .arg("build_all_a")
        .assert()
        .success();
    project
        .cargo_fuzz()
        .arg("add")
        .arg("build_all_b")
        .assert()
        .success();

    // Build to ensure that the build directory is created and
    // `fuzz_build_dir()` won't panic.
    project.cargo_fuzz().arg("build").assert().success();

    let build_dir = project.fuzz_build_dir().join("debug");

    let a_bin = build_dir.join("build_all_a");
    let b_bin = build_dir.join("build_all_b");

    // Remove the files we just built.
    fs::remove_file(&a_bin).unwrap();
    fs::remove_file(&b_bin).unwrap();

    assert!(!a_bin.is_file());
    assert!(!b_bin.is_file());

    // Test that building all fuzz targets does in fact recreate the files.
    project.cargo_fuzz().arg("build").assert().success();

    assert!(a_bin.is_file());
    assert!(b_bin.is_file());
}

#[test]
fn build_one() {
    let project = project("build_one").with_fuzz().build();

    // Create some targets.
    project
        .cargo_fuzz()
        .arg("add")
        .arg("build_one_a")
        .assert()
        .success();
    project
        .cargo_fuzz()
        .arg("add")
        .arg("build_one_b")
        .assert()
        .success();

    // Build to ensure that the build directory is created and
    // `fuzz_build_dir()` won't panic.
    project.cargo_fuzz().arg("build").assert().success();

    let build_dir = project.fuzz_build_dir().join("debug");
    let a_bin = build_dir.join("build_one_a");
    let b_bin = build_dir.join("build_one_b");

    // Remove the files we just built.
    fs::remove_file(&a_bin).unwrap();
    fs::remove_file(&b_bin).unwrap();

    assert!(!a_bin.is_file());
    assert!(!b_bin.is_file());

    // Test that we can build one and not the other.
    project
        .cargo_fuzz()
        .arg("build")
        .arg("build_one_a")
        .assert()
        .success();

    assert!(a_bin.is_file());
    assert!(!b_bin.is_file());
}
<|endoftext|>


<commit_before>import layers::{ARGB32Format, ContainerLayerKind, Image, ImageLayerKind, RGB24Format};
import layers::{TiledImageLayerKind};
import scene::Scene;

import geom::matrix::{Matrix4, ortho};
import opengles::gl2::{ARRAY_BUFFER, COLOR_BUFFER_BIT, COMPILE_STATUS};
import opengles::gl2::{FRAGMENT_SHADER, LINEAR, LINK_STATUS, NEAREST, NO_ERROR, REPEAT, RGB, RGBA,
                      BGRA};
import opengles::gl2::{STATIC_DRAW, TEXTURE_2D, TEXTURE_MAG_FILTER, TEXTURE_MIN_FILTER};
import opengles::gl2::{TEXTURE_WRAP_S, TEXTURE_WRAP_T};
import opengles::gl2::{TRIANGLE_STRIP, UNPACK_ALIGNMENT, UNSIGNED_BYTE, VERTEX_SHADER, GLclampf};
import opengles::gl2::{GLenum, GLint, GLsizei, GLuint, attach_shader, bind_buffer, bind_texture};
import opengles::gl2::{buffer_data, create_program, clear, clear_color};
import opengles::gl2::{compile_shader, create_shader, draw_arrays, enable};
import opengles::gl2::{enable_vertex_attrib_array, gen_buffers, gen_textures};
import opengles::gl2::{get_attrib_location, get_error, get_program_iv};
import opengles::gl2::{get_shader_info_log, get_shader_iv};
import opengles::gl2::{get_uniform_location, link_program, pixel_store_i, shader_source};
import opengles::gl2::{tex_image_2d, tex_parameter_i, uniform_1i, uniform_matrix_4fv, use_program};
import opengles::gl2::{vertex_attrib_pointer_f32, viewport};

import io::println;
import libc::c_int;
import str::bytes;

fn FRAGMENT_SHADER_SOURCE() -> ~str {
    ~"
        #ifdef GLES2
            precision mediump float;
        #endif

        varying vec2 vTextureCoord;

        uniform sampler2D uSampler;

        void main(void) {
            gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
        }
    "
}

fn VERTEX_SHADER_SOURCE() -> ~str {
    ~"
        attribute vec3 aVertexPosition;
        attribute vec2 aTextureCoord;

        uniform mat4 uMVMatrix;
        uniform mat4 uPMatrix;

        varying vec2 vTextureCoord;

        void main(void) {
            gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
            vTextureCoord = aTextureCoord;
        }
    "
}

fn load_shader(source_string: ~str, shader_type: GLenum) -> GLuint {
    let shader_id = create_shader(shader_type);
    shader_source(shader_id, ~[ bytes(source_string) ]);
    compile_shader(shader_id);

    if get_error() != NO_ERROR {
        println(#fmt("error: %d", get_error() as int));
        fail ~"failed to compile shader";
    }

    if get_shader_iv(shader_id, COMPILE_STATUS) == (0 as GLint) {
        println(#fmt("shader info log: %s", get_shader_info_log(shader_id)));
        fail ~"failed to compile shader";
    }

    return shader_id;
}

struct RenderContext {
    let program: GLuint;
    let vertex_position_attr: c_int;
    let texture_coord_attr: c_int;
    let modelview_uniform: c_int;
    let projection_uniform: c_int;
    let sampler_uniform: c_int;
    let vertex_buffer: GLuint;
    let texture_coord_buffer: GLuint;

    new(program: GLuint) {
        self.program = program;
        self.vertex_position_attr = get_attrib_location(program, ~"aVertexPosition");
        self.texture_coord_attr = get_attrib_location(program, ~"aTextureCoord");
        self.modelview_uniform = get_uniform_location(program, ~"uMVMatrix");
        self.projection_uniform = get_uniform_location(program, ~"uPMatrix");
        self.sampler_uniform = get_uniform_location(program, ~"uSampler");

        let (vertex_buffer, texture_coord_buffer) = init_buffers();
        self.vertex_buffer = vertex_buffer;
        self.texture_coord_buffer = texture_coord_buffer;

        enable_vertex_attrib_array(self.vertex_position_attr as GLuint);
        enable_vertex_attrib_array(self.texture_coord_attr as GLuint);
    }
}

fn init_render_context() -> RenderContext {
    let vertex_shader = load_shader(VERTEX_SHADER_SOURCE(), VERTEX_SHADER);
    let fragment_shader = load_shader(FRAGMENT_SHADER_SOURCE(), FRAGMENT_SHADER);

    let program = create_program();
    attach_shader(program, vertex_shader);
    attach_shader(program, fragment_shader);
    link_program(program);

    if get_program_iv(program, LINK_STATUS) == (0 as GLint) {
        fail ~"failed to initialize program";
    }

    use_program(program);

    enable(TEXTURE_2D);

    return RenderContext(program);
}

fn init_buffers() -> (GLuint, GLuint) {
    let triangle_vertex_buffer = gen_buffers(1 as GLsizei)[0];
    bind_buffer(ARRAY_BUFFER, triangle_vertex_buffer);

    let (_0, _1) = (0.0f32, 1.0f32);
    let vertices = ~[
        _0, _0, _0,
        _0, _1, _0,
        _1, _0, _0,
        _1, _1, _0
    ];

    buffer_data(ARRAY_BUFFER, vertices, STATIC_DRAW);

    let texture_coord_buffer = gen_buffers(1 as GLsizei)[0];
    bind_buffer(ARRAY_BUFFER, texture_coord_buffer);

    let vertices = ~[
        _0, _0,
        _0, _1,
        _1, _0,
        _1, _1
    ];

    buffer_data(ARRAY_BUFFER, vertices, STATIC_DRAW);

    return (triangle_vertex_buffer, texture_coord_buffer);
}

fn create_texture_for_image_if_necessary(image: @Image) {
    match image.texture {
        none => {}
        some(_) => { return; /* Nothing to do. */ }
    }

    #debug("making texture");

    let texture = gen_textures(1 as GLsizei)[0];
    bind_texture(TEXTURE_2D, texture);

    tex_parameter_i(TEXTURE_2D, TEXTURE_WRAP_S, REPEAT as GLint);
    tex_parameter_i(TEXTURE_2D, TEXTURE_WRAP_T, REPEAT as GLint);
    tex_parameter_i(TEXTURE_2D, TEXTURE_MAG_FILTER, LINEAR as GLint);
    tex_parameter_i(TEXTURE_2D, TEXTURE_MIN_FILTER, LINEAR as GLint);

    pixel_store_i(UNPACK_ALIGNMENT, 1);

    match image.format {
      RGB24Format => {
        tex_image_2d(TEXTURE_2D, 0 as GLint, RGB as GLint, image.width as GLsizei,
                     image.height as GLsizei, 0 as GLint, RGB, UNSIGNED_BYTE, image.data);
      }
      ARGB32Format => {
        tex_image_2d(TEXTURE_2D, 0 as GLint, RGBA as GLint, image.width as GLsizei,
                     image.height as GLsizei, 0 as GLint, BGRA, UNSIGNED_BYTE, image.data);
      }
    }

    image.texture = some(texture);
}

fn bind_and_render_quad(render_context: RenderContext, texture: GLuint) {
    bind_texture(TEXTURE_2D, texture);

    uniform_1i(render_context.sampler_uniform, 0);

    bind_buffer(ARRAY_BUFFER, render_context.vertex_buffer);
    vertex_attrib_pointer_f32(render_context.vertex_position_attr as GLuint, 3, false, 0, 0);

    bind_buffer(ARRAY_BUFFER, render_context.texture_coord_buffer);
    vertex_attrib_pointer_f32(render_context.texture_coord_attr as GLuint, 2, false, 0, 0);

    draw_arrays(TRIANGLE_STRIP, 0, 4);
}

// Layer rendering

trait Render {
    fn render(render_context: RenderContext);
}

impl @layers::ImageLayer : Render {
    fn render(render_context: RenderContext) {
        create_texture_for_image_if_necessary(self.image);

        uniform_matrix_4fv(render_context.modelview_uniform, false,
                           self.common.transform.to_array());

        bind_and_render_quad(render_context, option::get(self.image.texture));
    }
}

impl @layers::TiledImageLayer : Render {
    fn render(render_context: RenderContext) {
        let tiles_down = self.tiles.len() / self.tiles_across;
        for self.tiles.eachi |i, tile| {
            create_texture_for_image_if_necessary(tile);

            let x = ((i % self.tiles_across) as f32);
            let y = ((i / self.tiles_across) as f32);

            let transform = self.common.transform.scale(1.0f32 / (self.tiles_across as f32),
                                                        1.0f32 / (tiles_down as f32),
                                                        1.0f32);
            let transform = transform.translate(x * 1.0f32, y * 1.0f32, 0.0f32);

            uniform_matrix_4fv(render_context.modelview_uniform, false, transform.to_array());

            bind_and_render_quad(render_context, option::get(tile.texture));
        }
    }
}

fn render_scene(render_context: RenderContext, &scene: Scene) {
    // Set the viewport.
    viewport(0 as GLint, 0 as GLint, scene.size.width as GLsizei, scene.size.height as GLsizei);

    // Clear the screen.
    clear_color(0.0f32, 0.0f32, 1.0f32, 1.0f32);
    clear(COLOR_BUFFER_BIT);

    // Set the projection matrix.
    let projection_matrix = ortho(0.0f32, copy scene.size.width, copy scene.size.height, 0.0f32,
                                  -10.0f32, 10.0f32);
    uniform_matrix_4fv(render_context.projection_uniform, false, projection_matrix.to_array());

    match copy scene.root {
        ContainerLayerKind(*) => fail ~"container layers unsupported",
        ImageLayerKind(image_layer) => image_layer.render(render_context),
        TiledImageLayerKind(tiled_image_layer) => tiled_image_layer.render(render_context)
    }
}

<commit_msg>Fix for Rust language changes<commit_after>import layers::{ARGB32Format, ContainerLayerKind, Image, ImageLayerKind, RGB24Format};
import layers::{TiledImageLayerKind};
import scene::Scene;

import geom::matrix::{Matrix4, ortho};
import opengles::gl2::{ARRAY_BUFFER, COLOR_BUFFER_BIT, COMPILE_STATUS};
import opengles::gl2::{FRAGMENT_SHADER, LINEAR, LINK_STATUS, NEAREST, NO_ERROR, REPEAT, RGB, RGBA,
                      BGRA};
import opengles::gl2::{STATIC_DRAW, TEXTURE_2D, TEXTURE_MAG_FILTER, TEXTURE_MIN_FILTER};
import opengles::gl2::{TEXTURE_WRAP_S, TEXTURE_WRAP_T};
import opengles::gl2::{TRIANGLE_STRIP, UNPACK_ALIGNMENT, UNSIGNED_BYTE, VERTEX_SHADER, GLclampf};
import opengles::gl2::{GLenum, GLint, GLsizei, GLuint, attach_shader, bind_buffer, bind_texture};
import opengles::gl2::{buffer_data, create_program, clear, clear_color};
import opengles::gl2::{compile_shader, create_shader, draw_arrays, enable};
import opengles::gl2::{enable_vertex_attrib_array, gen_buffers, gen_textures};
import opengles::gl2::{get_attrib_location, get_error, get_program_iv};
import opengles::gl2::{get_shader_info_log, get_shader_iv};
import opengles::gl2::{get_uniform_location, link_program, pixel_store_i, shader_source};
import opengles::gl2::{tex_image_2d, tex_parameter_i, uniform_1i, uniform_matrix_4fv, use_program};
import opengles::gl2::{vertex_attrib_pointer_f32, viewport};

import io::println;
import libc::c_int;
import str::to_bytes;

fn FRAGMENT_SHADER_SOURCE() -> ~str {
    ~"
        #ifdef GLES2
            precision mediump float;
        #endif

        varying vec2 vTextureCoord;

        uniform sampler2D uSampler;

        void main(void) {
            gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
        }
    "
}

fn VERTEX_SHADER_SOURCE() -> ~str {
    ~"
        attribute vec3 aVertexPosition;
        attribute vec2 aTextureCoord;

        uniform mat4 uMVMatrix;
        uniform mat4 uPMatrix;

        varying vec2 vTextureCoord;

        void main(void) {
            gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
            vTextureCoord = aTextureCoord;
        }
    "
}

fn load_shader(source_string: ~str, shader_type: GLenum) -> GLuint {
    let shader_id = create_shader(shader_type);
    shader_source(shader_id, ~[ to_bytes(source_string) ]);
    compile_shader(shader_id);

    if get_error() != NO_ERROR {
        println(#fmt("error: %d", get_error() as int));
        fail ~"failed to compile shader";
    }

    if get_shader_iv(shader_id, COMPILE_STATUS) == (0 as GLint) {
        println(#fmt("shader info log: %s", get_shader_info_log(shader_id)));
        fail ~"failed to compile shader";
    }

    return shader_id;
}

struct RenderContext {
    let program: GLuint;
    let vertex_position_attr: c_int;
    let texture_coord_attr: c_int;
    let modelview_uniform: c_int;
    let projection_uniform: c_int;
    let sampler_uniform: c_int;
    let vertex_buffer: GLuint;
    let texture_coord_buffer: GLuint;

    new(program: GLuint) {
        self.program = program;
        self.vertex_position_attr = get_attrib_location(program, ~"aVertexPosition");
        self.texture_coord_attr = get_attrib_location(program, ~"aTextureCoord");
        self.modelview_uniform = get_uniform_location(program, ~"uMVMatrix");
        self.projection_uniform = get_uniform_location(program, ~"uPMatrix");
        self.sampler_uniform = get_uniform_location(program, ~"uSampler");

        let (vertex_buffer, texture_coord_buffer) = init_buffers();
        self.vertex_buffer = vertex_buffer;
        self.texture_coord_buffer = texture_coord_buffer;

        enable_vertex_attrib_array(self.vertex_position_attr as GLuint);
        enable_vertex_attrib_array(self.texture_coord_attr as GLuint);
    }
}

fn init_render_context() -> RenderContext {
    let vertex_shader = load_shader(VERTEX_SHADER_SOURCE(), VERTEX_SHADER);
    let fragment_shader = load_shader(FRAGMENT_SHADER_SOURCE(), FRAGMENT_SHADER);

    let program = create_program();
    attach_shader(program, vertex_shader);
    attach_shader(program, fragment_shader);
    link_program(program);

    if get_program_iv(program, LINK_STATUS) == (0 as GLint) {
        fail ~"failed to initialize program";
    }

    use_program(program);

    enable(TEXTURE_2D);

    return RenderContext(program);
}

fn init_buffers() -> (GLuint, GLuint) {
    let triangle_vertex_buffer = gen_buffers(1 as GLsizei)[0];
    bind_buffer(ARRAY_BUFFER, triangle_vertex_buffer);

    let (_0, _1) = (0.0f32, 1.0f32);
    let vertices = ~[
        _0, _0, _0,
        _0, _1, _0,
        _1, _0, _0,
        _1, _1, _0
    ];

    buffer_data(ARRAY_BUFFER, vertices, STATIC_DRAW);

    let texture_coord_buffer = gen_buffers(1 as GLsizei)[0];
    bind_buffer(ARRAY_BUFFER, texture_coord_buffer);

    let vertices = ~[
        _0, _0,
        _0, _1,
        _1, _0,
        _1, _1
    ];

    buffer_data(ARRAY_BUFFER, vertices, STATIC_DRAW);

    return (triangle_vertex_buffer, texture_coord_buffer);
}

fn create_texture_for_image_if_necessary(image: @Image) {
    match image.texture {
        none => {}
        some(_) => { return; /* Nothing to do. */ }
    }

    #debug("making texture");

    let texture = gen_textures(1 as GLsizei)[0];
    bind_texture(TEXTURE_2D, texture);

    tex_parameter_i(TEXTURE_2D, TEXTURE_WRAP_S, REPEAT as GLint);
    tex_parameter_i(TEXTURE_2D, TEXTURE_WRAP_T, REPEAT as GLint);
    tex_parameter_i(TEXTURE_2D, TEXTURE_MAG_FILTER, LINEAR as GLint);
    tex_parameter_i(TEXTURE_2D, TEXTURE_MIN_FILTER, LINEAR as GLint);

    pixel_store_i(UNPACK_ALIGNMENT, 1);

    match image.format {
      RGB24Format => {
        tex_image_2d(TEXTURE_2D, 0 as GLint, RGB as GLint, image.width as GLsizei,
                     image.height as GLsizei, 0 as GLint, RGB, UNSIGNED_BYTE, image.data);
      }
      ARGB32Format => {
        tex_image_2d(TEXTURE_2D, 0 as GLint, RGBA as GLint, image.width as GLsizei,
                     image.height as GLsizei, 0 as GLint, BGRA, UNSIGNED_BYTE, image.data);
      }
    }

    image.texture = some(texture);
}

fn bind_and_render_quad(render_context: RenderContext, texture: GLuint) {
    bind_texture(TEXTURE_2D, texture);

    uniform_1i(render_context.sampler_uniform, 0);

    bind_buffer(ARRAY_BUFFER, render_context.vertex_buffer);
    vertex_attrib_pointer_f32(render_context.vertex_position_attr as GLuint, 3, false, 0, 0);

    bind_buffer(ARRAY_BUFFER, render_context.texture_coord_buffer);
    vertex_attrib_pointer_f32(render_context.texture_coord_attr as GLuint, 2, false, 0, 0);

    draw_arrays(TRIANGLE_STRIP, 0, 4);
}

// Layer rendering

trait Render {
    fn render(render_context: RenderContext);
}

impl @layers::ImageLayer : Render {
    fn render(render_context: RenderContext) {
        create_texture_for_image_if_necessary(self.image);

        uniform_matrix_4fv(render_context.modelview_uniform, false,
                           self.common.transform.to_array());

        bind_and_render_quad(render_context, option::get(self.image.texture));
    }
}

impl @layers::TiledImageLayer : Render {
    fn render(render_context: RenderContext) {
        let tiles_down = self.tiles.len() / self.tiles_across;
        for self.tiles.eachi |i, tile| {
            create_texture_for_image_if_necessary(tile);

            let x = ((i % self.tiles_across) as f32);
            let y = ((i / self.tiles_across) as f32);

            let transform = self.common.transform.scale(1.0f32 / (self.tiles_across as f32),
                                                        1.0f32 / (tiles_down as f32),
                                                        1.0f32);
            let transform = transform.translate(x * 1.0f32, y * 1.0f32, 0.0f32);

            uniform_matrix_4fv(render_context.modelview_uniform, false, transform.to_array());

            bind_and_render_quad(render_context, option::get(tile.texture));
        }
    }
}

fn render_scene(render_context: RenderContext, &scene: Scene) {
    // Set the viewport.
    viewport(0 as GLint, 0 as GLint, scene.size.width as GLsizei, scene.size.height as GLsizei);

    // Clear the screen.
    clear_color(0.0f32, 0.0f32, 1.0f32, 1.0f32);
    clear(COLOR_BUFFER_BIT);

    // Set the projection matrix.
    let projection_matrix = ortho(0.0f32, copy scene.size.width, copy scene.size.height, 0.0f32,
                                  -10.0f32, 10.0f32);
    uniform_matrix_4fv(render_context.projection_uniform, false, projection_matrix.to_array());

    match copy scene.root {
        ContainerLayerKind(*) => fail ~"container layers unsupported",
        ImageLayerKind(image_layer) => image_layer.render(render_context),
        TiledImageLayerKind(tiled_image_layer) => tiled_image_layer.render(render_context)
    }
}

<|endoftext|>


<commit_before>//! OpenTelemetry shared core date types
#[cfg(feature = "serialize")]
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::fmt;

/// Key used for metric `LabelSet`s and trace `Span` attributes.
#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Key(Cow<'static, str>);

impl Key {
    /// Create a new `Key`.
    pub fn new<S: Into<Cow<'static, str>>>(value: S) -> Self {
        Key(value.into())
    }

    /// Create a new const `Key`.
    pub const fn from_static_str(value: &'static str) -> Self {
        Key(Cow::Borrowed(value))
    }

    /// Create a `KeyValue` pair for `bool` values.
    pub fn bool<T: Into<bool>>(&self, value: T) -> KeyValue {
        KeyValue {
            key: self.clone(),
            value: Value::Bool(value.into()),
        }
    }

    /// Create a `KeyValue` pair for `i64` values.
    pub fn i64(&self, value: i64) -> KeyValue {
        KeyValue {
            key: self.clone(),
            value: Value::I64(value),
        }
    }

    /// Create a `KeyValue` pair for `f64` values.
    pub fn f64(&self, value: f64) -> KeyValue {
        KeyValue {
            key: self.clone(),
            value: Value::F64(value),
        }
    }

    /// Create a `KeyValue` pair for `String` values.
    pub fn string<T: Into<Cow<'static, str>>>(&self, value: T) -> KeyValue {
        KeyValue {
            key: self.clone(),
            value: Value::String(value.into()),
        }
    }

    /// Create a `KeyValue` pair for arrays.
    pub fn array<T: Into<Array>>(&self, value: T) -> KeyValue {
        KeyValue {
            key: self.clone(),
            value: Value::Array(value.into()),
        }
    }

    /// Returns a reference to the underlying key name
    pub fn as_str(&self) -> &str {
        self.0.as_ref()
    }
}

impl From<&'static str> for Key {
    /// Convert a `&str` to a `Key`.
    fn from(key_str: &'static str) -> Self {
        Key(Cow::from(key_str))
    }
}

impl From<String> for Key {
    /// Convert a `String` to a `Key`.
    fn from(string: String) -> Self {
        Key(Cow::from(string))
    }
}

impl From<Key> for String {
    /// Converts `Key` instances into `String`.
    fn from(key: Key) -> Self {
        key.0.into_owned()
    }
}

impl fmt::Display for Key {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.fmt(fmt)
    }
}

/// Array of homogeneous values
#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum Array {
    /// Array of bools
    Bool(Vec<bool>),
    /// Array of integers
    I64(Vec<i64>),
    /// Array of floats
    F64(Vec<f64>),
    /// Array of strings
    String(Vec<Cow<'static, str>>),
}

impl fmt::Display for Array {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Array::Bool(values) => display_array_str(&values, fmt),
            Array::I64(values) => display_array_str(&values, fmt),
            Array::F64(values) => display_array_str(&values, fmt),
            Array::String(values) => {
                write!(fmt, "[")?;
                for (i, t) in values.iter().enumerate() {
                    if i > 0 {
                        write!(fmt, ",")?;
                    }
                    write!(fmt, "{:?}", t)?;
                }
                write!(fmt, "]")
            }
        }
    }
}

fn display_array_str<T: fmt::Display>(slice: &[T], fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
    write!(fmt, "[")?;
    for (i, t) in slice.iter().enumerate() {
        if i > 0 {
            write!(fmt, ",")?;
        }
        write!(fmt, "{}", t)?;
    }
    write!(fmt, "]")
}

macro_rules! into_array {
    ($(($t:ty, $val:expr),)+) => {
        $(
            impl From<$t> for Array {
                fn from(t: $t) -> Self {
                    $val(t)
                }
            }
        )+
    }
}

into_array!(
    (Vec<bool>, Array::Bool),
    (Vec<i64>, Array::I64),
    (Vec<f64>, Array::F64),
    (Vec<Cow<'static, str>>, Array::String),
);

/// Value types for use in `KeyValue` pairs.
#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum Value {
    /// bool values
    Bool(bool),
    /// i64 values
    I64(i64),
    /// f64 values
    F64(f64),
    /// String values
    String(Cow<'static, str>),
    /// Array of homogeneous values
    Array(Array),
}

impl Value {
    /// String representation of the `Value`
    ///
    /// This will allocate iff the underlying value is not a `String`.
    pub fn as_str(&self) -> Cow<'_, str> {
        match self {
            Value::Bool(v) => format!("{}", v).into(),
            Value::I64(v) => format!("{}", v).into(),
            Value::F64(v) => format!("{}", v).into(),
            Value::String(v) => Cow::Borrowed(v.as_ref()),
            Value::Array(v) => format!("{}", v).into(),
        }
    }
}

macro_rules! from_values {
   (
        $(
            ($t:ty, $val:expr);
        )+
    ) => {
        $(
            impl From<$t> for Value {
                fn from(t: $t) -> Self {
                    $val(t)
                }
            }
        )+
    }
}

from_values!(
    (bool, Value::Bool);
    (i64, Value::I64);
    (f64, Value::F64);
    (Cow<'static, str>, Value::String);
);

impl From<&'static str> for Value {
    /// Convenience method for creating a `Value` from a `&'static str`.
    fn from(s: &'static str) -> Self {
        Value::String(s.into())
    }
}

impl From<String> for Value {
    /// Convenience method for creating a `Value` from a `String`.
    fn from(s: String) -> Self {
        Value::String(s.into())
    }
}

impl fmt::Display for Value {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Value::Bool(v) => fmt.write_fmt(format_args!("{}", v)),
            Value::I64(v) => fmt.write_fmt(format_args!("{}", v)),
            Value::F64(v) => fmt.write_fmt(format_args!("{}", v)),
            Value::String(v) => fmt.write_fmt(format_args!("{}", v)),
            Value::Array(v) => fmt.write_fmt(format_args!("{}", v)),
        }
    }
}

/// `KeyValue` pairs are used by `LabelSet`s and `Span` attributes.
#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct KeyValue {
    /// Dimension or event key
    pub key: Key,
    /// Dimension or event value
    pub value: Value,
}

impl KeyValue {
    /// Create a new `KeyValue` pair.
    pub fn new<K, V>(key: K, value: V) -> Self
    where
        K: Into<Key>,
        V: Into<Value>,
    {
        KeyValue {
            key: key.into(),
            value: value.into(),
        }
    }
}

/// Units denote underlying data units tracked by `Meter`s.
#[derive(Clone, Default, Debug, PartialEq, Hash)]
pub struct Unit(String);

impl Unit {
    /// Create a new `Unit` from an `Into<String>`
    pub fn new<S: Into<String>>(value: S) -> Self {
        Unit(value.into())
    }

    /// View unit as &str
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl AsRef<str> for Unit {
    #[inline]
    fn as_ref(&self) -> &str {
        self.0.as_ref()
    }
}
<commit_msg>Remove unnecessary clone in `Key` type (#491)<commit_after>//! OpenTelemetry shared core date types
#[cfg(feature = "serialize")]
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::fmt;

/// Key used for metric `LabelSet`s and trace `Span` attributes.
#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Key(Cow<'static, str>);

impl Key {
    /// Create a new `Key`.
    pub fn new<S: Into<Cow<'static, str>>>(value: S) -> Self {
        Key(value.into())
    }

    /// Create a new const `Key`.
    pub const fn from_static_str(value: &'static str) -> Self {
        Key(Cow::Borrowed(value))
    }

    /// Create a `KeyValue` pair for `bool` values.
    pub fn bool<T: Into<bool>>(self, value: T) -> KeyValue {
        KeyValue {
            key: self,
            value: Value::Bool(value.into()),
        }
    }

    /// Create a `KeyValue` pair for `i64` values.
    pub fn i64(self, value: i64) -> KeyValue {
        KeyValue {
            key: self,
            value: Value::I64(value),
        }
    }

    /// Create a `KeyValue` pair for `f64` values.
    pub fn f64(self, value: f64) -> KeyValue {
        KeyValue {
            key: self,
            value: Value::F64(value),
        }
    }

    /// Create a `KeyValue` pair for `String` values.
    pub fn string<T: Into<Cow<'static, str>>>(self, value: T) -> KeyValue {
        KeyValue {
            key: self,
            value: Value::String(value.into()),
        }
    }

    /// Create a `KeyValue` pair for arrays.
    pub fn array<T: Into<Array>>(self, value: T) -> KeyValue {
        KeyValue {
            key: self,
            value: Value::Array(value.into()),
        }
    }

    /// Returns a reference to the underlying key name
    pub fn as_str(&self) -> &str {
        self.0.as_ref()
    }
}

impl From<&'static str> for Key {
    /// Convert a `&str` to a `Key`.
    fn from(key_str: &'static str) -> Self {
        Key(Cow::from(key_str))
    }
}

impl From<String> for Key {
    /// Convert a `String` to a `Key`.
    fn from(string: String) -> Self {
        Key(Cow::from(string))
    }
}

impl From<Key> for String {
    /// Converts `Key` instances into `String`.
    fn from(key: Key) -> Self {
        key.0.into_owned()
    }
}

impl fmt::Display for Key {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.fmt(fmt)
    }
}

/// Array of homogeneous values
#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum Array {
    /// Array of bools
    Bool(Vec<bool>),
    /// Array of integers
    I64(Vec<i64>),
    /// Array of floats
    F64(Vec<f64>),
    /// Array of strings
    String(Vec<Cow<'static, str>>),
}

impl fmt::Display for Array {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Array::Bool(values) => display_array_str(&values, fmt),
            Array::I64(values) => display_array_str(&values, fmt),
            Array::F64(values) => display_array_str(&values, fmt),
            Array::String(values) => {
                write!(fmt, "[")?;
                for (i, t) in values.iter().enumerate() {
                    if i > 0 {
                        write!(fmt, ",")?;
                    }
                    write!(fmt, "{:?}", t)?;
                }
                write!(fmt, "]")
            }
        }
    }
}

fn display_array_str<T: fmt::Display>(slice: &[T], fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
    write!(fmt, "[")?;
    for (i, t) in slice.iter().enumerate() {
        if i > 0 {
            write!(fmt, ",")?;
        }
        write!(fmt, "{}", t)?;
    }
    write!(fmt, "]")
}

macro_rules! into_array {
    ($(($t:ty, $val:expr),)+) => {
        $(
            impl From<$t> for Array {
                fn from(t: $t) -> Self {
                    $val(t)
                }
            }
        )+
    }
}

into_array!(
    (Vec<bool>, Array::Bool),
    (Vec<i64>, Array::I64),
    (Vec<f64>, Array::F64),
    (Vec<Cow<'static, str>>, Array::String),
);

/// Value types for use in `KeyValue` pairs.
#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, PartialEq)]
pub enum Value {
    /// bool values
    Bool(bool),
    /// i64 values
    I64(i64),
    /// f64 values
    F64(f64),
    /// String values
    String(Cow<'static, str>),
    /// Array of homogeneous values
    Array(Array),
}

impl Value {
    /// String representation of the `Value`
    ///
    /// This will allocate iff the underlying value is not a `String`.
    pub fn as_str(&self) -> Cow<'_, str> {
        match self {
            Value::Bool(v) => format!("{}", v).into(),
            Value::I64(v) => format!("{}", v).into(),
            Value::F64(v) => format!("{}", v).into(),
            Value::String(v) => Cow::Borrowed(v.as_ref()),
            Value::Array(v) => format!("{}", v).into(),
        }
    }
}

macro_rules! from_values {
   (
        $(
            ($t:ty, $val:expr);
        )+
    ) => {
        $(
            impl From<$t> for Value {
                fn from(t: $t) -> Self {
                    $val(t)
                }
            }
        )+
    }
}

from_values!(
    (bool, Value::Bool);
    (i64, Value::I64);
    (f64, Value::F64);
    (Cow<'static, str>, Value::String);
);

impl From<&'static str> for Value {
    /// Convenience method for creating a `Value` from a `&'static str`.
    fn from(s: &'static str) -> Self {
        Value::String(s.into())
    }
}

impl From<String> for Value {
    /// Convenience method for creating a `Value` from a `String`.
    fn from(s: String) -> Self {
        Value::String(s.into())
    }
}

impl fmt::Display for Value {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Value::Bool(v) => fmt.write_fmt(format_args!("{}", v)),
            Value::I64(v) => fmt.write_fmt(format_args!("{}", v)),
            Value::F64(v) => fmt.write_fmt(format_args!("{}", v)),
            Value::String(v) => fmt.write_fmt(format_args!("{}", v)),
            Value::Array(v) => fmt.write_fmt(format_args!("{}", v)),
        }
    }
}

/// `KeyValue` pairs are used by `LabelSet`s and `Span` attributes.
#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct KeyValue {
    /// Dimension or event key
    pub key: Key,
    /// Dimension or event value
    pub value: Value,
}

impl KeyValue {
    /// Create a new `KeyValue` pair.
    pub fn new<K, V>(key: K, value: V) -> Self
    where
        K: Into<Key>,
        V: Into<Value>,
    {
        KeyValue {
            key: key.into(),
            value: value.into(),
        }
    }
}

/// Units denote underlying data units tracked by `Meter`s.
#[derive(Clone, Default, Debug, PartialEq, Hash)]
pub struct Unit(String);

impl Unit {
    /// Create a new `Unit` from an `Into<String>`
    pub fn new<S: Into<String>>(value: S) -> Self {
        Unit(value.into())
    }

    /// View unit as &str
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl AsRef<str> for Unit {
    #[inline]
    fn as_ref(&self) -> &str {
        self.0.as_ref()
    }
}
<|endoftext|>


<commit_before>use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::ty::has_iter_method;
use clippy_utils::{match_trait_method, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
use rustc_span::source_map::Span;
use rustc_span::symbol::{sym, Symbol};

use super::INTO_ITER_ON_REF;

pub(super) fn check(
    cx: &LateContext<'_>,
    expr: &hir::Expr<'_>,
    method_span: Span,
    method_name: Symbol,
    args: &[hir::Expr<'_>],
) {
    let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
    if_chain! {
        if let ty::Ref(..) = self_ty.kind();
        if method_name == sym::into_iter;
        if match_trait_method(cx, expr, &paths::INTO_ITERATOR);
        if let Some((kind, method_name)) = ty_has_iter_method(cx, self_ty);
        then {
            span_lint_and_sugg(
                cx,
                INTO_ITER_ON_REF,
                method_span,
                &format!(
                    "this `.into_iter()` call is equivalent to `.{}()` and will not consume the `{}`",
                    method_name, kind,
                ),
                "call directly",
                method_name.to_string(),
                Applicability::MachineApplicable,
            );
        }
    }
}

fn ty_has_iter_method(cx: &LateContext<'_>, self_ref_ty: Ty<'_>) -> Option<(Symbol, &'static str)> {
    has_iter_method(cx, self_ref_ty).map(|ty_name| {
        let mutbl = match self_ref_ty.kind() {
            ty::Ref(_, _, mutbl) => mutbl,
            _ => unreachable!(),
        };
        let method_name = match mutbl {
            hir::Mutability::Not => "iter",
            hir::Mutability::Mut => "iter_mut",
        };
        (ty_name, method_name)
    })
}
<commit_msg>remove the use of paths<commit_after>use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_trait_method;
use clippy_utils::ty::has_iter_method;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
use rustc_span::source_map::Span;
use rustc_span::symbol::{sym, Symbol};

use super::INTO_ITER_ON_REF;

pub(super) fn check(
    cx: &LateContext<'_>,
    expr: &hir::Expr<'_>,
    method_span: Span,
    method_name: Symbol,
    args: &[hir::Expr<'_>],
) {
    let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
    if_chain! {
        if let ty::Ref(..) = self_ty.kind();
        if method_name == sym::into_iter;
        if is_trait_method(cx, expr, sym::IntoIterator);
        if let Some((kind, method_name)) = ty_has_iter_method(cx, self_ty);
        then {
            span_lint_and_sugg(
                cx,
                INTO_ITER_ON_REF,
                method_span,
                &format!(
                    "this `.into_iter()` call is equivalent to `.{}()` and will not consume the `{}`",
                    method_name, kind,
                ),
                "call directly",
                method_name.to_string(),
                Applicability::MachineApplicable,
            );
        }
    }
}

fn ty_has_iter_method(cx: &LateContext<'_>, self_ref_ty: Ty<'_>) -> Option<(Symbol, &'static str)> {
    has_iter_method(cx, self_ref_ty).map(|ty_name| {
        let mutbl = match self_ref_ty.kind() {
            ty::Ref(_, _, mutbl) => mutbl,
            _ => unreachable!(),
        };
        let method_name = match mutbl {
            hir::Mutability::Not => "iter",
            hir::Mutability::Mut => "iter_mut",
        };
        (ty_name, method_name)
    })
}
<|endoftext|>


<commit_before>//! Validates the MIR to ensure that invariants are upheld.

use super::{MirPass, MirSource};
use rustc_middle::mir::visit::Visitor;
use rustc_middle::{
    mir::{
        BasicBlock, Body, Location, Operand, Rvalue, Statement, StatementKind, Terminator,
        TerminatorKind,
    },
    ty::{self, ParamEnv, TyCtxt},
};

#[derive(Copy, Clone, Debug)]
enum EdgeKind {
    Unwind,
    Normal,
}

pub struct Validator {
    /// Describes at which point in the pipeline this validation is happening.
    pub when: String,
}

impl<'tcx> MirPass<'tcx> for Validator {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
        let param_env = tcx.param_env(source.def_id());
        TypeChecker { when: &self.when, source, body, tcx, param_env }.visit_body(body);
    }
}

struct TypeChecker<'a, 'tcx> {
    when: &'a str,
    source: MirSource<'tcx>,
    body: &'a Body<'tcx>,
    tcx: TyCtxt<'tcx>,
    param_env: ParamEnv<'tcx>,
}

impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
    fn fail(&self, location: Location, msg: impl AsRef<str>) {
        let span = self.body.source_info(location).span;
        // We use `delay_span_bug` as we might see broken MIR when other errors have already
        // occurred.
        self.tcx.sess.diagnostic().delay_span_bug(
            span,
            &format!(
                "broken MIR in {:?} ({}) at {:?}:\n{}",
                self.source.instance,
                self.when,
                location,
                msg.as_ref()
            ),
        );
    }

    fn check_edge(&self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) {
        if let Some(bb) = self.body.basic_blocks().get(bb) {
            let src = self.body.basic_blocks().get(location.block).unwrap();
            match (src.is_cleanup, bb.is_cleanup, edge_kind) {
                // Non-cleanup blocks can jump to non-cleanup blocks along non-unwind edges
                (false, false, EdgeKind::Normal)
                // Non-cleanup blocks can jump to cleanup blocks along unwind edges
                | (false, true, EdgeKind::Unwind)
                // Cleanup blocks can jump to cleanup blocks along non-unwind edges
                | (true, true, EdgeKind::Normal) => {}
                // All other jumps are invalid
                _ => {
                    self.fail(
                        location,
                        format!(
                            "{:?} edge to {:?} violates unwind invariants (cleanup {:?} -> {:?})",
                            edge_kind,
                            bb,
                            src.is_cleanup,
                            bb.is_cleanup,
                        )
                    )
                }
            }
        } else {
            self.fail(location, format!("encountered jump to invalid basic block {:?}", bb))
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
    fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
        // `Operand::Copy` is only supposed to be used with `Copy` types.
        if let Operand::Copy(place) = operand {
            let ty = place.ty(&self.body.local_decls, self.tcx).ty;
            let span = self.body.source_info(location).span;

            if !ty.is_copy_modulo_regions(self.tcx, self.param_env, span) {
                self.fail(location, format!("`Operand::Copy` with non-`Copy` type {}", ty));
            }
        }

        self.super_operand(operand, location);
    }

    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
        // The sides of an assignment must not alias. Currently this just checks whether the places
        // are identical.
        if let StatementKind::Assign(box (dest, rvalue)) = &statement.kind {
            match rvalue {
                Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) => {
                    if dest == src {
                        self.fail(
                            location,
                            "encountered `Assign` statement with overlapping memory",
                        );
                    }
                }
                _ => {}
            }
        }
    }

    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
        match &terminator.kind {
            TerminatorKind::Goto { target } => {
                self.check_edge(location, *target, EdgeKind::Normal);
            }
            TerminatorKind::SwitchInt { targets, values, .. } => {
                if targets.len() != values.len() + 1 {
                    self.fail(
                        location,
                        format!(
                            "encountered `SwitchInt` terminator with {} values, but {} targets (should be values+1)",
                            values.len(),
                            targets.len(),
                        ),
                    );
                }
                for target in targets {
                    self.check_edge(location, *target, EdgeKind::Normal);
                }
            }
            TerminatorKind::Drop { target, unwind, .. } => {
                self.check_edge(location, *target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_edge(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::DropAndReplace { target, unwind, .. } => {
                self.check_edge(location, *target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_edge(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Call { func, destination, cleanup, .. } => {
                let func_ty = func.ty(&self.body.local_decls, self.tcx);
                match func_ty.kind {
                    ty::FnPtr(..) | ty::FnDef(..) => {}
                    _ => self.fail(
                        location,
                        format!("encountered non-callable type {} in `Call` terminator", func_ty),
                    ),
                }
                if let Some((_, target)) = destination {
                    self.check_edge(location, *target, EdgeKind::Normal);
                }
                if let Some(cleanup) = cleanup {
                    self.check_edge(location, *cleanup, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Assert { cond, target, cleanup, .. } => {
                let cond_ty = cond.ty(&self.body.local_decls, self.tcx);
                if cond_ty != self.tcx.types.bool {
                    self.fail(
                        location,
                        format!(
                            "encountered non-boolean condition of type {} in `Assert` terminator",
                            cond_ty
                        ),
                    );
                }
                self.check_edge(location, *target, EdgeKind::Normal);
                if let Some(cleanup) = cleanup {
                    self.check_edge(location, *cleanup, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Yield { resume, drop, .. } => {
                self.check_edge(location, *resume, EdgeKind::Normal);
                if let Some(drop) = drop {
                    self.check_edge(location, *drop, EdgeKind::Normal);
                }
            }
            TerminatorKind::FalseEdge { real_target, imaginary_target } => {
                self.check_edge(location, *real_target, EdgeKind::Normal);
                self.check_edge(location, *imaginary_target, EdgeKind::Normal);
            }
            TerminatorKind::FalseUnwind { real_target, unwind } => {
                self.check_edge(location, *real_target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_edge(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::InlineAsm { destination, .. } => {
                if let Some(destination) = destination {
                    self.check_edge(location, *destination, EdgeKind::Normal);
                }
            }
            // Nothing to validate for these.
            TerminatorKind::Resume
            | TerminatorKind::Abort
            | TerminatorKind::Return
            | TerminatorKind::Unreachable
            | TerminatorKind::GeneratorDrop => {}
        }
    }
}
<commit_msg>MIR validation: check switch_ty<commit_after>//! Validates the MIR to ensure that invariants are upheld.

use super::{MirPass, MirSource};
use rustc_middle::mir::visit::Visitor;
use rustc_middle::{
    mir::{
        BasicBlock, Body, Location, Operand, Rvalue, Statement, StatementKind, Terminator,
        TerminatorKind,
    },
    ty::{self, ParamEnv, TyCtxt},
};

#[derive(Copy, Clone, Debug)]
enum EdgeKind {
    Unwind,
    Normal,
}

pub struct Validator {
    /// Describes at which point in the pipeline this validation is happening.
    pub when: String,
}

impl<'tcx> MirPass<'tcx> for Validator {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
        let param_env = tcx.param_env(source.def_id());
        TypeChecker { when: &self.when, source, body, tcx, param_env }.visit_body(body);
    }
}

struct TypeChecker<'a, 'tcx> {
    when: &'a str,
    source: MirSource<'tcx>,
    body: &'a Body<'tcx>,
    tcx: TyCtxt<'tcx>,
    param_env: ParamEnv<'tcx>,
}

impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
    fn fail(&self, location: Location, msg: impl AsRef<str>) {
        let span = self.body.source_info(location).span;
        // We use `delay_span_bug` as we might see broken MIR when other errors have already
        // occurred.
        self.tcx.sess.diagnostic().delay_span_bug(
            span,
            &format!(
                "broken MIR in {:?} ({}) at {:?}:\n{}",
                self.source.instance,
                self.when,
                location,
                msg.as_ref()
            ),
        );
    }

    fn check_edge(&self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) {
        if let Some(bb) = self.body.basic_blocks().get(bb) {
            let src = self.body.basic_blocks().get(location.block).unwrap();
            match (src.is_cleanup, bb.is_cleanup, edge_kind) {
                // Non-cleanup blocks can jump to non-cleanup blocks along non-unwind edges
                (false, false, EdgeKind::Normal)
                // Non-cleanup blocks can jump to cleanup blocks along unwind edges
                | (false, true, EdgeKind::Unwind)
                // Cleanup blocks can jump to cleanup blocks along non-unwind edges
                | (true, true, EdgeKind::Normal) => {}
                // All other jumps are invalid
                _ => {
                    self.fail(
                        location,
                        format!(
                            "{:?} edge to {:?} violates unwind invariants (cleanup {:?} -> {:?})",
                            edge_kind,
                            bb,
                            src.is_cleanup,
                            bb.is_cleanup,
                        )
                    )
                }
            }
        } else {
            self.fail(location, format!("encountered jump to invalid basic block {:?}", bb))
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
    fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
        // `Operand::Copy` is only supposed to be used with `Copy` types.
        if let Operand::Copy(place) = operand {
            let ty = place.ty(&self.body.local_decls, self.tcx).ty;
            let span = self.body.source_info(location).span;

            if !ty.is_copy_modulo_regions(self.tcx, self.param_env, span) {
                self.fail(location, format!("`Operand::Copy` with non-`Copy` type {}", ty));
            }
        }

        self.super_operand(operand, location);
    }

    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
        // The sides of an assignment must not alias. Currently this just checks whether the places
        // are identical.
        if let StatementKind::Assign(box (dest, rvalue)) = &statement.kind {
            match rvalue {
                Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) => {
                    if dest == src {
                        self.fail(
                            location,
                            "encountered `Assign` statement with overlapping memory",
                        );
                    }
                }
                _ => {}
            }
        }
    }

    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
        match &terminator.kind {
            TerminatorKind::Goto { target } => {
                self.check_edge(location, *target, EdgeKind::Normal);
            }
            TerminatorKind::SwitchInt { targets, values, switch_ty, discr } => {
                let ty = discr.ty(&self.body.local_decls, self.tcx);
                if ty != *switch_ty {
                    self.fail(
                        location,
                        format!(
                            "encountered `SwitchInt` terminator with type mismatch: {:?} != {:?}",
                            ty,
                            switch_ty,
                        ),
                    );
                }
                if targets.len() != values.len() + 1 {
                    self.fail(
                        location,
                        format!(
                            "encountered `SwitchInt` terminator with {} values, but {} targets (should be values+1)",
                            values.len(),
                            targets.len(),
                        ),
                    );
                }
                for target in targets {
                    self.check_edge(location, *target, EdgeKind::Normal);
                }
            }
            TerminatorKind::Drop { target, unwind, .. } => {
                self.check_edge(location, *target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_edge(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::DropAndReplace { target, unwind, .. } => {
                self.check_edge(location, *target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_edge(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Call { func, destination, cleanup, .. } => {
                let func_ty = func.ty(&self.body.local_decls, self.tcx);
                match func_ty.kind {
                    ty::FnPtr(..) | ty::FnDef(..) => {}
                    _ => self.fail(
                        location,
                        format!("encountered non-callable type {} in `Call` terminator", func_ty),
                    ),
                }
                if let Some((_, target)) = destination {
                    self.check_edge(location, *target, EdgeKind::Normal);
                }
                if let Some(cleanup) = cleanup {
                    self.check_edge(location, *cleanup, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Assert { cond, target, cleanup, .. } => {
                let cond_ty = cond.ty(&self.body.local_decls, self.tcx);
                if cond_ty != self.tcx.types.bool {
                    self.fail(
                        location,
                        format!(
                            "encountered non-boolean condition of type {} in `Assert` terminator",
                            cond_ty
                        ),
                    );
                }
                self.check_edge(location, *target, EdgeKind::Normal);
                if let Some(cleanup) = cleanup {
                    self.check_edge(location, *cleanup, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Yield { resume, drop, .. } => {
                self.check_edge(location, *resume, EdgeKind::Normal);
                if let Some(drop) = drop {
                    self.check_edge(location, *drop, EdgeKind::Normal);
                }
            }
            TerminatorKind::FalseEdge { real_target, imaginary_target } => {
                self.check_edge(location, *real_target, EdgeKind::Normal);
                self.check_edge(location, *imaginary_target, EdgeKind::Normal);
            }
            TerminatorKind::FalseUnwind { real_target, unwind } => {
                self.check_edge(location, *real_target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_edge(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::InlineAsm { destination, .. } => {
                if let Some(destination) = destination {
                    self.check_edge(location, *destination, EdgeKind::Normal);
                }
            }
            // Nothing to validate for these.
            TerminatorKind::Resume
            | TerminatorKind::Abort
            | TerminatorKind::Return
            | TerminatorKind::Unreachable
            | TerminatorKind::GeneratorDrop => {}
        }
    }
}
<|endoftext|>


<commit_before>use wpilib::wpilib_hal as hal;
use wpilib::Throttler;
use std::thread;
use std::sync::mpsc;
use std::ptr;
use std::mem::transmute;
use std::ffi::CString;
use std::sync::Arc;
use atom::*;

const MAX_JOYSTICK_PORTS: usize = 6;
const MAX_JOYSTICK_AXES: usize = 12;
const MAX_JOYSTICK_POVS: usize = 12;

#[derive(Default)]
struct Joysticks {
    axes: [hal::HAL_JoystickAxes; MAX_JOYSTICK_PORTS],
    povs: [hal::HAL_JoystickPOVs; MAX_JOYSTICK_PORTS],
    buttons: [hal::HAL_JoystickButtons; MAX_JOYSTICK_PORTS],
    descriptor: [hal::HAL_JoystickDescriptor; MAX_JOYSTICK_PORTS],
}

#[derive(Debug)]
pub enum RobotState {
    Disabled,
    Autonomous,
    Teleop,
    Test,
    EStop,
}

type DSBuffer = Box<(Joysticks, hal::HAL_ControlWord)>;

pub struct DriverStation {
    data: Arc<Atom<DSBuffer>>,
    joysticks: Joysticks,
    pub state: RobotState,
    pub fms_attached: bool,
    pub ds_attached: bool,

    report_throttler: Throttler<f64>,
}

static mut DRIVER_STATION: *mut DriverStation = 0 as *mut DriverStation;

#[derive(Debug)]
pub enum JoystickError {
    JoystickDNE,
    ChannelUnplugged,
    ChannelDNE,
}

impl DriverStation {
    fn new() -> DriverStation {
        let mut data_atom = Arc::new(Atom::empty());
        let mut other_atom = data_atom.clone();

        let join = thread::spawn(move || {
            let mut data_atom = data_atom.clone();
            loop {
                let mut joysticks = Joysticks::default();
                for stick in 0..MAX_JOYSTICK_PORTS {
                    unsafe {
                        hal::HAL_GetJoystickAxes(stick as i32,
                                                 &mut joysticks.axes[stick] as
                                                 *mut hal::HAL_JoystickAxes);
                        hal::HAL_GetJoystickPOVs(stick as i32,
                                                 &mut joysticks.povs[stick] as
                                                 *mut hal::HAL_JoystickPOVs);
                        hal::HAL_GetJoystickButtons(stick as i32,
                                                    &mut joysticks.buttons[stick] as
                                                    *mut hal::HAL_JoystickButtons);
                        hal::HAL_GetJoystickDescriptor(stick as i32,
                                                       &mut joysticks.descriptor[stick] as
                                                       *mut hal::HAL_JoystickDescriptor);
                    }
                }

                let mut control_word: hal::HAL_ControlWord = hal::HAL_ControlWord::default();
                unsafe {
                    hal::HAL_GetControlWord(&mut control_word as *mut hal::HAL_ControlWord);
                }

                data_atom.swap(Box::new((joysticks, control_word)));
            }
        });

        DriverStation {
            data: other_atom,
            joysticks: Joysticks::default(),
            state: RobotState::Disabled,
            fms_attached: false,
            ds_attached: false,

            // For now use an interval of 0 so we don't actually throttle messages, as the FPGA
            // timer isn't implemented yet.
            report_throttler: Throttler::new(0.0, 0.0),
        }
    }

    fn update_data(&mut self) {
        if let Some(boxed_data) = self.data.take() {
            let new_control_word = boxed_data.1;
            self.joysticks = boxed_data.0;
            self.state = if new_control_word.enabled() {
                if new_control_word.autonomous() {
                    RobotState::Autonomous
                } else {
                    RobotState::Teleop
                }
            } else if new_control_word.eStop() {
                RobotState::EStop
            } else {
                RobotState::Disabled
            };
            self.fms_attached = new_control_word.fmsAttached();
            self.ds_attached = new_control_word.dsAttached();
        }
    }

    fn report(&self, is_error: bool, code: i32, error: &str, location: &str, stack: &str) {
        unsafe {
            hal::HAL_SendError(is_error as i32,
                               code,
                               false as i32,
                               CString::new(error).unwrap().into_raw(),
                               CString::new(location).unwrap().into_raw(),
                               CString::new(stack).unwrap().into_raw(),
                               true as i32);
        }
    }

    fn report_error(&mut self, error: &str) {
        self.report(true, 1, error, "", "");
    }

    fn report_warning(&mut self, warning: &str) {
        self.report(false, 1, warning, "", "");
    }

    fn report_throttled(&mut self, is_error: bool, message: &str) {
        // Don't actually throttle it; FPGA timer is unimplemented
        let now = 1f64;
        if self.report_throttler.update(now) {
            self.report(is_error, 1, message, "", "");
        }
    }

    pub fn instance() -> &'static mut DriverStation {
        unsafe {
            if DRIVER_STATION == 0 as *mut DriverStation {
                DRIVER_STATION = transmute(Box::new(DriverStation::new()));
            }
            &mut *DRIVER_STATION
        }
    }

    pub fn get_joystick_axis(&mut self, stick: usize, axis: usize) -> Result<f32, JoystickError> {
        self.update_data();

        if stick >= MAX_JOYSTICK_PORTS {
            self.report_throttled(true, "Bad joystick");
            Err(JoystickError::JoystickDNE)
        } else if axis >= MAX_JOYSTICK_AXES {
            self.report_throttled(true, "Bad joystick axis");
            Err(JoystickError::ChannelDNE)
        } else if axis >= self.joysticks.axes[stick].count as usize {
            self.report_throttled(true,
                                  "Joystick axis missing, check if all controllers are plugged in");
            Err(JoystickError::ChannelUnplugged)
        } else {
            Ok(self.joysticks.axes[stick].axes[axis])
        }
    }

    pub fn get_joystick_pov(&mut self, stick: usize, pov: usize) -> Result<i16, JoystickError> {
        self.update_data();

        if stick >= MAX_JOYSTICK_POVS {
            self.report_throttled(true, "Bad joystick");
            Err(JoystickError::JoystickDNE)
        } else if pov >= MAX_JOYSTICK_AXES {
            self.report_throttled(true, "Bad joystick pov");
            Err(JoystickError::ChannelDNE)
        } else if pov >= self.joysticks.povs[stick].count as usize {
            self.report_throttled(true,
                                  "Joystick pov missing, check if all controllers are plugged in");
            Err(JoystickError::ChannelUnplugged)
        } else {
            Ok(self.joysticks.povs[stick].povs[pov])
        }
    }

    pub fn get_joystick_button(&mut self,
                               stick: usize,
                               button: usize)
                               -> Result<bool, JoystickError> {
        self.update_data();

        if stick >= MAX_JOYSTICK_POVS {
            self.report_throttled(true, "Bad joystick");
            Err(JoystickError::JoystickDNE)
        } else if button == 0 {
            self.report_throttled(true, "Bad joystick button (button IDs start from 1)");
            Err(JoystickError::ChannelDNE)
        } else if button >= self.joysticks.povs[stick].count as usize {
            self.report_throttled(true,
                                  "Joystick button missing, check if all controllers are plugged \
                                   in");
            Err(JoystickError::ChannelUnplugged)
        } else {
            let mask = 1 << (button - 1);
            Ok(self.joysticks.buttons[stick].buttons & mask != 0)
        }
    }
}
<commit_msg>DriverStation improvements<commit_after>use wpilib::wpilib_hal::*;
use wpilib::hal_call::*;
use wpilib::Throttler;
use std::thread;
use std::sync::mpsc;
use std::ptr;
use std::mem::transmute;
use std::ffi::CString;
use std::sync::{Arc, Condvar, Mutex};
use atom::*;
use std::time::Duration;

const MAX_JOYSTICK_PORTS: usize = 6;
const MAX_JOYSTICK_AXES: usize = 12;
const MAX_JOYSTICK_POVS: usize = 12;

#[derive(Default)]
struct Joysticks {
    axes: [HAL_JoystickAxes; MAX_JOYSTICK_PORTS],
    povs: [HAL_JoystickPOVs; MAX_JOYSTICK_PORTS],
    buttons: [HAL_JoystickButtons; MAX_JOYSTICK_PORTS],
    descriptor: [HAL_JoystickDescriptor; MAX_JOYSTICK_PORTS],
}

#[derive(Debug)]
pub enum RobotState {
    Disabled,
    Autonomous,
    Teleop,
    Test,
    EStop,
}

type DSBuffer = Box<(Joysticks, HAL_ControlWord)>;

pub struct DriverStation {
    data: Arc<Atom<DSBuffer>>,
    joysticks: Joysticks,
    pub state: RobotState,
    pub fms_attached: bool,
    pub ds_attached: bool,

    report_throttler: Throttler<f64>,

    waiter: Arc<(Mutex<bool>, Condvar)>,
}

static mut DRIVER_STATION: *mut DriverStation = 0 as *mut DriverStation;

#[derive(Debug, Copy, Clone)]
pub enum JoystickError {
    JoystickDNE,
    ChannelUnplugged,
    ChannelDNE,
}

#[derive(Debug, Copy, Clone)]
pub enum AllianceId {
    Red,
    Blue,
    Invalid,
}

impl DriverStation {
    fn new() -> DriverStation {
        let mut data_atom = Arc::new(Atom::empty());
        let mut other_atom = data_atom.clone();

        let mut waiter = Arc::new((Mutex::new(false), Condvar::new()));
        let mut other_waiter = waiter.clone();

        let join = thread::spawn(move || {
            let mut data_atom = other_atom;
            let mut waiter = other_waiter;

            loop {
                unsafe {
                    HAL_WaitForDSData();
                }

                let mut joysticks = Joysticks::default();
                for stick in 0..MAX_JOYSTICK_PORTS {
                    unsafe {
                        HAL_GetJoystickAxes(stick as i32,
                                            &mut joysticks.axes[stick] as *mut HAL_JoystickAxes);
                        HAL_GetJoystickPOVs(stick as i32,
                                            &mut joysticks.povs[stick] as *mut HAL_JoystickPOVs);
                        HAL_GetJoystickButtons(stick as i32,
                                                    &mut joysticks.buttons[stick] as
                                                    *mut HAL_JoystickButtons);
                        HAL_GetJoystickDescriptor(stick as i32,
                                                       &mut joysticks.descriptor[stick] as
                                                       *mut HAL_JoystickDescriptor);
                    }
                }

                let mut control_word: HAL_ControlWord = HAL_ControlWord::default();
                unsafe {
                    HAL_GetControlWord(&mut control_word as *mut HAL_ControlWord);
                }

                data_atom.swap(Box::new((joysticks, control_word)));

                {
                    let mut guard = waiter.0.lock().unwrap();
                    *guard = true;
                    waiter.1.notify_all();
                }
            }
        });

        DriverStation {
            data: data_atom,
            joysticks: Joysticks::default(),
            state: RobotState::Disabled,
            fms_attached: false,
            ds_attached: false,

            // For now use an interval of 0 so we don't actually throttle messages, as the FPGA
            // timer isn't implemented yet.
            report_throttler: Throttler::new(0.0, 0.0),

            waiter: waiter,
        }
    }

    fn update_data(&mut self) {
        if let Some(boxed_data) = self.data.take() {
            let new_control_word = boxed_data.1;
            self.joysticks = boxed_data.0;
            self.state = if new_control_word.enabled() {
                if new_control_word.autonomous() {
                    RobotState::Autonomous
                } else {
                    RobotState::Teleop
                }
            } else if new_control_word.eStop() {
                RobotState::EStop
            } else {
                RobotState::Disabled
            };
            self.fms_attached = new_control_word.fmsAttached();
            self.ds_attached = new_control_word.dsAttached();
        }
    }

    fn report(&self, is_error: bool, code: i32, error: &str, location: &str, stack: &str) {
        unsafe {
            HAL_SendError(is_error as i32,
                          code,
                          false as i32,
                          CString::new(error).unwrap().into_raw(),
                          CString::new(location).unwrap().into_raw(),
                          CString::new(stack).unwrap().into_raw(),
                          true as i32);
        }
    }

    fn report_error(&mut self, error: &str) {
        self.report(true, 1, error, "", "");
    }

    fn report_warning(&mut self, warning: &str) {
        self.report(false, 1, warning, "", "");
    }

    fn report_throttled(&mut self, is_error: bool, message: &str) {
        // Don't actually throttle it; FPGA timer is unimplemented
        let now = 1f64;
        if self.report_throttler.update(now) {
            self.report(is_error, 1, message, "", "");
        }
    }

    pub fn instance() -> &'static mut DriverStation {
        unsafe {
            if DRIVER_STATION == 0 as *mut DriverStation {
                DRIVER_STATION = transmute(Box::new(DriverStation::new()));
            }
            &mut *DRIVER_STATION
        }
    }

    pub fn get_joystick_axis(&mut self, stick: usize, axis: usize) -> Result<f32, JoystickError> {
        self.update_data();

        if stick >= MAX_JOYSTICK_PORTS {
            self.report_throttled(true, "Bad joystick");
            Err(JoystickError::JoystickDNE)
        } else if axis >= MAX_JOYSTICK_AXES {
            self.report_throttled(true, "Bad joystick axis");
            Err(JoystickError::ChannelDNE)
        } else if axis >= self.joysticks.axes[stick].count as usize {
            self.report_throttled(true,
                                  "Joystick axis missing, check if all controllers are plugged in");
            Err(JoystickError::ChannelUnplugged)
        } else {
            Ok(self.joysticks.axes[stick].axes[axis])
        }
    }

    pub fn get_joystick_pov(&mut self, stick: usize, pov: usize) -> Result<i16, JoystickError> {
        self.update_data();

        if stick >= MAX_JOYSTICK_POVS {
            self.report_throttled(true, "Bad joystick");
            Err(JoystickError::JoystickDNE)
        } else if pov >= MAX_JOYSTICK_AXES {
            self.report_throttled(true, "Bad joystick pov");
            Err(JoystickError::ChannelDNE)
        } else if pov >= self.joysticks.povs[stick].count as usize {
            self.report_throttled(true,
                                  "Joystick pov missing, check if all controllers are plugged in");
            Err(JoystickError::ChannelUnplugged)
        } else {
            Ok(self.joysticks.povs[stick].povs[pov])
        }
    }

    pub fn get_joystick_button(&mut self,
                               stick: usize,
                               button: usize)
                               -> Result<bool, JoystickError> {
        self.update_data();

        if stick >= MAX_JOYSTICK_POVS {
            self.report_throttled(true, "Bad joystick");
            Err(JoystickError::JoystickDNE)
        } else if button == 0 {
            self.report_throttled(true, "Bad joystick button (button IDs start from 1)");
            Err(JoystickError::ChannelDNE)
        } else if button >= self.joysticks.povs[stick].count as usize {
            self.report_throttled(true,
                                  "Joystick button missing, check if all controllers are plugged \
                                   in");
            Err(JoystickError::ChannelUnplugged)
        } else {
            let mask = 1 << (button - 1);
            Ok(self.joysticks.buttons[stick].buttons & mask != 0)
        }
    }

    pub fn get_alliance(&self) -> HalResult<AllianceId> {
        match hal_call!(HAL_GetAllianceStation())? {
            HAL_AllianceStationID::HAL_AllianceStationID_kRed1 |
            HAL_AllianceStationID::HAL_AllianceStationID_kRed2 |
            HAL_AllianceStationID::HAL_AllianceStationID_kRed3 => Ok(AllianceId::Red),
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue1 |
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue2 |
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue3 => Ok(AllianceId::Blue),
        }
    }

    pub fn get_station(&self) -> HalResult<i32> {
        match hal_call!(HAL_GetAllianceStation())? {
            HAL_AllianceStationID::HAL_AllianceStationID_kRed1 |
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue1 => Ok(1),
            HAL_AllianceStationID::HAL_AllianceStationID_kRed2 |
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue2 => Ok(2),
            HAL_AllianceStationID::HAL_AllianceStationID_kRed3 |
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue3 => Ok(3),
        }
    }

    /// Waits for a new driver station packet
    pub fn wait_for_data(&self) {
        let &(ref wait_lock, ref wait_cond) = &*self.waiter;
        let mut has_data = wait_lock.lock().unwrap();
        while !*has_data {
            has_data = wait_cond.wait(has_data).unwrap();
        }
    }

    /// Waits for a new driver station packet and returns true, or returns false if timeout is
    /// exceeded.
    pub fn wait_for_data_or_timeout(&self, timeout: Duration) -> bool {
        let &(ref wait_lock, ref wait_cond) = &*self.waiter;
        let mut has_data = wait_lock.lock().unwrap();

        while !*has_data {
            let result = wait_cond.wait_timeout(has_data, timeout).unwrap();
            if result.1.timed_out() {
                return false;
            } else {
                has_data = result.0;
            }
        }
        true
    }
}
<|endoftext|>