<commit_before>use std::{
  env,
  fs,
  path::Path
};
use std::path::PathBuf;

use expectest::prelude::*;
use pact_models::pact::ReadWritePact;
use pact_models::sync_pact::RequestResponsePact;
use rand::prelude::*;
use reqwest::Client;

use pact_consumer::{json_pattern, json_pattern_internal};
use pact_consumer::prelude::*;

/// This is supposed to be a doctest in mod, but it's breaking there, so
/// we have an executable copy here.
///
/// This test is currently ignored because it has a race condition when running in CI. Probably
/// because it is mutating environment variables that point to directories on disk
#[test_log::test(tokio::test)]
async fn mock_server_passing_validation() {
    let output_dir = output_dir("target/pact_dir");

    env::set_var("PACT_OUTPUT_DIR", &output_dir);

    {
      // Define the Pact for the test, specify the names of the consuming
      // application and the provider application.
      let alice_service = PactBuilder::new("Consumer", "Alice Service")
        // Start a new interaction. We can add as many interactions as we want.
        .interaction("a retrieve Mallory request", "", |mut i| {
          // Defines a provider state. It is optional.
          i.given("there is some good mallory");
          // Define the request, a GET (default) request to '/mallory'.
          i.request.path("/mallory");
          // Define the response we want returned.
          i.response
            .ok()
            .content_type("text/plain")
            .body("That is some good Mallory.");

          // Return the interaction back to the pact framework
          i.clone()
        })
        .start_mock_server(None);

      // You would use your actual client code here.
      let mallory_url = alice_service.path("/mallory");
      let response = reqwest::get(mallory_url).await.expect("could not fetch URL");
      let body = response.text().await.expect("could not read response body");
      assert_eq!(body, "That is some good Mallory.");
    }

    // When your test has finished running, all verifications will be performed
    // automatically, and an error will be thrown if any have failed.

    env::remove_var("PACT_OUTPUT_DIR");

    let path_file = Path::new("target/pact_dir/Consumer-Alice Service.json");
    expect!(path_file.exists()).to(be_true());
}

fn output_dir(path: &str) -> PathBuf {
  match Path::new(path).canonicalize() {
    Ok(path) => {
      fs::remove_dir_all(path.clone()).unwrap_or(());
      path
    }
    Err(_) => {
      let path = Path::new(path);
      fs::create_dir_all(path.clone()).unwrap_or(());
      path.canonicalize().unwrap()
    }
  }
}

#[test_log::test(tokio::test)]
async fn mock_server_passing_validation_async_version() {
  // Define the Pact for the test, specify the names of the consuming
  // application and the provider application.
  let alice_service = PactBuilderAsync::new("Consumer", "Alice Service")
    // Start a new interaction. We can add as many interactions as we want.
    .interaction("a retrieve Mallory request", "", |mut i| async move {
      // Defines a provider state. It is optional.
      i.given("there is some good mallory");
      // Define the request, a GET (default) request to '/mallory'.
      i.request.path("/mallory");
      // Define the response we want returned.
      i.response
        .ok()
        .content_type("text/plain")
        .body("That is some good Mallory.");

      // Return the interaction back to the pact framework
      i.clone()
    })
    .await
    .start_mock_server(None);

  // You would use your actual client code here.
  let mallory_url = alice_service.path("/mallory");
  let response = reqwest::get(mallory_url).await.expect("could not fetch URL");
  let body = response.text().await.expect("could not read response body");
  assert_eq!(body, "That is some good Mallory.");
}

#[test_log::test]
#[should_panic]
fn mock_server_failing_validation() {
    let hello_service = PactBuilder::new("Hello CLI", "Hello Server")
        .interaction("request a greeting", "", |mut i| {
          i.request.path("/hello");
          i.response.body("Hello!");
          i.clone()
        })
      .start_mock_server(None);
    // Call with the wrong URL, which should lead to a panic at the end of
    // the function.
    let url = hello_service.path("/goodbye");
    let _ = reqwest::blocking::get(url);
}

#[test_log::test(tokio::test)]
async fn duplicate_interactions() {
  let u8 = random::<u8>();
  let output_dir = output_dir(&*format!("target/pact_dir_{:03}", u8));

  for _ in 1..=3 {
    let mock_service = PactBuilder::new("consumer 1", "provider 1")
      .interaction("tricky test", "", |mut interaction| {
        interaction
          .request
          .put()
          .json_body(pact_consumer::json_pattern!({
                          "name": pact_consumer::like!("mai"),
                          "street": pact_consumer::like!("5th"),
                          "state": pact_consumer::like!("VA"),
                      }))
          .path("/rolex.html");
        interaction.response.body("TrixR4Kidz");
        interaction
      })
      .output_dir(&output_dir)
      .start_mock_server(None);

    let mock_url = mock_service.url();

    assert_eq!(
      Client::new()
        .put(&format!("{}rolex.html", mock_url))
        .json(&serde_json::json!({
                      "name": "mai",
                      "street": "5th",
                      "state": "VA",
                  }))
        .send()
        .await
        .unwrap()
        .text()
        .await
        .unwrap(),
      "TrixR4Kidz",
    );
  }

  let path = output_dir.join("consumer 1-provider 1.json");
  let written_pact = RequestResponsePact::read_pact(path.as_path()).unwrap();
  let _ = fs::remove_dir_all(output_dir);

  expect!(written_pact.interactions.len()).to(be_equal_to(1));
}
<commit_msg>chore: add a test with two near identical interactions<commit_after>use std::{
  env,
  fs,
  path::Path
};
use std::path::PathBuf;

use expectest::prelude::*;
use pact_models::pact::ReadWritePact;
use pact_models::sync_pact::RequestResponsePact;
use pact_models::v4::pact::V4Pact;
use rand::prelude::*;
use reqwest::Client;
use serde_json::json;

use pact_consumer::{json_pattern, json_pattern_internal, like};
use pact_consumer::prelude::*;

/// This is supposed to be a doctest in mod, but it's breaking there, so
/// we have an executable copy here.
///
/// This test is currently ignored because it has a race condition when running in CI. Probably
/// because it is mutating environment variables that point to directories on disk
#[test_log::test(tokio::test)]
async fn mock_server_passing_validation() {
    let output_dir = output_dir("target/pact_dir");

    env::set_var("PACT_OUTPUT_DIR", &output_dir);

    {
      // Define the Pact for the test, specify the names of the consuming
      // application and the provider application.
      let alice_service = PactBuilder::new("Consumer", "Alice Service")
        // Start a new interaction. We can add as many interactions as we want.
        .interaction("a retrieve Mallory request", "", |mut i| {
          // Defines a provider state. It is optional.
          i.given("there is some good mallory");
          // Define the request, a GET (default) request to '/mallory'.
          i.request.path("/mallory");
          // Define the response we want returned.
          i.response
            .ok()
            .content_type("text/plain")
            .body("That is some good Mallory.");

          // Return the interaction back to the pact framework
          i.clone()
        })
        .start_mock_server(None);

      // You would use your actual client code here.
      let mallory_url = alice_service.path("/mallory");
      let response = reqwest::get(mallory_url).await.expect("could not fetch URL");
      let body = response.text().await.expect("could not read response body");
      assert_eq!(body, "That is some good Mallory.");
    }

    // When your test has finished running, all verifications will be performed
    // automatically, and an error will be thrown if any have failed.

    env::remove_var("PACT_OUTPUT_DIR");

    let path_file = Path::new("target/pact_dir/Consumer-Alice Service.json");
    expect!(path_file.exists()).to(be_true());
}

fn output_dir(path: &str) -> PathBuf {
  match Path::new(path).canonicalize() {
    Ok(path) => {
      fs::remove_dir_all(path.clone()).unwrap_or(());
      path
    }
    Err(_) => {
      let path = Path::new(path);
      fs::create_dir_all(path.clone()).unwrap_or(());
      path.canonicalize().unwrap()
    }
  }
}

#[test_log::test(tokio::test)]
async fn mock_server_passing_validation_async_version() {
  // Define the Pact for the test, specify the names of the consuming
  // application and the provider application.
  let alice_service = PactBuilderAsync::new("Consumer", "Alice Service")
    // Start a new interaction. We can add as many interactions as we want.
    .interaction("a retrieve Mallory request", "", |mut i| async move {
      // Defines a provider state. It is optional.
      i.given("there is some good mallory");
      // Define the request, a GET (default) request to '/mallory'.
      i.request.path("/mallory");
      // Define the response we want returned.
      i.response
        .ok()
        .content_type("text/plain")
        .body("That is some good Mallory.");

      // Return the interaction back to the pact framework
      i.clone()
    })
    .await
    .start_mock_server(None);

  // You would use your actual client code here.
  let mallory_url = alice_service.path("/mallory");
  let response = reqwest::get(mallory_url).await.expect("could not fetch URL");
  let body = response.text().await.expect("could not read response body");
  assert_eq!(body, "That is some good Mallory.");
}

#[test_log::test]
#[should_panic]
fn mock_server_failing_validation() {
    let hello_service = PactBuilder::new("Hello CLI", "Hello Server")
        .interaction("request a greeting", "", |mut i| {
          i.request.path("/hello");
          i.response.body("Hello!");
          i.clone()
        })
      .start_mock_server(None);
    // Call with the wrong URL, which should lead to a panic at the end of
    // the function.
    let url = hello_service.path("/goodbye");
    let _ = reqwest::blocking::get(url);
}

#[test_log::test(tokio::test)]
async fn duplicate_interactions() {
  let u8 = random::<u8>();
  let output_dir = output_dir(&*format!("target/pact_dir_{:03}", u8));

  for _ in 1..=3 {
    let mock_service = PactBuilder::new("consumer 1", "provider 1")
      .interaction("tricky test", "", |mut interaction| {
        interaction
          .request
          .put()
          .json_body(pact_consumer::json_pattern!({
                          "name": pact_consumer::like!("mai"),
                          "street": pact_consumer::like!("5th"),
                          "state": pact_consumer::like!("VA"),
                      }))
          .path("/rolex.html");
        interaction.response.body("TrixR4Kidz");
        interaction
      })
      .output_dir(&output_dir)
      .start_mock_server(None);

    let mock_url = mock_service.url();

    assert_eq!(
      Client::new()
        .put(&format!("{}rolex.html", mock_url))
        .json(&serde_json::json!({
                      "name": "mai",
                      "street": "5th",
                      "state": "VA",
                  }))
        .send()
        .await
        .unwrap()
        .text()
        .await
        .unwrap(),
      "TrixR4Kidz",
    );
  }

  let path = output_dir.join("consumer 1-provider 1.json");
  let written_pact = RequestResponsePact::read_pact(path.as_path()).unwrap();
  let _ = fs::remove_dir_all(output_dir);

  expect!(written_pact.interactions.len()).to(be_equal_to(1));
}

#[test_log::test(tokio::test)]
async fn test_two_interactions() {
  {
    let mock_service = PactBuilder::new("test_two_interactions_consumer", "test_two_interactions_provider")
      .interaction("looks for something that doesn't exist", "", |mut i| {
        i.request
          .post()
          .path("/")
          .content_type("application/json")
          .json_body(like!(json!({"key": "i_dont_exist"})));
        i.response
          .content_type("application/json")
          .json_body(json!({"count": 0, "results": []}));
        i
      })
      .start_mock_server(None);

    let mock_url = mock_service.url();
    Client::new().post(mock_url).json(&json!({"key": "i_dont_exist"})).send().await.unwrap();
  }

  {
    let mock_service = PactBuilder::new("test_two_interactions_consumer", "test_two_interactions_provider")
      .interaction("looks for something that exists", "", |mut i| {
        i.request
          .post()
          .path("/")
          .content_type("application/json")
          .json_body(like!(json!({"key": "i_exist"})));
        i.response
          .content_type("application/json")
          .json_body(json!({"count": 1, "results": ["i_exist"]}));
        i
      })
      .start_mock_server(None);

    let mock_url = mock_service.url();
    Client::new().post(mock_url).json(&json!({"key": "i_exist"})).send().await.unwrap();
  }

  let path_file = Path::new("target/pacts/test_two_interactions_consumer-test_two_interactions_provider.json");
  expect!(path_file.exists()).to(be_true());

  let pact = RequestResponsePact::read_pact(&path_file).unwrap();
  expect!(pact.interactions.len()).to(be_equal_to(2));
}
<|endoftext|>


<commit_before>/* Stones esoteric programming language
 * (c) 2016 Zack Hixon - see LICENSE.txt */

/** NOTE: Hey there! This isn't even remotely started yet. I'm gonna need a
 * WHOLE lot of stuff to make this even *resemble* an interpreter. It also
 * isn't currently up-to-date with the "specification," so it features
 * misleading stuff like the Stone struct right below this, along with the
 * field vector.  Expect a lot of this stuff to change. */

#[derive(Debug)]
struct Stone {
    x: i32,
    y: i32,
    id: i32,
    color: Color
}

impl Stone {
    fn new(x: i32, y: i32, id: i32, color: Color) -> Stone {
        Stone {
            x: x,
            y: y,
            id: id,
            color: color
        }
    }

    /* only check stones with 0 <= x <= 9 and 0 <= y <= 9 */
    fn empty() -> Stone {
        Stone {
            x: -1,
            y: -1,
            id: -1,
            color: Color::Red
        }
    }
}

#[derive(Debug)]
enum Color {
    Red,
    Orange,
    Yellow,
    Green,
    Blue,
    Purple
}

fn main() {
    println!("test stone: (debug)");

    let mut test_stone = Stone {
        x: 0,
        y: 0,
        id: 1,
        color: Color::Yellow
    };

    println!("{:?}", test_stone);

    test_stone.x = 1;
    test_stone.y = 1;
    test_stone.id = 2;
    test_stone.color = Color::Blue;

    println!("{:?}", test_stone);

    let mut field: Vec<Vec<Stone>> =
        vec![vec![Stone::new(0, 0, 1, Color::Red),
                  Stone::new(1, 0, 2, Color::Blue),
                  Stone::new(2, 0, 3, Color::Green)],
             vec![Stone::empty(),
                  Stone::empty(),
                  Stone::empty()],
             vec![Stone::empty(),
                  Stone::empty(),
                  Stone::empty()]];

    println!("\nall stones:");
    for row in field {
        for stone in row {
            print!("{} {} {} {:?}\n", stone.x, stone.y, stone.id, stone.color);
        }
        println!("");
    }
}

<commit_msg>remove ID<commit_after>/* Stones esoteric programming language
 * (c) 2016 Zack Hixon - see LICENSE.txt */

/** NOTE: Hey there! This isn't even remotely started yet. I'm gonna need a
 * WHOLE lot of stuff to make this even *resemble* an interpreter. It also
 * isn't currently up-to-date with the "specification," so it features
 * misleading stuff like the Stone struct right below this, along with the
 * field vector.  Expect a lot of this stuff to change. */

#[derive(Debug)]
struct Stone {
    x: i32,
    y: i32,
    color: Color
}

impl Stone {
    fn new(x: i32, y: i32, color: Color) -> Stone {
        Stone {
            x: x,
            y: y,
            color: color
        }
    }

    /* only check stones with 0 <= x <= 9 and 0 <= y <= 9 */
    fn empty() -> Stone {
        Stone {
            x: -1,
            y: -1,
            color: Color::Invisible
        }
    }
}

#[derive(Debug)]
enum Color {
    Red,
    Orange,
    Yellow,
    Green,
    Blue,
    Purple,
    Invisible
}

fn main() {
    println!("test stone: (debug)");

    let mut test_stone = Stone {
        x: 0,
        y: 0,
        color: Color::Yellow
    };

    println!("{:?}", test_stone);

    test_stone.x = 1;
    test_stone.y = 1;
    test_stone.color = Color::Blue;

    println!("{:?}", test_stone);

    let mut field: Vec<Vec<Stone>> =
        vec![vec![Stone::new(0, 0, Color::Red),
                  Stone::new(1, 0, Color::Blue),
                  Stone::new(2, 0, Color::Green)],
             vec![Stone::empty(),
                  Stone::empty(),
                  Stone::empty()],
             vec![Stone::empty(),
                  Stone::empty(),
                  Stone::empty()]];

    println!("\nall stones:");
    for row in field {
        for stone in row {
            print!("{} {} {:?}\n", stone.x, stone.y, stone.color);
        }
        println!("");
    }
}

<|endoftext|>


<commit_before>extern crate aho_corasick;

use std::env::{self, VarError};
use std::io::{self, Read, Write};
use std::process;
use aho_corasick::{Automaton, AcAutomaton};

macro_rules! println_err(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("error: failed printing to stderr");
    } }
);

// Prefix env var namespace.
fn envvar(key: &str) -> Result<String, VarError> {
    env::var(format!("ENVSUB_{}", key))
}

fn replace(keys: &Vec<String>, values: &Vec<String>, target: &String) -> String {
    let aut = AcAutomaton::new(keys);
    let matches = aut.find(&target);
    let mut target_end = 0;
    let mut o: Vec<String> = vec![];

    for m in matches {
        o.push(target[target_end..m.start].into());
        o.push(values[m.pati].as_str().into());
        target_end = m.end;
    }

    o.push(target[target_end..].into());
    o.join("")
}

fn get_vars() -> Vec<String> {
    // Gather list of env vars.
    let args = env::args();
    let mut vars = Vec::new();
    if args.len() > 1 {
        // Vars are provided via CLI args.
        for arg in args.skip(1) {
            vars.push(arg);
        }
    } else {
        // Vars discovered from the environment.
        for (key, _) in env::vars() {
            vars.push(key);
        }
    };
    vars
}

fn get_kvs(vars: Vec<String>, prefix: &str, suffix: &str) -> (Vec<String>, Vec<String>) {
    // Build two vecs, one for search patterns, one for replacement text.
    let mut keys = Vec::new();
    let mut vals = Vec::new();
    for (i, var) in vars.iter().enumerate() {
        // Form the search key.
        let key = format!("{}{}{}", &prefix, &var, &suffix);
        let val = match env::var(&vars[i]) {
            Ok(v) => v,
            Err(_) => {
                println_err!("error: env var not found: {}", var);
                process::exit(1)
            },
        };
        keys.push(key);
        vals.push(val);
    }
    (keys, vals)
}

fn main() {
    let prefix = match envvar("PREFIX") {
        Ok(v) => v,
        Err(_) => String::from("%"),
    };
    let suffix = match envvar("SUFFIX") {
        Ok(v) => v,
        Err(_) => String::from("%"),
    };

    // Gather list of env vars.
    let vars = get_vars();
    // Build search keys and values.
    let (keys, vals) = get_kvs(vars, &prefix, &suffix);

    let reader = io::stdin();
    let writer = io::stdout();

    // Build an in-memory buffer so users can replace files in-place.
    // i.e. envsub < foo.txt > foo.txt
    let mut buffer = String::new();
    let _ = reader.lock().read_to_string(&mut buffer);
    let replaced = replace(&keys, &vals, &buffer);
    let _ = writer.lock().write(&replaced.as_bytes());
}

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

    #[test]
    fn test_replace_without_replacements() {
        let string = String::from("just\na\nnormal\nstring\n");
        let keys = vec!["x".to_owned()];
        let vals = vec!["xyz".to_owned()];
        let output = replace(&keys, &vals, &string);
        assert_eq!(string, output);
    }

    #[test]
    fn test_replace_with_one_variable() {
        let string = "%VAR%".to_owned();
        let replacement = "moo".to_owned();
        let keys = vec!["%VAR%".to_owned()];
        let vals = vec![replacement.clone()];
        let output = replace(&keys, &vals, &string);
        assert_eq!(output, replacement.clone());
    }

    #[test]
    fn test_replace_with_multiple_variables() {
        let string = "%VAR1%, %VAR2%, %VAR3%".to_owned();
        let keys = vec!["%VAR1%".to_owned(), "%VAR2%".to_owned(), "%VAR3%".to_owned()];
        let vals = vec!["foo".to_owned(), "bar".to_owned(), "baz".to_owned()];
        let output = replace(&keys, &vals, &string);
        assert_eq!(output, vals.join(", "));
    }

    #[test]
    fn test_replace_with_single_variable_multiple_times() {
        let string = "%VAR1%, %VAR1%, %VAR1%".to_owned();
        let keys = vec!["%VAR1%".to_owned(), "%VAR2%".to_owned(), "%VAR3%".to_owned()];
        let vals = vec!["foo".to_owned(), "bar".to_owned(), "baz".to_owned()];
        let output = replace(&keys, &vals, &string);
        assert_eq!(output, "foo, foo, foo".to_owned());
    }

    #[test]
    fn test_multiline_replacement() {
        let string = "%VAR3%\n%VAR1\n".to_owned();
        let keys = vec!["%VAR1%".to_owned(), "%VAR2%".to_owned(), "%VAR3%".to_owned()];
        let vals = vec!["foo".to_owned(), "bar".to_owned(), "baz".to_owned()];
        let output = replace(&keys, &vals, &string);
        assert_eq!(output, "baz\n%VAR1\n".to_owned());
    }
}
<commit_msg>Linting; allow str for replace, del useless borrow<commit_after>extern crate aho_corasick;

use std::env::{self, VarError};
use std::io::{self, Read, Write};
use std::process;
use aho_corasick::{Automaton, AcAutomaton};

macro_rules! println_err(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("error: failed printing to stderr");
    } }
);

// Prefix env var namespace.
fn envvar(key: &str) -> Result<String, VarError> {
    env::var(format!("ENVSUB_{}", key))
}

fn replace(keys: &[String], values: &[String], target: &str) -> String {
    let aut = AcAutomaton::new(keys);
    let matches = aut.find(&target);
    let mut target_end = 0;
    let mut o: Vec<String> = vec![];

    for m in matches {
        o.push(target[target_end..m.start].into());
        o.push(values[m.pati].as_str().into());
        target_end = m.end;
    }

    o.push(target[target_end..].into());
    o.join("")
}

fn get_vars() -> Vec<String> {
    // Gather list of env vars.
    let args = env::args();
    let mut vars = Vec::new();
    if args.len() > 1 {
        // Vars are provided via CLI args.
        for arg in args.skip(1) {
            vars.push(arg);
        }
    } else {
        // Vars discovered from the environment.
        for (key, _) in env::vars() {
            vars.push(key);
        }
    };
    vars
}

fn get_kvs(vars: Vec<String>, prefix: &str, suffix: &str) -> (Vec<String>, Vec<String>) {
    // Build two vecs, one for search patterns, one for replacement text.
    let mut keys = Vec::new();
    let mut vals = Vec::new();
    for (i, var) in vars.iter().enumerate() {
        // Form the search key.
        let key = format!("{}{}{}", &prefix, &var, &suffix);
        let val = match env::var(&vars[i]) {
            Ok(v) => v,
            Err(_) => {
                println_err!("error: env var not found: {}", var);
                process::exit(1)
            },
        };
        keys.push(key);
        vals.push(val);
    }
    (keys, vals)
}

fn main() {
    let prefix = match envvar("PREFIX") {
        Ok(v) => v,
        Err(_) => String::from("%"),
    };
    let suffix = match envvar("SUFFIX") {
        Ok(v) => v,
        Err(_) => String::from("%"),
    };

    // Gather list of env vars.
    let vars = get_vars();
    // Build search keys and values.
    let (keys, vals) = get_kvs(vars, &prefix, &suffix);

    let reader = io::stdin();
    let writer = io::stdout();

    // Build an in-memory buffer so users can replace files in-place.
    // i.e. envsub < foo.txt > foo.txt
    let mut buffer = String::new();
    let _ = reader.lock().read_to_string(&mut buffer);
    let replaced = replace(&keys, &vals, &buffer);
    let _ = writer.lock().write(replaced.as_bytes());
}

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

    #[test]
    fn test_replace_without_replacements() {
        let string = String::from("just\na\nnormal\nstring\n");
        let keys = vec!["x".to_owned()];
        let vals = vec!["xyz".to_owned()];
        let output = replace(&keys, &vals, &string);
        assert_eq!(string, output);
    }

    #[test]
    fn test_replace_with_one_variable() {
        let string = "%VAR%".to_owned();
        let replacement = "moo".to_owned();
        let keys = vec!["%VAR%".to_owned()];
        let vals = vec![replacement.clone()];
        let output = replace(&keys, &vals, &string);
        assert_eq!(output, replacement.clone());
    }

    #[test]
    fn test_replace_with_multiple_variables() {
        let string = "%VAR1%, %VAR2%, %VAR3%".to_owned();
        let keys = vec!["%VAR1%".to_owned(), "%VAR2%".to_owned(), "%VAR3%".to_owned()];
        let vals = vec!["foo".to_owned(), "bar".to_owned(), "baz".to_owned()];
        let output = replace(&keys, &vals, &string);
        assert_eq!(output, vals.join(", "));
    }

    #[test]
    fn test_replace_with_single_variable_multiple_times() {
        let string = "%VAR1%, %VAR1%, %VAR1%".to_owned();
        let keys = vec!["%VAR1%".to_owned(), "%VAR2%".to_owned(), "%VAR3%".to_owned()];
        let vals = vec!["foo".to_owned(), "bar".to_owned(), "baz".to_owned()];
        let output = replace(&keys, &vals, &string);
        assert_eq!(output, "foo, foo, foo".to_owned());
    }

    #[test]
    fn test_multiline_replacement() {
        let string = "%VAR3%\n%VAR1\n".to_owned();
        let keys = vec!["%VAR1%".to_owned(), "%VAR2%".to_owned(), "%VAR3%".to_owned()];
        let vals = vec!["foo".to_owned(), "bar".to_owned(), "baz".to_owned()];
        let output = replace(&keys, &vals, &string);
        assert_eq!(output, "baz\n%VAR1\n".to_owned());
    }
}
<|endoftext|>


<commit_before>#![feature(if_let)]

extern crate regex;
extern crate getopts;
extern crate lines;

use std::os;
use std::io::{File, IoResult, BufferedReader};
use std::io::stdio::{stderr, stdout};

use getopts::{getopts, optopt, optflag, usage};

use regex::Regex;
use lines::linemapper;

use buf::MemWriter;

mod buf;

struct Config {
    line_regex: Regex,
    line_numbers: bool,
    args: Vec<String>,
}

impl Config {
    fn from_cmdline(args: Vec<String>) -> Result<Config, String> {
        let opts = [
            optflag("h", "help", "print this help screen"),
            optflag("n", "line-numbers", "print line numbers"),
            optopt("e", "entry", "identify regular lines", "regex"),
            ];
        let matches = match getopts(args.tail(), opts) {
            Ok(m) => m,
            Err(f) => {
                return Err(format!("{}", f));
            }
        };
        if matches.opt_present("h") || matches.free.len() < 1 {
            return Err(usage("usage: [OPTIONS] <logfile> [<logfile> ...]", opts));
        }
        let re = {
            let s = matches.opt_str("e").unwrap_or_else(|| {
                String::from_str(r"^\d{4}-\d{2}-\d{2}\s+")
            });
            match Regex::new(s.as_slice()) {
                Ok(r) => r,
                Err(e) => {
                    return Err(format!("{}", e));
                }
            }
        };
        Ok(Config {
            line_regex: re,
            line_numbers: matches.opt_present("n"),
            args: matches.free,
        })
    }
}

// --------------------------------------------------------------------

fn main() {
    let cfg = match Config::from_cmdline(os::args()) {
        Ok(cfg) => cfg,
        Err(e) => {
            let _ = stderr().write_line(e.as_slice());
            std::os::set_exit_status(1);
            return;
        }
    };
    for arg in cfg.args.iter() {
        if let Err(e) = process_arg(&cfg, arg.as_slice()) {
            let _ = write!(stderr(), "{}", e);
            std::os::set_exit_status(1);
            return;
        }
    }
}

// ~ XXX use linereader api and return the io error instead of
// handling it here in this function
fn process_arg(cfg: &Config, arg: &str) -> IoResult<()> {
    let f = try!(File::open(&Path::new(arg)));
    let r = BufferedReader::new(f);

    let mut block = MemWriter::with_capacity(1024);
    let mut stdout = stdout().unwrap();

    let mut line_no = 0u;
    let mut collecting_block = false;
    linemapper::map_lines(r, |line| {
        line_no += 1;
        if cfg.line_regex.is_match(String::from_utf8_lossy(line).as_slice()) {
            if collecting_block {
                // ~ XXX only if we have already produced some output before
                let _ = block.write_char('\n');

                // ~ abort as soon as possible if the write didn't
                // succeed e.g. broken pipe
                if let Err(e) = stdout.write(block.get_ref()) {
                    let _ = write!(stderr(), "{}", e);
                    std::os::set_exit_status(1);
                    return false;
                }
                collecting_block = false;
            }
            block.clear();
        } else {
            collecting_block = true;
        }

        // XXX prevent the buffer from becoming too large by flushing
        // it out as of a certain size
        if cfg.line_numbers {
            let _ = write!(block, "{:7u}  ", line_no);
        }
        let _ = block.write(line);
        true
    })
}
<commit_msg>Optionally produce the filename in the output<commit_after>#![feature(if_let)]

extern crate regex;
extern crate getopts;
extern crate lines;

use std::os;
use std::io::{File, IoResult, BufferedReader};
use std::io::stdio::{stderr, stdout};

use getopts::{getopts, optopt, optflag, usage};

use regex::Regex;
use lines::linemapper;

use buf::MemWriter;

mod buf;

struct Config {
    print_line_numbers: bool,
    print_filenames: bool,
    line_regex: Regex,
    args: Vec<String>,
}

impl Config {
    fn from_cmdline(args: Vec<String>) -> Result<Config, String> {
        let opts = [
            optflag("h", "help", "print this help screen"),
            optflag("n", "line-numbers", "print line numbers"),
            optflag("f", "filename", "print file name"),
            optopt("e", "entry", "identify regular lines", "regex"),
            ];
        let matches = match getopts(args.tail(), opts) {
            Ok(m) => m,
            Err(f) => {
                return Err(format!("{}", f));
            }
        };
        if matches.opt_present("h") || matches.free.len() < 1 {
            return Err(usage("usage: [OPTIONS] <logfile> [<logfile> ...]", opts));
        }
        let re = {
            let s = matches.opt_str("e").unwrap_or_else(|| {
                String::from_str(r"^\d{4}-\d{2}-\d{2}\s+")
            });
            match Regex::new(s.as_slice()) {
                Ok(r) => r,
                Err(e) => {
                    return Err(format!("{}", e));
                }
            }
        };
        Ok(Config {
            print_line_numbers: matches.opt_present("n"),
            print_filenames: matches.opt_present("f"),
            line_regex: re,
            args: matches.free,
        })
    }
}

// --------------------------------------------------------------------

fn main() {
    let cfg = match Config::from_cmdline(os::args()) {
        Ok(cfg) => cfg,
        Err(e) => {
            let _ = stderr().write_line(e.as_slice());
            std::os::set_exit_status(1);
            return;
        }
    };
    for arg in cfg.args.iter() {
        if let Err(e) = process_arg(&cfg, arg.as_slice()) {
            let _ = write!(stderr(), "{}", e);
            std::os::set_exit_status(1);
            return;
        }
    }
}

// ~ XXX use linereader api and return the io error instead of
// handling it here in this function
fn process_arg(cfg: &Config, arg: &str) -> IoResult<()> {
    let f = try!(File::open(&Path::new(arg)));
    let r = BufferedReader::new(f);

    let mut block = MemWriter::with_capacity(1024);
    let mut stdout = stdout().unwrap();

    let mut line_no = 0u;
    let mut collecting_block = false;
    linemapper::map_lines(r, |line| {
        line_no += 1;
        if cfg.line_regex.is_match(String::from_utf8_lossy(line).as_slice()) {
            if collecting_block {
                // ~ XXX only if we have already produced some output before
                let _ = block.write_char('\n');

                // ~ abort as soon as possible if the write didn't
                // succeed e.g. broken pipe
                if let Err(e) = stdout.write(block.get_ref()) {
                    let _ = write!(stderr(), "{}", e);
                    std::os::set_exit_status(1);
                    return false;
                }
                collecting_block = false;
            }
            block.clear();
        } else {
            collecting_block = true;
        }

        // XXX prevent the buffer from becoming too large by flushing
        // it out as of a certain size

        if cfg.print_filenames {
            let _ = block.write_str(arg);
            let _ = block.write_str("  ");
        }
        if cfg.print_line_numbers {
            let _ = write!(block, "{:7u}  ", line_no);
        }
        let _ = block.write(line);
        true
    })
}
<|endoftext|>


<commit_before>extern crate rustbox;
extern crate rustc_serialize;
extern crate gag;
extern crate toml;
extern crate itertools;
extern crate docopt;
#[macro_use] extern crate rex_utils;

use std::path::Path;
use std::error::Error;
use std::io;
use std::io::Write;
use std::process;
use docopt::Docopt;
use rustbox::{RustBox, Event, InputMode, InitOptions};
use rustbox::keyboard::Key;
use gag::Hold;

mod config;
mod ui;

use ui::view::HexEdit;
use config::Config;

static USAGE: &'static str = "
Usage: rex [options] [-C CONF... FILE]
       rex --help
       rex --help-config

Options:
    -h, --help                  Show this help message
    -c FILE, --config FILE      Use FILE as the config file
    -C CONF                     Set a configuration option, for example: -C line_width=16
";

#[derive(RustcDecodable, Debug)]
#[allow(non_snake_case)]
struct Args {
    flag_help_config: bool,
    arg_FILE: Option<String>,
    flag_config: Option<String>,
    flag_C: Vec<String>,
}

fn exit_err<E: Error>(msg: &str, error: E) -> ! {
    write!(&mut io::stderr(), "{}: {}", msg, error).unwrap();
    process::exit(1);
}

fn main() {
    let args: Args = Docopt::new(USAGE).and_then(
        |d| d.decode()).unwrap_or_else(
        |e| e.exit());

    if args.flag_help_config {
        println!("{}", Config::get_config_usage());
        process::exit(0);
    }

    let config_res = if let Some(config_filename) = args.flag_config {
        Config::from_file(config_filename)
    } else {
        Config::open_default()
    };
    let mut config = config_res.unwrap_or_else(
        |e| exit_err("Couldn't open config file", e)
    );

    for config_line in &args.flag_C {
        config.set_from_string(config_line).unwrap_or_else(
            |e| exit_err("Couldn't parse command line config option", e)
        );
    }

    let mut edit = HexEdit::new(config);

    if let Some(ref filename) = args.arg_FILE {
        edit.open(&Path::new(filename));
    }

    let hold = (Hold::stdout().unwrap(), Hold::stderr().unwrap());

    let rb = RustBox::init(InitOptions{
        buffer_stderr: false,
        input_mode: InputMode::Esc,
    }).unwrap();

    edit.resize(rb.width() as i32, rb.height() as i32);
    edit.draw(&rb);
    rb.present();
    loop {
        let event = rb.poll_event(false).unwrap();
        match event {
            // This case is here, since we want to have a 'way ouy' till we fixed bugs
            Event::KeyEvent(Some(Key::Ctrl('q'))) => break,
            Event::KeyEvent(Some(key)) => edit.input(key),
            Event::ResizeEvent(w, h) => { edit.resize(w, h) }
            _ => ()
        };
        rb.clear();
        edit.draw(&rb);
        rb.present();
    }
    drop(rb);
    drop(hold);
}
<commit_msg>Improve help message<commit_after>extern crate rustbox;
extern crate rustc_serialize;
extern crate gag;
extern crate toml;
extern crate itertools;
extern crate docopt;
#[macro_use] extern crate rex_utils;

use std::path::Path;
use std::error::Error;
use std::io;
use std::io::Write;
use std::process;
use docopt::Docopt;
use rustbox::{RustBox, Event, InputMode, InitOptions};
use rustbox::keyboard::Key;
use gag::Hold;

mod config;
mod ui;

use ui::view::HexEdit;
use config::Config;

static USAGE: &'static str = "
Usage: rex [options] [-C CONF... FILE]
       rex --help

Options:
    -h, --help                  Show this help message
    -c FILE, --config FILE      Use FILE as the config file
    -C CONF                     Set a configuration option, for example: -C line_width=16
";

#[derive(RustcDecodable, Debug)]
#[allow(non_snake_case)]
struct Args {
    flag_help: bool,
    arg_FILE: Option<String>,
    flag_config: Option<String>,
    flag_C: Vec<String>,
}

fn exit_err<E: Error>(msg: &str, error: E) -> ! {
    write!(&mut io::stderr(), "{}: {}", msg, error).unwrap();
    process::exit(1);
}

fn main() {
    let args: Args = Docopt::new(USAGE).and_then(
        |d| d.help(false).decode()).unwrap_or_else(
        |e| e.exit());

    if args.flag_help {
        println!("{}", USAGE.trim());
        println!("");
        println!("{}", config::Config::get_config_usage().trim());
        process::exit(0);
    }

    let config_res = if let Some(config_filename) = args.flag_config {
        Config::from_file(config_filename)
    } else {
        Config::open_default()
    };
    let mut config = config_res.unwrap_or_else(
        |e| exit_err("Couldn't open config file", e)
    );

    for config_line in &args.flag_C {
        config.set_from_string(config_line).unwrap_or_else(
            |e| exit_err("Couldn't parse command line config option", e)
        );
    }

    let mut edit = HexEdit::new(config);

    if let Some(ref filename) = args.arg_FILE {
        edit.open(&Path::new(filename));
    }

    let hold = (Hold::stdout().unwrap(), Hold::stderr().unwrap());

    let rb = RustBox::init(InitOptions{
        buffer_stderr: false,
        input_mode: InputMode::Esc,
    }).unwrap();

    edit.resize(rb.width() as i32, rb.height() as i32);
    edit.draw(&rb);
    rb.present();
    loop {
        let event = rb.poll_event(false).unwrap();
        match event {
            // This case is here, since we want to have a 'way ouy' till we fixed bugs
            Event::KeyEvent(Some(Key::Ctrl('q'))) => break,
            Event::KeyEvent(Some(key)) => edit.input(key),
            Event::ResizeEvent(w, h) => { edit.resize(w, h) }
            _ => ()
        };
        rb.clear();
        edit.draw(&rb);
        rb.present();
    }
    drop(rb);
    drop(hold);
}
<|endoftext|>


<commit_before> // Copyright 2015 Robert Helmer <rhelmer@rhelmer.org>. See the LICENSE
// file at the top-level directory of this distribution.

/**
  * Symbolapi - a microservice which accepts lists of symbol+addresses, and returns
  * a list of symbolicated functions.
  *
  * The goal is to take an HTTP JSON request such as:
  * {"stacks":[
  *   [
  *     [0,11723767],
  *     [1, 65802]
  *    ]
  *  ],
  *  "memoryMap":[
  *    ["xul.pdb","44E4EC8C2F41492B9369D6B9A059577C2"],
  *    ["wntdll.pdb","D74F79EB1F8D4A45ABCD2F476CCABACC2"]
  *  ],
  *  "version":4
  * }
  *
  * The symbolapi service then downloads the corresponding symbol files (e.g. from S3), and returns
  * the function names for the corresponding addresses (in the "stacks" array) and returns JSON
  * such as:
  * {"symbolicatedStacks": [
  *   [
  *     "XREMain::XRE_mainRun() (in xul.pdb)",
  *     "KiUserCallbackDispatcher (in wntdll.pdb)"]
  *   ],
  *   "knownModules": [true, true]
  * }
  */

extern crate breakpad_symbols;
extern crate flate2;
extern crate hyper;
#[macro_use]
extern crate log;
extern crate log4rs;
extern crate rustc_serialize;
extern crate toml;

use std::collections::HashMap;
use std::env;
use std::fs::{File, create_dir_all};
use std::io::{Read, Write};
use std::path::{PathBuf};
use std::thread;

use breakpad_symbols::{Symbolizer, SimpleSymbolSupplier};
use flate2::read::GzDecoder;
use hyper::Client;
use hyper::header::{ContentEncoding, Encoding};
use hyper::server::{Server, Request, Response};
use hyper::status::StatusCode;
use rustc_serialize::json;

/**
  * Incoming JSON request format.
  */
// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcDecodable)]
pub struct SymbolRequest {
    memoryMap: Vec<(String,String)>,
    // index, offset
    stacks: Vec<Vec<(i8,u64)>>,
    version: u8,
}

/**
  * Outgoing JSON response format.
  */
// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcEncodable)]
pub struct SymbolResponse {
    symbolicatedStacks: Vec<Vec<String>>,
    knownModules: Vec<bool>,
}

fn main() {
    log4rs::init_file("config/log.toml", Default::default()).unwrap();

    let default_port = 5000;
    let port;
    match env::var("PORT") {
        Ok(val) => port = val,
        Err(_) => {
            println!("$PORT unset, using default {}", default_port);
            port = format!("{}", default_port);
        }
    }

    let address = &*format!("0.0.0.0:{}", port);

    info!("Listening on {}", address);
    Server::http(address).unwrap().handle(server).unwrap();
}

/**
  * Receives single HTTP requests and demuxes to symbols file fetches from S3 bucket.
  */
fn server(mut req: Request, mut res: Response) {
    info!("incoming connection from {}", req.remote_addr);

    match req.method {
        hyper::Post => {
            let mut res = res.start().unwrap();

            let mut buffer = String::new();
            req.read_to_string(&mut buffer).unwrap();
            debug!("raw POST: {:?}", &buffer);

            let decoded: SymbolRequest = json::decode(&buffer).unwrap();

            debug!("decoded memoryMap: {:?}", decoded.memoryMap);
            debug!("decoded stacks: {:?}", decoded.stacks);
            debug!("decoded version: {}", decoded.version);

            let symbol_url = get_config("symbol_urls.public");

            // stacks come in as an array, turn into hashmap
            let mut stack_map: HashMap<i8, Vec<u64>> = HashMap::new();

            debug!("decoded.stacks: {:?}", decoded.stacks);

            let stacks = decoded.stacks[0].clone();
            for stack in &stacks {
                let (index, offset) = *stack;

                let mut offsets = vec!();
                if stack_map.contains_key(&index) {
                    offsets = stack_map.get(&index).unwrap().clone();
                }
                offsets.push(offset);
                stack_map.insert(index, offsets);
            }

            debug!("stack_map: {:?}", stack_map);

            // FIXME limit the number of possible threads
            // TODO maybe push these into a queue and have a thread pool service the queue?
            let symbol_response = client(symbol_url, decoded.memoryMap, stack_map.clone());
            res.write_all(symbol_response.as_bytes()).unwrap();

            res.end().unwrap();
        },
        hyper::Get => {
            let mut res = res.start().unwrap();
            res.write_all("symbolapi, see github.com/rhelmer/symbolapi".as_bytes()).unwrap();
        },
        _ => { *res.status_mut() = StatusCode::MethodNotAllowed },
    }

    debug!("finished serving request");
}

/**
  * Creates multiple client connections and aggregates result.
  */
fn client(url: String, memory_map: Vec<(String,String)>, stack_map: HashMap<i8, Vec<u64>>) -> String {
    let mut handles = vec![];
    let mut counter: i8 = 0;
    for (debug_file, debug_id) in memory_map {
        let debug_file_name = debug_file.clone();
        let pdb = debug_file.find(".pdb").unwrap();
        let (symbol_name, _) = debug_file.split_at(pdb);
        let symbol_file = format!("{}.sym", symbol_name);
        let this_url = format!("{}/{}/{}/{}", url, debug_file, debug_id, symbol_file);

        let mut symbol_path = PathBuf::new();
        // TODO get from config
        symbol_path.push("testdata/symbols");

        let mut full_symbol_path = symbol_path.clone();
        full_symbol_path.push(&debug_file);
        full_symbol_path.push(&debug_id);
        full_symbol_path.push(&symbol_file);

        let stack_map_copy = stack_map.clone();

        create_dir_all(&full_symbol_path.parent().unwrap()).unwrap();

        let supplier = SimpleSymbolSupplier::new(vec!(symbol_path.clone()));

        // TODO most of the time is probably spent waiting on I/O, maybe async would be more appropriate?
        // TODO decide min/max possible threads, possibly based on number of cores?
        handles.push(thread::spawn(move || {

            let symbolizer = Symbolizer::new(supplier);
            // FIXME use Arc<Mutex<File>> to prevent concurrent writes
            // TODO only write contents if server version newer
            if !&full_symbol_path.exists() {

                let c = Client::new();
                let mut res = c.get(&this_url).send().unwrap();
                let is_gzipped = match res.headers.get::<ContentEncoding>() {
                    Some(x) => x.contains(&Encoding::Gzip),
                    None => false,
                };

                let mut body = String::new();
                if is_gzipped {
                    let mut raw_body = vec!();
                    res.read_to_end(&mut raw_body).unwrap();
                    let mut d = match GzDecoder::new(&raw_body[..]) {
                        Ok(x) => x,
                        Err(x) => panic!("cannot gunzip {:?}, {:?}", raw_body, x)
                    };
                    d.read_to_string(&mut body).unwrap();
                } else {
                    res.read_to_string(&mut body).unwrap();
                }

                let mut f = File::create(&full_symbol_path).unwrap();
                f.write_all(body.as_bytes()).unwrap();
            }

            debug!("symbol_path: {:?}", &symbol_path);
            // FIXME this is having problems with CRLF

            let mut symbols = vec!();
            let mut known_module = true;
            for stacks in stack_map_copy.get(&counter) {
                for address in stacks {
                    debug!("attempt to symbolicate: {} for: {:?}", *address, &symbol_path);
                    match symbolizer.get_symbol_at_address(&debug_file_name, &debug_id, *address) {
                        Some(x) => symbols.push(x),
                        // return the address rather than function name if symbol not found
                        None => {
                            symbols.push(format!("0x{:x}", address));
                            known_module = false;
                        },
                    }
                }
            }
            debug!("debug_file_name: {:?}, symbols: {:?}", debug_file_name, symbols);

            (debug_file_name, symbols, known_module)
        }));

        counter += 1;
    }

    let mut result = SymbolResponse {
        symbolicatedStacks: vec!(),
        knownModules: vec!(),
    };

    let mut symbolicated_stacks = vec!();

    // pass anything with index -1 through
    for stacks in stack_map.get(&-1) {
        for address in stacks {
            symbolicated_stacks.push(
                format!("0x{:x}", address)
            );
        }
    }

    for handle in handles {
        let (debug_file_name, symbols, known_module) = handle.join().unwrap();

        for symbol in symbols {
            symbolicated_stacks.push(
                format!("{} (in {})", symbol, debug_file_name)
            );
        }
        result.knownModules.push(known_module);
    }

    // the required result format requires this to be a vec-of-vecs
    result.symbolicatedStacks.push(symbolicated_stacks);

    json::encode(&result).unwrap()
}

/**
  * Returns individual values from the configuration file.
  */
fn get_config(value_name: &str) -> String {
    // TODO move to actual file, static str for the moment
    let toml: &'static str = r#"
        [symbol_urls]
        public = "https://s3-us-west-2.amazonaws.com/org.mozilla.crash-stats.symbols-public/v1"
    "#;
    // TODO support multiple URLs
    let value: toml::Value = toml.parse().unwrap();

    value.lookup(value_name).unwrap().as_str().unwrap().to_string()
}
<commit_msg>split out stacks parsing to separate function<commit_after> // Copyright 2015 Robert Helmer <rhelmer@rhelmer.org>. See the LICENSE
// file at the top-level directory of this distribution.

/**
  * Symbolapi - a microservice which accepts lists of symbol+addresses, and returns
  * a list of symbolicated functions.
  *
  * The goal is to take an HTTP JSON request such as:
  * {"stacks":[
  *   [
  *     [0,11723767],
  *     [1, 65802]
  *    ]
  *  ],
  *  "memoryMap":[
  *    ["xul.pdb","44E4EC8C2F41492B9369D6B9A059577C2"],
  *    ["wntdll.pdb","D74F79EB1F8D4A45ABCD2F476CCABACC2"]
  *  ],
  *  "version":4
  * }
  *
  * The symbolapi service then downloads the corresponding symbol files (e.g. from S3), and returns
  * the function names for the corresponding addresses (in the "stacks" array) and returns JSON
  * such as:
  * {"symbolicatedStacks": [
  *   [
  *     "XREMain::XRE_mainRun() (in xul.pdb)",
  *     "KiUserCallbackDispatcher (in wntdll.pdb)"]
  *   ],
  *   "knownModules": [true, true]
  * }
  */

extern crate breakpad_symbols;
extern crate flate2;
extern crate hyper;
#[macro_use]
extern crate log;
extern crate log4rs;
extern crate rustc_serialize;
extern crate toml;

use std::collections::HashMap;
use std::env;
use std::fs::{File, create_dir_all};
use std::io::{Read, Write};
use std::path::{PathBuf};
use std::thread;

use breakpad_symbols::{Symbolizer, SimpleSymbolSupplier};
use flate2::read::GzDecoder;
use hyper::Client;
use hyper::header::{ContentEncoding, Encoding};
use hyper::server::{Server, Request, Response};
use hyper::status::StatusCode;
use rustc_serialize::json;

/**
  * Incoming JSON request format.
  */
// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcDecodable)]
pub struct SymbolRequest {
    memoryMap: Vec<(String,String)>,
    // index, offset
    stacks: Vec<Vec<(i8,u64)>>,
    version: u8,
}

/**
  * Outgoing JSON response format.
  */
// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcEncodable)]
pub struct SymbolResponse {
    symbolicatedStacks: Vec<Vec<String>>,
    knownModules: Vec<bool>,
}

fn main() {
    log4rs::init_file("config/log.toml", Default::default()).unwrap();

    let default_port = 5000;
    let port;
    match env::var("PORT") {
        Ok(val) => port = val,
        Err(_) => {
            println!("$PORT unset, using default {}", default_port);
            port = format!("{}", default_port);
        }
    }

    let address = &*format!("0.0.0.0:{}", port);

    info!("Listening on {}", address);
    Server::http(address).unwrap().handle(server).unwrap();
}

fn stacks_to_stack_map(decoded_stacks: Vec<Vec<(i8,u64)>>) -> HashMap<i8, Vec<u64>> {
    debug!("decoded.stacks: {:?}", decoded_stacks);

    let mut stack_map: HashMap<i8, Vec<u64>> = HashMap::new();
    for stack in &decoded_stacks[0] {
        let (index, offset) = *stack;

        let mut offsets = vec!();
        if stack_map.contains_key(&index) {
            offsets = stack_map.get(&index).unwrap().clone();
        }
        offsets.push(offset);
        &stack_map.insert(index, offsets);
    }

    debug!("stack_map: {:?}", stack_map);
    stack_map
}

/**
  * Receives single HTTP requests and demuxes to symbols file fetches from S3 bucket.
  */
fn server(mut req: Request, mut res: Response) {
    info!("incoming connection from {}", req.remote_addr);

    match req.method {
        hyper::Post => {
            let mut res = res.start().unwrap();

            let mut buffer = String::new();
            req.read_to_string(&mut buffer).unwrap();
            debug!("raw POST: {:?}", &buffer);

            let decoded: SymbolRequest = json::decode(&buffer).unwrap();

            debug!("decoded memoryMap: {:?}", decoded.memoryMap);
            debug!("decoded stacks: {:?}", decoded.stacks);
            debug!("decoded version: {}", decoded.version);

            let symbol_url = get_config("symbol_urls.public");

            // stacks come in as an array, turn into hashmap
            let mut stack_map = stacks_to_stack_map(decoded.stacks);

            // FIXME limit the number of possible threads
            // TODO maybe push these into a queue and have a thread pool service the queue?
            let symbol_response = client(symbol_url, decoded.memoryMap, stack_map.clone());
            res.write_all(symbol_response.as_bytes()).unwrap();

            res.end().unwrap();
        },
        hyper::Get => {
            let mut res = res.start().unwrap();
            res.write_all("symbolapi, see github.com/rhelmer/symbolapi".as_bytes()).unwrap();
        },
        _ => { *res.status_mut() = StatusCode::MethodNotAllowed },
    }

    debug!("finished serving request");
}

/**
  * Creates multiple client connections and aggregates result.
  */
fn client(url: String, memory_map: Vec<(String,String)>, stack_map: HashMap<i8, Vec<u64>>) -> String {
    let mut handles = vec![];
    let mut counter: i8 = 0;
    for (debug_file, debug_id) in memory_map {
        let debug_file_name = debug_file.clone();
        let pdb = debug_file.find(".pdb").unwrap();
        let (symbol_name, _) = debug_file.split_at(pdb);
        let symbol_file = format!("{}.sym", symbol_name);
        let this_url = format!("{}/{}/{}/{}", url, debug_file, debug_id, symbol_file);

        let mut symbol_path = PathBuf::new();
        // TODO get from config
        symbol_path.push("testdata/symbols");

        let mut full_symbol_path = symbol_path.clone();
        full_symbol_path.push(&debug_file);
        full_symbol_path.push(&debug_id);
        full_symbol_path.push(&symbol_file);

        let stack_map_copy = stack_map.clone();

        create_dir_all(&full_symbol_path.parent().unwrap()).unwrap();

        let supplier = SimpleSymbolSupplier::new(vec!(symbol_path.clone()));

        // TODO most of the time is probably spent waiting on I/O, maybe async would be more appropriate?
        // TODO decide min/max possible threads, possibly based on number of cores?
        handles.push(thread::spawn(move || {

            let symbolizer = Symbolizer::new(supplier);
            // FIXME use Arc<Mutex<File>> to prevent concurrent writes
            // TODO only write contents if server version newer
            if !&full_symbol_path.exists() {

                let c = Client::new();
                let mut res = c.get(&this_url).send().unwrap();
                let is_gzipped = match res.headers.get::<ContentEncoding>() {
                    Some(x) => x.contains(&Encoding::Gzip),
                    None => false,
                };

                let mut body = String::new();
                if is_gzipped {
                    let mut raw_body = vec!();
                    res.read_to_end(&mut raw_body).unwrap();
                    let mut d = match GzDecoder::new(&raw_body[..]) {
                        Ok(x) => x,
                        Err(x) => panic!("cannot gunzip {:?}, {:?}", raw_body, x)
                    };
                    d.read_to_string(&mut body).unwrap();
                } else {
                    res.read_to_string(&mut body).unwrap();
                }

                let mut f = File::create(&full_symbol_path).unwrap();
                f.write_all(body.as_bytes()).unwrap();
            }

            debug!("symbol_path: {:?}", &symbol_path);
            // FIXME this is having problems with CRLF

            let mut symbols = vec!();
            let mut known_module = true;
            for stacks in stack_map_copy.get(&counter) {
                for address in stacks {
                    debug!("attempt to symbolicate: {} for: {:?}", *address, &symbol_path);
                    match symbolizer.get_symbol_at_address(&debug_file_name, &debug_id, *address) {
                        Some(x) => symbols.push(x),
                        // return the address rather than function name if symbol not found
                        None => {
                            symbols.push(format!("0x{:x}", address));
                            known_module = false;
                        },
                    }
                }
            }
            debug!("debug_file_name: {:?}, symbols: {:?}", debug_file_name, symbols);

            (debug_file_name, symbols, known_module)
        }));

        counter += 1;
    }

    let mut result = SymbolResponse {
        symbolicatedStacks: vec!(),
        knownModules: vec!(),
    };

    let mut symbolicated_stacks = vec!();

    // pass anything with index -1 through
    for stacks in stack_map.get(&-1) {
        for address in stacks {
            symbolicated_stacks.push(
                format!("0x{:x}", address)
            );
        }
    }

    for handle in handles {
        let (debug_file_name, symbols, known_module) = handle.join().unwrap();

        for symbol in symbols {
            symbolicated_stacks.push(
                format!("{} (in {})", symbol, debug_file_name)
            );
        }
        result.knownModules.push(known_module);
    }

    // the required result format requires this to be a vec-of-vecs
    result.symbolicatedStacks.push(symbolicated_stacks);

    json::encode(&result).unwrap()
}

/**
  * Returns individual values from the configuration file.
  */
fn get_config(value_name: &str) -> String {
    // TODO move to actual file, static str for the moment
    let toml: &'static str = r#"
        [symbol_urls]
        public = "https://s3-us-west-2.amazonaws.com/org.mozilla.crash-stats.symbols-public/v1"
    "#;
    // TODO support multiple URLs
    let value: toml::Value = toml.parse().unwrap();

    value.lookup(value_name).unwrap().as_str().unwrap().to_string()
}
<|endoftext|>


<commit_before>struct AddressSpace {
    data: [u8; 0xFFFF],
}

impl AddressSpace {

    fn new() -> AddressSpace {
        AddressSpace { data: [0; 0xFFFF] }
    }

    fn read(&self, addr: u16) -> u8 {
        self.data[addr as usize]
    }

    fn read_u16(&self, addr: u16) -> u16 {
        self.data[addr as usize] as u16 | ((self.data[addr as usize + 1] as u16) << 8)
    }

    fn read_slice(&self, addr: u16, bytes: u16) -> &[u8] {
        self.data.slice(addr as usize, (addr + bytes) as usize)
    }

    fn write(&mut self, addr: u16, data: u8) {
        self.data[addr as usize] = data;
    }

    fn write_u16(&mut self, addr: u16, data: u16) {
        let lo = (data & 0xFF) as u8;
        let hi = ((data & 0xFF00) >> 8) as u8;
        self.data[addr as usize] = lo;
        self.data[addr as usize + 1] = hi;
    }

}

struct Instr {
    opcode: u8,
    data: Vec<u8>,
    cycles: u32,
}

impl Instr {

    fn parse(reg: &mut RegData, rom: &AddressSpace) -> Instr {
        let opcode = rom.read(reg.advance_pc());
        Instr {
            opcode: opcode,
            data: Vec::new(),
            cycles: 4,
        }
    }

    fn opcode(&self) -> u8 {
        self.opcode
    }

    fn cycles(&self) -> u32 {
        self.cycles
    }

    fn param(&self, i: usize) -> u8 {
        self.data[i]
    }

    fn param_u16(&self, i: usize) -> u16 {
        (self.data[i] as u16) | ((self.data[i + 1] as u16) << 8)
    }

}

enum RegFlag {
    Zero,
    Subtract,
    HalfCarry,
    Carry,
}

#[derive(Copy)]
enum Register {
    A,
    B,
    C,
    D,
    E,
    F,
    H,
    L,
    AF,
    BC,
    DE,
    HL,
    SP,
    PC,
    Flag,
}

struct RegData {
    a: u8,
    b: u8,
    c: u8,
    d: u8,
    e: u8,
    f: u8,
    h: u8,
    l: u8,
    sp: u16,
    pc: u16,
    flag: u8,
}

impl RegData {

    fn new() -> RegData {
        RegData {
            a: 0,
            b: 0,
            c: 0,
            d: 0,
            e: 0,
            f: 0,
            h: 0,
            l: 0,
            sp: 0xFFFE,
            pc: 0x100,
            flag: 0,
        }
    }

    fn read(&self, reg: Register) -> u8 {
        match reg {
            Register::A => self.a,
            Register::B => self.b,
            Register::C => self.c,
            Register::D => self.d,
            Register::E => self.e,
            Register::F => self.f,
            Register::H => self.h,
            Register::L => self.l,
            Register::Flag => self.flag,
            _ => panic!("Register not available for 8-bit read"),
        }
    }

    fn read_u16(&self, reg: Register) -> u16 {
        match reg {
            Register::AF => (self.a as u16) << 8 | self.f as u16,
            Register::BC => (self.b as u16) << 8 | self.c as u16,
            Register::DE => (self.d as u16) << 8 | self.e as u16,
            Register::HL => (self.h as u16) << 8 | self.l as u16,
            Register::SP => self.sp,
            Register::PC => self.pc,
            _ => panic!("Register not available for 16-bit read"),
        }
    }

    fn write(&mut self, reg: Register, data: u8) {
        match reg {
            Register::A => self.a = data,
            Register::B => self.b = data,
            Register::C => self.c = data,
            Register::D => self.d = data,
            Register::E => self.e = data,
            Register::F => self.f = data,
            Register::H => self.h = data,
            Register::L => self.l = data,
            _ => panic!("Register not available for 8-bit write"),
        }
    }

    fn write_u16(&mut self, reg: Register, data: u16) {
        let (hi, lo) = ((data & 0xFF00 >> 8) as u8, (data & 0xFF) as u8);
        match reg {
            Register::AF => {
                self.a = hi;
                self.f = lo;
            },
            Register::BC => {
                self.b = hi;
                self.c = lo;
            },
            Register::DE => {
                self.d = hi;
                self.c = lo;
            },
            Register::HL => {
                self.h = hi;
                self.l = lo;
            },
            Register::SP => self.sp = data,
            Register::PC => self.pc = data,
            _ => panic!("Register not available for 16-bit write"),
        }
    }

    fn copy(&mut self, dst: Register, src: Register) {
        let data = self.read(src);
        self.write(dst, data);
    }

    fn copy_u16(&mut self, dst: Register, src: Register) {
        let data = self.read_u16(src);
        self.write_u16(dst, data);
    }

    fn set_flag(&mut self, flag: RegFlag, on: bool) {
        let bit = match flag {
            RegFlag::Zero => 0x80,
            RegFlag::Subtract => 0x40,
            RegFlag::HalfCarry => 0x20,
            RegFlag::Carry => 0x10,
        };
        if (on) {
            self.flag |= bit;
        } else {
            self.flag &= bit ^ 0xFF;
        }
    }

    fn advance_pc(&mut self) -> u16 {
        let pc = self.pc;
        self.pc += 1;
        pc
    }

}

static GB_FREQUENCY: u32 = 4194304;

struct Cpu {
    reg: RegData,
    ram: AddressSpace,
    freq: u32,
    clock: u64,
    cycle_block: u32,
}

impl Cpu {

    fn new() -> Cpu {
        Cpu::new_with_freq(GB_FREQUENCY)
    }

    fn new_with_freq(freq: u32) -> Cpu {
        Cpu {
            reg: RegData::new(),
            ram: AddressSpace::new(),
            freq: freq,
            clock: 0,
            cycle_block: 0,
        }
    }

    fn do_cycle(&mut self) {
        self.clock += 1;
        if self.cycle_block > 0 {
            self.cycle_block -= 1;
        } else {
            let instr = Instr::parse(&mut self.reg, &self.ram);
            match instr.opcode() {
                _ => panic!("Instruction not implemented!"),
            }
        }
    }

}

fn main() {
    println!("Hello, world!");
}
<commit_msg>Implement load/store instructions<commit_after>struct AddressSpace {
    data: [u8; 0xFFFF],
}

impl AddressSpace {

    fn new() -> AddressSpace {
        AddressSpace { data: [0; 0xFFFF] }
    }

    fn read(&self, addr: u16) -> u8 {
        self.data[addr as usize]
    }

    fn read_u16(&self, addr: u16) -> u16 {
        self.data[addr as usize] as u16 | ((self.data[addr as usize + 1] as u16) << 8)
    }

    fn read_slice(&self, addr: u16, bytes: u16) -> &[u8] {
        self.data.slice(addr as usize, (addr + bytes) as usize)
    }

    fn write(&mut self, addr: u16, data: u8) {
        self.data[addr as usize] = data;
    }

    fn write_u16(&mut self, addr: u16, data: u16) {
        let lo = (data & 0xFF) as u8;
        let hi = ((data & 0xFF00) >> 8) as u8;
        self.data[addr as usize] = lo;
        self.data[addr as usize + 1] = hi;
    }

}

struct Instr {
    opcode: u8,
    data: Vec<u8>,
    cycles: u32,
}

impl Instr {

    fn parse(reg: &mut RegData, rom: &AddressSpace) -> Instr {
        let opcode = rom.read(reg.advance_pc());
        Instr {
            opcode: opcode,
            data: Vec::new(),
            cycles: 4,
        }
    }

    fn opcode(&self) -> u8 {
        self.opcode
    }

    fn cycles(&self) -> u32 {
        self.cycles
    }

    fn param(&self, i: usize) -> u8 {
        self.data[i]
    }

    fn param_u16(&self, i: usize) -> u16 {
        (self.data[i] as u16) | ((self.data[i + 1] as u16) << 8)
    }

}

enum RegFlag {
    Zero,
    Subtract,
    HalfCarry,
    Carry,
}

#[derive(Copy)]
enum Register {
    A,
    B,
    C,
    D,
    E,
    F,
    H,
    L,
    AF,
    BC,
    DE,
    HL,
    SP,
    PC,
    Flag,
}

struct RegData {
    a: u8,
    b: u8,
    c: u8,
    d: u8,
    e: u8,
    f: u8,
    h: u8,
    l: u8,
    sp: u16,
    pc: u16,
    flag: u8,
}

impl RegData {

    fn new() -> RegData {
        RegData {
            a: 0,
            b: 0,
            c: 0,
            d: 0,
            e: 0,
            f: 0,
            h: 0,
            l: 0,
            sp: 0xFFFE,
            pc: 0x100,
            flag: 0,
        }
    }

    fn read(&self, reg: Register) -> u8 {
        match reg {
            Register::A => self.a,
            Register::B => self.b,
            Register::C => self.c,
            Register::D => self.d,
            Register::E => self.e,
            Register::F => self.f,
            Register::H => self.h,
            Register::L => self.l,
            Register::Flag => self.flag,
            _ => panic!("Register not available for 8-bit read"),
        }
    }

    fn read_u16(&self, reg: Register) -> u16 {
        match reg {
            Register::AF => (self.a as u16) << 8 | self.f as u16,
            Register::BC => (self.b as u16) << 8 | self.c as u16,
            Register::DE => (self.d as u16) << 8 | self.e as u16,
            Register::HL => (self.h as u16) << 8 | self.l as u16,
            Register::SP => self.sp,
            Register::PC => self.pc,
            _ => panic!("Register not available for 16-bit read"),
        }
    }

    fn write(&mut self, reg: Register, data: u8) {
        match reg {
            Register::A => self.a = data,
            Register::B => self.b = data,
            Register::C => self.c = data,
            Register::D => self.d = data,
            Register::E => self.e = data,
            Register::F => self.f = data,
            Register::H => self.h = data,
            Register::L => self.l = data,
            _ => panic!("Register not available for 8-bit write"),
        }
    }

    fn write_u16(&mut self, reg: Register, data: u16) {
        let (hi, lo) = ((data & 0xFF00 >> 8) as u8, (data & 0xFF) as u8);
        match reg {
            Register::AF => {
                self.a = hi;
                self.f = lo;
            },
            Register::BC => {
                self.b = hi;
                self.c = lo;
            },
            Register::DE => {
                self.d = hi;
                self.c = lo;
            },
            Register::HL => {
                self.h = hi;
                self.l = lo;
            },
            Register::SP => self.sp = data,
            Register::PC => self.pc = data,
            _ => panic!("Register not available for 16-bit write"),
        }
    }

    fn copy(&mut self, dst: Register, src: Register) {
        let data = self.read(src);
        self.write(dst, data);
    }

    fn copy_u16(&mut self, dst: Register, src: Register) {
        let data = self.read_u16(src);
        self.write_u16(dst, data);
    }

    fn set_flag(&mut self, flag: RegFlag, on: bool) {
        let bit = match flag {
            RegFlag::Zero => 0x80,
            RegFlag::Subtract => 0x40,
            RegFlag::HalfCarry => 0x20,
            RegFlag::Carry => 0x10,
        };
        if (on) {
            self.flag |= bit;
        } else {
            self.flag &= bit ^ 0xFF;
        }
    }

    fn advance_pc(&mut self) -> u16 {
        let pc = self.pc;
        self.pc += 1;
        pc
    }

}

static GB_FREQUENCY: u32 = 4194304;

struct Cpu {
    reg: RegData,
    ram: AddressSpace,
    freq: u32,
    clock: u64,
    cycle_block: u32,
}

impl Cpu {

    fn new() -> Cpu {
        Cpu::new_with_freq(GB_FREQUENCY)
    }

    fn new_with_freq(freq: u32) -> Cpu {
        Cpu {
            reg: RegData::new(),
            ram: AddressSpace::new(),
            freq: freq,
            clock: 0,
            cycle_block: 0,
        }
    }

    fn do_cycle(&mut self) {
        self.clock += 1;
        if self.cycle_block == 0 {
            let instr = Instr::parse(&mut self.reg, &self.ram);
            match instr.opcode() {
                // 8-bit immediate loads
                0x06 => self.reg.write(Register::B, instr.param(0)),
                0x0E => self.reg.write(Register::C, instr.param(0)),
                0x16 => self.reg.write(Register::D, instr.param(0)),
                0x1E => self.reg.write(Register::E, instr.param(0)),
                0x26 => self.reg.write(Register::H, instr.param(0)),
                0x2E => self.reg.write(Register::L, instr.param(0)),
                // 8-bit register loads
                0x47 => self.reg.copy(Register::B, Register::A),
                0x40 => self.reg.copy(Register::B, Register::B),
                0x41 => self.reg.copy(Register::B, Register::C),
                0x42 => self.reg.copy(Register::B, Register::D),
                0x43 => self.reg.copy(Register::B, Register::E),
                0x44 => self.reg.copy(Register::B, Register::H),
                0x45 => self.reg.copy(Register::B, Register::L),
                0x46 => {
                    let addr = self.reg.read_u16(Register::HL);
                    self.reg.write(Register::B, self.ram.read(addr));
                },
                0x4F => self.reg.copy(Register::C, Register::A),
                0x48 => self.reg.copy(Register::C, Register::B),
                0x49 => self.reg.copy(Register::C, Register::C),
                0x4A => self.reg.copy(Register::C, Register::D),
                0x4B => self.reg.copy(Register::C, Register::E),
                0x4C => self.reg.copy(Register::C, Register::H),
                0x4D => self.reg.copy(Register::C, Register::L),
                0x4E => {
                    let addr = self.reg.read_u16(Register::HL);
                    self.reg.write(Register::C, self.ram.read(addr));
                },
                0x57 => self.reg.copy(Register::D, Register::A),
                0x50 => self.reg.copy(Register::D, Register::B),
                0x51 => self.reg.copy(Register::D, Register::C),
                0x52 => self.reg.copy(Register::D, Register::D),
                0x53 => self.reg.copy(Register::D, Register::E),
                0x54 => self.reg.copy(Register::D, Register::H),
                0x55 => self.reg.copy(Register::D, Register::L),
                0x56 => {
                    let addr = self.reg.read_u16(Register::HL);
                    self.reg.write(Register::D, self.ram.read(addr));
                },
                0x5F => self.reg.copy(Register::E, Register::A),
                0x58 => self.reg.copy(Register::E, Register::B),
                0x59 => self.reg.copy(Register::E, Register::C),
                0x5A => self.reg.copy(Register::E, Register::D),
                0x5B => self.reg.copy(Register::E, Register::E),
                0x5C => self.reg.copy(Register::E, Register::H),
                0x5D => self.reg.copy(Register::E, Register::L),
                0x5E => {
                    let addr = self.reg.read_u16(Register::HL);
                    self.reg.write(Register::E, self.ram.read(addr));
                },
                0x67 => self.reg.copy(Register::H, Register::A),
                0x60 => self.reg.copy(Register::H, Register::B),
                0x61 => self.reg.copy(Register::H, Register::C),
                0x62 => self.reg.copy(Register::H, Register::D),
                0x63 => self.reg.copy(Register::H, Register::E),
                0x64 => self.reg.copy(Register::H, Register::H),
                0x65 => self.reg.copy(Register::H, Register::L),
                0x66 => {
                    let addr = self.reg.read_u16(Register::HL);
                    self.reg.write(Register::H, self.ram.read(addr));
                },
                0x6F => self.reg.copy(Register::L, Register::A),
                0x68 => self.reg.copy(Register::L, Register::B),
                0x69 => self.reg.copy(Register::L, Register::C),
                0x6A => self.reg.copy(Register::L, Register::D),
                0x6B => self.reg.copy(Register::L, Register::E),
                0x6C => self.reg.copy(Register::L, Register::H),
                0x6D => self.reg.copy(Register::L, Register::L),
                0x6E => {
                    let addr = self.reg.read_u16(Register::HL);
                    self.reg.write(Register::L, self.ram.read(addr));
                },
                // 8-bit load to ram
                0x70 => self.ram.write(self.reg.read_u16(Register::HL), self.reg.read(Register::B)),
                0x71 => self.ram.write(self.reg.read_u16(Register::HL), self.reg.read(Register::C)),
                0x72 => self.ram.write(self.reg.read_u16(Register::HL), self.reg.read(Register::D)),
                0x73 => self.ram.write(self.reg.read_u16(Register::HL), self.reg.read(Register::E)),
                0x74 => self.ram.write(self.reg.read_u16(Register::HL), self.reg.read(Register::H)),
                0x75 => self.ram.write(self.reg.read_u16(Register::HL), self.reg.read(Register::L)),
                0x36 => self.ram.write(self.reg.read_u16(Register::HL), instr.param(0)),
                // loads into register A
                0x7F => self.reg.copy(Register::A, Register::A),
                0x78 => self.reg.copy(Register::A, Register::B),
                0x79 => self.reg.copy(Register::A, Register::C),
                0x7A => self.reg.copy(Register::A, Register::D),
                0x7B => self.reg.copy(Register::A, Register::E),
                0x7C => self.reg.copy(Register::A, Register::H),
                0x7D => self.reg.copy(Register::A, Register::L),
                0x0A => {
                    let addr =self.reg.read_u16(Register::BC);
                    self.reg.write(Register::A, self.ram.read(addr));
                },
                0x1A => {
                    let addr =self.reg.read_u16(Register::DE);
                    self.reg.write(Register::A, self.ram.read(addr));
                },
                0x7E => {
                    let addr =self.reg.read_u16(Register::HL);
                    self.reg.write(Register::A, self.ram.read(addr));
                },
                0xFA => self.reg.write(Register::A, self.ram.read(instr.param_u16(0))),
                0x3E => self.reg.write(Register::A, instr.param(0)),
                // writes from register A
                0x02 => self.ram.write(self.reg.read_u16(Register::BC), self.reg.read(Register::A)),
                0x12 => self.ram.write(self.reg.read_u16(Register::DE), self.reg.read(Register::A)),
                0x77 => self.ram.write(self.reg.read_u16(Register::HL), self.reg.read(Register::A)),
                0xEA => self.ram.write(instr.param_u16(0), self.reg.read(Register::A)),
                // Read/Write ($FF00 + C) with A
                0xF2 => {
                    let addr = 0xFF00 + self.reg.read(Register::C) as u16;
                    self.reg.write(Register::A, self.ram.read(addr));
                },
                0xE2 => self.ram.write(0xFF00 + self.reg.read(Register::C) as u16, self.reg.read(Register::A)),
                // Load from (HL) and decrement
                0x3A => {
                    let addr = self.reg.read_u16(Register::HL);
                    self.reg.write(Register::A, self.ram.read(addr));
                    self.reg.write_u16(Register::HL, addr - 1);
                },
                // Write to (HL) and decrement
                0x32 => {
                    let addr = self.reg.read_u16(Register::HL);
                    self.ram.write(addr, self.reg.read(Register::A));
                    self.reg.write_u16(Register::HL, addr - 1);
                },
                // Load from (HL) and increment
                0x2A => {
                    let addr = self.reg.read_u16(Register::HL);
                    self.reg.write(Register::A, self.ram.read(addr));
                    self.reg.write_u16(Register::HL, addr + 1);
                },
                // Write to (HL) and increment
                0x22 => {
                    let addr = self.reg.read_u16(Register::HL);
                    self.ram.write(addr, self.reg.read(Register::A));
                    self.reg.write_u16(Register::HL, addr + 1);
                },
                // Write to ($FF00 + immediate)
                0xE0 => self.ram.write(0xFF00 + instr.param(0) as u16, self.reg.read(Register::A)),
                // Load ($FF00 + immediate)
                0xF0 => self.reg.write(Register::A, self.ram.read(0xFF00 + instr.param(0) as u16)),
                // 16-bit immediate loads
                0x01 => self.reg.write_u16(Register::BC, instr.param_u16(0)),
                0x11 => self.reg.write_u16(Register::DE, instr.param_u16(0)),
                0x21 => self.reg.write_u16(Register::HL, instr.param_u16(0)),
                0x31 => self.reg.write_u16(Register::SP, instr.param_u16(0)),
                // Copy HL into SP
                0xF9 => self.reg.copy_u16(Register::SP, Register::HL),
                // Load effective address SP + immediate into HL
                0xF8 => {
                    let addr = self.reg.read_u16(Register::SP) + instr.param(0) as u16;
                    self.reg.write_u16(Register::HL, addr);
                },
                // Put SP at (immediate)
                0x08 => self.ram.write_u16(instr.param_u16(0), self.reg.read_u16(Register::SP)),
                // Push instructions
                0xF5 => {
                    let addr = self.reg.read_u16(Register::SP);
                    self.ram.write_u16(addr, self.reg.read_u16(Register::AF));
                    self.reg.write_u16(Register::SP, addr - 2);
                },
                0xC5 => {
                    let addr = self.reg.read_u16(Register::SP);
                    self.ram.write_u16(addr, self.reg.read_u16(Register::BC));
                    self.reg.write_u16(Register::SP, addr - 2);
                },
                0xD5 => {
                    let addr = self.reg.read_u16(Register::SP);
                    self.ram.write_u16(addr, self.reg.read_u16(Register::DE));
                    self.reg.write_u16(Register::SP, addr - 2);
                },
                0xE5 => {
                    let addr = self.reg.read_u16(Register::SP);
                    self.ram.write_u16(addr, self.reg.read_u16(Register::HL));
                    self.reg.write_u16(Register::SP, addr - 2);
                },
                // Pop instructions
                0xF1 => {
                    let addr = self.reg.read_u16(Register::SP);
                    self.reg.write_u16(Register::AF, self.ram.read_u16(addr));
                    self.reg.write_u16(Register::SP, addr + 2);
                },
                0xC1 => {
                    let addr = self.reg.read_u16(Register::SP);
                    self.reg.write_u16(Register::BC, self.ram.read_u16(addr));
                    self.reg.write_u16(Register::SP, addr + 2);
                },
                0xD1 => {
                    let addr = self.reg.read_u16(Register::SP);
                    self.reg.write_u16(Register::DE, self.ram.read_u16(addr));
                    self.reg.write_u16(Register::SP, addr + 2);
                },
                0xE1 => {
                    let addr = self.reg.read_u16(Register::SP);
                    self.reg.write_u16(Register::HL, self.ram.read_u16(addr));
                    self.reg.write_u16(Register::SP, addr + 2);
                },
                _ => panic!("Instruction not implemented!"),
            }
            self.cycle_block = instr.cycles();
        }
        self.cycle_block -= 1;
    }

}

fn main() {
    println!("Hello, world!");
}
<|endoftext|>


<commit_before>fn main() {
	let reader = std::io::stdin();
	let lineReader = reader.lines();

	let mut lines : Vec<&str> = vec![];
	for line in lineReader.iter() {
		match lineContainer {
			Some(Ok(a)) => {
				lines.push(line);
			},
			_ => {

			}
		}
	}

	for i in lines.iter() {
		println!("{}", i);
	}

}<commit_msg>no more line matching<commit_after>fn main() {
	let reader = std::io::stdin();
	let lineReader = reader.lines();

	let mut lines : Vec<&str> = vec![];
	for lineContainer in lineReader.iter() {
		match lineContainer {
			Some(Ok(line)) => {
				lines.push(line);
			},
			_ => {

			}
		}
	}

	for i in lines.iter() {
		println!("{}", i);
	}

}<|endoftext|>


<commit_before>#![feature(start, std_misc, unsafe_destructor)]

#[macro_use]
extern crate android_glue;

extern crate cgmath;
extern crate libc;
extern crate time;

use std::default::Default;
use std::sync::mpsc;
use std::sync::mpsc::TryRecvError;

use android_glue::Event;
use cgmath::Matrix4;
use engine::{EglContext, Engine};

mod egl;
mod engine;
mod gl;
mod mesh;
mod program;

#[cfg(target_os = "android")]
android_start!(main);

/// Initialize EGL context for the current display.
fn init_display(engine: &mut Engine) {
  println!("Renderer initializing...");
  let start_ns = time::precise_time_ns();
  let app = android_glue::get_app();
  let window = app.window as *mut android_glue::ffi::ANativeWindow;
  let egl_context = Box::new(EglContext::new(window));
  engine.init(egl_context);
  let elapsed_ms = (time::precise_time_ns() - start_ns) as f32 / 1000000.0;
  println!("Renderer initialized, {:.3}ms", elapsed_ms);
}

/**
 * This is the main entry point of a native application that is using android_native_app_glue.
 * It runs in its own thread, with its own event loop for receiving input events and doing other
 * things.
 */
pub fn main() {
  println!("-------------------------------------------------------------------");

  // TODO: Implement restoring / saving state in android-rust-glue.
  let mut engine = Engine {
    animating: false,
    egl_context: None,
    state: Default::default(),
    program: None,
    view_projection_matrix: Matrix4::identity(),
    texture: Default::default(),
  };

  let (event_tx, event_rx) = mpsc::channel::<Event>();
  android_glue::add_sender_missing(event_tx);
  'event: loop {
    match event_rx.try_recv() {
      Ok(ev) => {
        println!("----- Event: {:?}", ev);
        match ev {
          Event::InitWindow => init_display(&mut engine),
          Event::GainedFocus => engine.gained_focus(),
          Event::Pause => engine.lost_focus(),
          Event::TermWindow => engine.term(),
          _ => (),
        }
      },
      Err(TryRecvError::Empty) => (),
      Err(TryRecvError::Disconnected) => {
        panic!("----- Failed to get next event, channel disconnected")
      },
    }
    engine.update_draw();

    let app = android_glue::get_app();
    if app.destroyRequested != 0 {
      engine.term();  // Double-termination is fine.
      break 'event;
    }
  }
}
<commit_msg>X11: Minimal working build.<commit_after>#![feature(start, std_misc, unsafe_destructor)]

#[macro_use]
#[cfg(target_os = "android")]
extern crate android_glue;

extern crate cgmath;
extern crate libc;
extern crate time;

#[cfg(target_os = "android")]
use std::default::Default;
#[cfg(target_os = "android")]
use std::sync::mpsc;
#[cfg(target_os = "android")]
use std::sync::mpsc::TryRecvError;

#[cfg(target_os = "android")]
use android_glue::Event;
#[cfg(target_os = "android")]
use cgmath::Matrix4;
#[cfg(target_os = "android")]
use engine::{EglContext, Engine};

#[cfg(target_os = "android")]
mod egl;
#[cfg(target_os = "android")]
mod engine;
#[cfg(target_os = "android")]
mod gl;
#[cfg(target_os = "android")]
mod mesh;
#[cfg(target_os = "android")]
mod program;

#[cfg(target_os = "android")]
android_start!(main);

/// Initialize EGL context for the current display.
#[cfg(target_os = "android")]
fn init_display(engine: &mut Engine) {
  println!("Renderer initializing...");
  let start_ns = time::precise_time_ns();
  let app = android_glue::get_app();
  let window = app.window as *mut android_glue::ffi::ANativeWindow;
  let egl_context = Box::new(EglContext::new(window));
  engine.init(egl_context);
  let elapsed_ms = (time::precise_time_ns() - start_ns) as f32 / 1000000.0;
  println!("Renderer initialized, {:.3}ms", elapsed_ms);
}

/**
 * This is the main entry point of a native application that is using android_native_app_glue.
 * It runs in its own thread, with its own event loop for receiving input events and doing other
 * things.
 */
#[cfg(target_os = "android")]
pub fn main() {
  println!("-------------------------------------------------------------------");

  // TODO: Implement restoring / saving state in android-rust-glue.
  let mut engine = Engine {
    animating: false,
    egl_context: None,
    state: Default::default(),
    program: None,
    view_projection_matrix: Matrix4::identity(),
    texture: Default::default(),
  };

  let (event_tx, event_rx) = mpsc::channel::<Event>();
  android_glue::add_sender_missing(event_tx);
  'event: loop {
    match event_rx.try_recv() {
      Ok(ev) => {
        println!("----- Event: {:?}", ev);
        match ev {
          Event::InitWindow => init_display(&mut engine),
          Event::GainedFocus => engine.gained_focus(),
          Event::Pause => engine.lost_focus(),
          Event::TermWindow => engine.term(),
          _ => (),
        }
      },
      Err(TryRecvError::Empty) => (),
      Err(TryRecvError::Disconnected) => {
        panic!("----- Failed to get next event, channel disconnected")
      },
    }
    engine.update_draw();

    let app = android_glue::get_app();
    if app.destroyRequested != 0 {
      engine.term();  // Double-termination is fine.
      break 'event;
    }
  }
}

#[cfg(target_os = "linux")]
pub fn main() {
  println!("-------------------------------------------------------------------");
}
<|endoftext|>