<commit_before>#![crate_id = "fragments#0.1-pre"]

#![comment = "A simple template library for Rust"]
#![license = "MIT"]
#![crate_type = "lib"]
#![crate_type = "rlib"]

#![doc(html_root_url = "http://www.rust-ci.org/Ogeon/fragments/doc/")]

extern crate collections;

use std::fmt;
use std::from_str::FromStr;
use std::io::{BufReader, IoError, EndOfFile};
use std::vec::Vec;
use collections::hashmap::{HashMap, HashSet};

type ParserState = Option<fn(&mut Buffer) -> ParserResult>;

struct ParserResult {
	token: Token,
	next_parser: ParserState
}

#[deriving(Eq, TotalEq, Show)]
enum Token {
	String(~str),
	Placeholder(~str),
	Conditional(~str, bool, Vec<Token>),
	ContentConditional(~str, bool, Vec<Token>)
}

///A string template with placeholders and conditional content.
///
///Placeholders are written as `[[:label]]`, where `label` becomes the name of the placeholder.
///The label is then used to insert content: `my_template.insert(~"label", my_content);`.
///The assigned content for a placeholder can be anything that implements `Show`.
///Even other templates may be inserted, which allows a more atomic structure.
///
///Conditional segments are surrounded by `[[?label]]...[[/]]`, where `label` becomes the name of the condition,
///and they are used to display content depending on whether its label exists in the `conditions` set.
///`[[/]]` marks the end of a block and may contain other characters after the `/`, which may be useful for labeling the end mark.
///Conditions can be made negative by writing `[[?!label]]...[[/]]`, which makes the content visible if the label
///is missing from the `conditions` set. Conditional segments can also depend on whether a placeholder has an assgined value.
///Just write them like this: `[[?:label]]...[[/]]` or `[[?!:label]]...[[/]]`.
///
///Any character can be escaped by writing `\` before it. It can be used like this: `\[[[:label1]], [[:label2]]]`
///which will result in `[content1, content2]`, since the first `[` will be ignored by the parser and just added to the
///rest of the content.
pub struct Template {
	///Content for the placeholders
	pub content: HashMap<~str, ~fmt::Show: Send>,
	///Conditional switches
	pub conditions: HashSet<~str>,
	tokens: Vec<Token>
}

impl Template {
	#[inline]
	pub fn from_buffer(b: &mut Buffer) -> Template {
		Template {
			content: HashMap::new(),
			conditions: HashSet::new(),
			tokens: parse_block(b)
		}
	}

	///Convenience method for inserting content.
	#[inline]
	pub fn insert<T: fmt::Show + Send>(&mut self, placeholder: ~str, item: ~T) {
		self.content.insert(placeholder, item as ~fmt::Show: Send);
	}

	///Convenience method for setting a condition.
	#[inline]
	pub fn set(&mut self, condition: ~str, value: bool) {
		if value {
			self.conditions.insert(condition);
		} else {
			self.conditions.remove(&condition);
		}
	}

	fn format_tokens(&self, tokens: &Vec<Token>, f: &mut fmt::Formatter) -> fmt::Result {
		for token in tokens.iter() {
			let res = match token {
				&String(ref s) => f.buf.write_str(*s),

				&Placeholder(ref k) => {
					match self.content.find(k) {
						Some(value) => value.fmt(f),
						None => Ok(())
					}
				},

				&Conditional(ref k, expected, ref tokens) => {
					if self.conditions.contains(k) == expected {
						self.format_tokens(tokens, f)
					} else {
						Ok(())
					}
				},

				&ContentConditional(ref k, expected, ref tokens) => {
					if self.content.contains_key(k) == expected {
						self.format_tokens(tokens, f)
					} else {
						Ok(())
					}
				}
			};

			match res {
				Err(e) => return Err(e),
				_ => {}
			}
		}

		Ok(())
	}
}

impl FromStr for Template {
	///Creates a new `Template` from a string.
	fn from_str(s: &str) -> Option<Template> {
		Some(Template::from_buffer(&mut BufReader::new(s.as_bytes())))
	}
}

impl fmt::Show for Template {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		self.format_tokens(&self.tokens, f)
	}
}

fn parse_block(b: &mut Buffer) -> Vec<Token> {
	let mut tokens = vec!();
	let mut parser = parse_string;

	loop {
		let ParserResult{token: token, next_parser: next} = parser(b);

		tokens.push(token);

		match next {
			Some(p) => parser = p,
			None => break
		}
	}

	tokens
}

fn parse_string(b: &mut Buffer) -> ParserResult {
	let mut content = ~"";

	loop {
		match b.read_char() {
			Ok('\\') => {
				match b.read_char() {
					Ok(c) => {
						content.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
					Err(e) => fail!("{}", e)
				}
			},
			Ok('[') => {
				match b.read_char() {
					Ok('[') => {
						match b.read_char() {
							Ok(':') => return ParserResult{
								token: String(content),
								next_parser: Some(parse_placeholder)
							},
							Ok('?') => return ParserResult{
								token: String(content),
								next_parser: Some(parse_conditional)
							},
							Ok('/') => {
								skip_to_token_end(b);

								return ParserResult{
									token: String(content),
									next_parser: None
								}
							},
							Ok(c) => fail!("Unknown token type: '{}'", c),
							Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
							Err(e) => fail!("{}", e)
						}
					},
					Ok(c) => {
						content.push_char('[');
						content.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => content.push_char('['),
					Err(e) => fail!("{}", e)
				}
			},
			Ok(c) => content.push_char(c),
			Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
			Err(e) => fail!("{}", e)
		}
	}

	ParserResult{
		token: String(content),
		next_parser: None
	}
}

fn skip_to_token_end(b: &mut Buffer) {
	loop {
		match b.read_char() {
			Ok('\\') => {
				match b.read_char() {
					Ok(_) => {},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
					Err(e) => fail!("{}", e)
				}
			},
			Ok(']') => {
				match b.read_char() {
					Ok(']') => break,
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
					Err(e) => fail!("{}", e),
					_ => {}
				}
			},
			Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
			Err(e) => fail!("{}", e),
			_ => {}
		}
	}
}

fn parse_placeholder(b: &mut Buffer) -> ParserResult {
	let mut label = ~"";

	loop {
		match b.read_char() {
			Ok('\\') => {
				match b.read_char() {
					Ok(c) => {
						label.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
					Err(e) => fail!("{}", e)
				}
			},
			Ok(']') => {
				match b.read_char() {
					Ok(']') => {
						return ParserResult{
							token: Placeholder(label),
							next_parser: Some(parse_string)
						}
					},
					Ok(c) => {
						label.push_char(']');
						label.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => label.push_char(']'),
					Err(e) => fail!("{}", e)
				}
			},
			Ok(c) => label.push_char(c),
			Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
			Err(e) => fail!("{}", e)
		}
	}

	ParserResult{
		token: Placeholder(label),
		next_parser: None
	}
}

fn parse_conditional(b: &mut Buffer) -> ParserResult {
	let mut label = ~"";
	let mut expected = true;
	let mut expected_set = false;
	let mut content_condition = false;
	let mut content_condition_set = false;

	loop {
		match b.read_char() {
			Ok('\\') => {
				match b.read_char() {
					Ok(c) => {
						label.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
					Err(e) => fail!("{}", e)
				}
			},
			Ok(']') => {
				match b.read_char() {
					Ok(']') => {
						return ParserResult{
							token: if content_condition {
								ContentConditional(label, expected, parse_block(b))
							} else {
								Conditional(label, expected, parse_block(b))
							},
							next_parser: Some(parse_string)
						}
					},
					Ok(c) => {
						label.push_char(']');
						label.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => label.push_char(']'),
					Err(e) => fail!("{}", e)
				}
			},
			Ok('!') => {
				if label.len() == 0 && !expected_set {
					expected = false;
					expected_set = true;
				} else {
					label.push_char('!');
				}
			},
			Ok(':') => {
				if label.len() == 0 && !content_condition_set {
					content_condition = true;
					content_condition_set = true;
					expected_set = true;
				} else {
					label.push_char(':');
				}
			},
			Ok(c) => {
				label.push_char(c)
			},
			Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
			Err(e) => fail!("{}", e)
		}
	}

	ParserResult{
		token: Conditional(label, expected, vec!()),
		next_parser: None
	}
}

#[cfg(test)]
mod test {
	use super::{Template, Placeholder, String};

	#[test]
	fn basic_tokens() {
		let template: Template = from_str("Hello, [[:name]]! This is a [[:something]] template.").unwrap();
		assert_eq!(template.tokens.get(0), &String(~"Hello, "));
		assert_eq!(template.tokens.get(1), &Placeholder(~"name"));
		assert_eq!(template.tokens.get(2), &String(~"! This is a "));
		assert_eq!(template.tokens.get(3), &Placeholder(~"something"));
		assert_eq!(template.tokens.get(4), &String(~" template."));
	}

	#[test]
	#[should_fail]
	fn strange_tokens() {
		let _: Template = from_str("Hello, [[[:name]]]! This is a [[[[:something]] template.").unwrap();
	}

	#[test]
	fn escaped_tokens() {
		let template: Template = from_str("Hello, [[:name]]! Write placeholders like \\[[:this]] and escape them like \\\\\\[[:this]]").unwrap();
		assert_eq!(template.tokens.get(0), &String(~"Hello, "));
		assert_eq!(template.tokens.get(1), &Placeholder(~"name"));
		assert_eq!(template.tokens.get(2), &String(~"! Write placeholders like [[:this]] and escape them like \\[[:this]]"));
	}

	#[test]
	fn replacement() {
		let mut template: Template = from_str("Hello, [[:name]]! This is a [[:something]] template.").unwrap();
		template.insert(~"name", ~("Peter"));
		template.insert(~"something", ~("nice"));
		assert_eq!(template.to_str(), ~"Hello, Peter! This is a nice template.");
	}

	#[test]
	fn templates_in_templates() {
		let mut template1: Template = from_str("Hello, [[:name]]! This is a [[:something]] template.").unwrap();
		let mut template2: ~Template = ~from_str("really [[:something]]").unwrap();
		template1.insert(~"name", ~("Peter"));
		template2.insert(~"something", ~("nice"));

		template1.insert(~"something", template2);

		assert_eq!(template1.to_str(), ~"Hello, Peter! This is a really nice template.");
	}

	#[test]
	fn conditional() {
		let mut template: Template = from_str("Hello, [[:name]]![[?condition]] The condition is true.[[/condition]]").unwrap();
		template.insert(~"name", ~("Peter"));
		assert_eq!(template.to_str(), ~"Hello, Peter!");
		template.set(~"condition", true);
		assert_eq!(template.to_str(), ~"Hello, Peter! The condition is true.");
	}

	#[test]
	fn conditional_switch() {
		let mut template: Template = from_str("Hello, [[:name]]! The condition is [[?condition]]true[[/condition]][[?!condition]]false[[/condition]].").unwrap();
		template.insert(~"name", ~("Peter"));
		assert_eq!(template.to_str(), ~"Hello, Peter! The condition is false.");
		template.set(~"condition", true);
		assert_eq!(template.to_str(), ~"Hello, Peter! The condition is true.");
	}

	#[test]
	fn content_conditional() {
		let mut template: Template = from_str("Hello[[?:name]], [[:name]][[/name]]![[?!:name]] I don't know you.[[/!name]]").unwrap();
		assert_eq!(template.to_str(), ~"Hello! I don't know you.");
		template.insert(~"name", ~("Peter"));
		assert_eq!(template.to_str(), ~"Hello, Peter!");
	}
}<commit_msg>Changed to StrBuf for dynamig strings<commit_after>#![crate_id = "fragments#0.1-pre"]

#![comment = "A simple template library for Rust"]
#![license = "MIT"]
#![crate_type = "lib"]
#![crate_type = "rlib"]

#![doc(html_root_url = "http://www.rust-ci.org/Ogeon/fragments/doc/")]

extern crate collections;

use std::fmt;
use std::from_str::FromStr;
use std::io::{BufReader, IoError, EndOfFile};
use std::vec::Vec;
use collections::hashmap::{HashMap, HashSet};

type ParserState = Option<fn(&mut Buffer) -> ParserResult>;

struct ParserResult {
	token: Token,
	next_parser: ParserState
}

#[deriving(Eq, TotalEq, Show)]
enum Token {
	String(~str),
	Placeholder(~str),
	Conditional(~str, bool, Vec<Token>),
	ContentConditional(~str, bool, Vec<Token>)
}

///A string template with placeholders and conditional content.
///
///Placeholders are written as `[[:label]]`, where `label` becomes the name of the placeholder.
///The label is then used to insert content: `my_template.insert(~"label", my_content);`.
///The assigned content for a placeholder can be anything that implements `Show`.
///Even other templates may be inserted, which allows a more atomic structure.
///
///Conditional segments are surrounded by `[[?label]]...[[/]]`, where `label` becomes the name of the condition,
///and they are used to display content depending on whether its label exists in the `conditions` set.
///`[[/]]` marks the end of a block and may contain other characters after the `/`, which may be useful for labeling the end mark.
///Conditions can be made negative by writing `[[?!label]]...[[/]]`, which makes the content visible if the label
///is missing from the `conditions` set. Conditional segments can also depend on whether a placeholder has an assgined value.
///Just write them like this: `[[?:label]]...[[/]]` or `[[?!:label]]...[[/]]`.
///
///Any character can be escaped by writing `\` before it. It can be used like this: `\[[[:label1]], [[:label2]]]`
///which will result in `[content1, content2]`, since the first `[` will be ignored by the parser and just added to the
///rest of the content.
pub struct Template {
	///Content for the placeholders
	pub content: HashMap<~str, ~fmt::Show: Send>,
	///Conditional switches
	pub conditions: HashSet<~str>,
	tokens: Vec<Token>
}

impl Template {
	#[inline]
	pub fn from_buffer(b: &mut Buffer) -> Template {
		Template {
			content: HashMap::new(),
			conditions: HashSet::new(),
			tokens: parse_block(b)
		}
	}

	///Convenience method for inserting content.
	#[inline]
	pub fn insert<T: fmt::Show + Send>(&mut self, placeholder: ~str, item: ~T) {
		self.content.insert(placeholder, item as ~fmt::Show: Send);
	}

	///Convenience method for setting a condition.
	#[inline]
	pub fn set(&mut self, condition: ~str, value: bool) {
		if value {
			self.conditions.insert(condition);
		} else {
			self.conditions.remove(&condition);
		}
	}

	fn format_tokens(&self, tokens: &Vec<Token>, f: &mut fmt::Formatter) -> fmt::Result {
		for token in tokens.iter() {
			let res = match token {
				&String(ref s) => f.buf.write_str(*s),

				&Placeholder(ref k) => {
					match self.content.find(k) {
						Some(value) => value.fmt(f),
						None => Ok(())
					}
				},

				&Conditional(ref k, expected, ref tokens) => {
					if self.conditions.contains(k) == expected {
						self.format_tokens(tokens, f)
					} else {
						Ok(())
					}
				},

				&ContentConditional(ref k, expected, ref tokens) => {
					if self.content.contains_key(k) == expected {
						self.format_tokens(tokens, f)
					} else {
						Ok(())
					}
				}
			};

			match res {
				Err(e) => return Err(e),
				_ => {}
			}
		}

		Ok(())
	}
}

impl FromStr for Template {
	///Creates a new `Template` from a string.
	fn from_str(s: &str) -> Option<Template> {
		Some(Template::from_buffer(&mut BufReader::new(s.as_bytes())))
	}
}

impl fmt::Show for Template {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		self.format_tokens(&self.tokens, f)
	}
}

fn parse_block(b: &mut Buffer) -> Vec<Token> {
	let mut tokens = vec!();
	let mut parser = parse_string;

	loop {
		let ParserResult{token: token, next_parser: next} = parser(b);

		tokens.push(token);

		match next {
			Some(p) => parser = p,
			None => break
		}
	}

	tokens
}

fn parse_string(b: &mut Buffer) -> ParserResult {
	let mut content = StrBuf::new();

	loop {
		match b.read_char() {
			Ok('\\') => {
				match b.read_char() {
					Ok(c) => {
						content.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
					Err(e) => fail!("{}", e)
				}
			},
			Ok('[') => {
				match b.read_char() {
					Ok('[') => {
						match b.read_char() {
							Ok(':') => return ParserResult{
								token: String(content.into_owned()),
								next_parser: Some(parse_placeholder)
							},
							Ok('?') => return ParserResult{
								token: String(content.into_owned()),
								next_parser: Some(parse_conditional)
							},
							Ok('/') => {
								skip_to_token_end(b);

								return ParserResult{
									token: String(content.into_owned()),
									next_parser: None
								}
							},
							Ok(c) => fail!("Unknown token type: '{}'", c),
							Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
							Err(e) => fail!("{}", e)
						}
					},
					Ok(c) => {
						content.push_char('[');
						content.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => content.push_char('['),
					Err(e) => fail!("{}", e)
				}
			},
			Ok(c) => content.push_char(c),
			Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
			Err(e) => fail!("{}", e)
		}
	}

	ParserResult{
		token: String(content.into_owned()),
		next_parser: None
	}
}

fn skip_to_token_end(b: &mut Buffer) {
	loop {
		match b.read_char() {
			Ok('\\') => {
				match b.read_char() {
					Ok(_) => {},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
					Err(e) => fail!("{}", e)
				}
			},
			Ok(']') => {
				match b.read_char() {
					Ok(']') => break,
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
					Err(e) => fail!("{}", e),
					_ => {}
				}
			},
			Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
			Err(e) => fail!("{}", e),
			_ => {}
		}
	}
}

fn parse_placeholder(b: &mut Buffer) -> ParserResult {
	let mut label = StrBuf::new();

	loop {
		match b.read_char() {
			Ok('\\') => {
				match b.read_char() {
					Ok(c) => {
						label.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
					Err(e) => fail!("{}", e)
				}
			},
			Ok(']') => {
				match b.read_char() {
					Ok(']') => {
						return ParserResult{
							token: Placeholder(label.into_owned()),
							next_parser: Some(parse_string)
						}
					},
					Ok(c) => {
						label.push_char(']');
						label.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => label.push_char(']'),
					Err(e) => fail!("{}", e)
				}
			},
			Ok(c) => label.push_char(c),
			Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
			Err(e) => fail!("{}", e)
		}
	}

	ParserResult{
		token: Placeholder(label.into_owned()),
		next_parser: None
	}
}

fn parse_conditional(b: &mut Buffer) -> ParserResult {
	let mut label = StrBuf::new();
	let mut expected = true;
	let mut expected_set = false;
	let mut content_condition = false;
	let mut content_condition_set = false;

	loop {
		match b.read_char() {
			Ok('\\') => {
				match b.read_char() {
					Ok(c) => {
						label.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
					Err(e) => fail!("{}", e)
				}
			},
			Ok(']') => {
				match b.read_char() {
					Ok(']') => {
						return ParserResult{
							token: if content_condition {
								ContentConditional(label.into_owned(), expected, parse_block(b))
							} else {
								Conditional(label.into_owned(), expected, parse_block(b))
							},
							next_parser: Some(parse_string)
						}
					},
					Ok(c) => {
						label.push_char(']');
						label.push_char(c);
					},
					Err(IoError{kind: EndOfFile, desc: _, detail: _}) => label.push_char(']'),
					Err(e) => fail!("{}", e)
				}
			},
			Ok('!') => {
				if label.len() == 0 && !expected_set {
					expected = false;
					expected_set = true;
				} else {
					label.push_char('!');
				}
			},
			Ok(':') => {
				if label.len() == 0 && !content_condition_set {
					content_condition = true;
					content_condition_set = true;
					expected_set = true;
				} else {
					label.push_char(':');
				}
			},
			Ok(c) => {
				label.push_char(c)
			},
			Err(IoError{kind: EndOfFile, desc: _, detail: _}) => break,
			Err(e) => fail!("{}", e)
		}
	}

	ParserResult{
		token: Conditional(label.into_owned(), expected, vec!()),
		next_parser: None
	}
}

#[cfg(test)]
mod test {
	use super::{Template, Placeholder, String};

	#[test]
	fn basic_tokens() {
		let template: Template = from_str("Hello, [[:name]]! This is a [[:something]] template.").unwrap();
		assert_eq!(template.tokens.get(0), &String(~"Hello, "));
		assert_eq!(template.tokens.get(1), &Placeholder(~"name"));
		assert_eq!(template.tokens.get(2), &String(~"! This is a "));
		assert_eq!(template.tokens.get(3), &Placeholder(~"something"));
		assert_eq!(template.tokens.get(4), &String(~" template."));
	}

	#[test]
	#[should_fail]
	fn strange_tokens() {
		let _: Template = from_str("Hello, [[[:name]]]! This is a [[[[:something]] template.").unwrap();
	}

	#[test]
	fn escaped_tokens() {
		let template: Template = from_str("Hello, [[:name]]! Write placeholders like \\[[:this]] and escape them like \\\\\\[[:this]]").unwrap();
		assert_eq!(template.tokens.get(0), &String(~"Hello, "));
		assert_eq!(template.tokens.get(1), &Placeholder(~"name"));
		assert_eq!(template.tokens.get(2), &String(~"! Write placeholders like [[:this]] and escape them like \\[[:this]]"));
	}

	#[test]
	fn replacement() {
		let mut template: Template = from_str("Hello, [[:name]]! This is a [[:something]] template.").unwrap();
		template.insert(~"name", ~("Peter"));
		template.insert(~"something", ~("nice"));
		assert_eq!(template.to_str(), ~"Hello, Peter! This is a nice template.");
	}

	#[test]
	fn templates_in_templates() {
		let mut template1: Template = from_str("Hello, [[:name]]! This is a [[:something]] template.").unwrap();
		let mut template2: ~Template = ~from_str("really [[:something]]").unwrap();
		template1.insert(~"name", ~("Peter"));
		template2.insert(~"something", ~("nice"));

		template1.insert(~"something", template2);

		assert_eq!(template1.to_str(), ~"Hello, Peter! This is a really nice template.");
	}

	#[test]
	fn conditional() {
		let mut template: Template = from_str("Hello, [[:name]]![[?condition]] The condition is true.[[/condition]]").unwrap();
		template.insert(~"name", ~("Peter"));
		assert_eq!(template.to_str(), ~"Hello, Peter!");
		template.set(~"condition", true);
		assert_eq!(template.to_str(), ~"Hello, Peter! The condition is true.");
	}

	#[test]
	fn conditional_switch() {
		let mut template: Template = from_str("Hello, [[:name]]! The condition is [[?condition]]true[[/condition]][[?!condition]]false[[/condition]].").unwrap();
		template.insert(~"name", ~("Peter"));
		assert_eq!(template.to_str(), ~"Hello, Peter! The condition is false.");
		template.set(~"condition", true);
		assert_eq!(template.to_str(), ~"Hello, Peter! The condition is true.");
	}

	#[test]
	fn content_conditional() {
		let mut template: Template = from_str("Hello[[?:name]], [[:name]][[/name]]![[?!:name]] I don't know you.[[/!name]]").unwrap();
		assert_eq!(template.to_str(), ~"Hello! I don't know you.");
		template.insert(~"name", ~("Peter"));
		assert_eq!(template.to_str(), ~"Hello, Peter!");
	}
}<|endoftext|>


<commit_before>#![warn(missing_docs)]

//! input-map
//! =========
//!
//! A library for binding input keys to actions, and manipulating mouse input

extern crate input;
extern crate piston_window;
extern crate rustc_serialize;
extern crate viewport;

mod builder;

use input::{Input, Button, Motion};
use piston_window::Size;
use std::collections::HashMap;
use std::default::Default;
use std::hash::Hash;
use viewport::Viewport;

pub use builder::InputTranslatorBuilder;

/// Represents a logical action to be bound to a particular button press, e.g.
/// jump, attack, or move forward
pub trait Action: Copy + PartialEq + Eq + Hash { }

/// A translated action.
#[derive(Debug, Copy, Clone)]
pub enum Translated<A: Action> {
    /// A keypress event which was bound to an action
    Press(A),

    /// A key release event which was bound to an action
    Release(A),

    /// A translated mouse motion. The logical origin of a translated MouseCursor event
    /// is in the top left corner of the window, and the logical scroll is non-natural.
    /// Relative events are unchanged for now.
    Move(Motion)
}

/// A three-element tuple of Option<Button>. For simplicity, a maximum number of 3
/// buttons can be bound to each action, and this is exposed through the `InputRebind`
/// struct.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct ButtonTuple(pub Option<Button>, pub Option<Button>, pub Option<Button>);

impl ButtonTuple {
    /// Creates a new tuple with no buttons in it (equivalent to `Default::default()`).
    pub fn new() -> Self { Default::default() }

    /// Check if the button is in the tuple.
    pub fn contains(&self, button: Button) -> bool {
        let sbtn = Some(button);
        self.0 == sbtn || self.1 == sbtn || self.2 == sbtn
    }

    /// Insert a button into the tuple if there is room, searching from left to right.
    /// If the button is inserted, returns true. Otherwise, if the button is not inserted,
    /// this function returns false.
    pub fn insert_inplace(&mut self, button: Button) -> bool {
        match self {
            &mut ButtonTuple(a, b, c) if a.is_none() => {*self = ButtonTuple(Some(button), b, c); true},
            &mut ButtonTuple(a, b, c) if b.is_none() => {*self = ButtonTuple(a, Some(button), c); true},
            &mut ButtonTuple(a, b, c) if c.is_none() => {*self = ButtonTuple(a, b, Some(button)); true}
            _ => false
        }
    }
}

impl Default for ButtonTuple {
    /// Creates a new tuple with no buttons in it.
    fn default() -> Self { ButtonTuple(None, None, None) }
}

/// An object which translates piston::input::Input events into input_map::Translated<A> events
#[derive(Clone)]
pub struct InputTranslator<A: Action> {
    keymap: HashMap<Button, A>,
    mouse_translator: MouseTranslator
}

impl<A: Action> InputTranslator<A> {

    /// Creates an empty InputTranslator.
    pub fn new(size: Size) -> Self {
        InputTranslator {
            keymap: HashMap::new(),
            mouse_translator: MouseTranslator::new(size)
        }
    }

    /// Translate an Input into a Translated<A> event
    pub fn translate(&self, input: &Input) -> Option<Translated<A>> {
        macro_rules! translate_button(($but_state:ident, $but_var:ident) => (
            match self.keymap.get(&$but_var).map(|x| *x) {
                Some(act) => Some(Translated::$but_state(act)),
                None => None
            });
        );

        match input {
            &Input::Press(button) => translate_button!(Press, button),
            &Input::Release(button) => translate_button!(Release, button),
            &Input::Move(motion) =>
                Some(Translated::Move(self.mouse_translator.translate(motion))),
            _ => None
        }
    }

    /// Re-set the mouse bounds size used for calculating mouse events
    pub fn set_size(&mut self, size: Size) {
        self.mouse_translator.data.viewport_size = size
    }

    /// Re-set the mouse bounds size from a viewport
    pub fn set_size_from_viewport(&mut self, vp: Viewport) {
        self.set_size(Size::from(vp.draw_size));
    }
}

#[derive(Clone)]
struct MouseTranslationData {
    x_axis_motion_inverted: bool,
    y_axis_motion_inverted: bool,
    x_axis_scroll_inverted: bool,
    y_axis_scroll_inverted: bool,
    viewport_size: Size
}

impl MouseTranslationData {
    fn new(size: Size) -> Self {
        MouseTranslationData {
            x_axis_motion_inverted: false,
            y_axis_motion_inverted: false,
            x_axis_scroll_inverted: false,
            y_axis_scroll_inverted: false,
            viewport_size: size
        }
    }
}

#[derive(Clone)]
struct MouseTranslator {
    data: MouseTranslationData
}

impl MouseTranslator {
    fn new(size: Size) -> Self {
        MouseTranslator {
            data: MouseTranslationData::new(size)
        }
    }

    fn translate(&self, motion: Motion) -> Motion {
        match motion {
            Motion::MouseCursor(x, y) => {
                let (sw, sh) = {
                    let Size {width, height} = self.data.viewport_size;
                    (width as f64, height as f64)
                };

                let cx = if self.data.x_axis_motion_inverted { sw - x } else { x };
                let cy = if self.data.y_axis_motion_inverted { sh - y } else { y };

                Motion::MouseCursor(cx, cy)
            },
            Motion::MouseScroll(x, y) => {
                let mx = if self.data.x_axis_scroll_inverted { -1.0f64 } else { 1.0 };
                let my = if self.data.y_axis_scroll_inverted { -1.0f64 } else { 1.0 };
                Motion::MouseScroll(x * mx, y * my)
            },
            relative => relative
        }
    }
}

#[allow(missing_docs)]
pub struct InputRebind<A: Action> {
    keymap: HashMap<A, ButtonTuple>,
    mouse_data: MouseTranslationData
}

impl<A: Action> InputRebind<A> {
    #[allow(missing_docs)]
    pub fn new(size: Size) -> Self {
        InputRebind {
            keymap: HashMap::new(),
            mouse_data: MouseTranslationData::new(size)
        }
    }

    #[allow(missing_docs)]
    pub fn insert_action(&mut self, action: A) -> Option<ButtonTuple> {
        self.keymap.insert(action, ButtonTuple::new())
    }

    #[allow(missing_docs)]
    pub fn insert_action_with_buttons(&mut self, action: A, buttons: ButtonTuple) -> Option<ButtonTuple> {
        self.keymap.insert(action, buttons)
    }

    #[allow(missing_docs)]
    pub fn get_bindings(&self, action: &A) -> Option<&ButtonTuple> {
        self.keymap.get(action)
    }

    #[allow(missing_docs)]
    pub fn get_bindings_mut(&mut self, action: &mut A) -> Option<&mut ButtonTuple> {
        self.keymap.get_mut(action)
    }

    #[allow(missing_docs)]
    pub fn get_x_scroll_inverted(&self) -> &bool {
        &self.mouse_data.x_axis_scroll_inverted
    }

    #[allow(missing_docs)]
    pub fn get_x_scroll_inverted_mut(&mut self) -> &mut bool {
        &mut self.mouse_data.x_axis_scroll_inverted
    }

    #[allow(missing_docs)]
    pub fn get_y_scroll_inverted(&self) -> &bool {
        &self.mouse_data.y_axis_scroll_inverted
    }

    #[allow(missing_docs)]
    pub fn get_y_scroll_inverted_mut(&mut self) -> &mut bool {
        &mut self.mouse_data.y_axis_scroll_inverted
    }

    #[allow(missing_docs)]
    pub fn get_x_motion_inverted(&self) -> &bool {
        &self.mouse_data.x_axis_motion_inverted
    }

    #[allow(missing_docs)]
    pub fn get_x_motion_inverted_mut(&mut self) -> &mut bool {
        &mut self.mouse_data.x_axis_motion_inverted
    }

    #[allow(missing_docs)]
    pub fn get_y_motion_inverted(&self) -> &bool {
        &self.mouse_data.y_axis_motion_inverted
    }

    #[allow(missing_docs)]
    pub fn get_y_motion_inverted_mut(&mut self) -> &mut bool {
        &mut self.mouse_data.y_axis_motion_inverted
    }

    #[allow(missing_docs)]
    pub fn get_viewport_size(&self) -> &Size {
        &self.mouse_data.viewport_size
    }

    #[allow(missing_docs)]
    pub fn get_viewport_size_mut(&mut self) -> &mut Size {
        &mut self.mouse_data.viewport_size
    }
}

impl<A: Action> Default for InputRebind<A> {
    #[allow(missing_docs)]
    fn default() -> Self {
        InputRebind::new((800, 600).into())
    }
}

impl<A: Action> Into<InputTranslator<A>> for InputRebind<A> {
    #[allow(missing_docs)]
    fn into(self) -> InputTranslator<A> {
        let mut input_translator = InputTranslator::new(self.mouse_data.viewport_size);
        input_translator.mouse_translator.data = self.mouse_data;
        input_translator.keymap = self.keymap.values()
                                             .flat_map(|&bt| bt.into_iter())
                                             .filter_map(|x| x)
                                             .zip(self.keymap.keys().map(|x| *x))
                                             .collect();

        input_translator
    }
}

impl<A: Action> Into<InputRebind<A>> for InputTranslator<A> {
    #[allow(missing_docs)]
    fn into(self) -> InputRebind<A> {
        let mut input_rebind = InputRebind::new(self.mouse_translator.data.viewport_size);
        input_rebind.mouse_data = self.mouse_translator.data;
        //input_rebind.keymap = self.keymap.btn_map.
        //input_rebind.keymap = self.keymap.btn_map.drain().map(|(k, v)| (v, k)).collect();
        input_rebind
    }
}

impl ButtonTuple {
    fn into_iter(self) -> ButtonTupleIterator {
        ButtonTupleIterator {
            button_tuple: self,
            i: 0
        }
    }
}

struct ButtonTupleIterator {
    button_tuple: ButtonTuple,
    i: usize
}

impl Iterator for ButtonTupleIterator {
    type Item = Option<Button>;

    fn next(&mut self) -> Option<Self::Item> {
        let i = self.i;
        self.i += 1;
        match i {
            0 => Some(self.button_tuple.0),
            1 => Some(self.button_tuple.1),
            2 => Some(self.button_tuple.2),
            _ => None
        }
    }
}

<commit_msg>Improve documentation on Action (Discuss Hash bounds<commit_after>#![warn(missing_docs)]

//! input-map
//! =========
//!
//! A library for binding input keys to actions, and manipulating mouse input

extern crate input;
extern crate piston_window;
extern crate rustc_serialize;
extern crate viewport;

mod builder;

use input::{Input, Button, Motion};
use piston_window::Size;
use std::collections::HashMap;
use std::default::Default;
use std::hash::Hash;
use viewport::Viewport;

pub use builder::InputTranslatorBuilder;

/// Represents a logical action to be bound to a particular button press, e.g.
/// jump, attack, or move forward. Needs to be hashable, as it is used as a
/// lookup key when rebinding an action to a different key.
pub trait Action: Copy + PartialEq + Eq + Hash { }

/// A translated action.
#[derive(Debug, Copy, Clone)]
pub enum Translated<A: Action> {
    /// A keypress event which was bound to an action
    Press(A),

    /// A key release event which was bound to an action
    Release(A),

    /// A translated mouse motion. The logical origin of a translated MouseCursor event
    /// is in the top left corner of the window, and the logical scroll is non-natural.
    /// Relative events are unchanged for now.
    Move(Motion)
}

/// A three-element tuple of Option<Button>. For simplicity, a maximum number of 3
/// buttons can be bound to each action, and this is exposed through the `InputRebind`
/// struct.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct ButtonTuple(pub Option<Button>, pub Option<Button>, pub Option<Button>);

impl ButtonTuple {
    /// Creates a new tuple with no buttons in it (equivalent to `Default::default()`).
    pub fn new() -> Self { Default::default() }

    /// Check if the button is in the tuple.
    pub fn contains(&self, button: Button) -> bool {
        let sbtn = Some(button);
        self.0 == sbtn || self.1 == sbtn || self.2 == sbtn
    }

    /// Insert a button into the tuple if there is room, searching from left to right.
    /// If the button is inserted, returns true. Otherwise, if the button is not inserted,
    /// this function returns false.
    pub fn insert_inplace(&mut self, button: Button) -> bool {
        match self {
            &mut ButtonTuple(a, b, c) if a.is_none() => {*self = ButtonTuple(Some(button), b, c); true},
            &mut ButtonTuple(a, b, c) if b.is_none() => {*self = ButtonTuple(a, Some(button), c); true},
            &mut ButtonTuple(a, b, c) if c.is_none() => {*self = ButtonTuple(a, b, Some(button)); true}
            _ => false
        }
    }
}

impl Default for ButtonTuple {
    /// Creates a new tuple with no buttons in it.
    fn default() -> Self { ButtonTuple(None, None, None) }
}

/// An object which translates piston::input::Input events into input_map::Translated<A> events
#[derive(Clone)]
pub struct InputTranslator<A: Action> {
    keymap: HashMap<Button, A>,
    mouse_translator: MouseTranslator
}

impl<A: Action> InputTranslator<A> {

    /// Creates an empty InputTranslator.
    pub fn new(size: Size) -> Self {
        InputTranslator {
            keymap: HashMap::new(),
            mouse_translator: MouseTranslator::new(size)
        }
    }

    /// Translate an Input into a Translated<A> event
    pub fn translate(&self, input: &Input) -> Option<Translated<A>> {
        macro_rules! translate_button(($but_state:ident, $but_var:ident) => (
            match self.keymap.get(&$but_var).map(|x| *x) {
                Some(act) => Some(Translated::$but_state(act)),
                None => None
            });
        );

        match input {
            &Input::Press(button) => translate_button!(Press, button),
            &Input::Release(button) => translate_button!(Release, button),
            &Input::Move(motion) =>
                Some(Translated::Move(self.mouse_translator.translate(motion))),
            _ => None
        }
    }

    /// Re-set the mouse bounds size used for calculating mouse events
    pub fn set_size(&mut self, size: Size) {
        self.mouse_translator.data.viewport_size = size
    }

    /// Re-set the mouse bounds size from a viewport
    pub fn set_size_from_viewport(&mut self, vp: Viewport) {
        self.set_size(Size::from(vp.draw_size));
    }
}

#[derive(Clone)]
struct MouseTranslationData {
    x_axis_motion_inverted: bool,
    y_axis_motion_inverted: bool,
    x_axis_scroll_inverted: bool,
    y_axis_scroll_inverted: bool,
    viewport_size: Size
}

impl MouseTranslationData {
    fn new(size: Size) -> Self {
        MouseTranslationData {
            x_axis_motion_inverted: false,
            y_axis_motion_inverted: false,
            x_axis_scroll_inverted: false,
            y_axis_scroll_inverted: false,
            viewport_size: size
        }
    }
}

#[derive(Clone)]
struct MouseTranslator {
    data: MouseTranslationData
}

impl MouseTranslator {
    fn new(size: Size) -> Self {
        MouseTranslator {
            data: MouseTranslationData::new(size)
        }
    }

    fn translate(&self, motion: Motion) -> Motion {
        match motion {
            Motion::MouseCursor(x, y) => {
                let (sw, sh) = {
                    let Size {width, height} = self.data.viewport_size;
                    (width as f64, height as f64)
                };

                let cx = if self.data.x_axis_motion_inverted { sw - x } else { x };
                let cy = if self.data.y_axis_motion_inverted { sh - y } else { y };

                Motion::MouseCursor(cx, cy)
            },
            Motion::MouseScroll(x, y) => {
                let mx = if self.data.x_axis_scroll_inverted { -1.0f64 } else { 1.0 };
                let my = if self.data.y_axis_scroll_inverted { -1.0f64 } else { 1.0 };
                Motion::MouseScroll(x * mx, y * my)
            },
            relative => relative
        }
    }
}

#[allow(missing_docs)]
pub struct InputRebind<A: Action> {
    keymap: HashMap<A, ButtonTuple>,
    mouse_data: MouseTranslationData
}

impl<A: Action> InputRebind<A> {
    #[allow(missing_docs)]
    pub fn new(size: Size) -> Self {
        InputRebind {
            keymap: HashMap::new(),
            mouse_data: MouseTranslationData::new(size)
        }
    }

    #[allow(missing_docs)]
    pub fn insert_action(&mut self, action: A) -> Option<ButtonTuple> {
        self.keymap.insert(action, ButtonTuple::new())
    }

    #[allow(missing_docs)]
    pub fn insert_action_with_buttons(&mut self, action: A, buttons: ButtonTuple) -> Option<ButtonTuple> {
        self.keymap.insert(action, buttons)
    }

    #[allow(missing_docs)]
    pub fn get_bindings(&self, action: &A) -> Option<&ButtonTuple> {
        self.keymap.get(action)
    }

    #[allow(missing_docs)]
    pub fn get_bindings_mut(&mut self, action: &mut A) -> Option<&mut ButtonTuple> {
        self.keymap.get_mut(action)
    }

    #[allow(missing_docs)]
    pub fn get_x_scroll_inverted(&self) -> &bool {
        &self.mouse_data.x_axis_scroll_inverted
    }

    #[allow(missing_docs)]
    pub fn get_x_scroll_inverted_mut(&mut self) -> &mut bool {
        &mut self.mouse_data.x_axis_scroll_inverted
    }

    #[allow(missing_docs)]
    pub fn get_y_scroll_inverted(&self) -> &bool {
        &self.mouse_data.y_axis_scroll_inverted
    }

    #[allow(missing_docs)]
    pub fn get_y_scroll_inverted_mut(&mut self) -> &mut bool {
        &mut self.mouse_data.y_axis_scroll_inverted
    }

    #[allow(missing_docs)]
    pub fn get_x_motion_inverted(&self) -> &bool {
        &self.mouse_data.x_axis_motion_inverted
    }

    #[allow(missing_docs)]
    pub fn get_x_motion_inverted_mut(&mut self) -> &mut bool {
        &mut self.mouse_data.x_axis_motion_inverted
    }

    #[allow(missing_docs)]
    pub fn get_y_motion_inverted(&self) -> &bool {
        &self.mouse_data.y_axis_motion_inverted
    }

    #[allow(missing_docs)]
    pub fn get_y_motion_inverted_mut(&mut self) -> &mut bool {
        &mut self.mouse_data.y_axis_motion_inverted
    }

    #[allow(missing_docs)]
    pub fn get_viewport_size(&self) -> &Size {
        &self.mouse_data.viewport_size
    }

    #[allow(missing_docs)]
    pub fn get_viewport_size_mut(&mut self) -> &mut Size {
        &mut self.mouse_data.viewport_size
    }
}

impl<A: Action> Default for InputRebind<A> {
    #[allow(missing_docs)]
    fn default() -> Self {
        InputRebind::new((800, 600).into())
    }
}

impl<A: Action> Into<InputTranslator<A>> for InputRebind<A> {
    #[allow(missing_docs)]
    fn into(self) -> InputTranslator<A> {
        let mut input_translator = InputTranslator::new(self.mouse_data.viewport_size);
        input_translator.mouse_translator.data = self.mouse_data;
        input_translator.keymap = self.keymap.values()
                                             .flat_map(|&bt| bt.into_iter())
                                             .filter_map(|x| x)
                                             .zip(self.keymap.keys().map(|x| *x))
                                             .collect();

        input_translator
    }
}

impl<A: Action> Into<InputRebind<A>> for InputTranslator<A> {
    #[allow(missing_docs)]
    fn into(self) -> InputRebind<A> {
        let mut input_rebind = InputRebind::new(self.mouse_translator.data.viewport_size);
        input_rebind.mouse_data = self.mouse_translator.data;
        //input_rebind.keymap = self.keymap.btn_map.
        //input_rebind.keymap = self.keymap.btn_map.drain().map(|(k, v)| (v, k)).collect();
        input_rebind
    }
}

impl ButtonTuple {
    fn into_iter(self) -> ButtonTupleIterator {
        ButtonTupleIterator {
            button_tuple: self,
            i: 0
        }
    }
}

struct ButtonTupleIterator {
    button_tuple: ButtonTuple,
    i: usize
}

impl Iterator for ButtonTupleIterator {
    type Item = Option<Button>;

    fn next(&mut self) -> Option<Self::Item> {
        let i = self.i;
        self.i += 1;
        match i {
            0 => Some(self.button_tuple.0),
            1 => Some(self.button_tuple.1),
            2 => Some(self.button_tuple.2),
            _ => None
        }
    }
}

<|endoftext|>


<commit_before>#![allow(dead_code)]
pub mod stm {
    use std::result::Result as Either;
    use std::mem::{transmute, forget, uninitialized};
    use std::ptr::swap;
    /// We cannot make a real monad, so this is going to be a fake linear type a la Clean.
    pub struct Result(Either);

    impl Result {
        pub fn ret(a: A) -> Result {
            Result(Either::Ok(a))
        }

        pub fn abort() -> Result {
            Result(Either::Err(()))
        }

        /// just don't look
        fn spec_move(mut self) -> Either {
            unsafe {
                let mut x = uninitialized();
                swap(&mut x, transmute(&mut self));
                forget(self);
                x
            }
        }

        pub fn bind(self, f : F) -> Result where F : FnOnce(A) -> Result {
            Result(self.spec_move().and_then(|x| f(x).spec_move()))
        }
    }

    impl Drop for Result {
        // Best we can do absent a true linear type
        fn drop(&mut self) {
            panic!()
        }
    }

    pub trait GlobalState {
        /// A hook for transaction-local state
        type LocalState;

        fn run(&self, F) -> A
            where F: FnMut(&mut Self::LocalState) -> Result;

        fn or_else(&self, F, G) -> Result
            where F: FnMut(&mut Self::LocalState) -> Result,
                  G: FnMut(&mut Self::LocalState) -> Result;
    }

    pub trait TVar {
        /// A wrapper around an object whose access is mediated by the STM implementation
        type LocalState;

        fn read(&mut self, Self::LocalState) -> Result;

        fn write(&mut self, Self::LocalState, A) -> Result<()>;
    }
}
pub mod no_rec {
    use std::sync::atomic::{AtomicUsize, Ordering};
    use std::vec::Vec;
    use std::collections::HashMap;
    use std::cell::UnsafeCell;
    use std::marker::PhantomData;
    use std::mem::transmute;
    use stm::Result;

    pub struct GlobalState {
        version : AtomicUsize
    }

    #[allow(raw_pointer_derive)]
    #[derive(Hash, Eq, PartialEq, Debug, Copy, Clone)]
    /// Lies... Lies
    struct TVarAddr<'a, 'b : 'a>(*mut TVar<'b, ()>, PhantomData<&'a TVar<'b, ()>>);

    impl<'a, 'b : 'a> TVarAddr<'a, 'b> {
        fn version(self) -> &'a AtomicUsize {
            unsafe { &(*self.0).version }
        }
    }

    pub struct LocalState<'v, 'g : 'v> {
        /// the norec paper has the reads stored as a list of <address, value> pairs. For us it
        /// would probably be better to have versioned TVars. That way we always have a fast
        /// comparison operation and avoid doing extra dynamic dispatch when validating reads.
        reads : Vec<(TVarAddr<'v, 'g>, usize)>,

        /// for writes we will probably have to cast unsafely to get this to work. TVars just hold
        /// arbitrary types and we only have one table.  XXX: gross
        writes : HashMap<TVarAddr<'v, 'g>, Box<[u8]>>,

        /// STM environment we are using
        global : &'g GlobalState,

        /// last version at which our view of the global state was valid
        snapshot : usize
    }

    impl<'v, 'g : 'v> LocalState<'v, 'g> {

        /// Validate the current local state against the state of the world,
        /// Aborts if a TVar in our readset has been written, otherwise
        /// returns the version number to with we are consistent
        fn validate(&self) -> Result<usize> {
            loop {
                let time = self.global.version.load(Ordering::SeqCst);
                if (time & 1) == 0 {
                    continue;
                }
                for &(addr, version) in &self.reads {
                    if addr.version().load(Ordering::SeqCst) != version {
                        return Result::abort();
                    }
                }
                return Result::ret(time);
            }
        }

        /// Acquire reference to value in TVar within the transaction
        fn read(&mut self, tvar : &'v TVar<'g, A>) -> Result<&A> {
            let addr = unsafe { tvar.to_addr() } ;
            if let Some(v) =  self.writes.get(&addr) {
                // TODO(ezrosent) may not be transmuting the right thing here
                return unsafe { Result::ret(transmute(v)) };
            }

            if self.snapshot == self.global.version.load(Ordering::SeqCst) {
                self.reads.push((addr, tvar.version.load(Ordering::SeqCst)));
                Result::ret(unsafe {tvar.get_val()})
            } else {
                self.validate().bind( | v | {
                    self.snapshot = v;
                    self.reads.push((addr, tvar.version.load(Ordering::SeqCst)));
                    Result::ret(unsafe {tvar.get_val()})
                })
            }
        }

        /// Add the TVar and value to our writeset, these are thread-local until the
        /// transaction commits
        fn write(&mut self, tvar : &'v mut TVar<'g, A>, val : A) -> Result<()> {
            //XXX: transmute doesn't work here. It says that Box<[u8]> could be 128 bits, while
            //Box could be 64 bits.

            /*
            unsafe {
                self.writes.insert(tvar.to_addr(), transmute(Box::new(val.clone())));
            }
            */

            Result::ret(())
        }

        /// if (read−only transaction)
        ///     return
        /// while (!CAS(&global lock, snapshot, snapshot + 1))
        ///     snapshot = Validate()
        /// for each (addr, val) in writes
        ///     ∗addr = val
        /// global lock = snapshot + 2 // one more than CAS above
        fn commit(&mut self) -> Result<()> {
            fn commit_loop(l : &mut LocalState) -> Result<()> {
               if l.global.version.compare_and_swap(l.snapshot, l.snapshot + 1, Ordering::SeqCst)
                   == l.snapshot {
                       Result::ret(())
               } else {
                   l.validate().bind(|v| {
                       l.snapshot = v;
                       commit_loop(l)
                   })
               }
            }
            if self.writes.is_empty() {
                Result::ret(())
            } else {
                commit_loop(self).bind(| _ | {
                    for (addr, value) in &self.writes {
                        //XXX: how do we safely write back the values now? we need to
                        //store some size/type-related information now.
                        //maybe we do want Any? or are we better off just with a
                        //struct {
                        //  size
                        //  A /* size of A is size */
                        //}
                        let tvar : &mut TVar<()> =  unsafe { transmute(addr.0) };
                        tvar.version.fetch_add(1, Ordering::SeqCst);
                    }
                    self.global.version.compare_and_swap(self.snapshot + 1,
                                                         self.snapshot + 2, Ordering::SeqCst);
                    Result::ret(())
                })
            }
        }
    }

    /// Haskell-style container of mutable state within a transaction
    pub struct TVar<'a, A> {
        version : AtomicUsize,
        val : UnsafeCell,
        global : &'a GlobalState
    }

    impl<'a, A> TVar<'a, A> {
        unsafe fn get_val(&self) -> &A {
            &*self.val.get()
        }

        unsafe fn get_val_mut(&self) -> &mut A {
            &mut *self.val.get()
        }
        unsafe fn to_addr<'b>(&'b self) -> TVarAddr<'b, 'a> {
            let ptr : *mut TVar<'a, ()> = transmute(self);
            TVarAddr(ptr, PhantomData)
        }
    }

}

#[test]
fn it_works() {
}
<commit_msg>attempts to work around sizing issues, with limited success<commit_after>#![allow(dead_code)]
pub mod stm {
    use std::result::Result as Either;
    use std::mem::{transmute, forget, uninitialized};
    use std::ptr::swap;
    /// We cannot make a real monad, so this is going to be a fake linear type a la Clean.
    pub struct Result(Either);

    impl Result {
        pub fn ret(a: A) -> Result {
            Result(Either::Ok(a))
        }

        pub fn abort() -> Result {
            Result(Either::Err(()))
        }

        /// just don't look
        fn spec_move(mut self) -> Either {
            unsafe {
                let mut x = uninitialized();
                swap(&mut x, transmute(&mut self));
                forget(self);
                x
            }
        }

        pub fn bind(self, f : F) -> Result where F : FnOnce(A) -> Result {
            Result(self.spec_move().and_then(|x| f(x).spec_move()))
        }
    }

    impl Drop for Result {
        // Best we can do absent a true linear type
        fn drop(&mut self) {
            panic!()
        }
    }

    pub trait GlobalState {
        /// A hook for transaction-local state
        type LocalState;

        fn run(&self, F) -> A
            where F: FnMut(&mut Self::LocalState) -> Result;

        fn or_else(&self, F, G) -> Result
            where F: FnMut(&mut Self::LocalState) -> Result,
                  G: FnMut(&mut Self::LocalState) -> Result;
    }

    pub trait TVar {
        /// A wrapper around an object whose access is mediated by the STM implementation
        type LocalState;

        fn read(&mut self, Self::LocalState) -> Result;

        fn write(&mut self, Self::LocalState, A) -> Result<()>;
    }
}
pub mod no_rec {
    use std::sync::atomic::{AtomicUsize, Ordering};
    use std::vec::Vec;
    use std::collections::HashMap;
    use std::cell::UnsafeCell;
    use std::marker::PhantomData;
    use std::mem::transmute;
    use stm::Result;

    pub struct GlobalState {
        version : AtomicUsize
    }

    #[allow(raw_pointer_derive)]
    #[derive(Hash, Eq, PartialEq, Copy, Clone)]
    /// Lies... Lies
    struct TVarAddr<'a, 'b : 'a>(*mut TVar<'b, ()>, PhantomData<&'a TVar<'b, ()>>);

    impl<'a, 'b : 'a> TVarAddr<'a, 'b> {
        fn version(self) -> &'a AtomicUsize {
            unsafe { &(*self.0).version }
        }
    }

    pub struct LocalState<'v, 'g : 'v> {
        /// the norec paper has the reads stored as a list of <address, value> pairs. For us it
        /// would probably be better to have versioned TVars. That way we always have a fast
        /// comparison operation and avoid doing extra dynamic dispatch when validating reads.
        reads : Vec<(TVarAddr<'v, 'g>, usize)>,

        /// for writes we will probably have to cast unsafely to get this to work. TVars just hold
        /// arbitrary types and we only have one table.  XXX: gross
        writes : HashMap<TVarAddr<'v, 'g>, Box<[u8]>>,

        /// STM environment we are using
        global : &'g GlobalState,

        /// last version at which our view of the global state was valid
        snapshot : usize
    }

    impl<'v, 'g : 'v> LocalState<'v, 'g> {

        /// Validate the current local state against the state of the world,
        /// Aborts if a TVar in our readset has been written, otherwise
        /// returns the version number to with we are consistent
        fn validate(&self) -> Result<usize> {
            loop {
                let time = self.global.version.load(Ordering::SeqCst);
                if (time & 1) == 0 {
                    continue;
                }
                for &(addr, version) in &self.reads {
                    if addr.version().load(Ordering::SeqCst) != version {
                        return Result::abort();
                    }
                }
                return Result::ret(time);
            }
        }

        /// Acquire reference to value in TVar within the transaction
        fn read<'l, A>(&'l mut self, tvar : &'l TVar<'g, A>) -> Result<&'l A>
            where 'v : 'l {
                let addr : TVarAddr<'l, 'g> = unsafe { tvar.to_addr() } ;
                if let Some(v) = self.writes.get(&addr) {
                    let r : &'l [u8] = &*v;
                    return Result::abort();
                }

                if self.snapshot == self.global.version.load(Ordering::SeqCst) {
                    self.reads.push((addr, tvar.version.load(Ordering::SeqCst)));
                    unsafe {tvar.get_val()}
                } else {
                    self.validate().bind( | v | {
                        self.snapshot = v;
                        self.reads.push((addr, tvar.version.load(Ordering::SeqCst)));
                        unsafe {tvar.get_val()}
                    })
                }
        }

        /// Add the TVar and value to our writeset, these are thread-local until the
        /// transaction commits
        fn write(&mut self, tvar : &'v mut TVar<'g, A>, val : A) -> Result<()> {
            let b : Box<[A]> = Box::new([val]);
            unsafe {
                self.writes.insert(tvar.to_addr(), transmute(b));
            }
            Result::ret(())
        }

        /// if (read−only transaction)
        ///     return
        /// while (!CAS(&global lock, snapshot, snapshot + 1))
        ///     snapshot = Validate()
        /// for each (addr, val) in writes
        ///     ∗addr = val
        /// global lock = snapshot + 2 // one more than CAS above
        fn commit(&mut self) -> Result<()> {
            fn commit_loop(l : &mut LocalState) -> Result<()> {
               if l.global.version.compare_and_swap(l.snapshot, l.snapshot + 1, Ordering::SeqCst)
                   == l.snapshot {
                       Result::ret(())
               } else {
                   l.validate().bind(|v| {
                       l.snapshot = v;
                       commit_loop(l)
                   })
               }
            }
            if self.writes.is_empty() {
                Result::ret(())
            } else {
                commit_loop(self).bind(| _ | {
                    for (addr, value) in &self.writes {
                        //XXX: how do we safely write back the values now? we need to
                        //store some size/type-related information now.
                        //maybe we do want Any? or are we better off just with a
                        //struct {
                        //  size
                        //  A /* size of A is size */
                        //}
                        let tvar : &mut TVar<()> =  unsafe { transmute(addr.0) };
                        tvar.version.fetch_add(1, Ordering::SeqCst);
                    }
                    self.global.version.compare_and_swap(self.snapshot + 1,
                                                         self.snapshot + 2, Ordering::SeqCst);
                    Result::ret(())
                })
            }
        }
    }

    /// Haskell-style container of mutable state within a transaction
    //pub struct TVar<'a, A: ?Sized> {
    pub struct TVar<'a, A> {
        version : AtomicUsize,
        global : &'a GlobalState,
        val : UnsafeCell,
    }

    //impl<'a, A: ?Sized> TVar<'a, A> {
    impl<'a, A> TVar<'a, A> {
        unsafe fn get_val(&self) -> &A {
            &*self.val.get()
        }

        unsafe fn get_val_mut(&self) -> &mut A {
            &mut *self.val.get()
        }
        unsafe fn to_addr<'b>(&'b self) -> TVarAddr<'b, 'a> {
            let ptr : *mut TVar<'a, ()> = transmute(self);
            TVarAddr(ptr, PhantomData)
        }
    }

}

#[test]
fn it_works() {
}
<|endoftext|>


<commit_before>#[macro_use] extern crate bitflags;
extern crate byteorder;

use std::io;

use byteorder::{ReadBytesExt, LittleEndian};

use std::io::Read;
use std::io::Seek;

mod block_groups;
mod extents;
mod parse;

pub mod mbr;

use extents::TreeReader;

bitflags! {
    struct InodeFlags: u32 {
        const INODE_SECRM        = 0x00000001; /* Secure deletion */
        const INODE_UNRM         = 0x00000002; /* Undelete */
        const INODE_COMPR        = 0x00000004; /* Compress file */
        const INODE_SYNC         = 0x00000008; /* Synchronous updates */
        const INODE_IMMUTABLE    = 0x00000010; /* Immutable file */
        const INODE_APPEND       = 0x00000020; /* writes to file may only append */
        const INODE_NODUMP       = 0x00000040; /* do not dump file */
        const INODE_NOATIME      = 0x00000080; /* do not update atime */
        const INODE_DIRTY        = 0x00000100; /* reserved for compression */
        const INODE_COMPRBLK     = 0x00000200; /* One or more compressed clusters */
        const INODE_NOCOMPR      = 0x00000400; /* Don't compress */
        const INODE_ENCRYPT      = 0x00000800; /* encrypted file */
        const INODE_INDEX        = 0x00001000; /* hash-indexed directory */
        const INODE_IMAGIC       = 0x00002000; /* AFS directory */
        const INODE_JOURNAL_DATA = 0x00004000; /* file data should be journaled */
        const INODE_NOTAIL       = 0x00008000; /* file tail should not be merged */
        const INODE_DIRSYNC      = 0x00010000; /* dirsync behaviour (directories only) */
        const INODE_TOPDIR       = 0x00020000; /* Top of directory hierarchies*/
        const INODE_HUGE_FILE    = 0x00040000; /* Set to each huge file */
        const INODE_EXTENTS      = 0x00080000; /* Inode uses extents */
        const INODE_EA_INODE     = 0x00200000; /* Inode used for large EA */
        const INODE_EOFBLOCKS    = 0x00400000; /* Blocks allocated beyond EOF */
        const INODE_INLINE_DATA  = 0x10000000; /* Inode has inline data. */
        const INODE_PROJINHERIT  = 0x20000000; /* Create with parents projid */
        const INODE_RESERVED     = 0x80000000; /* reserved for ext4 lib */
    }
}

#[derive(Debug, PartialEq)]
pub enum FileType {
    RegularFile,     // S_IFREG (Regular file)
    SymbolicLink,    // S_IFLNK (Symbolic link)
    CharacterDevice, // S_IFCHR (Character device)
    BlockDevice,     // S_IFBLK (Block device)
    Directory,       // S_IFDIR (Directory)
    Fifo,            // S_IFIFO (FIFO)
    Socket,          // S_IFSOCK (Socket)
}

#[derive(Debug)]
pub enum Enhanced {
    RegularFile,
    SymbolicLink(String),
    CharacterDevice(u16, u32),
    BlockDevice(u16, u32),
    Directory(Vec<DirEntry>),
    Fifo,
    Socket,
}

impl FileType {
    fn from_mode(mode: u16) -> Option<FileType> {
        match mode >> 12 {
            0x1 => Some(FileType::Fifo),
            0x2 => Some(FileType::CharacterDevice),
            0x4 => Some(FileType::Directory),
            0x6 => Some(FileType::BlockDevice),
            0x8 => Some(FileType::RegularFile),
            0xA => Some(FileType::SymbolicLink),
            0xC => Some(FileType::Socket),
            _ => None,
        }
    }

    fn from_dir_hint(hint: u8) -> Option<FileType> {
        match hint {
            1 => Some(FileType::RegularFile),
            2 => Some(FileType::Directory),
            3 => Some(FileType::CharacterDevice),
            4 => Some(FileType::BlockDevice),
            5 => Some(FileType::Fifo),
            6 => Some(FileType::Socket),
            7 => Some(FileType::SymbolicLink),
            _ => None,
        }
    }
}

#[derive(Debug)]
pub struct DirEntry {
    pub inode: u32,
    pub file_type: FileType,
    pub name: String,
}

#[derive(Debug)]
pub struct Stat {
    pub extracted_type: FileType,
    pub file_mode: u16,
    pub uid: u32,
    pub gid: u32,
    pub size: u64,
    pub atime: Time,
    pub ctime: Time,
    pub mtime: Time,
    pub btime: Option<Time>,
    pub link_count: u16,
}

pub struct Inode {
    pub stat: Stat,
    pub number: u32,
    flags: InodeFlags,
    block: [u8; 4 * 15],
    block_size: u32,
}

#[derive(Debug)]
pub struct SuperBlock<R> {
    inner: R,
    groups: block_groups::BlockGroups,
}

#[derive(Debug)]
pub struct Time {
    pub epoch_secs: u32,
    pub nanos: Option<u32>,
}

impl<R> SuperBlock<R>
where R: io::Read + io::Seek {

    pub fn new(mut inner: R) -> io::Result<SuperBlock<R>> {
        inner.seek(io::SeekFrom::Start(1024))?;
        parse::superblock(inner)
    }

    pub fn load_inode(&mut self, inode: u32) -> io::Result<Inode> {
        self.inner.seek(io::SeekFrom::Start(self.groups.index_of(inode)))?;
        parse::inode(&mut self.inner, inode, self.groups.block_size)
    }

    pub fn root(&mut self) -> io::Result<Inode> {
        self.load_inode(2)
    }

    pub fn walk<F>(&mut self, inode: &Inode, path: String, visit: &F) -> io::Result<bool>
    where F: Fn(&str, u32, &Stat, &Enhanced) -> io::Result<bool> {
        let enhanced = inode.enhance(&mut self.inner)?;

        if !visit(path.as_str(), inode.number, &inode.stat, &enhanced)? {
            return Ok(false);
        }

        if let Enhanced::Directory(entries) = enhanced {
            for entry in entries {
                if "." == entry.name || ".." == entry.name {
                    continue;
                }

                let child_node = self.load_inode(entry.inode)?;
                if !self.walk(&child_node, format!("{}/{}", path, entry.name), visit)? {
                    return Ok(false)
                }
            }
        }

//    self.walk(inner, &i, format!("{}/{}", path, entry.name)).map_err(|e|
//    parse_error(format!("while processing {}: {}", path, e)))?;

        Ok(true)
    }

    pub fn resolve_path(&mut self, path: &str) -> io::Result<DirEntry> {
        let path = path.trim_right_matches('/');
        if path.is_empty() {
            // this is a bit of a lie, but it works..?
            return Ok(DirEntry {
                inode: 2,
                file_type: FileType::Directory,
                name: "/".to_string(),
            });
        }

        let mut curr = self.root()?;

        let mut parts = path.split('/').collect::<Vec<&str>>();
        let last = parts.pop().unwrap();
        for part in parts {
            if part.is_empty() {
                continue;
            }

            let child_inode = self.dir_entry_named(&curr, part)?.inode;
            curr = self.load_inode(child_inode)?;
        }

        self.dir_entry_named(&curr, last)
    }

    fn dir_entry_named(&mut self, inode: &Inode, name: &str) -> io::Result<DirEntry> {
        if let Enhanced::Directory(entries) = self.enhance(inode)? {
            entries.into_iter().filter(|entry| entry.name == name).next().ok_or_else(||
                io::Error::new(io::ErrorKind::NotFound, format!("component {} isn't there", name)))
        } else {
            Err(io::Error::new(io::ErrorKind::NotFound, format!("component {} isn't a directory", name)))
        }
    }

    pub fn open(&mut self, inode: &Inode) -> io::Result<TreeReader<&mut R>> {
        inode.reader(&mut self.inner)
    }

    pub fn enhance(&mut self, inode: &Inode) -> io::Result<Enhanced> {
        inode.enhance(&mut self.inner)
    }
}

impl Inode {

    fn reader<R>(&self, inner: R) -> io::Result<TreeReader<R>>
    where R: io::Read + io::Seek {
        TreeReader::new(inner, self.block_size, self.block)
    }

    fn enhance<R>(&self, inner: R) -> io::Result<Enhanced>
    where R: io::Read + io::Seek {
        Ok(match self.stat.extracted_type {
            FileType::RegularFile => Enhanced::RegularFile,
            FileType::Socket => Enhanced::Socket,
            FileType::Fifo => Enhanced::Fifo,

            FileType::Directory => Enhanced::Directory(self.read_directory(inner)?),
            FileType::SymbolicLink =>
                Enhanced::SymbolicLink(if self.stat.size < 60 {
                    assert!(self.flags.is_empty());
                    std::str::from_utf8(&self.block[0..self.stat.size as usize]).expect("utf-8").to_string()
                } else {
                    assert!(self.only_relevant_flag_is_extents());
                    std::str::from_utf8(&self.load_all(inner)?).expect("utf-8").to_string()
                }),
            FileType::CharacterDevice => {
                let (maj, min) = load_maj_min(self.block);
                Enhanced::CharacterDevice(maj, min)
            }
            FileType::BlockDevice => {
                let (maj, min) = load_maj_min(self.block);
                Enhanced::BlockDevice(maj, min)
            }
        })
    }

    fn load_all<R>(&self, inner: R) -> io::Result<Vec<u8>>
    where R: io::Read + io::Seek {
        let size = usize_check(self.stat.size)?;
        let mut ret = Vec::with_capacity(size);

        assert_eq!(size, self.reader(inner)?.read_to_end(&mut ret)?);

        Ok(ret)
    }

    fn read_directory<R>(&self, inner: R) -> io::Result<Vec<DirEntry>>
    where R: io::Read + io::Seek {

        let mut dirs = Vec::with_capacity(40);

        let data = {
            // if the flags, minus irrelevant flags, isn't just EXTENTS...
            if !self.only_relevant_flag_is_extents() {
                return Err(parse_error(format!("inode without unsupported flags: {0:x} {0:b}", self.flags)));
            }

            self.load_all(inner)?
        };

        let total_len = data.len();

        let mut cursor = io::Cursor::new(data);
        let mut read = 0usize;
        loop {
            let child_inode = cursor.read_u32::<LittleEndian>()?;
            let rec_len = cursor.read_u16::<LittleEndian>()?;
            let name_len = cursor.read_u8()?;
            let file_type = cursor.read_u8()?;
            let mut name = vec![0u8; name_len as usize];
            cursor.read_exact(&mut name)?;
            cursor.seek(io::SeekFrom::Current(rec_len as i64 - name_len as i64 - 4 - 2 - 2))?;
            if 0 != child_inode {
                let name = std::str::from_utf8(&name).map_err(|e|
                    parse_error(format!("invalid utf-8 in file name: {}", e)))?;

                dirs.push(DirEntry {
                    inode: child_inode,
                    name: name.to_string(),
                    file_type: FileType::from_dir_hint(file_type)
                        .expect("valid file type"),
                });
            }

            read += rec_len as usize;
            if read >= total_len {
                assert_eq!(read, total_len);
                break;
            }
        }

        Ok(dirs)
    }

    fn only_relevant_flag_is_extents(&self) -> bool {
        self.flags & (
            INODE_COMPR
            | INODE_DIRTY
            | INODE_COMPRBLK
            | INODE_ENCRYPT
            | INODE_IMAGIC
            | INODE_NOTAIL
            | INODE_TOPDIR
            | INODE_HUGE_FILE
            | INODE_EXTENTS
            | INODE_EA_INODE
            | INODE_EOFBLOCKS
            | INODE_INLINE_DATA
        ) == INODE_EXTENTS
    }
}

fn load_maj_min(block: [u8; 4 * 15]) -> (u16, u32) {
    if 0 != block[0] || 0 != block[1] {
        (block[1] as u16, block[0] as u32)
    } else {
        // if you think reading this is bad, I had to write it
        (block[5] as u16
             | (((block[6] & 0b0000_1111) as u16) << 8),
         block[4] as u32
             | ((block[7] as u32) << 12)
             | (((block[6] & 0b1111_0000) as u32) >> 4) << 8)
    }
}

fn as_u16(buf: &[u8]) -> u16 {
    buf[0] as u16 + buf[1] as u16 * 0x100
}

fn as_u32(buf: &[u8]) -> u32 {
    as_u16(buf) as u32 + as_u16(&buf[2..]) as u32 * 0x10000
}

fn parse_error(msg: String) -> io::Error {
    io::Error::new(io::ErrorKind::InvalidInput, msg)
}

#[allow(unknown_lints, absurd_extreme_comparisons)]
fn usize_check(val: u64) -> io::Result<usize> {
    // this check only makes sense on non-64-bit platforms; on 64-bit usize == u64.
    if val > std::usize::MAX as u64 {
        Err(io::Error::new(io::ErrorKind::InvalidData,
                                  format!("value is too big for memory on this platform: {}",
                                          val)))
    } else {
        Ok(val as usize)
    }
}
<commit_msg>clippy<commit_after>#[macro_use] extern crate bitflags;
extern crate byteorder;

use std::io;

use byteorder::{ReadBytesExt, LittleEndian};

use std::io::Read;
use std::io::Seek;

mod block_groups;
mod extents;
mod parse;

pub mod mbr;

use extents::TreeReader;

bitflags! {
    struct InodeFlags: u32 {
        const INODE_SECRM        = 0x00000001; /* Secure deletion */
        const INODE_UNRM         = 0x00000002; /* Undelete */
        const INODE_COMPR        = 0x00000004; /* Compress file */
        const INODE_SYNC         = 0x00000008; /* Synchronous updates */
        const INODE_IMMUTABLE    = 0x00000010; /* Immutable file */
        const INODE_APPEND       = 0x00000020; /* writes to file may only append */
        const INODE_NODUMP       = 0x00000040; /* do not dump file */
        const INODE_NOATIME      = 0x00000080; /* do not update atime */
        const INODE_DIRTY        = 0x00000100; /* reserved for compression */
        const INODE_COMPRBLK     = 0x00000200; /* One or more compressed clusters */
        const INODE_NOCOMPR      = 0x00000400; /* Don't compress */
        const INODE_ENCRYPT      = 0x00000800; /* encrypted file */
        const INODE_INDEX        = 0x00001000; /* hash-indexed directory */
        const INODE_IMAGIC       = 0x00002000; /* AFS directory */
        const INODE_JOURNAL_DATA = 0x00004000; /* file data should be journaled */
        const INODE_NOTAIL       = 0x00008000; /* file tail should not be merged */
        const INODE_DIRSYNC      = 0x00010000; /* dirsync behaviour (directories only) */
        const INODE_TOPDIR       = 0x00020000; /* Top of directory hierarchies*/
        const INODE_HUGE_FILE    = 0x00040000; /* Set to each huge file */
        const INODE_EXTENTS      = 0x00080000; /* Inode uses extents */
        const INODE_EA_INODE     = 0x00200000; /* Inode used for large EA */
        const INODE_EOFBLOCKS    = 0x00400000; /* Blocks allocated beyond EOF */
        const INODE_INLINE_DATA  = 0x10000000; /* Inode has inline data. */
        const INODE_PROJINHERIT  = 0x20000000; /* Create with parents projid */
        const INODE_RESERVED     = 0x80000000; /* reserved for ext4 lib */
    }
}

#[derive(Debug, PartialEq)]
pub enum FileType {
    RegularFile,     // S_IFREG (Regular file)
    SymbolicLink,    // S_IFLNK (Symbolic link)
    CharacterDevice, // S_IFCHR (Character device)
    BlockDevice,     // S_IFBLK (Block device)
    Directory,       // S_IFDIR (Directory)
    Fifo,            // S_IFIFO (FIFO)
    Socket,          // S_IFSOCK (Socket)
}

#[derive(Debug)]
pub enum Enhanced {
    RegularFile,
    SymbolicLink(String),
    CharacterDevice(u16, u32),
    BlockDevice(u16, u32),
    Directory(Vec<DirEntry>),
    Fifo,
    Socket,
}

impl FileType {
    fn from_mode(mode: u16) -> Option<FileType> {
        match mode >> 12 {
            0x1 => Some(FileType::Fifo),
            0x2 => Some(FileType::CharacterDevice),
            0x4 => Some(FileType::Directory),
            0x6 => Some(FileType::BlockDevice),
            0x8 => Some(FileType::RegularFile),
            0xA => Some(FileType::SymbolicLink),
            0xC => Some(FileType::Socket),
            _ => None,
        }
    }

    fn from_dir_hint(hint: u8) -> Option<FileType> {
        match hint {
            1 => Some(FileType::RegularFile),
            2 => Some(FileType::Directory),
            3 => Some(FileType::CharacterDevice),
            4 => Some(FileType::BlockDevice),
            5 => Some(FileType::Fifo),
            6 => Some(FileType::Socket),
            7 => Some(FileType::SymbolicLink),
            _ => None,
        }
    }
}

#[derive(Debug)]
pub struct DirEntry {
    pub inode: u32,
    pub file_type: FileType,
    pub name: String,
}

#[derive(Debug)]
pub struct Stat {
    pub extracted_type: FileType,
    pub file_mode: u16,
    pub uid: u32,
    pub gid: u32,
    pub size: u64,
    pub atime: Time,
    pub ctime: Time,
    pub mtime: Time,
    pub btime: Option<Time>,
    pub link_count: u16,
}

pub struct Inode {
    pub stat: Stat,
    pub number: u32,
    flags: InodeFlags,
    block: [u8; 4 * 15],
    block_size: u32,
}

#[derive(Debug)]
pub struct SuperBlock<R> {
    inner: R,
    groups: block_groups::BlockGroups,
}

#[derive(Debug)]
pub struct Time {
    pub epoch_secs: u32,
    pub nanos: Option<u32>,
}

impl<R> SuperBlock<R>
where R: io::Read + io::Seek {

    pub fn new(mut inner: R) -> io::Result<SuperBlock<R>> {
        inner.seek(io::SeekFrom::Start(1024))?;
        parse::superblock(inner)
    }

    pub fn load_inode(&mut self, inode: u32) -> io::Result<Inode> {
        self.inner.seek(io::SeekFrom::Start(self.groups.index_of(inode)))?;
        parse::inode(&mut self.inner, inode, self.groups.block_size)
    }

    pub fn root(&mut self) -> io::Result<Inode> {
        self.load_inode(2)
    }

    pub fn walk<F>(&mut self, inode: &Inode, path: String, visit: &F) -> io::Result<bool>
    where F: Fn(&str, u32, &Stat, &Enhanced) -> io::Result<bool> {
        let enhanced = inode.enhance(&mut self.inner)?;

        if !visit(path.as_str(), inode.number, &inode.stat, &enhanced)? {
            return Ok(false);
        }

        if let Enhanced::Directory(entries) = enhanced {
            for entry in entries {
                if "." == entry.name || ".." == entry.name {
                    continue;
                }

                let child_node = self.load_inode(entry.inode)?;
                if !self.walk(&child_node, format!("{}/{}", path, entry.name), visit)? {
                    return Ok(false)
                }
            }
        }

//    self.walk(inner, &i, format!("{}/{}", path, entry.name)).map_err(|e|
//    parse_error(format!("while processing {}: {}", path, e)))?;

        Ok(true)
    }

    pub fn resolve_path(&mut self, path: &str) -> io::Result<DirEntry> {
        let path = path.trim_right_matches('/');
        if path.is_empty() {
            // this is a bit of a lie, but it works..?
            return Ok(DirEntry {
                inode: 2,
                file_type: FileType::Directory,
                name: "/".to_string(),
            });
        }

        let mut curr = self.root()?;

        let mut parts = path.split('/').collect::<Vec<&str>>();
        let last = parts.pop().unwrap();
        for part in parts {
            if part.is_empty() {
                continue;
            }

            let child_inode = self.dir_entry_named(&curr, part)?.inode;
            curr = self.load_inode(child_inode)?;
        }

        self.dir_entry_named(&curr, last)
    }

    fn dir_entry_named(&mut self, inode: &Inode, name: &str) -> io::Result<DirEntry> {
        if let Enhanced::Directory(entries) = self.enhance(inode)? {
            entries.into_iter().find(|entry| entry.name == name).ok_or_else(||
                io::Error::new(io::ErrorKind::NotFound, format!("component {} isn't there", name)))
        } else {
            Err(io::Error::new(io::ErrorKind::NotFound, format!("component {} isn't a directory", name)))
        }
    }

    pub fn open(&mut self, inode: &Inode) -> io::Result<TreeReader<&mut R>> {
        inode.reader(&mut self.inner)
    }

    pub fn enhance(&mut self, inode: &Inode) -> io::Result<Enhanced> {
        inode.enhance(&mut self.inner)
    }
}

impl Inode {

    fn reader<R>(&self, inner: R) -> io::Result<TreeReader<R>>
    where R: io::Read + io::Seek {
        TreeReader::new(inner, self.block_size, self.block)
    }

    fn enhance<R>(&self, inner: R) -> io::Result<Enhanced>
    where R: io::Read + io::Seek {
        Ok(match self.stat.extracted_type {
            FileType::RegularFile => Enhanced::RegularFile,
            FileType::Socket => Enhanced::Socket,
            FileType::Fifo => Enhanced::Fifo,

            FileType::Directory => Enhanced::Directory(self.read_directory(inner)?),
            FileType::SymbolicLink =>
                Enhanced::SymbolicLink(if self.stat.size < 60 {
                    assert!(self.flags.is_empty());
                    std::str::from_utf8(&self.block[0..self.stat.size as usize]).expect("utf-8").to_string()
                } else {
                    assert!(self.only_relevant_flag_is_extents());
                    std::str::from_utf8(&self.load_all(inner)?).expect("utf-8").to_string()
                }),
            FileType::CharacterDevice => {
                let (maj, min) = load_maj_min(self.block);
                Enhanced::CharacterDevice(maj, min)
            }
            FileType::BlockDevice => {
                let (maj, min) = load_maj_min(self.block);
                Enhanced::BlockDevice(maj, min)
            }
        })
    }

    fn load_all<R>(&self, inner: R) -> io::Result<Vec<u8>>
    where R: io::Read + io::Seek {
        let size = usize_check(self.stat.size)?;
        let mut ret = Vec::with_capacity(size);

        assert_eq!(size, self.reader(inner)?.read_to_end(&mut ret)?);

        Ok(ret)
    }

    fn read_directory<R>(&self, inner: R) -> io::Result<Vec<DirEntry>>
    where R: io::Read + io::Seek {

        let mut dirs = Vec::with_capacity(40);

        let data = {
            // if the flags, minus irrelevant flags, isn't just EXTENTS...
            if !self.only_relevant_flag_is_extents() {
                return Err(parse_error(format!("inode without unsupported flags: {0:x} {0:b}", self.flags)));
            }

            self.load_all(inner)?
        };

        let total_len = data.len();

        let mut cursor = io::Cursor::new(data);
        let mut read = 0usize;
        loop {
            let child_inode = cursor.read_u32::<LittleEndian>()?;
            let rec_len = cursor.read_u16::<LittleEndian>()?;
            let name_len = cursor.read_u8()?;
            let file_type = cursor.read_u8()?;
            let mut name = vec![0u8; name_len as usize];
            cursor.read_exact(&mut name)?;
            cursor.seek(io::SeekFrom::Current(rec_len as i64 - name_len as i64 - 4 - 2 - 2))?;
            if 0 != child_inode {
                let name = std::str::from_utf8(&name).map_err(|e|
                    parse_error(format!("invalid utf-8 in file name: {}", e)))?;

                dirs.push(DirEntry {
                    inode: child_inode,
                    name: name.to_string(),
                    file_type: FileType::from_dir_hint(file_type)
                        .expect("valid file type"),
                });
            }

            read += rec_len as usize;
            if read >= total_len {
                assert_eq!(read, total_len);
                break;
            }
        }

        Ok(dirs)
    }

    fn only_relevant_flag_is_extents(&self) -> bool {
        self.flags & (
            INODE_COMPR
            | INODE_DIRTY
            | INODE_COMPRBLK
            | INODE_ENCRYPT
            | INODE_IMAGIC
            | INODE_NOTAIL
            | INODE_TOPDIR
            | INODE_HUGE_FILE
            | INODE_EXTENTS
            | INODE_EA_INODE
            | INODE_EOFBLOCKS
            | INODE_INLINE_DATA
        ) == INODE_EXTENTS
    }
}

fn load_maj_min(block: [u8; 4 * 15]) -> (u16, u32) {
    if 0 != block[0] || 0 != block[1] {
        (block[1] as u16, block[0] as u32)
    } else {
        // if you think reading this is bad, I had to write it
        (block[5] as u16
             | (((block[6] & 0b0000_1111) as u16) << 8),
         block[4] as u32
             | ((block[7] as u32) << 12)
             | (((block[6] & 0b1111_0000) as u32) >> 4) << 8)
    }
}

fn as_u16(buf: &[u8]) -> u16 {
    buf[0] as u16 + buf[1] as u16 * 0x100
}

fn as_u32(buf: &[u8]) -> u32 {
    as_u16(buf) as u32 + as_u16(&buf[2..]) as u32 * 0x10000
}

fn parse_error(msg: String) -> io::Error {
    io::Error::new(io::ErrorKind::InvalidInput, msg)
}

#[allow(unknown_lints, absurd_extreme_comparisons)]
fn usize_check(val: u64) -> io::Result<usize> {
    // this check only makes sense on non-64-bit platforms; on 64-bit usize == u64.
    if val > std::usize::MAX as u64 {
        Err(io::Error::new(io::ErrorKind::InvalidData,
                                  format!("value is too big for memory on this platform: {}",
                                          val)))
    } else {
        Ok(val as usize)
    }
}
<|endoftext|>


<commit_before>#![feature(core)]
#![feature(alloc)]

pub mod unsafe_rc;
pub mod unsafe_weak_set;
pub mod rc_cell;
pub mod weak_cell_set;

pub use unsafe_rc::{UnsafeRc, UnsafeWeak};
pub use rc_cell::{RcCell, WeakCell};
pub use unsafe_weak_set::UnsafeWeakSet;
pub use weak_cell_set::WeakCellSet;
<commit_msg>Added id_rc<commit_after>#![feature(core)]
#![feature(alloc)]

pub mod unsafe_rc;
pub mod unsafe_weak_set;
pub mod rc_cell;
pub mod id_rc;
pub mod id_weak_set;
pub mod weak_cell_set;

pub use unsafe_rc::{UnsafeRc, UnsafeWeak};
pub use rc_cell::{RcCell, WeakCell};
pub use id_rc::{IdRc, IdWeak};
pub use unsafe_weak_set::UnsafeWeakSet;
pub use weak_cell_set::WeakCellSet;
pub use id_weak_set::IdWeakSet;
<|endoftext|>


<commit_before>//! Complex numbers

#![deny(missing_docs, warnings)]
#![feature(macro_rules, associated_types, default_type_params)]

extern crate onezero;

use std::fmt;
use std::ops::{Add, Sub, Mul, Div, Neg};
use std::rand::{Rand, Rng};

use onezero::{One, Zero};

mod ffi;
mod math;

pub mod f32;
pub mod f64;

/// A complex number in Cartesian form.
#[repr(C)]
#[derive(Clone, Copy, PartialEq)]
pub struct Complex<T> {
    /// The real part
    pub re: T,
    /// The imaginary part
    pub im: T,
}

/// Single precision complex number
#[allow(non_camel_case_types)]
pub type c64 = Complex<f32>;
/// Double precision complex number
#[allow(non_camel_case_types)]
pub type c128 = Complex<f64>;

impl<T> Complex<T> {
    /// Create a new complex number
    pub fn new(re: T, im: T) -> Complex<T> {
        Complex {
            im: im,
            re: re,
        }
    }
}

impl<T> Complex<T> where T: Add<Output=T> + Clone + Mul<Output=T> {
    fn norm_sqr(self) -> T {
        self.re.clone() * self.re + self.im.clone() * self.im
    }
}

impl<T> Add<T> for Complex<T> where T: Add<Output=T> + Clone {
    type Output = Complex<T>;

    fn add(self, rhs: T) -> Complex<T> {
        Complex {
            re: self.re + rhs,
            im: self.im.clone(),
        }
    }
}

impl<T> Add for Complex<T> where T: Add<Output=T> {
    type Output = Complex<T>;

    fn add(self, rhs: Complex<T>) -> Complex<T> {
        Complex {
            re: self.re + rhs.re,
            im: self.im + rhs.im,
        }
    }
}

impl<T> Add<Complex<T>> for T where T: Add<Output=T> + Clone {
    type Output = Complex<T>;

    fn add(self, rhs: Complex<T>) -> Complex<T> {
        rhs + self
    }
}

impl<T> Div<T> for Complex<T> where T: Clone + Div<Output=T> {
    type Output = Complex<T>;

    fn div(self, rhs: T) -> Complex<T> {
        Complex {
            re: self.re / rhs.clone(),
            im: self.im / rhs,
        }
    }
}

impl<T> Div for Complex<T> where
    T: Add<Output=T> + Clone + Div<Output=T> + Mul<Output=T> + Sub<Output=T>
{
    type Output = Complex<T>;

    fn div(self, rhs: Complex<T>) -> Complex<T> {
        let den = rhs.clone().norm_sqr();

        Complex {
            re: {
                self.re.clone() * rhs.re.clone() + self.im.clone() * rhs.im.clone()
            } / den.clone(),
            im: (self.im * rhs.re - self.re * rhs.im) / den,
        }
    }
}

impl<T> Div<Complex<T>> for T where
    T: Add<Output=T> + Clone + Div<Output=T> + Mul<Output=T> + Neg<Output=T>,
{
    type Output = Complex<T>;

    fn div(self, rhs: Complex<T>) -> Complex<T> {
        let den = rhs.clone().norm_sqr();

        Complex {
            re: (self.clone() * rhs.re) / den.clone(),
            im: -(self * rhs.im) / den,
        }
    }
}

impl<T> Mul<T> for Complex<T> where T: Clone + Mul<Output=T> {
    type Output = Complex<T>;

    fn mul(self, rhs: T) -> Complex<T> {
        Complex {
            re: self.re * rhs.clone(),
            im: self.im * rhs,
        }
    }
}

impl<T> Mul for Complex<T> where
    T: Add<Output=T> + Clone + Mul<Output=T> + Sub<Output=T>,
{
    type Output = Complex<T>;

    fn mul(self, rhs: Complex<T>) -> Complex<T> {
        Complex {
            re: self.re.clone() * rhs.re.clone() - self.im.clone() * rhs.im.clone(),
            im: self.re * rhs.im + self.im * rhs.re,
        }
    }
}

impl<T> Mul<Complex<T>> for T where T: Clone + Mul<Output=T> {
    type Output = Complex<T>;

    fn mul(self, rhs: Complex<T>) -> Complex<T> {
        rhs * self
    }
}

impl<T> Neg for Complex<T> where T: Neg<Output=T> {
    type Output = Complex<T>;

    fn neg(self) -> Complex<T> {
        Complex {
            re: -self.re,
            im: -self.im,
        }
    }
}

impl<T> One for Complex<T> where T: Mul<Output=T> + One + Sub<Output=T> + Zero {
    fn one() -> Complex<T> {
        Complex {
            re: One::one(),
            im: Zero::zero(),
        }
    }
}

impl<T> Rand for Complex<T> where T: Rand {
    fn rand<R>(rng: &mut R) -> Complex<T> where R: Rng {
        Complex::new(rng.gen(), rng.gen())
    }
}

impl<T> fmt::Show for Complex<T> where T: PartialOrd + fmt::Show + Zero {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.im < Zero::zero() {
            write!(f, "{}-{}i", self.re, self.im)
        } else {
            write!(f, "{}+{}i", self.re, self.im)
        }
    }
}

impl<T> Sub<T> for Complex<T> where T: Clone + Sub<Output=T> {
    type Output = Complex<T>;

    fn sub(self, rhs: T) -> Complex<T> {
        Complex {
            re: self.re - rhs,
            im: self.im.clone(),
        }
    }
}

impl<T> Sub for Complex<T> where T: Sub<Output=T> {
    type Output = Complex<T>;

    fn sub(self, rhs: Complex<T>) -> Complex<T> {
        Complex {
            re: self.re - rhs.re,
            im: self.im - rhs.im,
        }
    }
}

impl<T> Sub<Complex<T>> for T where T: Neg<Output=T> + Sub<Output=T> {
    type Output = Complex<T>;

    fn sub(self, rhs: Complex<T>) -> Complex<T> {
        Complex {
            re: self - rhs.re,
            im: -rhs.im,
        }
    }
}

impl<T> Zero for Complex<T> where T: Zero {
    fn zero() -> Complex<T> {
        Complex {
            re: Zero::zero(),
            im: Zero::zero(),
        }
    }
}

/// Mathematical operations on complex numbers
// FIXME (AI) `T` should be an associated output type
pub trait Math<T> {
    /// Computes the complex absolute value (also called norm, modulus or magnitude)
    fn abs(self) -> T;

    /// Computes the arc cosine
    fn acos(self) -> Self;

    /// Computes the arc hyperbolic cosine
    fn acosh(self) -> Self;

    /// Computes the argument (also called the phase angle)
    fn arg(self) -> T;

    /// Computes the arc sine
    fn asin(self) -> Self;

    /// Computes the arc hyperbolic sine
    fn asinh(self) -> Self;

    /// Computes the arc tangent
    fn atan(self) -> Self;

    /// Computes the arc hyperbolic tangent
    fn atanh(self) -> Self;

    /// Computes the arc cosine
    fn cos(self) -> Self;

    /// Computes the arc hyperbolic cosine
    fn cosh(self) -> Self;

    /// Computes the complex base-e exponential
    fn exp(self) -> Self;

    /// Returns the imaginary part
    fn imag(self) -> T;

    /// Computes the complex base-e logarithm
    fn log(self) -> Self;

    /// Computes the complex conjugate
    fn conj(self) -> Self;

    /// Computes the complex power
    fn pow(self, Self) -> Self;

    /// Computes the Riemann sphere projection
    fn proj(self) -> Self;

    /// Returns the real part
    fn real(self) -> T;

    /// Computes the arc sine
    fn sin(self) -> Self;

    /// Computes the arc hyperbolic sine
    fn sinh(self) -> Self;

    /// Computes the square root
    fn sqrt(self) -> Self;

    /// Computes the arc tangent
    fn tan(self) -> Self;

    /// Computes the arc hyperbolic tangent
    fn tanh(self) -> Self;
}

#[cfg(test)]
mod test {
    macro_rules! test {
        ($($ty:ident),+) => {$(
            mod $ty {
                use Complex;
                use $ty::I;

                #[test]
                fn constructor() {
                    assert_eq!(Complex::new(3., 4.), I * 4. + 3.)
                }
            })+
         }
    }

    test!(f32, f64);
}
<commit_msg>feat(One): relax bounds on `impl One for Complex`<commit_after>//! Complex numbers

#![deny(missing_docs, warnings)]
#![feature(macro_rules, associated_types, default_type_params)]

extern crate onezero;

use std::fmt;
use std::ops::{Add, Sub, Mul, Div, Neg};
use std::rand::{Rand, Rng};

use onezero::{One, Zero};

mod ffi;
mod math;

pub mod f32;
pub mod f64;

/// A complex number in Cartesian form.
#[repr(C)]
#[derive(Clone, Copy, PartialEq)]
pub struct Complex<T> {
    /// The real part
    pub re: T,
    /// The imaginary part
    pub im: T,
}

/// Single precision complex number
#[allow(non_camel_case_types)]
pub type c64 = Complex<f32>;
/// Double precision complex number
#[allow(non_camel_case_types)]
pub type c128 = Complex<f64>;

impl<T> Complex<T> {
    /// Create a new complex number
    pub fn new(re: T, im: T) -> Complex<T> {
        Complex {
            im: im,
            re: re,
        }
    }
}

impl<T> Complex<T> where T: Add<Output=T> + Clone + Mul<Output=T> {
    fn norm_sqr(self) -> T {
        self.re.clone() * self.re + self.im.clone() * self.im
    }
}

impl<T> Add<T> for Complex<T> where T: Add<Output=T> + Clone {
    type Output = Complex<T>;

    fn add(self, rhs: T) -> Complex<T> {
        Complex {
            re: self.re + rhs,
            im: self.im.clone(),
        }
    }
}

impl<T> Add for Complex<T> where T: Add<Output=T> {
    type Output = Complex<T>;

    fn add(self, rhs: Complex<T>) -> Complex<T> {
        Complex {
            re: self.re + rhs.re,
            im: self.im + rhs.im,
        }
    }
}

impl<T> Add<Complex<T>> for T where T: Add<Output=T> + Clone {
    type Output = Complex<T>;

    fn add(self, rhs: Complex<T>) -> Complex<T> {
        rhs + self
    }
}

impl<T> Div<T> for Complex<T> where T: Clone + Div<Output=T> {
    type Output = Complex<T>;

    fn div(self, rhs: T) -> Complex<T> {
        Complex {
            re: self.re / rhs.clone(),
            im: self.im / rhs,
        }
    }
}

impl<T> Div for Complex<T> where
    T: Add<Output=T> + Clone + Div<Output=T> + Mul<Output=T> + Sub<Output=T>
{
    type Output = Complex<T>;

    fn div(self, rhs: Complex<T>) -> Complex<T> {
        let den = rhs.clone().norm_sqr();

        Complex {
            re: {
                self.re.clone() * rhs.re.clone() + self.im.clone() * rhs.im.clone()
            } / den.clone(),
            im: (self.im * rhs.re - self.re * rhs.im) / den,
        }
    }
}

impl<T> Div<Complex<T>> for T where
    T: Add<Output=T> + Clone + Div<Output=T> + Mul<Output=T> + Neg<Output=T>,
{
    type Output = Complex<T>;

    fn div(self, rhs: Complex<T>) -> Complex<T> {
        let den = rhs.clone().norm_sqr();

        Complex {
            re: (self.clone() * rhs.re) / den.clone(),
            im: -(self * rhs.im) / den,
        }
    }
}

impl<T> Mul<T> for Complex<T> where T: Clone + Mul<Output=T> {
    type Output = Complex<T>;

    fn mul(self, rhs: T) -> Complex<T> {
        Complex {
            re: self.re * rhs.clone(),
            im: self.im * rhs,
        }
    }
}

impl<T> Mul for Complex<T> where
    T: Add<Output=T> + Clone + Mul<Output=T> + Sub<Output=T>,
{
    type Output = Complex<T>;

    fn mul(self, rhs: Complex<T>) -> Complex<T> {
        Complex {
            re: self.re.clone() * rhs.re.clone() - self.im.clone() * rhs.im.clone(),
            im: self.re * rhs.im + self.im * rhs.re,
        }
    }
}

impl<T> Mul<Complex<T>> for T where T: Clone + Mul<Output=T> {
    type Output = Complex<T>;

    fn mul(self, rhs: Complex<T>) -> Complex<T> {
        rhs * self
    }
}

impl<T> Neg for Complex<T> where T: Neg<Output=T> {
    type Output = Complex<T>;

    fn neg(self) -> Complex<T> {
        Complex {
            re: -self.re,
            im: -self.im,
        }
    }
}

impl<T> One for Complex<T> where T: One + Zero {
    fn one() -> Complex<T> {
        Complex {
            re: One::one(),
            im: Zero::zero(),
        }
    }
}

impl<T> Rand for Complex<T> where T: Rand {
    fn rand<R>(rng: &mut R) -> Complex<T> where R: Rng {
        Complex::new(rng.gen(), rng.gen())
    }
}

impl<T> fmt::Show for Complex<T> where T: PartialOrd + fmt::Show + Zero {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.im < Zero::zero() {
            write!(f, "{}-{}i", self.re, self.im)
        } else {
            write!(f, "{}+{}i", self.re, self.im)
        }
    }
}

impl<T> Sub<T> for Complex<T> where T: Clone + Sub<Output=T> {
    type Output = Complex<T>;

    fn sub(self, rhs: T) -> Complex<T> {
        Complex {
            re: self.re - rhs,
            im: self.im.clone(),
        }
    }
}

impl<T> Sub for Complex<T> where T: Sub<Output=T> {
    type Output = Complex<T>;

    fn sub(self, rhs: Complex<T>) -> Complex<T> {
        Complex {
            re: self.re - rhs.re,
            im: self.im - rhs.im,
        }
    }
}

impl<T> Sub<Complex<T>> for T where T: Neg<Output=T> + Sub<Output=T> {
    type Output = Complex<T>;

    fn sub(self, rhs: Complex<T>) -> Complex<T> {
        Complex {
            re: self - rhs.re,
            im: -rhs.im,
        }
    }
}

impl<T> Zero for Complex<T> where T: Zero {
    fn zero() -> Complex<T> {
        Complex {
            re: Zero::zero(),
            im: Zero::zero(),
        }
    }
}

/// Mathematical operations on complex numbers
// FIXME (AI) `T` should be an associated output type
pub trait Math<T> {
    /// Computes the complex absolute value (also called norm, modulus or magnitude)
    fn abs(self) -> T;

    /// Computes the arc cosine
    fn acos(self) -> Self;

    /// Computes the arc hyperbolic cosine
    fn acosh(self) -> Self;

    /// Computes the argument (also called the phase angle)
    fn arg(self) -> T;

    /// Computes the arc sine
    fn asin(self) -> Self;

    /// Computes the arc hyperbolic sine
    fn asinh(self) -> Self;

    /// Computes the arc tangent
    fn atan(self) -> Self;

    /// Computes the arc hyperbolic tangent
    fn atanh(self) -> Self;

    /// Computes the arc cosine
    fn cos(self) -> Self;

    /// Computes the arc hyperbolic cosine
    fn cosh(self) -> Self;

    /// Computes the complex base-e exponential
    fn exp(self) -> Self;

    /// Returns the imaginary part
    fn imag(self) -> T;

    /// Computes the complex base-e logarithm
    fn log(self) -> Self;

    /// Computes the complex conjugate
    fn conj(self) -> Self;

    /// Computes the complex power
    fn pow(self, Self) -> Self;

    /// Computes the Riemann sphere projection
    fn proj(self) -> Self;

    /// Returns the real part
    fn real(self) -> T;

    /// Computes the arc sine
    fn sin(self) -> Self;

    /// Computes the arc hyperbolic sine
    fn sinh(self) -> Self;

    /// Computes the square root
    fn sqrt(self) -> Self;

    /// Computes the arc tangent
    fn tan(self) -> Self;

    /// Computes the arc hyperbolic tangent
    fn tanh(self) -> Self;
}

#[cfg(test)]
mod test {
    macro_rules! test {
        ($($ty:ident),+) => {$(
            mod $ty {
                use Complex;
                use $ty::I;

                #[test]
                fn constructor() {
                    assert_eq!(Complex::new(3., 4.), I * 4. + 3.)
                }
            })+
         }
    }

    test!(f32, f64);
}
<|endoftext|>