<commit_before>extern mod std;
use std::hashmap::HashMap;
use sexpr;

// A very simple LISP-like language
// Globally scoped, no closures

/// Our value types
#[deriving(Clone)]
pub enum LispValue {
	List(~[LispValue]),
	Atom(~str),
	Str(~str),
	Num(float),
	Fn(~[~str], ~LispValue), // args, body
	BIF(~str, int, ~[~str], extern fn(@mut HashMap<~str, ~LispValue>, ~[~LispValue])->~LispValue) // built-in function (args, closure)
}

// XXX: this is ugly but it won't automatically derive Eq because of the extern fn
impl Eq for LispValue {
	fn eq(&self, other: &LispValue) -> bool {
		match (self.clone(), other.clone()) {
			(BIF(ref x, _, _, _), BIF(ref y, _, _, _)) if *x == *y => true,
			(Str(ref x), Str(ref y)) if *x == *y => true,
			(Num(ref x), Num(ref y)) if *x == *y => true,
			(Atom(ref x), Atom(ref y)) if *x == *y => true,
			(List(ref x), List(ref y)) if *x == *y => true,
			(Fn(ref x, ref x2), Fn(ref y, ref y2)) if *x == *y && *x2 == *y2 => true,
			_ => false
		}
	}
}

fn from_sexpr(sexpr: &sexpr::Value) -> ~LispValue {
	match *sexpr {
		sexpr::List(ref v) => ~List(v.map(|x| *from_sexpr(x))),
		sexpr::Num(v) => ~Num(v),
		sexpr::Str(ref v) => ~Str(v.clone()),
		sexpr::Atom(ref v) => ~Atom(v.clone())
	}
}

/// The type of the global symbol table (string to a value mapping).
type SymbolTable = HashMap<~str, ~LispValue>;

/// Returns a value representing the empty list
#[inline]
pub fn nil() -> ~LispValue {
	~List(~[])
}

/// Creates a new symbol table and returns it
pub fn new_symt() -> SymbolTable {
	HashMap::new()
}

/// Binds a symbol in the symbol table. Replaces if it already exists.
pub fn bind(symt: @mut SymbolTable, name: ~str, value: ~LispValue) {
	symt.insert(name, value);
}

/// Look up a symbol in the symbol table. Fails if not found.
pub fn lookup(symt: @mut SymbolTable, name: ~str) -> ~LispValue {
	match symt.find(&name) {
		Some(v) => v.clone(),
		None => fail!("couldn't find symbol: %s", name)
	}
}

/// Identity function
fn id_(_symt: @mut SymbolTable, v: ~[~LispValue]) -> ~LispValue { v[0] }

fn cons_(_symt: @mut SymbolTable, v: ~[~LispValue]) -> ~LispValue {
	match v {
		[~a, ~b, .._] => ~List(~[a, b]),
		_ => fail!("cons: requires two arguments")
	}
}

fn car_(_symt: @mut SymbolTable, v: ~[~LispValue]) -> ~LispValue {
	match v[0] {
		~List(v_) => ~v_[0],
		_ => fail!("car: need a list")
	}
}

fn cdr_(_symt: @mut SymbolTable, v: ~[~LispValue]) -> ~LispValue {
	match v[0] {
		~List(v_) => ~v_[1],
		_ => fail!("cdr: need a list")
	}
}

// Print function
fn print_(_symt: @mut SymbolTable, v: ~[~LispValue]) -> ~LispValue {
	match v[0] {
		~Str(s) => printfln!("%s", s),
		_ => fail!("print takes an str")
	}
	nil()
}

// There are several bugs in the macroing system (#8853, or, #8852 and #8851)
// that prevent us from making these functions macros. In addition, we can't
// return a closure because the type needs to be `extern "Rust" fn`, which
// closures aren't. So we have a little bit of code duplication.

fn plus_(_symt: @mut SymbolTable, v: ~[~LispValue]) -> ~LispValue {
	if(v.len() == 0) { fail!("+ needs operands"); }
	else if(v.len() == 1) {
		return v[0];
	}

	let add = |acc: ~LispValue, b: &~LispValue| {
		match (*acc, b) {
			(Num(ref x), &~Num(ref y)) => ~Num(x.clone() + y.clone()),
			(Str(ref x), &~Str(ref y)) => ~Str(x.clone() + y.clone()),
			_ => fail!("invalid operands to +")
		}
	};

	v.iter().skip(1).fold(v[0].clone(), add)
}

fn minus_(_symt: @mut SymbolTable, v: ~[~LispValue]) -> ~LispValue {
	if(v.len() == 0) { fail!("- needs operands"); }
	else if(v.len() == 1) {
		return v[0];
	}

	let sub = |acc: ~LispValue, b: &~LispValue| {
		match (*acc, b) {
			(Num(ref x), &~Num(ref y)) => ~Num(x.clone() - y.clone()),
			_ => fail!("invalid operands to -")
		}
	};

	v.iter().skip(1).fold(v[0].clone(), sub)
}

fn mul_(_symt: @mut SymbolTable, v: ~[~LispValue]) -> ~LispValue {
	if(v.len() == 0) { fail!("* needs operands"); }
	else if(v.len() == 1) {
		return v[0];
	}

	let mul = |acc: ~LispValue, b: &~LispValue| {
		match (*acc, b) {
			(Num(ref x), &~Num(ref y)) => ~Num(x.clone() * y.clone()),
			_ => fail!("invalid operands to *")
		}
	};

	v.iter().skip(1).fold(v[0].clone(), mul)
}

fn div_(_symt: @mut SymbolTable, v: ~[~LispValue]) -> ~LispValue {
	if(v.len() == 0) { fail!("/ needs operands"); }
	else if(v.len() == 1) {
		return v[0];
	}

	let div = |acc: ~LispValue, b: &~LispValue| {
		match (*acc, b) {
			(Num(ref x), &~Num(ref y)) => ~Num(x.clone() / y.clone()),
			_ => fail!("invalid operands to /")
		}
	};

	v.iter().skip(1).fold(v[0].clone(), div)
}

/// Initializes standard library functions
pub fn init_std(symt: @mut SymbolTable) {
	bind(symt, ~"id", ~BIF(~"id", 1, ~[~"x"], id_));
	bind(symt, ~"print", ~BIF(~"print", 1, ~[~"msg"], print_));
	bind(symt, ~"cons", ~BIF(~"cons", 2, ~[~"x", ~"y"], cons_));
	bind(symt, ~"car", ~BIF(~"car", 1, ~[~"x"], car_));
	bind(symt, ~"cdr", ~BIF(~"cdr", 1, ~[~"x"], cdr_));
	bind(symt, ~"+", ~BIF(~"+", -1, ~[], plus_));
	bind(symt, ~"*", ~BIF(~"*", -1, ~[], mul_));
	bind(symt, ~"-", ~BIF(~"-", -1, ~[], minus_));
	bind(symt, ~"/", ~BIF(~"/", -1, ~[], div_));
}

fn apply(symt: @mut SymbolTable, f: ~LispValue, args: ~[~LispValue]) -> ~LispValue {
	match *f {
		BIF(name, arity, fnargs, bif) => {
			// apply built-in function
			if arity > 0 && fnargs.len() as int != arity {
				fail!("function '%s' requires %d arguments, but it received %u arguments",
					name, arity, args.len())
			}

			bif(symt, args)
		}

		_ => fail!("apply: need function")
	}
}

/// Evaluates an s-expression and returns a value.
pub fn eval(symt: @mut SymbolTable, input: sexpr::Value) -> ~LispValue {
	match input {
		sexpr::List(v) => {
			if(v.len() == 0) {
				fail!("eval given empty list")
			}

			// evaluate a list as a function call
			match v {
				[sexpr::Atom(~"quote"), arg] => from_sexpr(&arg),
				[sexpr::Atom(sym), ..args] => {
					let f = lookup(symt, sym);
					let xargs = args.map(|x| eval(symt, x.clone())); // eval'd args
					apply(symt, f, xargs)
				}
				_ => fail!("function calls take an atom"),
			}

		}
		_ => from_sexpr(&input) // return non-list values as they are
	}
}

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

	fn read(input: &str) -> sexpr::Value {
		from_str(input).unwrap()
	}

	#[test]
	fn test_eval() {
		let symt = @mut new_symt();
		init_std(symt);
		assert_eq!(eval(symt, read("123")), ~Num(123.0));
		assert_eq!(eval(symt, read("(id 123)")), ~Num(123.0));
		assert_eq!(eval(symt, read("(id (id (id 123)))")), ~Num(123.0));
		// should fail: assert_eq!(eval(&mut symt, read("(1 2 3)")), ~List(~[Num(1.0), Num(2.0), Num(3.0)]));
	}

	#[test]
	fn test_str() {
		let symt = @mut new_symt();
		init_std(symt);
		assert_eq!(eval(symt, read("(id \"hi\")")), ~Str(~"hi"));
		assert_eq!(eval(symt, read("(car (cons \"a\" \"b\"))")), ~Str(~"a"));
	}

	#[test]
	fn test_cons() {
		let symt = @mut new_symt();
		init_std(symt);
		assert_eq!(eval(symt, read("(cons 1 2)")), ~List(~[Num(1.0), Num(2.0)]));
		assert_eq!(eval(symt, read("(cons 1 (cons 2 3))")), ~List(~[Num(1.0),
			List(~[Num(2.0), Num(3.0)])]));
	}

	#[test]
	fn test_car() {
		let symt = @mut new_symt();
		init_std(symt);
		assert_eq!(eval(symt, read("(car (cons 1 2))")), ~Num(1.0));
	}

	#[test]
	fn test_cdr() {
		let symt = @mut new_symt();
		init_std(symt);
		assert_eq!(eval(symt, read("(cdr (cons 1 2))")), ~Num(2.0));
		assert_eq!(eval(symt, read("(cdr (cons 1 (cons 2 3)))")), ~List(~[Num(2.0), Num(3.0)]));
	}

	#[test]
	fn test_arithmetic() {
		let symt = @mut new_symt();
		init_std(symt);
		assert_eq!(eval(symt, read("(+ 1 3)")), ~Num(4.0));
		assert_eq!(eval(symt, read("(+ 1.5 3)")), ~Num(4.5));
		assert_eq!(eval(symt, read("(+ 5 -3)")), ~Num(2.0));

		assert_eq!(eval(symt, read("(- 5 3)")), ~Num(2.0));
		assert_eq!(eval(symt, read("(- 3 5)")), ~Num(-2.0));
		assert_eq!(eval(symt, read("(- 5 -3)")), ~Num(8.0));

		assert_eq!(eval(symt, read("(* 2 5)")), ~Num(10.0));
		assert_eq!(eval(symt, read("(* 2 -5)")), ~Num(-10.0));

		assert_eq!(eval(symt, read("(/ 10 2)")), ~Num(5.0));
		assert_eq!(eval(symt, read("(/ 10 -2)")), ~Num(-5.0));
	}

	#[test]
	fn test_quote() {
		let symt = @mut new_symt();
		init_std(symt);
		assert_eq!(eval(symt, read("(quote 5)")), ~Num(5.0));
		assert_eq!(eval(symt, read("(quote x)")), ~Atom(~"x"));
		assert_eq!(eval(symt, read("(quote (1 2 3))")), ~List(~[Num(1f), Num(2f), Num(3f)]));
	}
}<commit_msg>add def BIF<commit_after>extern mod std;
use std::hashmap::HashMap;
use sexpr;

// A very simple LISP-like language
// Globally scoped, no closures

/// Our value types
#[deriving(Clone)]
pub enum LispValue {
	List(~[LispValue]),
	Atom(~str),
	Str(~str),
	Num(float),
	Fn(~[~str], ~LispValue), // args, body
	BIF(~str, int, ~[~str], extern fn(@mut HashMap<~str, ~LispValue>, ~[~LispValue])->~LispValue) // built-in function (args, closure)
}

// XXX: this is ugly but it won't automatically derive Eq because of the extern fn
impl Eq for LispValue {
	fn eq(&self, other: &LispValue) -> bool {
		match (self.clone(), other.clone()) {
			(BIF(ref x, _, _, _), BIF(ref y, _, _, _)) if *x == *y => true,
			(Str(ref x), Str(ref y)) if *x == *y => true,
			(Num(ref x), Num(ref y)) if *x == *y => true,
			(Atom(ref x), Atom(ref y)) if *x == *y => true,
			(List(ref x), List(ref y)) if *x == *y => true,
			(Fn(ref x, ref x2), Fn(ref y, ref y2)) if *x == *y && *x2 == *y2 => true,
			_ => false
		}
	}
}

fn from_sexpr(sexpr: &sexpr::Value) -> ~LispValue {
	match *sexpr {
		sexpr::List(ref v) => ~List(v.map(|x| *from_sexpr(x))),
		sexpr::Num(v) => ~Num(v),
		sexpr::Str(ref v) => ~Str(v.clone()),
		sexpr::Atom(ref v) => ~Atom(v.clone())
	}
}

/// The type of the global symbol table (string to a value mapping).
type SymbolTable = HashMap<~str, ~LispValue>;

/// Returns a value representing the empty list
#[inline]
pub fn nil() -> ~LispValue {
	~List(~[])
}

/// Creates a new symbol table and returns it
pub fn new_symt() -> SymbolTable {
	HashMap::new()
}

/// Binds a symbol in the symbol table. Replaces if it already exists.
pub fn bind(symt: @mut SymbolTable, name: ~str, value: ~LispValue) {
	symt.insert(name, value);
}

/// Look up a symbol in the symbol table. Fails if not found.
pub fn lookup(symt: @mut SymbolTable, name: ~str) -> ~LispValue {
	match symt.find(&name) {
		Some(v) => v.clone(),
		None => fail!("couldn't find symbol: %s", name)
	}
}

/// Identity function
fn id_(_symt: @mut SymbolTable, v: ~[~LispValue]) -> ~LispValue { v[0] }

fn cons_(_symt: @mut SymbolTable, v: ~[~LispValue]) -> ~LispValue {
	match v {
		[~a, ~b, .._] => ~List(~[a, b]),
		_ => fail!("cons: requires two arguments")
	}
}

fn car_(_symt: @mut SymbolTable, v: ~[~LispValue]) -> ~LispValue {
	match v[0] {
		~List(v_) => ~v_[0],
		_ => fail!("car: need a list")
	}
}

fn cdr_(_symt: @mut SymbolTable, v: ~[~LispValue]) -> ~LispValue {
	match v[0] {
		~List(v_) => ~v_[1],
		_ => fail!("cdr: need a list")
	}
}

// Print function
fn print_(_symt: @mut SymbolTable, v: ~[~LispValue]) -> ~LispValue {
	match v[0] {
		~Str(s) => printfln!("%s", s),
		_ => fail!("print takes an str")
	}
	nil()
}

// There are several bugs in the macroing system (#8853, or, #8852 and #8851)
// that prevent us from making these functions macros. In addition, we can't
// return a closure because the type needs to be `extern "Rust" fn`, which
// closures aren't. So we have a little bit of code duplication.

fn plus_(_symt: @mut SymbolTable, v: ~[~LispValue]) -> ~LispValue {
	if(v.len() == 0) { fail!("+ needs operands"); }
	else if(v.len() == 1) {
		return v[0];
	}

	let add = |acc: ~LispValue, b: &~LispValue| {
		match (*acc, b) {
			(Num(ref x), &~Num(ref y)) => ~Num(x.clone() + y.clone()),
			(Str(ref x), &~Str(ref y)) => ~Str(x.clone() + y.clone()),
			_ => fail!("invalid operands to +")
		}
	};

	v.iter().skip(1).fold(v[0].clone(), add)
}

fn minus_(_symt: @mut SymbolTable, v: ~[~LispValue]) -> ~LispValue {
	if(v.len() == 0) { fail!("- needs operands"); }
	else if(v.len() == 1) {
		return v[0];
	}

	let sub = |acc: ~LispValue, b: &~LispValue| {
		match (*acc, b) {
			(Num(ref x), &~Num(ref y)) => ~Num(x.clone() - y.clone()),
			_ => fail!("invalid operands to -")
		}
	};

	v.iter().skip(1).fold(v[0].clone(), sub)
}

fn mul_(_symt: @mut SymbolTable, v: ~[~LispValue]) -> ~LispValue {
	if(v.len() == 0) { fail!("* needs operands"); }
	else if(v.len() == 1) {
		return v[0];
	}

	let mul = |acc: ~LispValue, b: &~LispValue| {
		match (*acc, b) {
			(Num(ref x), &~Num(ref y)) => ~Num(x.clone() * y.clone()),
			_ => fail!("invalid operands to *")
		}
	};

	v.iter().skip(1).fold(v[0].clone(), mul)
}

fn div_(_symt: @mut SymbolTable, v: ~[~LispValue]) -> ~LispValue {
	if(v.len() == 0) { fail!("/ needs operands"); }
	else if(v.len() == 1) {
		return v[0];
	}

	let div = |acc: ~LispValue, b: &~LispValue| {
		match (*acc, b) {
			(Num(ref x), &~Num(ref y)) => ~Num(x.clone() / y.clone()),
			_ => fail!("invalid operands to /")
		}
	};

	v.iter().skip(1).fold(v[0].clone(), div)
}

/// Binds a value to an identifier.
fn def_(symt: @mut SymbolTable, v: ~[~LispValue]) -> ~LispValue {
	match v {
		[~Atom(ident), value] => {
			bind(symt, ident, value);
			nil()
		}
		_ => fail!("invalid arguments to def")
	}
}

/// Initializes standard library functions
pub fn init_std(symt: @mut SymbolTable) {
	bind(symt, ~"id", ~BIF(~"id", 1, ~[~"x"], id_));
	bind(symt, ~"print", ~BIF(~"print", 1, ~[~"msg"], print_));
	bind(symt, ~"cons", ~BIF(~"cons", 2, ~[~"x", ~"y"], cons_));
	bind(symt, ~"car", ~BIF(~"car", 1, ~[~"x"], car_));
	bind(symt, ~"cdr", ~BIF(~"cdr", 1, ~[~"x"], cdr_));
	bind(symt, ~"+", ~BIF(~"+", -1, ~[], plus_));
	bind(symt, ~"*", ~BIF(~"*", -1, ~[], mul_));
	bind(symt, ~"-", ~BIF(~"-", -1, ~[], minus_));
	bind(symt, ~"/", ~BIF(~"/", -1, ~[], div_));
	bind(symt, ~"def", ~BIF(~"def", 2, ~[~"ident", ~"value"], def_));
}

fn apply(symt: @mut SymbolTable, f: ~LispValue, args: ~[~LispValue]) -> ~LispValue {
	match *f {
		BIF(name, arity, fnargs, bif) => {
			// apply built-in function
			if arity > 0 && fnargs.len() as int != arity {
				fail!("function '%s' requires %d arguments, but it received %u arguments",
					name, arity, args.len())
			}

			bif(symt, args)
		}

		_ => fail!("apply: need function")
	}
}

/// Evaluates an s-expression and returns a value.
pub fn eval(symt: @mut SymbolTable, input: sexpr::Value) -> ~LispValue {
	match input {
		sexpr::List(v) => {
			if(v.len() == 0) {
				fail!("eval given empty list")
			}

			// evaluate a list as a function call
			match v {
				[sexpr::Atom(~"quote"), arg] => from_sexpr(&arg),
				[sexpr::Atom(sym), ..args] => {
					let f = lookup(symt, sym);
					let xargs = args.map(|x| eval(symt, x.clone())); // eval'd args
					apply(symt, f, xargs)
				}
				_ => fail!("function calls take an atom"),
			}

		}
		_ => from_sexpr(&input) // return non-list values as they are
	}
}

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

	fn read(input: &str) -> sexpr::Value {
		from_str(input).unwrap()
	}

	#[test]
	fn test_eval() {
		let symt = @mut new_symt();
		init_std(symt);
		assert_eq!(eval(symt, read("123")), ~Num(123.0));
		assert_eq!(eval(symt, read("(id 123)")), ~Num(123.0));
		assert_eq!(eval(symt, read("(id (id (id 123)))")), ~Num(123.0));
		// should fail: assert_eq!(eval(&mut symt, read("(1 2 3)")), ~List(~[Num(1.0), Num(2.0), Num(3.0)]));
	}

	#[test]
	fn test_str() {
		let symt = @mut new_symt();
		init_std(symt);
		assert_eq!(eval(symt, read("(id \"hi\")")), ~Str(~"hi"));
		assert_eq!(eval(symt, read("(car (cons \"a\" \"b\"))")), ~Str(~"a"));
	}

	#[test]
	fn test_cons() {
		let symt = @mut new_symt();
		init_std(symt);
		assert_eq!(eval(symt, read("(cons 1 2)")), ~List(~[Num(1.0), Num(2.0)]));
		assert_eq!(eval(symt, read("(cons 1 (cons 2 3))")), ~List(~[Num(1.0),
			List(~[Num(2.0), Num(3.0)])]));
	}

	#[test]
	fn test_car() {
		let symt = @mut new_symt();
		init_std(symt);
		assert_eq!(eval(symt, read("(car (cons 1 2))")), ~Num(1.0));
	}

	#[test]
	fn test_cdr() {
		let symt = @mut new_symt();
		init_std(symt);
		assert_eq!(eval(symt, read("(cdr (cons 1 2))")), ~Num(2.0));
		assert_eq!(eval(symt, read("(cdr (cons 1 (cons 2 3)))")), ~List(~[Num(2.0), Num(3.0)]));
	}

	#[test]
	fn test_arithmetic() {
		let symt = @mut new_symt();
		init_std(symt);
		assert_eq!(eval(symt, read("(+ 1 3)")), ~Num(4.0));
		assert_eq!(eval(symt, read("(+ 1.5 3)")), ~Num(4.5));
		assert_eq!(eval(symt, read("(+ 5 -3)")), ~Num(2.0));

		assert_eq!(eval(symt, read("(- 5 3)")), ~Num(2.0));
		assert_eq!(eval(symt, read("(- 3 5)")), ~Num(-2.0));
		assert_eq!(eval(symt, read("(- 5 -3)")), ~Num(8.0));

		assert_eq!(eval(symt, read("(* 2 5)")), ~Num(10.0));
		assert_eq!(eval(symt, read("(* 2 -5)")), ~Num(-10.0));

		assert_eq!(eval(symt, read("(/ 10 2)")), ~Num(5.0));
		assert_eq!(eval(symt, read("(/ 10 -2)")), ~Num(-5.0));
	}

	#[test]
	fn test_quote() {
		let symt = @mut new_symt();
		init_std(symt);
		assert_eq!(eval(symt, read("(quote 5)")), ~Num(5.0));
		assert_eq!(eval(symt, read("(quote x)")), ~Atom(~"x"));
		assert_eq!(eval(symt, read("(quote (1 2 3))")), ~List(~[Num(1f), Num(2f), Num(3f)]));
	}

	#[test]
	fn test_def() {
		let symt = @mut new_symt();
		init_std(symt);
		eval(symt, read("(def x 5)"));
		eval(symt, read("(def y 10)"));
	}
}<|endoftext|>


<commit_before>#[allow(unused_imports)]
#[macro_use]
extern crate log;
extern crate libc;
extern crate env_logger;
extern crate catfs;
extern crate rand;
extern crate fuse;
extern crate xattr;

use std::env;
use std::ffi::OsString;
use std::fs;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::{Read, Write};
use std::process::Command;
use std::path::{Path, PathBuf};
use std::os::unix::fs::FileExt;

use rand::{thread_rng, Rng};
use xattr::FileExt as XattrFileExt;

use catfs::CatFS;
use catfs::catfs::error;

#[macro_use]
mod test_suite;

trait Fixture {
    fn setup() -> error::Result<Self>
    where
        Self: std::marker::Sized;
    fn teardown(self) -> error::Result<()>;
}

struct CatFSTests<'a> {
    prefix: PathBuf,
    mnt: PathBuf,
    src: *const PathBuf,
    cache: *const PathBuf,
    session: Option<fuse::BackgroundSession<'a>>,
}

fn copy_all(dir1: &AsRef<Path>, dir2: &AsRef<Path>) -> error::Result<()> {
    fs::create_dir(dir2)?;

    for entry in fs::read_dir(dir1)? {
        let entry = entry?;
        let to = dir2.as_ref().join(entry.file_name());

        if entry.file_type()?.is_dir() {
            copy_all(&entry.path(), &to)?;
        } else {
            fs::copy(entry.path(), to)?;
        }
    }

    return Ok(());
}

impl<'a> CatFSTests<'a> {
    fn get_orig_dir() -> PathBuf {
        let manifest = env::var_os("CARGO_MANIFEST_DIR").unwrap();
        return PathBuf::from(manifest).join("tests/resources");
    }

    fn get_from(&self) -> PathBuf {
        return self.prefix.join("resources");
    }

    fn get_cache(&self) -> PathBuf {
        return self.prefix.join("cache");
    }

    fn extend(&self, s: *const PathBuf) -> &'static PathBuf {
        return unsafe { std::mem::transmute(s) };
    }

    fn mount(&self) -> error::Result<fuse::BackgroundSession<'a>> {
        let src = self.extend(self.src);
        let cache = self.extend(self.cache);
        let fs = CatFS::new(src, cache)?;

        return Ok(unsafe { fuse::spawn_mount(fs, &self.mnt, &[])? });
    }
}

impl<'a> Fixture for CatFSTests<'a> {
    fn setup() -> error::Result<CatFSTests<'a>> {
        #[allow(unused_must_use)] env_logger::init();

        let manifest = env::var_os("CARGO_MANIFEST_DIR").unwrap();
        let prefix = PathBuf::from(manifest).join("target/test").join(
            thread_rng().gen_ascii_chars().take(10).collect::<String>(),
        );
        let mnt = prefix.join("mnt");
        let resources = prefix.join("resources");
        let cache = prefix.join("cache");

        fs::create_dir_all(&mnt)?;
        fs::create_dir_all(&cache)?;

        fs::create_dir(CatFSTests::get_orig_dir().join("dir2"));
        copy_all(&CatFSTests::get_orig_dir(), &resources)?;

        let mut t = CatFSTests {
            prefix: prefix,
            mnt: mnt,
            src: &resources as *const PathBuf,
            cache: &cache as *const PathBuf,
            session: Default::default(),
        };

        std::mem::forget(resources);
        std::mem::forget(cache);

        t.session = Some(t.mount()?);

        return Ok(t);
    }

    fn teardown(self) -> error::Result<()> {
        {
            // move out the session to let us umount first
            #[allow(unused_variables)]
            let session = self.session;
        }
        fs::remove_dir_all(&self.prefix)?;
        // TODO do I need to free self.src/cache?
        return Ok(());
    }
}

fn diff(dir1: &AsRef<Path>, dir2: &AsRef<Path>) {
    let status = Command::new("diff")
        .arg("-ru")
        .arg(dir1.as_ref().as_os_str())
        .arg(dir2.as_ref().as_os_str())
        .status()
        .expect("failed to execute `diff'");
    assert!(status.success());
}

unit_tests!{
    fn read_one(f: &CatFSTests) {
        let mut s = String::new();
        File::open(f.mnt.join("dir1/file1")).unwrap().read_to_string(&mut s).unwrap();
        assert_eq!(s, "dir1/file1\n");
    }

    fn read_all(f: &CatFSTests) {
        diff(&CatFSTests::get_orig_dir(), &f.mnt);
    }

    fn create(f: &CatFSTests) {
        {
            let mut fh = OpenOptions::new().write(true).create(true)
                .open(Path::new(&f.mnt).join("foo")).unwrap();
            fh.write_all(b"hello world").unwrap();
        }

        fs::symlink_metadata(&f.get_from().join("foo")).unwrap();
        diff(&f.get_from(), &f.mnt);
    }

    fn large_write(f: &CatFSTests) {
        let mut of=OsString::from("of=");
        of.push(f.mnt.join("foo"));
        let status = Command::new("dd")
            .arg("if=/dev/zero").arg(of)
            .arg("bs=1M").arg("count=100")
            .status().expect("failed to execute `dd'");
        assert!(status.success());

        let foo = fs::symlink_metadata(&f.get_from().join("foo")).unwrap();
        assert_eq!(foo.len(), 100 * 1024 * 1024);
        diff(&f.get_from(), &f.mnt);
    }

    fn large_dir(f: &CatFSTests) {
        let dir2 = Path::new(&f.mnt).join("dir2");
        for i in 1..1001 {
            let _fh = OpenOptions::new().write(true).create(true)
                .open(dir2.join(format!("{}", i))).unwrap();
        }

        let mut i = 0;
        let mut total = 0;
        for entry in fs::read_dir(dir2).unwrap() {
            i += 1;
            total += entry.unwrap().file_name().to_str().unwrap().parse().unwrap();
        }
        assert_eq!(i, 1000);
        assert_eq!(total, (1000 * (1000 + 1)) / 2);
        diff(&f.get_from(), &f.mnt);
    }

    fn read_modify_write(f: &CatFSTests) {
        let file1 = f.mnt.join("dir1/file1");
        {
            let fh = OpenOptions::new().write(true).open(&file1).unwrap();
            let nbytes = fh.write_at("*".as_bytes(), 9).unwrap();
            assert_eq!(nbytes, 1);
        }

        let mut s = String::new();
        File::open(&file1).unwrap().read_to_string(&mut s).unwrap();
        assert_eq!(s, "dir1/file*\n");
        diff(&f.get_from(), &f.mnt);
    }

    fn unlink(f: &CatFSTests) {
        let file1 = f.mnt.join("dir1/file1");
        fs::remove_file(&file1).unwrap();
        if let Err(e) = fs::symlink_metadata(&file1) {
            assert_eq!(e.kind(), io::ErrorKind::NotFound);
        } else {
            panic!("{:?} still exists", file1);
        }
        diff(&f.get_from(), &f.mnt);
    }

    fn read_unlink(f: &CatFSTests) {
        let file1 = f.mnt.join("dir1/file1");
        {
            let mut s = String::new();
            File::open(&file1).unwrap().read_to_string(&mut s).unwrap();
            assert_eq!(s, "dir1/file1\n");
        }
        fs::remove_file(&file1).unwrap();
        if let Err(e) = fs::symlink_metadata(&file1) {
            assert_eq!(e.kind(), io::ErrorKind::NotFound);
        } else {
            panic!("{:?} still exists", file1);
        }
        diff(&f.get_from(), &f.mnt);
    }

    fn rmdir(f: &CatFSTests) {
        let dir2 = f.mnt.join("dir2");
        fs::remove_dir(&dir2).unwrap();
        if let Err(e) = fs::symlink_metadata(&dir2) {
            assert_eq!(e.kind(), io::ErrorKind::NotFound);
        } else {
            panic!("{:?} still exists", dir2);
        }
        diff(&f.get_from(), &f.mnt);
    }

    fn rmdir_not_empty(f: &CatFSTests) {
        let dir1 = f.mnt.join("dir1");
        if let Err(e) = fs::remove_dir(&dir1) {
            assert_eq!(e.raw_os_error().unwrap(), libc::ENOTEMPTY);
        } else {
            panic!("{:?} deleted", dir1);
        }
    }

    fn check_dirty(f: &CatFSTests) {
        let foo = f.mnt.join("foo");
        let foo_cache = f.get_cache().join("foo");
        {
            let mut fh = OpenOptions::new().write(true).create(true)
                .open(&foo).unwrap();
            fh.write_all(b"hello world").unwrap();

            // at this point the file is NOT flushed yet, so pristine
            // should not be set
            assert!(xattr::get(&foo_cache, "user.catfs.pristine").unwrap().is_none());
        }

        let v = xattr::get(&foo_cache, "user.catfs.pristine").unwrap().unwrap();
        assert_eq!(v, catfs::catfs::file::PRISTINE);

        {
            let mut fh = OpenOptions::new().write(true)
                .open(&foo).unwrap();
            fh.write_all(b"hello world").unwrap();

            // at this point the file is NOT flushed yet, so pristine
            // should be dirty
            let v = xattr::get(&foo_cache, "user.catfs.pristine").unwrap().unwrap();
            assert_eq!(v, catfs::catfs::file::DIRTY);
        }

        let v = xattr::get(&foo_cache, "user.catfs.pristine").unwrap().unwrap();
        assert_eq!(v, catfs::catfs::file::PRISTINE);
    }
}
<commit_msg>add another test to make sure we can write to an already cached file<commit_after>#[allow(unused_imports)]
#[macro_use]
extern crate log;
extern crate libc;
extern crate env_logger;
extern crate catfs;
extern crate rand;
extern crate fuse;
extern crate xattr;

use std::env;
use std::ffi::OsString;
use std::fs;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::{Read, Write};
use std::process::Command;
use std::path::{Path, PathBuf};
use std::os::unix::fs::FileExt;

use rand::{thread_rng, Rng};

use catfs::CatFS;
use catfs::catfs::error;

#[macro_use]
mod test_suite;

trait Fixture {
    fn setup() -> error::Result<Self>
    where
        Self: std::marker::Sized;
    fn teardown(self) -> error::Result<()>;
}

struct CatFSTests<'a> {
    prefix: PathBuf,
    mnt: PathBuf,
    src: *const PathBuf,
    cache: *const PathBuf,
    session: Option<fuse::BackgroundSession<'a>>,
}

fn copy_all(dir1: &AsRef<Path>, dir2: &AsRef<Path>) -> error::Result<()> {
    fs::create_dir(dir2)?;

    for entry in fs::read_dir(dir1)? {
        let entry = entry?;
        let to = dir2.as_ref().join(entry.file_name());

        if entry.file_type()?.is_dir() {
            copy_all(&entry.path(), &to)?;
        } else {
            fs::copy(entry.path(), to)?;
        }
    }

    return Ok(());
}

impl<'a> CatFSTests<'a> {
    fn get_orig_dir() -> PathBuf {
        let manifest = env::var_os("CARGO_MANIFEST_DIR").unwrap();
        return PathBuf::from(manifest).join("tests/resources");
    }

    fn get_from(&self) -> PathBuf {
        return self.prefix.join("resources");
    }

    fn get_cache(&self) -> PathBuf {
        return self.prefix.join("cache");
    }

    fn extend(&self, s: *const PathBuf) -> &'static PathBuf {
        return unsafe { std::mem::transmute(s) };
    }

    fn mount(&self) -> error::Result<fuse::BackgroundSession<'a>> {
        let src = self.extend(self.src);
        let cache = self.extend(self.cache);
        let fs = CatFS::new(src, cache)?;

        return Ok(unsafe { fuse::spawn_mount(fs, &self.mnt, &[])? });
    }
}

impl<'a> Fixture for CatFSTests<'a> {
    fn setup() -> error::Result<CatFSTests<'a>> {
        #[allow(unused_must_use)] env_logger::init();

        let manifest = env::var_os("CARGO_MANIFEST_DIR").unwrap();
        let prefix = PathBuf::from(manifest).join("target/test").join(
            thread_rng().gen_ascii_chars().take(10).collect::<String>(),
        );
        let mnt = prefix.join("mnt");
        let resources = prefix.join("resources");
        let cache = prefix.join("cache");

        fs::create_dir_all(&mnt)?;
        fs::create_dir_all(&cache)?;

        fs::create_dir(CatFSTests::get_orig_dir().join("dir2"));
        copy_all(&CatFSTests::get_orig_dir(), &resources)?;

        let mut t = CatFSTests {
            prefix: prefix,
            mnt: mnt,
            src: &resources as *const PathBuf,
            cache: &cache as *const PathBuf,
            session: Default::default(),
        };

        std::mem::forget(resources);
        std::mem::forget(cache);

        t.session = Some(t.mount()?);

        return Ok(t);
    }

    fn teardown(self) -> error::Result<()> {
        {
            // move out the session to let us umount first
            #[allow(unused_variables)]
            let session = self.session;
        }
        fs::remove_dir_all(&self.prefix)?;
        // TODO do I need to free self.src/cache?
        return Ok(());
    }
}

fn diff(dir1: &AsRef<Path>, dir2: &AsRef<Path>) {
    let status = Command::new("diff")
        .arg("-ru")
        .arg(dir1.as_ref().as_os_str())
        .arg(dir2.as_ref().as_os_str())
        .status()
        .expect("failed to execute `diff'");
    assert!(status.success());
}

unit_tests!{
    fn read_one(f: &CatFSTests) {
        let mut s = String::new();
        File::open(f.mnt.join("dir1/file1")).unwrap().read_to_string(&mut s).unwrap();
        assert_eq!(s, "dir1/file1\n");
    }

    fn read_all(f: &CatFSTests) {
        diff(&CatFSTests::get_orig_dir(), &f.mnt);
    }

    fn create(f: &CatFSTests) {
        {
            let mut fh = OpenOptions::new().write(true).create(true)
                .open(Path::new(&f.mnt).join("foo")).unwrap();
            fh.write_all(b"hello world").unwrap();
        }

        fs::symlink_metadata(&f.get_from().join("foo")).unwrap();
        diff(&f.get_from(), &f.mnt);
    }

    fn large_write(f: &CatFSTests) {
        let mut of=OsString::from("of=");
        of.push(f.mnt.join("foo"));
        let status = Command::new("dd")
            .arg("if=/dev/zero").arg(of)
            .arg("bs=1M").arg("count=100")
            .status().expect("failed to execute `dd'");
        assert!(status.success());

        let foo = fs::symlink_metadata(&f.get_from().join("foo")).unwrap();
        assert_eq!(foo.len(), 100 * 1024 * 1024);
        diff(&f.get_from(), &f.mnt);
    }

    fn large_dir(f: &CatFSTests) {
        let dir2 = Path::new(&f.mnt).join("dir2");
        for i in 1..1001 {
            let _fh = OpenOptions::new().write(true).create(true)
                .open(dir2.join(format!("{}", i))).unwrap();
        }

        let mut i = 0;
        let mut total = 0;
        for entry in fs::read_dir(dir2).unwrap() {
            i += 1;
            total += entry.unwrap().file_name().to_str().unwrap().parse().unwrap();
        }
        assert_eq!(i, 1000);
        assert_eq!(total, (1000 * (1000 + 1)) / 2);
        diff(&f.get_from(), &f.mnt);
    }

    fn read_modify_write(f: &CatFSTests) {
        let file1 = f.mnt.join("dir1/file1");
        {
            let fh = OpenOptions::new().write(true).open(&file1).unwrap();
            let nbytes = fh.write_at("*".as_bytes(), 9).unwrap();
            assert_eq!(nbytes, 1);
        }

        let mut s = String::new();
        File::open(&file1).unwrap().read_to_string(&mut s).unwrap();
        assert_eq!(s, "dir1/file*\n");
        diff(&f.get_from(), &f.mnt);
    }

    fn write_twice(f: &CatFSTests) {
        for _ in 0..2 {
            let mut fh = OpenOptions::new().write(true).create(true)
                .open(Path::new(&f.mnt).join("foo")).unwrap();
            fh.write_all(b"hello world").unwrap();
        }

        fs::symlink_metadata(&f.get_from().join("foo")).unwrap();
        diff(&f.get_from(), &f.mnt);
    }

    fn unlink(f: &CatFSTests) {
        let file1 = f.mnt.join("dir1/file1");
        fs::remove_file(&file1).unwrap();
        if let Err(e) = fs::symlink_metadata(&file1) {
            assert_eq!(e.kind(), io::ErrorKind::NotFound);
        } else {
            panic!("{:?} still exists", file1);
        }
        diff(&f.get_from(), &f.mnt);
    }

    fn read_unlink(f: &CatFSTests) {
        let file1 = f.mnt.join("dir1/file1");
        {
            let mut s = String::new();
            File::open(&file1).unwrap().read_to_string(&mut s).unwrap();
            assert_eq!(s, "dir1/file1\n");
        }
        fs::remove_file(&file1).unwrap();
        if let Err(e) = fs::symlink_metadata(&file1) {
            assert_eq!(e.kind(), io::ErrorKind::NotFound);
        } else {
            panic!("{:?} still exists", file1);
        }
        diff(&f.get_from(), &f.mnt);
    }

    fn rmdir(f: &CatFSTests) {
        let dir2 = f.mnt.join("dir2");
        fs::remove_dir(&dir2).unwrap();
        if let Err(e) = fs::symlink_metadata(&dir2) {
            assert_eq!(e.kind(), io::ErrorKind::NotFound);
        } else {
            panic!("{:?} still exists", dir2);
        }
        diff(&f.get_from(), &f.mnt);
    }

    fn rmdir_not_empty(f: &CatFSTests) {
        let dir1 = f.mnt.join("dir1");
        if let Err(e) = fs::remove_dir(&dir1) {
            assert_eq!(e.raw_os_error().unwrap(), libc::ENOTEMPTY);
        } else {
            panic!("{:?} deleted", dir1);
        }
    }

    fn check_dirty(f: &CatFSTests) {
        let foo = f.mnt.join("foo");
        let foo_cache = f.get_cache().join("foo");
        {
            let mut fh = OpenOptions::new().write(true).create(true)
                .open(&foo).unwrap();
            fh.write_all(b"hello world").unwrap();

            // at this point the file is NOT flushed yet, so pristine
            // should not be set
            assert!(xattr::get(&foo_cache, "user.catfs.pristine").unwrap().is_none());
        }

        let v = xattr::get(&foo_cache, "user.catfs.pristine").unwrap().unwrap();
        assert_eq!(v, catfs::catfs::file::PRISTINE);

        {
            let mut fh = OpenOptions::new().write(true)
                .open(&foo).unwrap();
            fh.write_all(b"hello world").unwrap();

            // at this point the file is NOT flushed yet, so pristine
            // should be dirty
            let v = xattr::get(&foo_cache, "user.catfs.pristine").unwrap().unwrap();
            assert_eq!(v, catfs::catfs::file::DIRTY);
        }

        let v = xattr::get(&foo_cache, "user.catfs.pristine").unwrap().unwrap();
        assert_eq!(v, catfs::catfs::file::PRISTINE);
    }
}
<|endoftext|>


<commit_before>extern crate rustc_serialize;

extern crate docopt;
extern crate turbine_plugins;

use docopt::Docopt;

use turbine_plugins::procfs::MemInfo;

static USAGE: &'static str = "
Usage: check-ram [options]

Options:
    -h, --help             Show this help message

    -w, --warn=<percent>   Percent to warn at     [default: 80]
    -c, --crit=<percent>   Percent to critical at [default: 95]
";

#[derive(RustcDecodable, Debug)]
struct Args {
    flag_help: bool,
    flag_warn:  f64,
    flag_crit:  f64,
}

#[cfg_attr(test, allow(dead_code))]
fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());
    if args.flag_help {
        print!("{}", USAGE);
        return;
    }
    let mem = MemInfo::load();
    println!("Got some meminfo: {:?}", mem);
    match mem.percent_used() {
        Ok(percent) => if percent > args.flag_crit {
            println!("check-ram critical: {:.1}% > {}%", percent, args.flag_crit);
            std::process::exit(2);
        } else if percent > args.flag_warn {
            println!("check-ram warning: {:.1}% > {}%", percent, args.flag_warn)
        } else {
            println!("check-ram okay: {:.1}% < {}%", percent, args.flag_warn)
        },
        Err(e) => {
            println!("check-ram UNEXPECTED ERROR: {:?}", e);
            std::process::exit(3)
        }
    }
}

#[cfg(test)]
mod test {
    use docopt::Docopt;
    use super::{USAGE, Args};

    #[test]
    fn usage_is_valid() {
        let _: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap();
    }
}
<commit_msg>check-ram: More turbine-y and more testy<commit_after>extern crate rustc_serialize;

extern crate docopt;
extern crate turbine_plugins;

use docopt::Docopt;

use turbine_plugins::ExitStatus;
use turbine_plugins::procfs::MemInfo;

static USAGE: &'static str = "
Usage: check-ram [options]

Options:
    -h, --help             Show this help message

    -w, --warn=<percent>   Percent used to warn at      [default: 80]
    -c, --crit=<percent>   Percent used to critical at  [default: 95]
";

#[derive(RustcDecodable, Debug)]
struct Args {
    flag_help: bool,
    flag_warn:  f64,
    flag_crit:  f64,
}

fn compare_status(crit: f64, warn: f64, mem: MemInfo) -> ExitStatus {
    match mem.percent_used() {
        Ok(percent) => {
            if percent > crit {
                println!("check-ram critical: {:.1}% > {}%", percent, crit);
                ExitStatus::Critical
            } else if percent > warn {
                println!("check-ram warning: {:.1}% > {}%", percent, warn);
                ExitStatus::Warning
            } else {
                println!("check-ram okay: {:.1}% < {}%", percent, warn);
                ExitStatus::Ok
            }
        },
        Err(e) => {
            println!("check-ram UNEXPECTED ERROR: {:?}", e);
            ExitStatus::Unknown
        }
    }
}

#[cfg_attr(test, allow(dead_code))]
fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());
    if args.flag_help {
        print!("{}", USAGE);
        return;
    }
    let mem = MemInfo::load();
    let status = compare_status(args.flag_crit, args.flag_warn, mem);
    status.exit();
}

#[cfg(test)]
mod test {
    use docopt::Docopt;
    use super::{USAGE, Args, compare_status};
    use turbine_plugins::ExitStatus;
    use turbine_plugins::procfs::MemInfo;

    #[test]
    fn usage_is_valid() {
        let _: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap();
    }

    #[test]
    fn alerts_when_told_to() {
        let mem = MemInfo {
            total: Some(100),
            available: Some(15),  // 15% free means 85% used
            free: None,
            cached: None
        };
        let crit_threshold = 80.0;

        assert_eq!(compare_status(crit_threshold, 25.0, mem), ExitStatus::Critical);
    }
}
<|endoftext|>


<commit_before>use parser::ast::ctxt::*;

use sym::Sym::*;
use sym::BuiltinType;

pub fn init(ctxt: &Context) {
    add_builtin_types(ctxt);
    add_builtin_functions(ctxt);
}

fn add_builtin_types(ctxt: &Context) {
    builtin_type("int", BuiltinType::Int, ctxt);
    builtin_type("bool", BuiltinType::Bool, ctxt);
    builtin_type("str", BuiltinType::Str, ctxt);
}

fn builtin_type(name: &str, ty: BuiltinType, ctxt: &Context) {
    let name = ctxt.interner.intern(name.into());
    assert!(ctxt.sym.borrow_mut().insert(name, SymType(ty)).is_none());
}

fn add_builtin_functions(ctxt: &Context) {
    // TODO: add println(str) and assert(bool)
    let name = ctxt.interner.intern("assert");

    let fct_info = FctInfo {
        name: name,
        params_types: vec![BuiltinType::Bool],
        return_type: BuiltinType::Unit,
        ast: None,
    };

    assert!(ctxt.add_function(fct_info).is_ok());
}
<commit_msg>semck: added builtin functions<commit_after>use parser::ast::ctxt::*;

use sym::Sym::*;
use sym::BuiltinType;

pub fn init(ctxt: &Context) {
    add_builtin_types(ctxt);
    add_builtin_functions(ctxt);
}

fn add_builtin_types(ctxt: &Context) {
    builtin_type("int", BuiltinType::Int, ctxt);
    builtin_type("bool", BuiltinType::Bool, ctxt);
    builtin_type("str", BuiltinType::Str, ctxt);
}

fn builtin_type(name: &str, ty: BuiltinType, ctxt: &Context) {
    let name = ctxt.interner.intern(name.into());
    assert!(ctxt.sym.borrow_mut().insert(name, SymType(ty)).is_none());
}

fn add_builtin_functions(ctxt: &Context) {
    builtin_function("assert", BuiltinType::Bool, BuiltinType::Unit, ctxt);
    builtin_function("print", BuiltinType::Str, BuiltinType::Unit, ctxt);
    builtin_function("println", BuiltinType::Str, BuiltinType::Unit, ctxt);
}

fn builtin_function(name: &str, arg: BuiltinType, ret: BuiltinType, ctxt: &Context) {
    let name = ctxt.interner.intern(name);

    let fct_info = FctInfo {
        name: name,
        params_types: vec![arg],
        return_type: ret,
        ast: None,
    };

    assert!(ctxt.add_function(fct_info).is_ok());
}

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

    #[test]
    fn builtin_functions() {
        ok("fn f() { assert(true); }");
        ok("fn f() { print(\"test\"); }");
        ok("fn f() { println(\"test\"); }");
    }
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

mod signature_tests;
<commit_msg>[language][bytecode verifier] remove unreachable code/file unit_tests.rs<commit_after><|endoftext|>


<commit_before>//! A solver for dataflow problems.

use std::borrow::BorrowMut;
use std::ffi::OsString;
use std::fs;
use std::path::PathBuf;

use rustc_ast as ast;
use rustc_data_structures::work_queue::WorkQueue;
use rustc_graphviz as dot;
use rustc_hir::def_id::DefId;
use rustc_index::bit_set::BitSet;
use rustc_index::vec::{Idx, IndexVec};
use rustc_middle::mir::{self, traversal, BasicBlock};
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::symbol::{sym, Symbol};

use super::fmt::DebugWithContext;
use super::graphviz;
use super::{
    visit_results, Analysis, Direction, GenKill, GenKillAnalysis, GenKillSet, JoinSemiLattice,
    ResultsCursor, ResultsVisitor,
};
use crate::util::pretty::dump_enabled;

/// A dataflow analysis that has converged to fixpoint.
pub struct Results<'tcx, A>
where
    A: Analysis<'tcx>,
{
    pub analysis: A,
    pub(super) entry_sets: IndexVec<BasicBlock, A::Domain>,
}

impl<A> Results<'tcx, A>
where
    A: Analysis<'tcx>,
{
    /// Creates a `ResultsCursor` that can inspect these `Results`.
    pub fn into_results_cursor(self, body: &'mir mir::Body<'tcx>) -> ResultsCursor<'mir, 'tcx, A> {
        ResultsCursor::new(body, self)
    }

    /// Gets the dataflow state for the given block.
    pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain {
        &self.entry_sets[block]
    }

    pub fn visit_with(
        &self,
        body: &'mir mir::Body<'tcx>,
        blocks: impl IntoIterator<Item = BasicBlock>,
        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
    ) {
        visit_results(body, blocks, self, vis)
    }

    pub fn visit_reachable_with(
        &self,
        body: &'mir mir::Body<'tcx>,
        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
    ) {
        let blocks = mir::traversal::reachable(body);
        visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
    }

    pub fn visit_in_rpo_with(
        &self,
        body: &'mir mir::Body<'tcx>,
        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
    ) {
        let blocks = mir::traversal::reverse_postorder(body);
        visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
    }
}

/// A solver for dataflow problems.
pub struct Engine<'a, 'tcx, A>
where
    A: Analysis<'tcx>,
{
    tcx: TyCtxt<'tcx>,
    body: &'a mir::Body<'tcx>,
    dead_unwinds: Option<&'a BitSet<BasicBlock>>,
    entry_sets: IndexVec<BasicBlock, A::Domain>,
    pass_name: Option<&'static str>,
    analysis: A,

    /// Cached, cumulative transfer functions for each block.
    //
    // FIXME(ecstaticmorse): This boxed `Fn` trait object is invoked inside a tight loop for
    // gen/kill problems on cyclic CFGs. This is not ideal, but it doesn't seem to degrade
    // performance in practice. I've tried a few ways to avoid this, but they have downsides. See
    // the message for the commit that added this FIXME for more information.
    apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
}

impl<A, D, T> Engine<'a, 'tcx, A>
where
    A: GenKillAnalysis<'tcx, Idx = T, Domain = D>,
    D: Clone + JoinSemiLattice + GenKill<T> + BorrowMut<BitSet<T>>,
    T: Idx,
{
    /// Creates a new `Engine` to solve a gen-kill dataflow problem.
    pub fn new_gen_kill(tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, analysis: A) -> Self {
        // If there are no back-edges in the control-flow graph, we only ever need to apply the
        // transfer function for each block exactly once (assuming that we process blocks in RPO).
        //
        // In this case, there's no need to compute the block transfer functions ahead of time.
        if !body.is_cfg_cyclic() {
            return Self::new(tcx, body, analysis, None);
        }

        // Otherwise, compute and store the cumulative transfer function for each block.

        let identity = GenKillSet::identity(analysis.bottom_value(body).borrow().domain_size());
        let mut trans_for_block = IndexVec::from_elem(identity, body.basic_blocks());

        for (block, block_data) in body.basic_blocks().iter_enumerated() {
            let trans = &mut trans_for_block[block];
            A::Direction::gen_kill_effects_in_block(&analysis, trans, block, block_data);
        }

        let apply_trans = Box::new(move |bb: BasicBlock, state: &mut A::Domain| {
            trans_for_block[bb].apply(state.borrow_mut());
        });

        Self::new(tcx, body, analysis, Some(apply_trans as Box<_>))
    }
}

impl<A, D> Engine<'a, 'tcx, A>
where
    A: Analysis<'tcx, Domain = D>,
    D: Clone + JoinSemiLattice,
{
    /// Creates a new `Engine` to solve a dataflow problem with an arbitrary transfer
    /// function.
    ///
    /// Gen-kill problems should use `new_gen_kill`, which will coalesce transfer functions for
    /// better performance.
    pub fn new_generic(tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, analysis: A) -> Self {
        Self::new(tcx, body, analysis, None)
    }

    fn new(
        tcx: TyCtxt<'tcx>,
        body: &'a mir::Body<'tcx>,
        analysis: A,
        apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
    ) -> Self {
        let bottom_value = analysis.bottom_value(body);
        let mut entry_sets = IndexVec::from_elem(bottom_value.clone(), body.basic_blocks());
        analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]);

        if A::Direction::is_backward() && entry_sets[mir::START_BLOCK] != bottom_value {
            bug!("`initialize_start_block` is not yet supported for backward dataflow analyses");
        }

        Engine {
            analysis,
            tcx,
            body,
            dead_unwinds: None,
            pass_name: None,
            entry_sets,
            apply_trans_for_block,
        }
    }

    /// Signals that we do not want dataflow state to propagate across unwind edges for these
    /// `BasicBlock`s.
    ///
    /// You must take care that `dead_unwinds` does not contain a `BasicBlock` that *can* actually
    /// unwind during execution. Otherwise, your dataflow results will not be correct.
    pub fn dead_unwinds(mut self, dead_unwinds: &'a BitSet<BasicBlock>) -> Self {
        self.dead_unwinds = Some(dead_unwinds);
        self
    }

    /// Adds an identifier to the graphviz output for this particular run of a dataflow analysis.
    ///
    /// Some analyses are run multiple times in the compilation pipeline. Give them a `pass_name`
    /// to differentiate them. Otherwise, only the results for the latest run will be saved.
    pub fn pass_name(mut self, name: &'static str) -> Self {
        self.pass_name = Some(name);
        self
    }

    /// Computes the fixpoint for this dataflow problem and returns it.
    pub fn iterate_to_fixpoint(self) -> Results<'tcx, A>
    where
        A::Domain: DebugWithContext<A>,
    {
        let Engine {
            analysis,
            body,
            dead_unwinds,
            mut entry_sets,
            tcx,
            apply_trans_for_block,
            pass_name,
            ..
        } = self;

        let mut dirty_queue: WorkQueue<BasicBlock> =
            WorkQueue::with_none(body.basic_blocks().len());

        if A::Direction::is_forward() {
            for (bb, _) in traversal::reverse_postorder(body) {
                dirty_queue.insert(bb);
            }
        } else {
            // Reverse post-order on the reverse CFG may generate a better iteration order for
            // backward dataflow analyses, but probably not enough to matter.
            for (bb, _) in traversal::postorder(body) {
                dirty_queue.insert(bb);
            }
        }

        let mut state = analysis.bottom_value(body);
        while let Some(bb) = dirty_queue.pop() {
            let bb_data = &body[bb];

            // Apply the block transfer function, using the cached one if it exists.
            state.clone_from(&entry_sets[bb]);
            match &apply_trans_for_block {
                Some(apply) => apply(bb, &mut state),
                None => A::Direction::apply_effects_in_block(&analysis, &mut state, bb, bb_data),
            }

            A::Direction::join_state_into_successors_of(
                &analysis,
                tcx,
                body,
                dead_unwinds,
                &mut state,
                (bb, bb_data),
                |target: BasicBlock, state: &A::Domain| {
                    let set_changed = entry_sets[target].join(state);
                    if set_changed {
                        dirty_queue.insert(target);
                    }
                },
            );
        }

        let results = Results { analysis, entry_sets };

        let res = write_graphviz_results(tcx, &body, &results, pass_name);
        if let Err(e) = res {
            warn!("Failed to write graphviz dataflow results: {}", e);
        }

        results
    }
}

// Graphviz

/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via
/// `rustc_mir` attributes.
fn write_graphviz_results<A>(
    tcx: TyCtxt<'tcx>,
    body: &mir::Body<'tcx>,
    results: &Results<'tcx, A>,
    pass_name: Option<&'static str>,
) -> std::io::Result<()>
where
    A: Analysis<'tcx>,
    A::Domain: DebugWithContext<A>,
{
    let def_id = body.source.def_id();
    let attrs = match RustcMirAttrs::parse(tcx, def_id) {
        Ok(attrs) => attrs,

        // Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse`
        Err(()) => return Ok(()),
    };

    let path = match attrs.output_path(A::NAME) {
        Some(path) => path,

        None if tcx.sess.opts.debugging_opts.dump_mir_dataflow
            && dump_enabled(tcx, A::NAME, def_id) =>
        {
            // FIXME: Use some variant of `pretty::dump_path` for this
            let mut path = PathBuf::from(&tcx.sess.opts.debugging_opts.dump_mir_dir);

            let crate_name = tcx.crate_name(def_id.krate);
            let item_name = ty::print::with_forced_impl_filename_line(|| {
                tcx.def_path(def_id).to_filename_friendly_no_crate()
            });

            let pass_name = pass_name.map(|s| format!(".{}", s)).unwrap_or_default();

            path.push(format!("{}.{}.{}{}.dot", crate_name, item_name, A::NAME, pass_name));
            path
        }

        None => return Ok(()),
    };

    let style = match attrs.formatter {
        Some(sym::two_phase) => graphviz::OutputStyle::BeforeAndAfter,
        _ => graphviz::OutputStyle::AfterOnly,
    };

    debug!("printing dataflow results for {:?} to {}", def_id, path.display());
    let mut buf = Vec::new();

    let graphviz = graphviz::Formatter::new(body, results, style);
    let mut render_opts =
        vec![dot::RenderOption::Fontname(tcx.sess.opts.debugging_opts.graphviz_font.clone())];
    if tcx.sess.opts.debugging_opts.graphviz_dark_mode {
        render_opts.push(dot::RenderOption::DarkTheme);
    }
    dot::render_opts(&graphviz, &mut buf, &render_opts)?;

    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent)?;
    }
    fs::write(&path, buf)?;

    Ok(())
}

#[derive(Default)]
struct RustcMirAttrs {
    basename_and_suffix: Option<PathBuf>,
    formatter: Option<Symbol>,
}

impl RustcMirAttrs {
    fn parse(tcx: TyCtxt<'tcx>, def_id: DefId) -> Result<Self, ()> {
        let attrs = tcx.get_attrs(def_id);

        let mut result = Ok(());
        let mut ret = RustcMirAttrs::default();

        let rustc_mir_attrs = attrs
            .iter()
            .filter(|attr| tcx.sess.check_name(attr, sym::rustc_mir))
            .flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter()));

        for attr in rustc_mir_attrs {
            let attr_result = if attr.has_name(sym::borrowck_graphviz_postflow) {
                Self::set_field(&mut ret.basename_and_suffix, tcx, &attr, |s| {
                    let path = PathBuf::from(s.to_string());
                    match path.file_name() {
                        Some(_) => Ok(path),
                        None => {
                            tcx.sess.span_err(attr.span(), "path must end in a filename");
                            Err(())
                        }
                    }
                })
            } else if attr.has_name(sym::borrowck_graphviz_format) {
                Self::set_field(&mut ret.formatter, tcx, &attr, |s| match s {
                    sym::gen_kill | sym::two_phase => Ok(s),
                    _ => {
                        tcx.sess.span_err(attr.span(), "unknown formatter");
                        Err(())
                    }
                })
            } else {
                Ok(())
            };

            result = result.and(attr_result);
        }

        result.map(|()| ret)
    }

    fn set_field<T>(
        field: &mut Option<T>,
        tcx: TyCtxt<'tcx>,
        attr: &ast::NestedMetaItem,
        mapper: impl FnOnce(Symbol) -> Result<T, ()>,
    ) -> Result<(), ()> {
        if field.is_some() {
            tcx.sess
                .span_err(attr.span(), &format!("duplicate values for `{}`", attr.name_or_empty()));

            return Err(());
        }

        if let Some(s) = attr.value_str() {
            *field = Some(mapper(s)?);
            Ok(())
        } else {
            tcx.sess
                .span_err(attr.span(), &format!("`{}` requires an argument", attr.name_or_empty()));
            Err(())
        }
    }

    /// Returns the path where dataflow results should be written, or `None`
    /// `borrowck_graphviz_postflow` was not specified.
    ///
    /// This performs the following transformation to the argument of `borrowck_graphviz_postflow`:
    ///
    /// "path/suffix.dot" -> "path/analysis_name_suffix.dot"
    fn output_path(&self, analysis_name: &str) -> Option<PathBuf> {
        let mut ret = self.basename_and_suffix.as_ref().cloned()?;
        let suffix = ret.file_name().unwrap(); // Checked when parsing attrs

        let mut file_name: OsString = analysis_name.into();
        file_name.push("_");
        file_name.push(suffix);
        ret.set_file_name(file_name);

        Some(ret)
    }
}
<commit_msg>Use MIR dump interface for dataflow<commit_after>//! A solver for dataflow problems.

use std::borrow::BorrowMut;
use std::ffi::OsString;
use std::path::PathBuf;

use rustc_ast as ast;
use rustc_data_structures::work_queue::WorkQueue;
use rustc_graphviz as dot;
use rustc_hir::def_id::DefId;
use rustc_index::bit_set::BitSet;
use rustc_index::vec::{Idx, IndexVec};
use rustc_middle::mir::{self, traversal, BasicBlock};
use rustc_middle::ty::TyCtxt;
use rustc_span::symbol::{sym, Symbol};

use super::fmt::DebugWithContext;
use super::graphviz;
use super::{
    visit_results, Analysis, Direction, GenKill, GenKillAnalysis, GenKillSet, JoinSemiLattice,
    ResultsCursor, ResultsVisitor,
};
use crate::util::pretty::{create_dump_file, dump_enabled};

/// A dataflow analysis that has converged to fixpoint.
pub struct Results<'tcx, A>
where
    A: Analysis<'tcx>,
{
    pub analysis: A,
    pub(super) entry_sets: IndexVec<BasicBlock, A::Domain>,
}

impl<A> Results<'tcx, A>
where
    A: Analysis<'tcx>,
{
    /// Creates a `ResultsCursor` that can inspect these `Results`.
    pub fn into_results_cursor(self, body: &'mir mir::Body<'tcx>) -> ResultsCursor<'mir, 'tcx, A> {
        ResultsCursor::new(body, self)
    }

    /// Gets the dataflow state for the given block.
    pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain {
        &self.entry_sets[block]
    }

    pub fn visit_with(
        &self,
        body: &'mir mir::Body<'tcx>,
        blocks: impl IntoIterator<Item = BasicBlock>,
        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
    ) {
        visit_results(body, blocks, self, vis)
    }

    pub fn visit_reachable_with(
        &self,
        body: &'mir mir::Body<'tcx>,
        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
    ) {
        let blocks = mir::traversal::reachable(body);
        visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
    }

    pub fn visit_in_rpo_with(
        &self,
        body: &'mir mir::Body<'tcx>,
        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
    ) {
        let blocks = mir::traversal::reverse_postorder(body);
        visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
    }
}

/// A solver for dataflow problems.
pub struct Engine<'a, 'tcx, A>
where
    A: Analysis<'tcx>,
{
    tcx: TyCtxt<'tcx>,
    body: &'a mir::Body<'tcx>,
    dead_unwinds: Option<&'a BitSet<BasicBlock>>,
    entry_sets: IndexVec<BasicBlock, A::Domain>,
    pass_name: Option<&'static str>,
    analysis: A,

    /// Cached, cumulative transfer functions for each block.
    //
    // FIXME(ecstaticmorse): This boxed `Fn` trait object is invoked inside a tight loop for
    // gen/kill problems on cyclic CFGs. This is not ideal, but it doesn't seem to degrade
    // performance in practice. I've tried a few ways to avoid this, but they have downsides. See
    // the message for the commit that added this FIXME for more information.
    apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
}

impl<A, D, T> Engine<'a, 'tcx, A>
where
    A: GenKillAnalysis<'tcx, Idx = T, Domain = D>,
    D: Clone + JoinSemiLattice + GenKill<T> + BorrowMut<BitSet<T>>,
    T: Idx,
{
    /// Creates a new `Engine` to solve a gen-kill dataflow problem.
    pub fn new_gen_kill(tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, analysis: A) -> Self {
        // If there are no back-edges in the control-flow graph, we only ever need to apply the
        // transfer function for each block exactly once (assuming that we process blocks in RPO).
        //
        // In this case, there's no need to compute the block transfer functions ahead of time.
        if !body.is_cfg_cyclic() {
            return Self::new(tcx, body, analysis, None);
        }

        // Otherwise, compute and store the cumulative transfer function for each block.

        let identity = GenKillSet::identity(analysis.bottom_value(body).borrow().domain_size());
        let mut trans_for_block = IndexVec::from_elem(identity, body.basic_blocks());

        for (block, block_data) in body.basic_blocks().iter_enumerated() {
            let trans = &mut trans_for_block[block];
            A::Direction::gen_kill_effects_in_block(&analysis, trans, block, block_data);
        }

        let apply_trans = Box::new(move |bb: BasicBlock, state: &mut A::Domain| {
            trans_for_block[bb].apply(state.borrow_mut());
        });

        Self::new(tcx, body, analysis, Some(apply_trans as Box<_>))
    }
}

impl<A, D> Engine<'a, 'tcx, A>
where
    A: Analysis<'tcx, Domain = D>,
    D: Clone + JoinSemiLattice,
{
    /// Creates a new `Engine` to solve a dataflow problem with an arbitrary transfer
    /// function.
    ///
    /// Gen-kill problems should use `new_gen_kill`, which will coalesce transfer functions for
    /// better performance.
    pub fn new_generic(tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, analysis: A) -> Self {
        Self::new(tcx, body, analysis, None)
    }

    fn new(
        tcx: TyCtxt<'tcx>,
        body: &'a mir::Body<'tcx>,
        analysis: A,
        apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
    ) -> Self {
        let bottom_value = analysis.bottom_value(body);
        let mut entry_sets = IndexVec::from_elem(bottom_value.clone(), body.basic_blocks());
        analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]);

        if A::Direction::is_backward() && entry_sets[mir::START_BLOCK] != bottom_value {
            bug!("`initialize_start_block` is not yet supported for backward dataflow analyses");
        }

        Engine {
            analysis,
            tcx,
            body,
            dead_unwinds: None,
            pass_name: None,
            entry_sets,
            apply_trans_for_block,
        }
    }

    /// Signals that we do not want dataflow state to propagate across unwind edges for these
    /// `BasicBlock`s.
    ///
    /// You must take care that `dead_unwinds` does not contain a `BasicBlock` that *can* actually
    /// unwind during execution. Otherwise, your dataflow results will not be correct.
    pub fn dead_unwinds(mut self, dead_unwinds: &'a BitSet<BasicBlock>) -> Self {
        self.dead_unwinds = Some(dead_unwinds);
        self
    }

    /// Adds an identifier to the graphviz output for this particular run of a dataflow analysis.
    ///
    /// Some analyses are run multiple times in the compilation pipeline. Give them a `pass_name`
    /// to differentiate them. Otherwise, only the results for the latest run will be saved.
    pub fn pass_name(mut self, name: &'static str) -> Self {
        self.pass_name = Some(name);
        self
    }

    /// Computes the fixpoint for this dataflow problem and returns it.
    pub fn iterate_to_fixpoint(self) -> Results<'tcx, A>
    where
        A::Domain: DebugWithContext<A>,
    {
        let Engine {
            analysis,
            body,
            dead_unwinds,
            mut entry_sets,
            tcx,
            apply_trans_for_block,
            pass_name,
            ..
        } = self;

        let mut dirty_queue: WorkQueue<BasicBlock> =
            WorkQueue::with_none(body.basic_blocks().len());

        if A::Direction::is_forward() {
            for (bb, _) in traversal::reverse_postorder(body) {
                dirty_queue.insert(bb);
            }
        } else {
            // Reverse post-order on the reverse CFG may generate a better iteration order for
            // backward dataflow analyses, but probably not enough to matter.
            for (bb, _) in traversal::postorder(body) {
                dirty_queue.insert(bb);
            }
        }

        let mut state = analysis.bottom_value(body);
        while let Some(bb) = dirty_queue.pop() {
            let bb_data = &body[bb];

            // Apply the block transfer function, using the cached one if it exists.
            state.clone_from(&entry_sets[bb]);
            match &apply_trans_for_block {
                Some(apply) => apply(bb, &mut state),
                None => A::Direction::apply_effects_in_block(&analysis, &mut state, bb, bb_data),
            }

            A::Direction::join_state_into_successors_of(
                &analysis,
                tcx,
                body,
                dead_unwinds,
                &mut state,
                (bb, bb_data),
                |target: BasicBlock, state: &A::Domain| {
                    let set_changed = entry_sets[target].join(state);
                    if set_changed {
                        dirty_queue.insert(target);
                    }
                },
            );
        }

        let results = Results { analysis, entry_sets };

        let res = write_graphviz_results(tcx, &body, &results, pass_name);
        if let Err(e) = res {
            warn!("Failed to write graphviz dataflow results: {}", e);
        }

        results
    }
}

// Graphviz

/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via
/// `rustc_mir` attributes.
fn write_graphviz_results<A>(
    tcx: TyCtxt<'tcx>,
    body: &mir::Body<'tcx>,
    results: &Results<'tcx, A>,
    pass_name: Option<&'static str>,
) -> std::io::Result<()>
where
    A: Analysis<'tcx>,
    A::Domain: DebugWithContext<A>,
{
    use std::fs;
    use std::io::{self, Write};

    let def_id = body.source.def_id();
    let attrs = match RustcMirAttrs::parse(tcx, def_id) {
        Ok(attrs) => attrs,

        // Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse`
        Err(()) => return Ok(()),
    };

    let mut file = match attrs.output_path(A::NAME) {
        Some(path) => {
            debug!("printing dataflow results for {:?} to {}", def_id, path.display());
            if let Some(parent) = path.parent() {
                fs::create_dir_all(parent)?;
            }
            io::BufWriter::new(fs::File::create(&path)?)
        }

        None if tcx.sess.opts.debugging_opts.dump_mir_dataflow
            && dump_enabled(tcx, A::NAME, def_id) =>
        {
            create_dump_file(
                tcx,
                ".dot",
                None,
                A::NAME,
                &pass_name.unwrap_or("-----"),
                body.source,
            )?
        }

        _ => return Ok(()),
    };

    let style = match attrs.formatter {
        Some(sym::two_phase) => graphviz::OutputStyle::BeforeAndAfter,
        _ => graphviz::OutputStyle::AfterOnly,
    };

    let mut buf = Vec::new();

    let graphviz = graphviz::Formatter::new(body, results, style);
    let mut render_opts =
        vec![dot::RenderOption::Fontname(tcx.sess.opts.debugging_opts.graphviz_font.clone())];
    if tcx.sess.opts.debugging_opts.graphviz_dark_mode {
        render_opts.push(dot::RenderOption::DarkTheme);
    }
    dot::render_opts(&graphviz, &mut buf, &render_opts)?;

    file.write_all(&buf)?;

    Ok(())
}

#[derive(Default)]
struct RustcMirAttrs {
    basename_and_suffix: Option<PathBuf>,
    formatter: Option<Symbol>,
}

impl RustcMirAttrs {
    fn parse(tcx: TyCtxt<'tcx>, def_id: DefId) -> Result<Self, ()> {
        let attrs = tcx.get_attrs(def_id);

        let mut result = Ok(());
        let mut ret = RustcMirAttrs::default();

        let rustc_mir_attrs = attrs
            .iter()
            .filter(|attr| tcx.sess.check_name(attr, sym::rustc_mir))
            .flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter()));

        for attr in rustc_mir_attrs {
            let attr_result = if attr.has_name(sym::borrowck_graphviz_postflow) {
                Self::set_field(&mut ret.basename_and_suffix, tcx, &attr, |s| {
                    let path = PathBuf::from(s.to_string());
                    match path.file_name() {
                        Some(_) => Ok(path),
                        None => {
                            tcx.sess.span_err(attr.span(), "path must end in a filename");
                            Err(())
                        }
                    }
                })
            } else if attr.has_name(sym::borrowck_graphviz_format) {
                Self::set_field(&mut ret.formatter, tcx, &attr, |s| match s {
                    sym::gen_kill | sym::two_phase => Ok(s),
                    _ => {
                        tcx.sess.span_err(attr.span(), "unknown formatter");
                        Err(())
                    }
                })
            } else {
                Ok(())
            };

            result = result.and(attr_result);
        }

        result.map(|()| ret)
    }

    fn set_field<T>(
        field: &mut Option<T>,
        tcx: TyCtxt<'tcx>,
        attr: &ast::NestedMetaItem,
        mapper: impl FnOnce(Symbol) -> Result<T, ()>,
    ) -> Result<(), ()> {
        if field.is_some() {
            tcx.sess
                .span_err(attr.span(), &format!("duplicate values for `{}`", attr.name_or_empty()));

            return Err(());
        }

        if let Some(s) = attr.value_str() {
            *field = Some(mapper(s)?);
            Ok(())
        } else {
            tcx.sess
                .span_err(attr.span(), &format!("`{}` requires an argument", attr.name_or_empty()));
            Err(())
        }
    }

    /// Returns the path where dataflow results should be written, or `None`
    /// `borrowck_graphviz_postflow` was not specified.
    ///
    /// This performs the following transformation to the argument of `borrowck_graphviz_postflow`:
    ///
    /// "path/suffix.dot" -> "path/analysis_name_suffix.dot"
    fn output_path(&self, analysis_name: &str) -> Option<PathBuf> {
        let mut ret = self.basename_and_suffix.as_ref().cloned()?;
        let suffix = ret.file_name().unwrap(); // Checked when parsing attrs

        let mut file_name: OsString = analysis_name.into();
        file_name.push("_");
        file_name.push(suffix);
        ret.set_file_name(file_name);

        Some(ret)
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::str::FromStr;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

use base64;
use clap::ArgMatches;
use hcore::package::{PackageArchive, PackageIdent};
use common::ui::UI;
use rand;
use rand::Rng;

use export_docker::Result;

use habitat_sup::manager::service::Topology;
use manifestjson::ManifestJson;
use bind;

#[derive(Debug, Clone)]
pub struct Manifest {
    pub metadata_name: String,
    pub habitat_name: String,
    pub image: String,
    pub count: u64,
    pub service_topology: Topology,
    pub service_group: Option<String>,
    pub config: Option<String>,
    pub ring_secret_name: Option<String>,
    pub binds: Vec<bind::Bind>,
}

impl Manifest {
    pub fn new_from_cli_matches(_ui: &mut UI, matches: &ArgMatches) -> Result<Self> {
        let count = matches.value_of("COUNT").unwrap_or("1").parse()?;
        let topology: Topology = matches
            .value_of("TOPOLOGY")
            .unwrap_or("standalone")
            .parse()
            .unwrap_or(Default::default());
        let group = matches.value_of("GROUP").map(|s| s.to_string());
        let config_file = matches.value_of("CONFIG");
        let ring_secret_name = matches.value_of("RING_SECRET_NAME").map(|s| s.to_string());
        // clap ensures that we do have the mandatory args so unwrap() is fine here
        let pkg_ident_str = matches.value_of("PKG_IDENT_OR_ARTIFACT").expect(
            "No package specified",
        );
        let pkg_ident = if Path::new(pkg_ident_str).is_file() {
            // We're going to use the `$pkg_origin/$pkg_name`, fuzzy form of a package
            // identifier to ensure that update strategies will work if desired
            PackageArchive::new(pkg_ident_str).ident()?
        } else {
            PackageIdent::from_str(pkg_ident_str)?
        };

        // To allow multiple instances of Habitat application in Kubernetes,
        // random suffix in metadata_name is needed.
        let metadata_name =
            format!(
            "{}-{}",
            pkg_ident.name,
            rand::thread_rng()
                .gen_ascii_chars()
                .filter(|c| c.is_lowercase() || c.is_numeric())
                .take(5)
                .collect::<String>(),
        );

        let image = match matches.value_of("IMAGE_NAME") {
            Some(i) => i.to_string(),
            None => pkg_ident.origin + "/" + &pkg_ident.name + ":latest",
        };

        let binds = bind::parse_bind_args(&matches)?;

        let config = match config_file {
            None => None,
            Some(name) => {
                let mut contents = String::new();
                File::open(name)?.read_to_string(&mut contents)?;

                Some(base64::encode(&format!("{}", contents)))
            }
        };

        Ok(Manifest {
            metadata_name: metadata_name,
            habitat_name: pkg_ident.name,
            image: image,
            count: count,
            service_topology: topology,
            service_group: group,
            config: config,
            ring_secret_name: ring_secret_name,
            binds: binds,
        })
    }

    pub fn generate(&mut self, write: &mut Write) -> Result<()> {
        let out = ManifestJson::new(&self).into_string()?;

        write.write(out.as_bytes())?;

        Ok(())
    }
}

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

    #[test]
    fn test_manifest_generation() {
        let mut m = Manifest {
            metadata_name: "nginx-f84iq".to_owned(),
            habitat_name: "nginx".to_owned(),
            image: "core/nginx".to_owned(),
            count: 3,
            service_topology: Default::default(),
            service_group: Some("group1".to_owned()),
            config: Some(base64::encode(&format!("{}", "port = 4444"))),
            ring_secret_name: Some("deltaechofoxtrot".to_owned()),
            binds: vec![],
        };

        let expected = include_str!("../tests/KubernetesManifestTest.yaml");

        let mut o = vec![];
        m.generate(&mut o).unwrap();

        let out = String::from_utf8(o).unwrap();

        assert_eq!(out, expected);
    }

    #[test]
    fn test_manifest_generation_binds() {
        let mut m = Manifest {
            metadata_name: "nginx-f84iq".to_owned(),
            habitat_name: "nginx".to_owned(),
            image: "core/nginx".to_owned(),
            count: 3,
            service_topology: Default::default(),
            service_group: Some("group1".to_owned()),
            config: None,
            ring_secret_name: Some("deltaechofoxtrot".to_owned()),
            binds: vec![
                bind::Bind {
                    name: "name1".to_owned(),
                    service: "service1".to_owned(),
                    group: "group1".to_owned(),
                },
            ],
        };

        let expected = include_str!("../tests/KubernetesManifestTestBinds.yaml");

        let mut o = vec![];
        m.generate(&mut o).unwrap();

        let out = String::from_utf8(o).unwrap();

        assert_eq!(out, expected);
    }
}
<commit_msg>pkg-export-k8s: Add docs to Manifest<commit_after>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::str::FromStr;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

use base64;
use clap::ArgMatches;
use hcore::package::{PackageArchive, PackageIdent};
use common::ui::UI;
use rand;
use rand::Rng;

use export_docker::Result;

use habitat_sup::manager::service::Topology;
use manifestjson::ManifestJson;
use bind;

/// Represents a Kubernetes manifest.
#[derive(Debug, Clone)]
pub struct Manifest {
    /// The service name in the Kubernetes cluster.
    pub metadata_name: String,
    /// The Habitat service name.
    pub habitat_name: String,
    /// The docker image.
    pub image: String,
    /// The number of desired instances in the service group.
    pub count: u64,
    /// The relationship of a service with peers in the same service group.
    pub service_topology: Topology,
    /// The logical group of services in the service group.
    pub service_group: Option<String>,
    /// The config file content (in base64 encoded format).
    pub config: Option<String>,
    /// The name of the Kubernetes secret that contains the ring key, which encrypts the
    /// communication between Habitat supervisors.
    pub ring_secret_name: Option<String>,

    /// Any binds, as [`Bind`] instances.
    ///
    /// [`Bind`]: ../bind/struct.Bind.html
    pub binds: Vec<bind::Bind>,
}

impl Manifest {
    ///
    /// Create a Manifest instance from command-line arguments passed as [`clap::ArgMatches`].
    ///
    /// [`clap::ArgMatches`]: https://kbknapp.github.io/clap-rs/clap/struct.ArgMatches.html
    pub fn new_from_cli_matches(_ui: &mut UI, matches: &ArgMatches) -> Result<Self> {
        let count = matches.value_of("COUNT").unwrap_or("1").parse()?;
        let topology: Topology = matches
            .value_of("TOPOLOGY")
            .unwrap_or("standalone")
            .parse()
            .unwrap_or(Default::default());
        let group = matches.value_of("GROUP").map(|s| s.to_string());
        let config_file = matches.value_of("CONFIG");
        let ring_secret_name = matches.value_of("RING_SECRET_NAME").map(|s| s.to_string());
        // clap ensures that we do have the mandatory args so unwrap() is fine here
        let pkg_ident_str = matches.value_of("PKG_IDENT_OR_ARTIFACT").expect(
            "No package specified",
        );
        let pkg_ident = if Path::new(pkg_ident_str).is_file() {
            // We're going to use the `$pkg_origin/$pkg_name`, fuzzy form of a package
            // identifier to ensure that update strategies will work if desired
            PackageArchive::new(pkg_ident_str).ident()?
        } else {
            PackageIdent::from_str(pkg_ident_str)?
        };

        // To allow multiple instances of Habitat application in Kubernetes,
        // random suffix in metadata_name is needed.
        let metadata_name =
            format!(
            "{}-{}",
            pkg_ident.name,
            rand::thread_rng()
                .gen_ascii_chars()
                .filter(|c| c.is_lowercase() || c.is_numeric())
                .take(5)
                .collect::<String>(),
        );

        let image = match matches.value_of("IMAGE_NAME") {
            Some(i) => i.to_string(),
            None => pkg_ident.origin + "/" + &pkg_ident.name + ":latest",
        };

        let binds = bind::parse_bind_args(&matches)?;

        let config = match config_file {
            None => None,
            Some(name) => {
                let mut contents = String::new();
                File::open(name)?.read_to_string(&mut contents)?;

                Some(base64::encode(&format!("{}", contents)))
            }
        };

        Ok(Manifest {
            metadata_name: metadata_name,
            habitat_name: pkg_ident.name,
            image: image,
            count: count,
            service_topology: topology,
            service_group: group,
            config: config,
            ring_secret_name: ring_secret_name,
            binds: binds,
        })
    }

    /// Generates the manifest as a string and writes it to `write`.
    pub fn generate(&mut self, write: &mut Write) -> Result<()> {
        let out = ManifestJson::new(&self).into_string()?;

        write.write(out.as_bytes())?;

        Ok(())
    }
}

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

    #[test]
    fn test_manifest_generation() {
        let mut m = Manifest {
            metadata_name: "nginx-f84iq".to_owned(),
            habitat_name: "nginx".to_owned(),
            image: "core/nginx:latest".to_owned(),
            count: 3,
            service_topology: Default::default(),
            service_group: Some("group1".to_owned()),
            config: Some(base64::encode(&format!("{}", "port = 4444"))),
            ring_secret_name: Some("deltaechofoxtrot".to_owned()),
            binds: vec![],
        };

        let expected = include_str!("../tests/KubernetesManifestTest.yaml");

        let mut o = vec![];
        m.generate(&mut o).unwrap();

        let out = String::from_utf8(o).unwrap();

        assert_eq!(out, expected);
    }

    #[test]
    fn test_manifest_generation_binds() {
        let mut m = Manifest {
            metadata_name: "nginx-f84iq".to_owned(),
            habitat_name: "nginx".to_owned(),
            image: "core/nginx:latest".to_owned(),
            count: 3,
            service_topology: Default::default(),
            service_group: Some("group1".to_owned()),
            config: None,
            ring_secret_name: Some("deltaechofoxtrot".to_owned()),
            binds: vec![
                bind::Bind {
                    name: "name1".to_owned(),
                    service: "service1".to_owned(),
                    group: "group1".to_owned(),
                },
            ],
        };

        let expected = include_str!("../tests/KubernetesManifestTestBinds.yaml");

        let mut o = vec![];
        m.generate(&mut o).unwrap();

        let out = String::from_utf8(o).unwrap();

        assert_eq!(out, expected);
    }
}
<|endoftext|>


<commit_before>extern crate serde;

use Rect;

struct RectMapVisitor<'a> {
    value: &'a Rect
}

impl<'a> serde::ser::MapVisitor for RectMapVisitor<'a> {
    fn visit<S>(&mut self, serializer: &mut S) -> Result<Option<()>, S::Error>
        where S: serde::Serializer
    {
        try!(serializer.serialize_struct_elt("x", &self.value.x));
        try!(serializer.serialize_struct_elt("y", &self.value.y));
        try!(serializer.serialize_struct_elt("width", &self.value.width));
        try!(serializer.serialize_struct_elt("height", &self.value.height));
        Ok(None)
    }
}

impl serde::ser::Serialize for Rect {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: serde::ser::Serializer {
        serializer.serialize_struct("", RectMapVisitor { value: self }).map(|_| ())
    }
}

enum RectField {
    X,
    Y,
    WIDTH,
    HEIGHT,
}

impl serde::Deserialize for RectField  {
    fn deserialize<D>(deserializer: &mut D) -> Result<RectField, D::Error>
        where D: serde::de::Deserializer
    {
        struct RectFieldVisitor;

        impl serde::de::Visitor for RectFieldVisitor {
            type Value = RectField;

            fn visit_str<E>(&mut self, value: &str) -> Result<RectField, E>
                where E: serde::de::Error
            {
                match value {
                    "x" => Ok(RectField::X),
                    "y" => Ok(RectField::Y),
                    "width" => Ok(RectField::WIDTH),
                    "height" => Ok(RectField::HEIGHT),
                    _ => Err(serde::de::Error::custom("expected x,y,width or height")),
                }
            }
        }

        deserializer.deserialize(RectFieldVisitor)
    }
}

struct RectVisitor;

impl serde::Deserialize for Rect {
    fn deserialize<D>(deserializer: &mut D) -> Result<Rect, D::Error>
        where D: serde::de::Deserializer
    {
        static FIELDS: &'static [&'static str] = &["x", "y", "width", "height"];
        deserializer.deserialize_struct("Rect", FIELDS, RectVisitor)
    }
}

impl serde::de::Visitor for RectVisitor {
    type Value = Rect;

    fn visit_map<V>(&mut self, mut visitor: V) -> Result<Rect, V::Error>
        where V: serde::de::MapVisitor
    {
        let mut x = None;
        let mut y = None;
        let mut width = None;
        let mut height = None;

        loop {
            match try!(visitor.visit_key()) {
                Some(RectField::X) => { x = Some(try!(visitor.visit_value())); }
                Some(RectField::Y) => { y = Some(try!(visitor.visit_value())); }
                Some(RectField::WIDTH) => { width = Some(try!(visitor.visit_value())); }
                Some(RectField::HEIGHT) => { height = Some(try!(visitor.visit_value())); }
                None => { break; }
            }
        }

        let x = match x {
            Some(x) => x,
            None => try!(visitor.missing_field("x")),
        };

        let y = match y {
            Some(y) => y,
            None => try!(visitor.missing_field("y")),
        };

        let width = match width {
            Some(width) => width,
            None => try!(visitor.missing_field("width")),
        };

        let height = match height {
            Some(height) => height,
            None => try!(visitor.missing_field("height")),
        };

        try!(visitor.end());

        Ok(Rect{ x: x, y: y, width: width, height: height })
    }
}

/*
extern crate serde;
extern crate serde_json;

#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

enum PointField {
    X,
    Y,
}

impl serde::Deserialize for PointField {
    fn deserialize<D>(deserializer: &mut D) -> Result<PointField, D::Error>
        where D: serde::de::Deserializer
    {
        struct PointFieldVisitor;

        impl serde::de::Visitor for PointFieldVisitor {
            type Value = PointField;

            fn visit_str<E>(&mut self, value: &str) -> Result<PointField, E>
                where E: serde::de::Error
            {
                match value {
                    "x" => Ok(PointField::X),
                    "y" => Ok(PointField::Y),
                    _ => Err(serde::de::Error::custom("expected x or y")),
                }
            }
        }

        deserializer.deserialize(PointFieldVisitor)
    }
}

impl serde::Deserialize for Point {
    fn deserialize<D>(deserializer: &mut D) -> Result<Point, D::Error>
        where D: serde::de::Deserializer
    {
        static FIELDS: &'static [&'static str] = &["x", "y"];
        deserializer.deserialize_struct("Point", FIELDS, PointVisitor)
    }
}

struct PointVisitor;

impl serde::de::Visitor for PointVisitor {
    type Value = Point;

    fn visit_map<V>(&mut self, mut visitor: V) -> Result<Point, V::Error>
        where V: serde::de::MapVisitor
    {
        let mut x = None;
        let mut y = None;

        loop {
            match try!(visitor.visit_key()) {
                Some(PointField::X) => { x = Some(try!(visitor.visit_value())); }
                Some(PointField::Y) => { y = Some(try!(visitor.visit_value())); }
                None => { break; }
            }
        }

        let x = match x {
            Some(x) => x,
            None => try!(visitor.missing_field("x")),
        };

        let y = match y {
            Some(y) => y,
            None => try!(visitor.missing_field("y")),
        };

        try!(visitor.end());

        Ok(Point{ x: x, y: y })
    }
}
*/
<commit_msg>Started with DockHandle serialize<commit_after>extern crate serde;

use Rect;
use DockHandle;

struct RectMapVisitor<'a> {
    value: &'a Rect
}

impl<'a> serde::ser::MapVisitor for RectMapVisitor<'a> {
    fn visit<S>(&mut self, serializer: &mut S) -> Result<Option<()>, S::Error>
        where S: serde::Serializer
    {
        try!(serializer.serialize_struct_elt("x", &self.value.x));
        try!(serializer.serialize_struct_elt("y", &self.value.y));
        try!(serializer.serialize_struct_elt("width", &self.value.width));
        try!(serializer.serialize_struct_elt("height", &self.value.height));
        Ok(None)
    }
}

impl serde::ser::Serialize for Rect {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: serde::ser::Serializer {
        serializer.serialize_struct("", RectMapVisitor { value: self }).map(|_| ())
    }
}

enum RectField {
    X,
    Y,
    WIDTH,
    HEIGHT,
}

impl serde::Deserialize for RectField  {
    fn deserialize<D>(deserializer: &mut D) -> Result<RectField, D::Error>
        where D: serde::de::Deserializer
    {
        struct RectFieldVisitor;

        impl serde::de::Visitor for RectFieldVisitor {
            type Value = RectField;

            fn visit_str<E>(&mut self, value: &str) -> Result<RectField, E>
                where E: serde::de::Error
            {
                match value {
                    "x" => Ok(RectField::X),
                    "y" => Ok(RectField::Y),
                    "width" => Ok(RectField::WIDTH),
                    "height" => Ok(RectField::HEIGHT),
                    _ => Err(serde::de::Error::custom("expected x,y,width or height")),
                }
            }
        }

        deserializer.deserialize(RectFieldVisitor)
    }
}

struct RectVisitor;

impl serde::Deserialize for Rect {
    fn deserialize<D>(deserializer: &mut D) -> Result<Rect, D::Error>
        where D: serde::de::Deserializer
    {
        static FIELDS: &'static [&'static str] = &["x", "y", "width", "height"];
        deserializer.deserialize_struct("Rect", FIELDS, RectVisitor)
    }
}

impl serde::de::Visitor for RectVisitor {
    type Value = Rect;

    fn visit_map<V>(&mut self, mut visitor: V) -> Result<Rect, V::Error>
        where V: serde::de::MapVisitor
    {
        let mut x = None;
        let mut y = None;
        let mut width = None;
        let mut height = None;

        loop {
            match try!(visitor.visit_key()) {
                Some(RectField::X) => { x = Some(try!(visitor.visit_value())); }
                Some(RectField::Y) => { y = Some(try!(visitor.visit_value())); }
                Some(RectField::WIDTH) => { width = Some(try!(visitor.visit_value())); }
                Some(RectField::HEIGHT) => { height = Some(try!(visitor.visit_value())); }
                None => { break; }
            }
        }

        let x = match x {
            Some(x) => x,
            None => try!(visitor.missing_field("x")),
        };

        let y = match y {
            Some(y) => y,
            None => try!(visitor.missing_field("y")),
        };

        let width = match width {
            Some(width) => width,
            None => try!(visitor.missing_field("width")),
        };

        let height = match height {
            Some(height) => height,
            None => try!(visitor.missing_field("height")),
        };

        try!(visitor.end());

        Ok(Rect{ x: x, y: y, width: width, height: height })
    }
}

impl serde::ser::Serialize for DockHandle {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: serde::ser::Serializer {
        serializer.serialize_newtype_struct("DockHandle", &self.0)
    }
}

/*
extern crate serde;
extern crate serde_json;

#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

enum PointField {
    X,
    Y,
}

impl serde::Deserialize for PointField {
    fn deserialize<D>(deserializer: &mut D) -> Result<PointField, D::Error>
        where D: serde::de::Deserializer
    {
        struct PointFieldVisitor;

        impl serde::de::Visitor for PointFieldVisitor {
            type Value = PointField;

            fn visit_str<E>(&mut self, value: &str) -> Result<PointField, E>
                where E: serde::de::Error
            {
                match value {
                    "x" => Ok(PointField::X),
                    "y" => Ok(PointField::Y),
                    _ => Err(serde::de::Error::custom("expected x or y")),
                }
            }
        }

        deserializer.deserialize(PointFieldVisitor)
    }
}

impl serde::Deserialize for Point {
    fn deserialize<D>(deserializer: &mut D) -> Result<Point, D::Error>
        where D: serde::de::Deserializer
    {
        static FIELDS: &'static [&'static str] = &["x", "y"];
        deserializer.deserialize_struct("Point", FIELDS, PointVisitor)
    }
}

struct PointVisitor;

impl serde::de::Visitor for PointVisitor {
    type Value = Point;

    fn visit_map<V>(&mut self, mut visitor: V) -> Result<Point, V::Error>
        where V: serde::de::MapVisitor
    {
        let mut x = None;
        let mut y = None;

        loop {
            match try!(visitor.visit_key()) {
                Some(PointField::X) => { x = Some(try!(visitor.visit_value())); }
                Some(PointField::Y) => { y = Some(try!(visitor.visit_value())); }
                None => { break; }
            }
        }

        let x = match x {
            Some(x) => x,
            None => try!(visitor.missing_field("x")),
        };

        let y = match y {
            Some(y) => y,
            None => try!(visitor.missing_field("y")),
        };

        try!(visitor.end());

        Ok(Point{ x: x, y: y })
    }
}
*/
<|endoftext|>