<commit_before>use std::error::Error;
use std::fmt;
use std::collections::BTreeMap;

use iron::{Request, Response, Handler, IronResult, IronError, Url};
use iron::status;
use iron::typemap::Key;

use recognizer::Router as Recognizer;
use recognizer::Params as RecognizerParams;
use recognizer::Match;

use url::parse_path;

const LEFTOVER_PARAM_NAME: &'static str = "mountrouter_leftover";

/// `Params` is stored in request extensions and gives access to captured dynamic parameters
///
/// ```ignore
/// let ref params = req.extensions.get::<Router>().unwrap()
/// ```
pub type Params = BTreeMap<String, String>;

struct RouteParams {
	handler: Box<Handler>,
	is_mounted: bool,
}

/// `Router` provides an interface to apply different handlers to different routes as middleware for
/// the Iron framework.
pub struct Router {
    recognizer: Recognizer<RouteParams>
}

impl Router {
    /// Construct new `Router`.
    ///
    /// ```
    /// # use iron_mountrouter::Router;
    /// let router = Router::new();
    /// ```
    pub fn new() -> Router {
        Router {
            recognizer: Recognizer::new()
        }
    }

    /// Add a new route to a `Router`, matching given pattern.
    ///
    /// `route` is a pattern supported by [route recognizer]
    /// (https://crates.io/crates/route-recognizer):
    ///
    /// string like `/static/page/here` is matched entirely;
    ///
    /// `:name` is a dynamic param, matching any string that does not contain slash `/`, i.e.
    ///
    /// `/page/:page-num/content` will match `/page/17/content` or `/page/abbra-t/content`
    /// but not `/page/17/5/content`.
    ///
    /// Colon only works at the start of the segment so `/page-:num` works like usual string match
    /// and will not match `/page-5`.
    ///
    /// Currently there is no way to set param type so it catches only strings and does not perform
    /// any additional checks.
    ///
    /// `*name` is a dynamic wildcard param, matching any string (even those containing slashes`/`).
    /// `/redirect/to/*path` will match `/redirect/to/example.com/example`.
    ///
    /// Captured parameters are stored in [Params](type.Params.html) type and can be accessed
    /// in request extensions:
    ///
    /// ```ignore
    /// let ref params = req.extensions.get::<Router>().unwrap()
    /// ```
    ///
    /// Unwrap is safe here because router always adds extension if route is matched, even if no
    /// params were captured.
    ///
    /// `handler` is any [Iron handler]
    /// (http://ironframework.io/doc/iron/middleware/trait.Handler.html). Working with mountrouter
    /// you will most often use [MethodPicker](struct.MethodPicker.html) to pick appropriate
    /// handler for given http method.
    ///
    /// If `is_mounted` is true, router will strip matched part of the request url. Unlike [Mount]
    /// (http://ironframework.io/doc/mount/index.html) middleware from the Iron's core bundle, this
    /// parameter allows you to strip dynamical (e.g. with dynamic params) strings from request
    /// path. Original url is stored in request extensions using `OriginalUrl`. For example,
    ///
    /// ```ignore
    /// let mut router = Router::new();
    /// router.add_route("/book/:page/", handler, true);
    /// fn handler(req: &mut Request) -> IronResult<Response> {
    ///     // for path "/book/17/page/3"
    ///
    ///     // ["page", "3"]
    ///     println!("{:?}", req.url.path);
    ///     // ["book", "17", "page", "3"]
    ///     println!("{:?}", req.extensions.get::<OriginalUrl>().unwrap().path);
    /// }
    /// ```
    /// If `is_mounted` is set, route is forced to end with slash: slash is appended to the end of
    /// route if it is not there.
    pub fn add_route<H, S>(&mut self, route: S, handler: H, is_mounted: bool) -> &mut Router
    where H: Handler, S: AsRef<str> {
    	let params = RouteParams {
    		handler: Box::new(handler),
    		is_mounted: is_mounted
    	};
    	if is_mounted {
    		let r = route.as_ref();
    		let mut modified_route = String::with_capacity(r.len() + 2 + LEFTOVER_PARAM_NAME.len());
    		modified_route.push_str(r);
    		if !r.ends_with('/') {
				modified_route.push('/');
			}
    		modified_route.push('*');
    		modified_route.push_str(LEFTOVER_PARAM_NAME);
	        self.recognizer.add(&modified_route, params);
    	} else {
	        self.recognizer.add(route.as_ref(), params);
    	}
        self
    }

    fn recognize(&self, path: &str) -> Option<Match<&RouteParams>> {
        self.recognizer.recognize(path).ok()
    }

    fn append_params(req: &mut Request, params: &RecognizerParams) {
    	if !req.extensions.contains::<Router>() {
			req.extensions.insert::<Router>(Params::new());
    	}
    	let p = req.extensions.get_mut::<Router>().unwrap();
    	for name in params.keys() {
    		if name != LEFTOVER_PARAM_NAME {
    			p.insert(name.to_string(), params[name.as_ref()].clone());
			}
    	}
    }
}

impl Key for Router { type Value = Params; }

impl Handler for Router {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        let path = req.url.path.join("/");
        match self.recognize(&path) {
        	Some(matched) => {
	            if matched.handler.is_mounted {
	            	let leftover = "/".to_string() + &matched.params[LEFTOVER_PARAM_NAME];
	            	let (new_path, _, _) = parse_path(&leftover).unwrap();
			        if !req.extensions.contains::<OriginalUrl>() {
			            req.extensions.insert::<OriginalUrl>(req.url.clone());
			        }
	            	req.url.path = new_path;
	            }
            	Router::append_params(req, &matched.params);
	            matched.handler.handler.handle(req)
        	},
        	None => Err(IronError::new(NoRoute, status::NotFound))
        }
    }
}

/// `OriginalUrl` serves as a key in request extensions to original url if router strips url for
/// mounted handlers.
///
/// ```ignore
/// let ref original_url = req.extensions.get::<OriginalUrl>()
/// ```
#[derive(Copy, Clone)]
pub struct OriginalUrl;
impl Key for OriginalUrl { type Value = Url; }

/// `NoRoute` is error type returned to Iron if no route was matched in Router
#[derive(Debug)]
pub struct NoRoute;

impl fmt::Display for NoRoute {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("No matching route found.")
    }
}

impl Error for NoRoute {
    fn description(&self) -> &str { "No Route" }
}<commit_msg>Router now stores stripped url in extension and keeps original url in request.<commit_after>use std::error::Error;
use std::fmt;
use std::collections::BTreeMap;

use iron::{Request, Response, Handler, IronResult, IronError, Url};
use iron::status;
use iron::typemap::Key;

use recognizer::Router as Recognizer;
use recognizer::Params as RecognizerParams;
use recognizer::Match;

use url::parse_path;

const LEFTOVER_PARAM_NAME: &'static str = "mountrouter_leftover";

/// `Params` is stored in request extensions and gives access to captured dynamic parameters
///
/// ```ignore
/// let ref params = req.extensions.get::<Router>().unwrap()
/// ```
pub type Params = BTreeMap<String, String>;

struct RouteParams {
	handler: Box<Handler>,
	is_mounted: bool,
}

/// `Router` provides an interface to apply different handlers to different routes as middleware for
/// the Iron framework.
pub struct Router {
    recognizer: Recognizer<RouteParams>
}

impl Router {
    /// Construct new `Router`.
    ///
    /// ```
    /// # use iron_mountrouter::Router;
    /// let router = Router::new();
    /// ```
    pub fn new() -> Router {
        Router {
            recognizer: Recognizer::new()
        }
    }

    /// Add a new route to a `Router`, matching given pattern.
    ///
    /// `route` is a pattern supported by [route recognizer]
    /// (https://crates.io/crates/route-recognizer):
    ///
    /// string like `/static/page/here` is matched entirely;
    ///
    /// `:name` is a dynamic param, matching any string that does not contain slash `/`, i.e.
    ///
    /// `/page/:page-num/content` will match `/page/17/content` or `/page/abbra-t/content`
    /// but not `/page/17/5/content`.
    ///
    /// Colon only works at the start of the segment so `/page-:num` works like usual string match
    /// and will not match `/page-5`.
    ///
    /// Currently there is no way to set param type so it catches only strings and does not perform
    /// any additional checks.
    ///
    /// `*name` is a dynamic wildcard param, matching any string (even those containing slashes`/`).
    /// `/redirect/to/*path` will match `/redirect/to/example.com/example`.
    ///
    /// Captured parameters are stored in [Params](type.Params.html) type and can be accessed
    /// in request extensions:
    ///
    /// ```ignore
    /// let ref params = req.extensions.get::<Router>().unwrap()
    /// ```
    ///
    /// Unwrap is safe here because router always adds extension if route is matched, even if no
    /// params were captured.
    ///
    /// `handler` is any [Iron handler]
    /// (http://ironframework.io/doc/iron/middleware/trait.Handler.html). Working with mountrouter
    /// you will most often use [MethodPicker](struct.MethodPicker.html) to pick appropriate
    /// handler for given http method.
    ///
    /// If `is_mounted` is true, router will match any string *starting* with route. Original url is
    /// preserved, while stripped part is stored in request extensions using `StrippedUrl`. Further
    /// `Router`s will used stripped url to match their routes. For example,
    ///
    /// ```ignore
    /// let mut router = Router::new();
    /// router.add_route("/book/:page/", handler, true);
    /// fn handler(req: &mut Request) -> IronResult<Response> {
    ///     // for path "/book/17/page/3"
    ///
    ///     // ["book", "17", "page", "3"]
    ///     println!("{:?}", req.url.path);
    ///     // ["page", "3"]
    ///     println!("{:?}", req.extensions.get::<StrippedUrl>().unwrap().path);
    /// }
    /// ```
    /// If `is_mounted` is set, route is forced to end with slash: slash is appended to the end of
    /// route if it is not there.
    pub fn add_route<H, S>(&mut self, route: S, handler: H, is_mounted: bool) -> &mut Router
    where H: Handler, S: AsRef<str> {
    	let params = RouteParams {
    		handler: Box::new(handler),
    		is_mounted: is_mounted
    	};
    	if is_mounted {
    		let r = route.as_ref();
    		let mut modified_route = String::with_capacity(r.len() + 2 + LEFTOVER_PARAM_NAME.len());
    		modified_route.push_str(r);
    		if !r.ends_with('/') {
				modified_route.push('/');
			}
    		modified_route.push('*');
    		modified_route.push_str(LEFTOVER_PARAM_NAME);
	        self.recognizer.add(&modified_route, params);
    	} else {
	        self.recognizer.add(route.as_ref(), params);
    	}
        self
    }

    fn recognize(&self, path: &str) -> Option<Match<&RouteParams>> {
        self.recognizer.recognize(path).ok()
    }

    fn append_params(req: &mut Request, params: &RecognizerParams) {
    	if !req.extensions.contains::<Router>() {
			req.extensions.insert::<Router>(Params::new());
    	}
    	let p = req.extensions.get_mut::<Router>().unwrap();
    	for name in params.keys() {
    		if name != LEFTOVER_PARAM_NAME {
    			p.insert(name.to_string(), params[name.as_ref()].clone());
			}
    	}
    }
}

impl Key for Router { type Value = Params; }

impl Handler for Router {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
    	let path = if req.extensions.contains::<StrippedUrl>() {
    		req.extensions.get::<StrippedUrl>().unwrap().path.join("/")
    	} else {
	        req.url.path.join("/")
    	};
        match self.recognize(&path) {
        	Some(matched) => {
	            if matched.handler.is_mounted {
	            	let leftover = "/".to_string() + &matched.params[LEFTOVER_PARAM_NAME];
	            	let (new_path, _, _) = parse_path(&leftover).unwrap();
			        if !req.extensions.contains::<StrippedUrl>() {
			        	let mut stripped_url = req.url.clone();
			        	stripped_url.path = new_path;
			            req.extensions.insert::<StrippedUrl>(stripped_url);
			        }
	            }
            	Router::append_params(req, &matched.params);
	            matched.handler.handler.handle(req)
        	},
        	None => Err(IronError::new(NoRoute, status::NotFound))
        }
    }
}

/// `StrippedUrl` serves as a key in request extensions. It contains non-matched part of url if
/// route was mounted
///
/// ```ignore
/// let ref stripped_url = req.extensions.get::<StrippedUrl>()
/// ```
#[derive(Copy, Clone)]
pub struct StrippedUrl;
impl Key for StrippedUrl { type Value = Url; }

/// `NoRoute` is error type returned to Iron if no route was matched in Router
#[derive(Debug)]
pub struct NoRoute;

impl fmt::Display for NoRoute {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("No matching route found.")
    }
}

impl Error for NoRoute {
    fn description(&self) -> &str { "No Route" }
}<|endoftext|>


<commit_before>
use std::comm::{Chan, Port, SharedChan};

use std::hashmap::{HashMap};

use std::task::{task};

pub enum ObjectBrokerMessage {

	// ## Object management ##

	// request from thread a to addref object b
	// if the object is not known to the broker yet,
	// this registers the object for as pertaining
	// to thread a.
	OB_RQ_ADD_REF(uint, uint),

	// request from thread a to release object b
	// if thread a owns this object, the message informs
	// the broker that the object has been deleted.
	OB_RQ_RELEASE(uint, uint),

	// thread a asks which thread owns object b
	// response is the same message with a (c, b) tuple,
	// c is the id of the the owning thread.
	OB_RQ_WHO_OWNS(uint, uint),

	// thread a asks to take over ownership of object b
	OB_RQ_OWN(uint, uint),

	// thread a abandons ownership of object b
	// when send from broker to a thread c, this means that this
	// thread should take over ownership of the object. When
	// send from a thread to broker in response to a RQ_OWN
	// message, the last tuple element indicates the original
	// asker.
	OB_RQ_DISOWN(uint, uint, ~[u8], uint),

	// ## Thread management ##

	// a new thread a registers with the object broker
	OB_REGISTER(uint, Chan<ObjectBrokerMessage>),

	// ## VM management ##
	OB_SHUTDOWN
}

// The ObjectBroker handles ownership for concurrently accessed
// objects. At every time, an object has one well-defined owner.
// If a thread needs access to an object that it does not currently
// own, it submits a OB_RQ_OWN message to the object broker, which
// in turn asks the thread who owns the object to relinquish it using
// a OB_RQ_DISOWN message. The owning thread gives up ownership and
// sends a OB_RQ_DISOWN message to the broker, which forwards it to
// the original thread and updates its book-keeping to reflect the
// change in ownership.
//
// The ObjectBroker keeps a HM of object ids mapped to their owning
// thread ids.
//
// When a thread dies, it forwards all of its alive objects to the
// object broker using a OB_OUT_RE_OBJ message. The broker, in turn,
// keeps those objects internally until another thread demands to
// own them.
pub struct ObjectBroker {
	objects_with_owners: HashMap<uint, uint>,

	in_port : Port<ObjectBrokerMessage>,
	out_chan : HashMap<uint, Chan<ObjectBrokerMessage>>,

	// this is duplicated into all
	in_shared_chan : SharedChan<ObjectBrokerMessage>,
}

static OB_INITIAL_OBJ_HASHMAP_CAPACITY : uint = 4096;
static OB_INITIAL_THREAD_CAPACITY : uint = 16;

impl ObjectBroker {

	// ----------------------------------------------
	pub fn new() -> ObjectBroker
	{
	 	let (out,input) = SharedChan::new();
		ObjectBroker {
			objects_with_owners : HashMap::with_capacity(OB_INITIAL_OBJ_HASHMAP_CAPACITY),

			in_port : out,
			in_shared_chan : input,
			out_chan : HashMap::with_capacity(OB_INITIAL_THREAD_CAPACITY),
		}
	}

	// ----------------------------------------------
	// Launches the object broker. Returns a SharedChan object that can be used
	// to direct messages to the broker. Send an OB_SHUTDOWN to terminate
	// operation.
	pub fn launch(mut self) -> SharedChan<ObjectBrokerMessage> {
		let ret_chan = self.in_shared_chan.clone();
		do spawn {
			let mut objects = self.objects_with_owners;
			let mut threads = self.out_chan;

			loop {
				match self.in_port.recv() {
					OB_RQ_ADD_REF(a,b) => {
						// for somebody to have a reference to a field and thus
						// being able to addref/release it, they must have had
						// accss to an object that was owned by this thread.
						// As this object must have been transferred using the
						// object broker and messages are guaranteed to be
						// ordered, we must already know about that object.

						// Therefore, whether the object is present in the HM
						// is safe for determining whether it is new.
						match objects.find(&b) {
							Some(owner) => {
								let t = threads.get(owner);
								t.send(OB_RQ_ADD_REF(a,b));
								continue;
							},
							_ => (),
						}
						objects.insert(b,a);
					}

					OB_RQ_RELEASE(a,b) => {
						// correctness follows by the same reasoning as for AddRef()
						let owner = *objects.get(&b);
						if owner == a {
							objects.remove(&b);
						}
						else {
							let t = threads.get(&owner);
							t.send(OB_RQ_RELEASE(a,b));
						}
					},

					OB_RQ_WHO_OWNS(a,b) => {
						let t = threads.get(&a);
						t.send(OB_RQ_WHO_OWNS(*objects.get(&b),b));
					},

					OB_RQ_OWN(a,b) => {
						// b must be in objects as per the same reasoning as
						// OB_RQ_ADD_REF() is sound.

						let owner = *objects.get(&b);
						// cannot request object oned by oneself
						// bookkeeping of own owned objects is consistent,
						// so failure to hold this would be a logic error.
						assert!(owner != a);

						// TODO: what if somebody else concurrently requested
						// owning the object, but has not received it yet?

						let t = threads.get(&owner);
						t.send(OB_RQ_OWN(a, b));
					},

					OB_RQ_DISOWN(a,b,obj,receiver) => {
						// must own object to be able to disown it
						assert!(*objects.get(&b) == a);

						*objects.get_mut(&b) = receiver;
						let t = threads.get(&receiver);
						t.send(OB_RQ_DISOWN(a, b, obj, receiver));
					},

					OB_REGISTER(a, chan) => {
						assert!(!threads.contains_key(&a));
						threads.insert(a, chan);
						debug!("object broker registered with thread {}", a);
					},

					OB_SHUTDOWN => {
						debug!("object broker shutting down");
						break;
					},
				}
			}
		}
		return ret_chan;
	}
}

#[cfg(test)]
mod tests {
	use objectbroker::*;
	use std::task::{task};

	type test_proc = proc(SharedChan<ObjectBrokerMessage>, Port<ObjectBrokerMessage>) -> ();

	fn test_setup(a : test_proc, b : test_proc) {
		let mut ob = ObjectBroker::new();
		let chan = ob.launch();

		// thread 1
		let input1 = chan.clone();
		let mut t1 = task();
		let f1 = t1.future_result();
		do t1.spawn {
			let (port, chan) = Chan::new();
			input1.send(OB_REGISTER(1, chan));

			a(input1, port);
		}

		// thread 2
		let input2 = chan.clone();
		let mut t2 = task();
		let f2 = t2.future_result();
		do t2.spawn {
			let (port, chan) = Chan::new();
			input2.send(OB_REGISTER(2, chan));

			b(input2, port);
		}

		f1.recv();
		f2.recv();
	}

	#[test]
	fn test_object_broker() {
		let (sync_port, sync_chan) = Chan::new();
		test_setup(
			proc(input : SharedChan<ObjectBrokerMessage>, output: Port<ObjectBrokerMessage>) {
				// register object 15
				input.send(OB_RQ_ADD_REF(1,15));
				sync_chan.send(1);

				let request = output.recv();
				match request {
					OB_RQ_OWN(2,15) => input.send(OB_RQ_DISOWN(1,15,~[0x12 as u8],2)),
					_ => assert!(false),
				}
			},
			proc(input : SharedChan<ObjectBrokerMessage>, output: Port<ObjectBrokerMessage>) {
				// we have to ensure that object 15 is registered. Normally,
				// this is implicitly guaranteed because how would we otherwise
				// know about its id?
				sync_port.recv();

				// want to own object 15
				input.send(OB_RQ_OWN(2,15));

				let response = output.recv();
				match response {
					OB_RQ_DISOWN(1,15,val,2) => {
						assert_eq!(val[0], 0x12 as u8);
						input.send(OB_SHUTDOWN);
					},
					_ => assert!(false),
				}
			}
		);
	}
}

// TODO: tests of more complex scenarios
<commit_msg>objectbroker: refactor<commit_after>
use std::comm::{Chan, Port, SharedChan};

use std::hashmap::{HashMap};

use std::task::{task};

pub enum ObjectBrokerMessage {

	// ## Object management ##

	// request from thread a to addref object b
	// if the object is not known to the broker yet,
	// this registers the object for as pertaining
	// to thread a.
	OB_RQ_ADD_REF(uint, uint),

	// request from thread a to release object b
	// if thread a owns this object, the message informs
	// the broker that the object has been deleted.
	OB_RQ_RELEASE(uint, uint),

	// thread a asks which thread owns object b
	// response is the same message with a (c, b) tuple,
	// c is the id of the the owning thread.
	OB_RQ_WHO_OWNS(uint, uint),

	// thread a asks to take over ownership of object b
	OB_RQ_OWN(uint, uint),

	// thread a abandons ownership of object b
	// when send from broker to a thread c, this means that this
	// thread should take over ownership of the object. When
	// send from a thread to broker in response to a RQ_OWN
	// message, the last tuple element indicates the original
	// asker.
	OB_RQ_DISOWN(uint, uint, ~[u8], uint),

	// ## Thread management ##

	// a new thread a registers with the object broker
	OB_REGISTER(uint, Chan<ObjectBrokerMessage>),

	// ## VM management ##
	OB_SHUTDOWN
}

// The ObjectBroker handles ownership for concurrently accessed
// objects. At every time, an object has one well-defined owner.
// If a thread needs access to an object that it does not currently
// own, it submits a OB_RQ_OWN message to the object broker, which
// in turn asks the thread who owns the object to relinquish it using
// a OB_RQ_DISOWN message. The owning thread gives up ownership and
// sends a OB_RQ_DISOWN message to the broker, which forwards it to
// the original thread and updates its book-keeping to reflect the
// change in ownership.
//
// The ObjectBroker keeps a HM of object ids mapped to their owning
// thread ids.
//
// When a thread dies, it forwards all of its alive objects to the
// object broker using a OB_OUT_RE_OBJ message. The broker, in turn,
// keeps those objects internally until another thread demands to
// own them.
pub struct ObjectBroker {
	objects_with_owners: HashMap<uint, uint>,

	in_port : Port<ObjectBrokerMessage>,
	out_chan : HashMap<uint, Chan<ObjectBrokerMessage>>,

	// this is duplicated into all
	in_shared_chan : SharedChan<ObjectBrokerMessage>,
}

static OB_INITIAL_OBJ_HASHMAP_CAPACITY : uint = 4096;
static OB_INITIAL_THREAD_CAPACITY : uint = 16;

impl ObjectBroker {

	// ----------------------------------------------
	pub fn new() -> ObjectBroker
	{
	 	let (out,input) = SharedChan::new();
		ObjectBroker {
			objects_with_owners : HashMap::with_capacity(OB_INITIAL_OBJ_HASHMAP_CAPACITY),

			in_port : out,
			in_shared_chan : input,
			out_chan : HashMap::with_capacity(OB_INITIAL_THREAD_CAPACITY),
		}
	}

	// ----------------------------------------------
	// Launches the object broker. Returns a SharedChan object that can be used
	// to direct messages to the broker. Send an OB_SHUTDOWN to terminate
	// operation.
	pub fn launch(mut self) -> SharedChan<ObjectBrokerMessage> {
		let ret_chan = self.in_shared_chan.clone();
		do spawn {
			self.task_main();
		}
		return ret_chan;
	}

	// IMPL

	// ----------------------------------------------
	fn task_main(mut self) {
		let ref mut objects = self.objects_with_owners;
		let ref mut threads = self.out_chan;

		loop {
			match self.in_port.recv() {
				OB_RQ_ADD_REF(a,b) => {
					// for somebody to have a reference to a field and thus
					// being able to addref/release it, they must have had
					// accss to an object that was owned by this thread.
					// As this object must have been transferred using the
					// object broker and messages are guaranteed to be
					// ordered, we must already know about that object.

					// Therefore, whether the object is present in the HM
					// is safe for determining whether it is new.
					match objects.find(&b) {
						Some(owner) => {
							let t = threads.get(owner);
							t.send(OB_RQ_ADD_REF(a,b));
							continue;
						},
						_ => (),
					}
					objects.insert(b,a);
				}

				OB_RQ_RELEASE(a,b) => {
					// correctness follows by the same reasoning as for AddRef()
					let owner = *objects.get(&b);
					if owner == a {
						objects.remove(&b);
					}
					else {
						let t = threads.get(&owner);
						t.send(OB_RQ_RELEASE(a,b));
					}
				},

				OB_RQ_WHO_OWNS(a,b) => {
					let t = threads.get(&a);
					t.send(OB_RQ_WHO_OWNS(*objects.get(&b),b));
				},

				OB_RQ_OWN(a,b) => {
					// b must be in objects as per the same reasoning as
					// OB_RQ_ADD_REF() is sound.

					let owner = *objects.get(&b);
					// cannot request object oned by oneself
					// bookkeeping of own owned objects is consistent,
					// so failure to hold this would be a logic error.
					assert!(owner != a);

					// TODO: what if somebody else concurrently requested
					// owning the object, but has not received it yet?

					let t = threads.get(&owner);
					t.send(OB_RQ_OWN(a, b));
				},

				OB_RQ_DISOWN(a,b,obj,receiver) => {
					// must own object to be able to disown it
					assert!(*objects.get(&b) == a);

					*objects.get_mut(&b) = receiver;
					let t = threads.get(&receiver);
					t.send(OB_RQ_DISOWN(a, b, obj, receiver));
				},

				OB_REGISTER(a, chan) => {
					assert!(!threads.contains_key(&a));
					threads.insert(a, chan);
					debug!("object broker registered with thread {}", a);
				},

				OB_SHUTDOWN => {
					debug!("object broker shutting down");
					break;
				},
			}
		}
	}
}

#[cfg(test)]
mod tests {
	use objectbroker::*;
	use std::task::{task};

	type test_proc = proc(SharedChan<ObjectBrokerMessage>, Port<ObjectBrokerMessage>) -> ();

	fn test_setup(a : test_proc, b : test_proc) {
		let mut ob = ObjectBroker::new();
		let chan = ob.launch();

		// thread 1
		let input1 = chan.clone();
		let mut t1 = task();
		let f1 = t1.future_result();
		do t1.spawn {
			let (port, chan) = Chan::new();
			input1.send(OB_REGISTER(1, chan));

			a(input1, port);
		}

		// thread 2
		let input2 = chan.clone();
		let mut t2 = task();
		let f2 = t2.future_result();
		do t2.spawn {
			let (port, chan) = Chan::new();
			input2.send(OB_REGISTER(2, chan));

			b(input2, port);
		}

		f1.recv();
		f2.recv();
	}

	#[test]
	fn test_object_broker() {
		let (sync_port, sync_chan) = Chan::new();
		test_setup(
			proc(input : SharedChan<ObjectBrokerMessage>, output: Port<ObjectBrokerMessage>) {
				// register object 15
				input.send(OB_RQ_ADD_REF(1,15));
				sync_chan.send(1);

				let request = output.recv();
				match request {
					OB_RQ_OWN(2,15) => input.send(OB_RQ_DISOWN(1,15,~[0x12 as u8],2)),
					_ => assert!(false),
				}
			},
			proc(input : SharedChan<ObjectBrokerMessage>, output: Port<ObjectBrokerMessage>) {
				// we have to ensure that object 15 is registered. Normally,
				// this is implicitly guaranteed because how would we otherwise
				// know about its id?
				sync_port.recv();

				// want to own object 15
				input.send(OB_RQ_OWN(2,15));

				let response = output.recv();
				match response {
					OB_RQ_DISOWN(1,15,val,2) => {
						assert_eq!(val[0], 0x12 as u8);
						input.send(OB_SHUTDOWN);
					},
					_ => assert!(false),
				}
			}
		);
	}
}

// TODO: tests of more complex scenarios
<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::result::Result as RResult;
use std::ops::Deref;

use toml::{Parser, Value};

generate_error_module!(
    generate_error_types!(ConfigError, ConfigErrorKind,
        NoConfigFileFound   => "No config file found"
    );
);

use self::error::{ConfigError, ConfigErrorKind};

/**
 * Result type of this module. Either `T` or `ConfigError`
 */
pub type Result<T> = RResult<T, ConfigError>;

/// `Configuration` object
///
/// Holds all config variables which are globally available plus the configuration object from the
/// config parser, which can be accessed.
#[derive(Debug)]
pub struct Configuration {

    /// The plain configuration object for direct access if necessary
    config: Value,

    /// The verbosity the program should run with
    verbosity: bool,

    /// The editor which should be used
    editor: Option<String>,

    ///The options the editor should get when opening some file
    editor_opts: String,
}

impl Configuration {

    /// Get a new configuration object.
    ///
    /// The passed runtimepath is used for searching the configuration file, whereas several file
    /// names are tested. If that does not work, the home directory and the XDG basedir are tested
    /// with all variants.
    ///
    /// If that doesn't work either, an error is returned.
    pub fn new(rtp: &PathBuf) -> Result<Configuration> {
        fetch_config(&rtp).map(|cfg| {
            let verbosity   = get_verbosity(&cfg);
            let editor      = get_editor(&cfg);
            let editor_opts = get_editor_opts(&cfg);

            debug!("Building configuration");
            debug!("  - verbosity  : {:?}", verbosity);
            debug!("  - editor     : {:?}", editor);
            debug!("  - editor-opts: {}", editor_opts);

            Configuration {
                config: cfg,
                verbosity: verbosity,
                editor: editor,
                editor_opts: editor_opts,
            }
        })
    }

    pub fn editor(&self) -> Option<&String> {
        self.editor.as_ref()
    }

    #[allow(dead_code)] // Why do I actually need this annotation on a pub function?
    pub fn config(&self) -> &Value {
        &self.config
    }

    pub fn store_config(&self) -> Option<&Value> {
        match self.config {
            Value::Table(ref tabl) => tabl.get("store"),
            _ => None,
        }
    }

}

impl Deref for Configuration {
    type Target = Value;

    fn deref(&self) -> &Value {
        &self.config
    }

}

fn get_verbosity(v: &Value) -> bool {
    match *v {
        Value::Table(ref t) => t.get("verbose")
                .map_or(false, |v| match *v { Value::Boolean(b) => b, _ => false, }),
        _ => false,
    }
}

fn get_editor(v: &Value) -> Option<String> {
    match *v {
        Value::Table(ref t) => t.get("editor")
                .and_then(|v| match *v { Value::String(ref s) => Some(s.clone()), _ => None, }),
        _ => None,
    }
}

fn get_editor_opts(v: &Value) -> String {
    match *v {
        Value::Table(ref t) => t.get("editor-opts")
                .and_then(|v| match *v { Value::String(ref s) => Some(s.clone()), _ => None, })
                .unwrap_or_default(),
        _ => String::new(),
    }
}

/**
 * Helper to fetch the config file
 *
 * Tests several variants for the config file path and uses the first one which works.
 */
fn fetch_config(rtp: &PathBuf) -> Result<Value> {
    use std::env;
    use std::fs::File;
    use std::io::Read;
    use std::io::Write;
    use std::io::stderr;

    use xdg_basedir;
    use itertools::Itertools;

    use libimagutil::variants::generate_variants as gen_vars;

    let variants = vec!["config", "config.toml", "imagrc", "imagrc.toml"];
    let modifier = |base: &PathBuf, v: &'static str| {
        let mut base = base.clone();
        base.push(String::from(v));
        base
    };

    vec![
        vec![rtp.clone()],
        gen_vars(rtp.clone(), variants.clone(), &modifier),

        env::var("HOME").map(|home| gen_vars(PathBuf::from(home), variants.clone(), &modifier))
                        .unwrap_or(vec![]),

        xdg_basedir::get_data_home().map(|data_dir| gen_vars(data_dir, variants.clone(), &modifier))
                                    .unwrap_or(vec![]),
    ].iter()
        .flatten()
        .filter(|path| path.exists() && path.is_file())
        .map(|path| {
            let content = {
                let mut s = String::new();
                let f = File::open(path);
                if f.is_err() {
                    return None
                }
                let mut f = f.unwrap();
                f.read_to_string(&mut s).ok();
                s
            };

            let mut parser = Parser::new(&content[..]);
            let res = parser.parse();

            if res.is_none() {
                write!(stderr(), "Config file parser error:").ok();
                for error in parser.errors {
                    write!(stderr(), "At [{}][{}] <> {}", error.lo, error.hi, error).ok();
                    write!(stderr(), "in: '{}'", &content[error.lo..error.hi]).ok();
                }
                None
            } else {
                res
            }
        })
        .filter(|loaded| loaded.is_some())
        .nth(0)
        .map(|inner| Value::Table(inner.unwrap()))
        .ok_or(ConfigErrorKind::NoConfigFileFound.into())
}

<commit_msg>Add error kinds for config override errors<commit_after>use std::path::PathBuf;
use std::result::Result as RResult;
use std::ops::Deref;

use toml::{Parser, Value};

generate_error_module!(
    generate_error_types!(ConfigError, ConfigErrorKind,
        NoConfigFileFound   => "No config file found",

        ConfigOverrideError => "Config override error",
        ConfigOverrideKeyNotAvailable => "Key not available",
        ConfigOverrideTypeNotMatching => "Configuration Type not matching"

    );
);

use self::error::{ConfigError, ConfigErrorKind};

/**
 * Result type of this module. Either `T` or `ConfigError`
 */
pub type Result<T> = RResult<T, ConfigError>;

/// `Configuration` object
///
/// Holds all config variables which are globally available plus the configuration object from the
/// config parser, which can be accessed.
#[derive(Debug)]
pub struct Configuration {

    /// The plain configuration object for direct access if necessary
    config: Value,

    /// The verbosity the program should run with
    verbosity: bool,

    /// The editor which should be used
    editor: Option<String>,

    ///The options the editor should get when opening some file
    editor_opts: String,
}

impl Configuration {

    /// Get a new configuration object.
    ///
    /// The passed runtimepath is used for searching the configuration file, whereas several file
    /// names are tested. If that does not work, the home directory and the XDG basedir are tested
    /// with all variants.
    ///
    /// If that doesn't work either, an error is returned.
    pub fn new(rtp: &PathBuf) -> Result<Configuration> {
        fetch_config(&rtp).map(|cfg| {
            let verbosity   = get_verbosity(&cfg);
            let editor      = get_editor(&cfg);
            let editor_opts = get_editor_opts(&cfg);

            debug!("Building configuration");
            debug!("  - verbosity  : {:?}", verbosity);
            debug!("  - editor     : {:?}", editor);
            debug!("  - editor-opts: {}", editor_opts);

            Configuration {
                config: cfg,
                verbosity: verbosity,
                editor: editor,
                editor_opts: editor_opts,
            }
        })
    }

    pub fn editor(&self) -> Option<&String> {
        self.editor.as_ref()
    }

    #[allow(dead_code)] // Why do I actually need this annotation on a pub function?
    pub fn config(&self) -> &Value {
        &self.config
    }

    pub fn store_config(&self) -> Option<&Value> {
        match self.config {
            Value::Table(ref tabl) => tabl.get("store"),
            _ => None,
        }
    }

}

impl Deref for Configuration {
    type Target = Value;

    fn deref(&self) -> &Value {
        &self.config
    }

}

fn get_verbosity(v: &Value) -> bool {
    match *v {
        Value::Table(ref t) => t.get("verbose")
                .map_or(false, |v| match *v { Value::Boolean(b) => b, _ => false, }),
        _ => false,
    }
}

fn get_editor(v: &Value) -> Option<String> {
    match *v {
        Value::Table(ref t) => t.get("editor")
                .and_then(|v| match *v { Value::String(ref s) => Some(s.clone()), _ => None, }),
        _ => None,
    }
}

fn get_editor_opts(v: &Value) -> String {
    match *v {
        Value::Table(ref t) => t.get("editor-opts")
                .and_then(|v| match *v { Value::String(ref s) => Some(s.clone()), _ => None, })
                .unwrap_or_default(),
        _ => String::new(),
    }
}

/**
 * Helper to fetch the config file
 *
 * Tests several variants for the config file path and uses the first one which works.
 */
fn fetch_config(rtp: &PathBuf) -> Result<Value> {
    use std::env;
    use std::fs::File;
    use std::io::Read;
    use std::io::Write;
    use std::io::stderr;

    use xdg_basedir;
    use itertools::Itertools;

    use libimagutil::variants::generate_variants as gen_vars;

    let variants = vec!["config", "config.toml", "imagrc", "imagrc.toml"];
    let modifier = |base: &PathBuf, v: &'static str| {
        let mut base = base.clone();
        base.push(String::from(v));
        base
    };

    vec![
        vec![rtp.clone()],
        gen_vars(rtp.clone(), variants.clone(), &modifier),

        env::var("HOME").map(|home| gen_vars(PathBuf::from(home), variants.clone(), &modifier))
                        .unwrap_or(vec![]),

        xdg_basedir::get_data_home().map(|data_dir| gen_vars(data_dir, variants.clone(), &modifier))
                                    .unwrap_or(vec![]),
    ].iter()
        .flatten()
        .filter(|path| path.exists() && path.is_file())
        .map(|path| {
            let content = {
                let mut s = String::new();
                let f = File::open(path);
                if f.is_err() {
                    return None
                }
                let mut f = f.unwrap();
                f.read_to_string(&mut s).ok();
                s
            };

            let mut parser = Parser::new(&content[..]);
            let res = parser.parse();

            if res.is_none() {
                write!(stderr(), "Config file parser error:").ok();
                for error in parser.errors {
                    write!(stderr(), "At [{}][{}] <> {}", error.lo, error.hi, error).ok();
                    write!(stderr(), "in: '{}'", &content[error.lo..error.hi]).ok();
                }
                None
            } else {
                res
            }
        })
        .filter(|loaded| loaded.is_some())
        .nth(0)
        .map(|inner| Value::Table(inner.unwrap()))
        .ok_or(ConfigErrorKind::NoConfigFileFound.into())
}

<|endoftext|>


<commit_before>use std::fs;
use std::io;

use crate::checkout_sources;
use crate::rust_install_toolchain;
use crate::Job;
use crate::RustToolchain;
use crate::Step;

fn find_sync_readme_crates() -> Vec<String> {
    fn walk(path: &str, depth: u32, crates: &mut Vec<String>) {
        let path_fs = if path.is_empty() { "." } else { path };
        for cr in fs::read_dir(path_fs).unwrap() {
            let cr = cr.unwrap();
            if !fs::metadata(cr.path()).unwrap().is_dir() {
                continue;
            }

            let file_name = cr.file_name().to_str().unwrap().to_owned();
            if file_name == "target" {
                continue;
            }

            let child_path = if path.is_empty() {
                file_name.clone()
            } else {
                format!("{}/{}", path, file_name)
            };

            if depth == 0 {
                walk(&child_path, depth + 1, crates);
            }

            let readme = cr.path().join("README.md");
            let readme = match fs::read_to_string(&readme) {
                Ok(readme) => readme,
                Err(e) if e.kind() == io::ErrorKind::NotFound => {
                    continue;
                }
                Err(e) => panic!("failed to read {}: {}", readme.display(), e),
            };
            if readme.contains("<!-- cargo-sync-readme") {
                crates.push(child_path);
            }
        }
    }

    let mut crates = Vec::new();
    walk("", 0, &mut crates);
    crates.sort();
    crates
}

pub(crate) fn cargo_sync_readme_job() -> Job {
    let mut steps = vec![
        checkout_sources(),
        rust_install_toolchain(RustToolchain::Stable),
        Step::run(
            "install cargo sync-readme",
            "cargo install cargo-sync-readme",
        ),
    ];
    for cr in find_sync_readme_crates() {
        steps.push(Step::run(
            &format!("sync-readme {}", cr),
            &format!("cd {} && cargo sync-readme --check", cr),
        ));
    }
    Job {
        id: "cargo-sync-readme".to_owned(),
        name: "Check sync-readme".to_owned(),
        steps,
        ..Job::default()
    }
}
<commit_msg>Run sync-readme in ci-gen<commit_after>use std::fs;
use std::io;
use std::process::Command;

use crate::checkout_sources;
use crate::rust_install_toolchain;
use crate::Job;
use crate::RustToolchain;
use crate::Step;

fn find_sync_readme_crates() -> Vec<String> {
    fn walk(path: &str, depth: u32, crates: &mut Vec<String>) {
        let path_fs = if path.is_empty() { "." } else { path };
        for cr in fs::read_dir(path_fs).unwrap() {
            let cr = cr.unwrap();
            if !fs::metadata(cr.path()).unwrap().is_dir() {
                continue;
            }

            let file_name = cr.file_name().to_str().unwrap().to_owned();
            if file_name == "target" {
                continue;
            }

            let child_path = if path.is_empty() {
                file_name.clone()
            } else {
                format!("{}/{}", path, file_name)
            };

            if depth == 0 {
                walk(&child_path, depth + 1, crates);
            }

            let readme = cr.path().join("README.md");
            let readme = match fs::read_to_string(&readme) {
                Ok(readme) => readme,
                Err(e) if e.kind() == io::ErrorKind::NotFound => {
                    continue;
                }
                Err(e) => panic!("failed to read {}: {}", readme.display(), e),
            };
            if readme.contains("<!-- cargo-sync-readme") {
                crates.push(child_path);
            }
        }
    }

    let mut crates = Vec::new();
    walk("", 0, &mut crates);
    crates.sort();
    crates
}

pub(crate) fn cargo_sync_readme_job() -> Job {
    let mut steps = vec![
        checkout_sources(),
        rust_install_toolchain(RustToolchain::Stable),
        Step::run(
            "install cargo sync-readme",
            "cargo install cargo-sync-readme",
        ),
    ];
    for cr in find_sync_readme_crates() {
        Command::new("cargo")
            .arg("sync-readme")
            .current_dir(&cr)
            .output()
            .unwrap();

        steps.push(Step::run(
            &format!("sync-readme {}", cr),
            &format!("cd {} && cargo sync-readme --check", cr),
        ));
    }
    Job {
        id: "cargo-sync-readme".to_owned(),
        name: "Check sync-readme".to_owned(),
        steps,
        ..Job::default()
    }
}
<|endoftext|>


<commit_before>#![no_std]

#[macro_use]
extern crate enum_map;

use enum_map::EnumMap;

#[derive(Debug, EnumMap, PartialEq)]
enum Discriminants {
    A = 2000,
    B = 3000,
    C = 1000,
}

#[test]
fn discriminants() {
    let mut map = EnumMap::new();
    map[Discriminants::A] = 3;
    map[Discriminants::B] = 2;
    map[Discriminants::C] = 1;
    let mut pairs = (&map).into_iter();
    assert_eq!(pairs.next(), Some((Discriminants::A, &3)));
    assert_eq!(pairs.next(), Some((Discriminants::B, &2)));
    assert_eq!(pairs.next(), Some((Discriminants::C, &1)));
    assert_eq!(pairs.next(), None);
}
<commit_msg>(&map).into_iter() can be written as map.iter()<commit_after>#![no_std]

#[macro_use]
extern crate enum_map;

use enum_map::EnumMap;

#[derive(Debug, EnumMap, PartialEq)]
enum Discriminants {
    A = 2000,
    B = 3000,
    C = 1000,
}

#[test]
fn discriminants() {
    let mut map = EnumMap::new();
    map[Discriminants::A] = 3;
    map[Discriminants::B] = 2;
    map[Discriminants::C] = 1;
    let mut pairs = map.iter();
    assert_eq!(pairs.next(), Some((Discriminants::A, &3)));
    assert_eq!(pairs.next(), Some((Discriminants::B, &2)));
    assert_eq!(pairs.next(), Some((Discriminants::C, &1)));
    assert_eq!(pairs.next(), None);
}
<|endoftext|>


<commit_before>//! The lexer module.
//!
//! The simpler implementation of a lexer I could think of. It only takes a
//! stream of characters and tansforms it into a tokens one.
//!
//! It doesn't keep track of the current line or column. In a real compiler that
//! information is crucial when you write something wrong and don't know where.

use std::iter::Peekable;
use std::str::Chars;

/// Token types that our language admits.
///
/// All the token types that our little language will need. As it is a very
/// simple language, it doesn't have many. The lexer takes an input and has a
/// token iterator as the output.
///
/// The traits are mostly for tests.
#[derive(Debug, PartialEq)]
pub enum Token {
    /// For anything that we don't recognize.
    Illegal,
    /// Last token, when the input has ended.
    EOF,

    /// Any word: a variable name, a type name...
    Ident(String),

    /// Left parenthesis.
    ParL,
    /// Right parenthesis.
    ParR,
    /// The ':' character.
    Colon,
    /// The ';' character.
    Semicolon,
    /// The ',' character.
    Comma,

    /// The only keyword we have in the language.
    Type,
}

/// The lexer in our language.
///
/// The lexer, also known as tokenizer, transforms the input text into tokens.
pub struct Lexer<'a> {
    input: Peekable<Chars<'a>>,
}

impl<'a> Lexer<'a> {
    /// Create a new lexer.
    ///
    /// To build the lexer, you feed it a `str` with the code. Then you can use
    /// the lexer as a normal `Iterator`, which iterates over [`Token`s][1].
    ///
    /// # Examples
    ///
    ///     use simcom::lexer::{Lexer, Token};
    ///
    ///     let mut tokens = Lexer::new(": tipo");
    ///     assert_eq!(tokens.next().unwrap(), Token::Colon);
    ///     assert_eq!(tokens.next().unwrap(), Token::Type);
    ///     assert_eq!(tokens.next(), None);
    ///
    /// [1]: enum.Token.html
    pub fn new(input: &'a str) -> Lexer {
        Lexer { input: input.chars().peekable() }
    }

    /// The next char in the input, advances the internal iterator.
    fn read_char(&mut self) -> Option<char> {
        self.input.next()
    }

    /// The next char in the input. *doesn't* advance the internal iterator.
    fn peek_char(&mut self) -> Option<&char> {
        self.input.peek()
    }

    /// If an alphabetic char was found, keep reading chars to build a
    /// identifier. Finally, look if it was a keyword.
    fn read_identifier(&mut self, ch: char) -> Token {
        let content = {
            // Since in ::next_token we need to call ::read_char the first
            // char was consumed, so we add it here.
            let mut content = String::new();
            content.push(ch);

            // Keep building the string with allowed characters.
            while let Some(&ch) = self.peek_char() {
                if !(ch.is_alphabetic() || ch.is_digit(10)) {
                    break;
                }

                // We call ::unwrap because we already made sure there is an
                // element with the ::peek above.
                content.push(self.read_char().unwrap());
            }

            content
        };

        // Match the identifier to all the known keywords to see if it is one
        // of them. Otherwise, return a normal identifier.
        match &content[..] {
            "tipo" => Token::Type,
            _ => Token::Ident(content),
        }
    }

    /// Advance the internal iterator when we find whitespace.
    fn consume_whitespace(&mut self) {
        while let Some(&ch) = self.peek_char() {
            if ch.is_whitespace() {
                self.read_char();
            } else {
                break;
            }
        }
    }

    /// The basis for the iterator, matches the characters to Tokens.
    fn next_token(&mut self) -> Token {
        self.consume_whitespace();

        if let Some(ch) = self.read_char() {
            match ch {
                '(' => Token::ParL,
                ')' => Token::ParR,
                ':' => Token::Colon,
                ';' => Token::Semicolon,
                ',' => Token::Comma,
                '\0' => Token::EOF,
                _ => if ch.is_alphabetic() {
                    // As ::read_identifier already advances the iterator, we
                    // can safely perform an early return.
                    return self.read_identifier(ch);
                } else {
                    Token::Illegal
                }
            }
        // If the internal iterator has given us a None, that means there are no
        // characters left. In other words, EOF was reached.
        } else {
            Token::EOF
        }
    }
}

impl<'a> Iterator for Lexer<'a> {
    type Item = Token;

    fn next(&mut self) -> Option<Self::Item> {
        match self.next_token() {
            Token::EOF => None,
            x => Some(x),
        }
    }
}
<commit_msg>Clean code and comment<commit_after>//! The lexer module.
//!
//! The simpler implementation of a lexer I could think of. It only takes a
//! stream of characters and tansforms it into a tokens one.
//!
//! It doesn't keep track of the current line or column. In a real compiler that
//! information is crucial when you write something wrong and don't know where.

use std::iter::Peekable;
use std::str::Chars;

/// Token types that our language admits.
///
/// All the token types that our little language will need. As it is a very
/// simple language, it doesn't have many. The lexer takes an input and has a
/// token iterator as the output.
///
/// The traits are mostly for tests.
#[derive(Debug, PartialEq)]
pub enum Token {
    /// For anything that we don't recognize.
    Illegal,
    /// Last token, when the input has ended.
    EOF,

    /// Any word: a variable name, a type name...
    Ident(String),

    /// Left parenthesis.
    ParL,
    /// Right parenthesis.
    ParR,
    /// The ':' character.
    Colon,
    /// The ';' character.
    Semicolon,
    /// The ',' character.
    Comma,

    /// The only keyword we have in the language.
    Type,
}

/// The lexer in our language.
///
/// The lexer, also known as tokenizer, transforms the input text into tokens.
pub struct Lexer<'a> {
    input: Peekable<Chars<'a>>,
}

impl<'a> Lexer<'a> {
    /// Create a new lexer.
    ///
    /// To build the lexer, you feed it a `str` with the code. Then you can use
    /// the lexer as a normal `Iterator`, which iterates over [`Token`s][1].
    ///
    /// # Examples
    ///
    ///     use simcom::lexer::{Lexer, Token};
    ///
    ///     let mut tokens = Lexer::new(": tipo");
    ///     assert_eq!(tokens.next().unwrap(), Token::Colon);
    ///     assert_eq!(tokens.next().unwrap(), Token::Type);
    ///     assert_eq!(tokens.next(), None);
    ///
    /// [1]: enum.Token.html
    pub fn new(input: &'a str) -> Lexer {
        Lexer { input: input.chars().peekable() }
    }

    /// The next char in the input, advances the internal iterator.
    fn read_char(&mut self) -> Option<char> {
        self.input.next()
    }

    /// The next char in the input. *doesn't* advance the internal iterator.
    fn peek_char(&mut self) -> Option<&char> {
        self.input.peek()
    }

    /// If an alphabetic char was found, keep reading chars to build a
    /// identifier. Finally, look if it was a keyword.
    fn read_identifier(&mut self, ch: char) -> Token {
        let content = {
            // Since in ::next_token we need to call ::read_char the first
            // char was consumed, so we add it here.
            let mut content = String::new();
            content.push(ch);

            // Keep building the string with allowed characters.
            while let Some(&ch) = self.peek_char() {
                if !(ch.is_alphabetic() || ch.is_digit(10)) {
                    break;
                }

                // We call ::unwrap because we already made sure there is an
                // element with the ::peek above.
                content.push(self.read_char().unwrap());
            }

            content
        };

        // Match the identifier to all the known keywords to see if it is one
        // of them. Otherwise, return a normal identifier.
        match &content[..] {
            "tipo" => Token::Type,
            _ => Token::Ident(content),
        }
    }

    /// Advance the internal iterator when we find whitespace.
    fn consume_whitespace(&mut self) {
        while let Some(&ch) = self.peek_char() {
            if ch.is_whitespace() {
                self.read_char();
            } else {
                break;
            }
        }
    }

    /// The basis for the iterator, matches the characters to Tokens.
    fn next_token(&mut self) -> Token {
        self.consume_whitespace();

        if let Some(ch) = self.read_char() {
            match ch {
                '(' => Token::ParL,
                ')' => Token::ParR,
                ':' => Token::Colon,
                ';' => Token::Semicolon,
                ',' => Token::Comma,
                '\0' => Token::EOF,
                _ => if ch.is_alphabetic() {
                    // Read the remainder part of the identifier, passing its
                    // first character, as we already read it.
                    self.read_identifier(ch)
                } else {
                    Token::Illegal
                }
            }
        // If the internal iterator has given us a None, that means there are no
        // characters left. In other words, EOF was reached.
        } else {
            Token::EOF
        }
    }
}

impl<'a> Iterator for Lexer<'a> {
    type Item = Token;

    fn next(&mut self) -> Option<Self::Item> {
        match self.next_token() {
            Token::EOF => None,
            x => Some(x),
        }
    }
}
<|endoftext|>


<commit_before>#![cfg(not(debug_assertions))]
// Tests the available fork-choice algorithms

pub use beacon_chain::BeaconChain;
use bls::Signature;
use store::MemoryStore;
use store::Store;
// use env_logger::{Builder, Env};
use fork_choice::{
    BitwiseLMDGhost, ForkChoice, ForkChoiceAlgorithm, LongestChain, OptimizedLMDGhost, SlowLMDGhost,
};
use std::collections::HashMap;
use std::sync::Arc;
use std::{fs::File, io::prelude::*, path::PathBuf};
use types::test_utils::TestingBeaconStateBuilder;
use types::{
    BeaconBlock, BeaconBlockBody, Eth1Data, EthSpec, FoundationEthSpec, Hash256, Keypair, Slot,
};
use yaml_rust::yaml;

// Note: We Assume the block Id's are hex-encoded.

#[test]
fn test_optimized_lmd_ghost() {
    // set up logging
    // Builder::from_env(Env::default().default_filter_or("trace")).init();

    test_yaml_vectors(
        ForkChoiceAlgorithm::OptimizedLMDGhost,
        "tests/lmd_ghost_test_vectors.yaml",
        100,
    );
}

#[test]
fn test_bitwise_lmd_ghost() {
    // set up logging
    //Builder::from_env(Env::default().default_filter_or("trace")).init();

    test_yaml_vectors(
        ForkChoiceAlgorithm::BitwiseLMDGhost,
        "tests/bitwise_lmd_ghost_test_vectors.yaml",
        100,
    );
}

#[test]
fn test_slow_lmd_ghost() {
    test_yaml_vectors(
        ForkChoiceAlgorithm::SlowLMDGhost,
        "tests/lmd_ghost_test_vectors.yaml",
        100,
    );
}

#[test]
fn test_longest_chain() {
    test_yaml_vectors(
        ForkChoiceAlgorithm::LongestChain,
        "tests/longest_chain_test_vectors.yaml",
        100,
    );
}

// run a generic test over given YAML test vectors
fn test_yaml_vectors(
    fork_choice_algo: ForkChoiceAlgorithm,
    yaml_file_path: &str,
    emulated_validators: usize, // the number of validators used to give weights.
) {
    // load test cases from yaml
    let test_cases = load_test_cases_from_yaml(yaml_file_path);

    // default vars
    let spec = FoundationEthSpec::spec();
    let zero_hash = Hash256::zero();
    let eth1_data = Eth1Data {
        deposit_root: zero_hash.clone(),
        block_hash: zero_hash.clone(),
    };
    let randao_reveal = Signature::empty_signature();
    let signature = Signature::empty_signature();
    let body = BeaconBlockBody {
        eth1_data,
        randao_reveal,
        proposer_slashings: vec![],
        attester_slashings: vec![],
        attestations: vec![],
        deposits: vec![],
        voluntary_exits: vec![],
        transfers: vec![],
    };

    // process the tests
    for test_case in test_cases {
        // setup a fresh test
        let (mut fork_choice, store, state_root) =
            setup_inital_state(&fork_choice_algo, emulated_validators);

        // keep a hashmap of block_id's to block_hashes (random hashes to abstract block_id)
        //let mut block_id_map: HashMap<String, Hash256> = HashMap::new();
        // keep a list of hash to slot
        let mut block_slot: HashMap<Hash256, Slot> = HashMap::new();
        // assume the block tree is given to us in order.
        let mut genesis_hash = None;
        for block in test_case["blocks"].clone().into_vec().unwrap() {
            let block_id = block["id"].as_str().unwrap().to_string();
            let parent_id = block["parent"].as_str().unwrap().to_string();

            // default params for genesis
            let block_hash = id_to_hash(&block_id);
            let mut slot = spec.genesis_slot;
            let previous_block_root = id_to_hash(&parent_id);

            // set the slot and parent based off the YAML. Start with genesis;
            // if not the genesis, update slot
            if parent_id != block_id {
                // find parent slot
                slot = *(block_slot
                    .get(&previous_block_root)
                    .expect("Parent should have a slot number"))
                    + 1;
            } else {
                genesis_hash = Some(block_hash);
            }

            // update slot mapping
            block_slot.insert(block_hash, slot);

            // build the BeaconBlock
            let beacon_block = BeaconBlock {
                slot,
                previous_block_root,
                state_root: state_root.clone(),
                signature: signature.clone(),
                body: body.clone(),
            };

            // Store the block.
            store.put(&block_hash, &beacon_block).unwrap();

            // run add block for fork choice if not genesis
            if parent_id != block_id {
                fork_choice
                    .add_block(&beacon_block, &block_hash, &spec)
                    .unwrap();
            }
        }

        // add the weights (attestations)
        let mut current_validator = 0;
        for id_map in test_case["weights"].clone().into_vec().unwrap() {
            // get the block id and weights
            for (map_id, map_weight) in id_map.as_hash().unwrap().iter() {
                let id = map_id.as_str().unwrap();
                let block_root = id_to_hash(&id.to_string());
                let weight = map_weight.as_i64().unwrap();
                // we assume a validator has a value 1 and add an attestation for to achieve the
                // correct weight
                for _ in 0..weight {
                    assert!(
                        current_validator <= emulated_validators,
                        "Not enough validators to emulate weights"
                    );
                    fork_choice
                        .add_attestation(current_validator as u64, &block_root, &spec)
                        .unwrap();
                    current_validator += 1;
                }
            }
        }

        // everything is set up, run the fork choice, using genesis as the head
        let head = fork_choice
            .find_head(&genesis_hash.unwrap(), &spec)
            .unwrap();

        // compare the result to the expected test
        let success = test_case["heads"]
            .clone()
            .into_vec()
            .unwrap()
            .iter()
            .find(|heads| id_to_hash(&heads["id"].as_str().unwrap().to_string()) == head)
            .is_some();

        println!("Head found: {}", head);
        assert!(success, "Did not find one of the possible heads");
    }
}

// loads the test_cases from the supplied yaml file
fn load_test_cases_from_yaml(file_path: &str) -> Vec<yaml_rust::Yaml> {
    // load the yaml
    let mut file = {
        let mut file_path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        file_path_buf.push(file_path);
        File::open(file_path_buf).unwrap()
    };
    let mut yaml_str = String::new();
    file.read_to_string(&mut yaml_str).unwrap();
    let docs = yaml::YamlLoader::load_from_str(&yaml_str).unwrap();
    let doc = &docs[0];
    doc["test_cases"].as_vec().unwrap().clone()
}

// initialise a single validator and state. All blocks will reference this state root.
fn setup_inital_state(
    fork_choice_algo: &ForkChoiceAlgorithm,
    num_validators: usize,
) -> (Box<ForkChoice>, Arc<MemoryStore>, Hash256) {
    let store = Arc::new(MemoryStore::open());

    // the fork choice instantiation
    let fork_choice: Box<ForkChoice> = match fork_choice_algo {
        ForkChoiceAlgorithm::OptimizedLMDGhost => {
            let f: OptimizedLMDGhost<MemoryStore, FoundationEthSpec> =
                OptimizedLMDGhost::new(store.clone());
            Box::new(f)
        }
        ForkChoiceAlgorithm::BitwiseLMDGhost => {
            let f: BitwiseLMDGhost<MemoryStore, FoundationEthSpec> =
                BitwiseLMDGhost::new(store.clone());
            Box::new(f)
        }
        ForkChoiceAlgorithm::SlowLMDGhost => {
            let f: SlowLMDGhost<MemoryStore, FoundationEthSpec> = SlowLMDGhost::new(store.clone());
            Box::new(f)
        }
        ForkChoiceAlgorithm::LongestChain => Box::new(LongestChain::new(store.clone())),
    };

    let spec = FoundationEthSpec::spec();

    let mut state_builder: TestingBeaconStateBuilder<FoundationEthSpec> =
        TestingBeaconStateBuilder::from_single_keypair(num_validators, &Keypair::random(), &spec);
    state_builder.build_caches(&spec).unwrap();
    let (state, _keypairs) = state_builder.build();

    let state_root = state.canonical_root();
    store.put(&state_root, &state).unwrap();

    // return initialised vars
    (fork_choice, store, state_root)
}

// convert a block_id into a Hash256 -- assume input is hex encoded;
fn id_to_hash(id: &String) -> Hash256 {
    let bytes = hex::decode(id).expect("Block ID should be hex");

    let len = std::cmp::min(bytes.len(), 32);
    let mut fixed_bytes = [0u8; 32];
    for (index, byte) in bytes.iter().take(32).enumerate() {
        fixed_bytes[32 - len + index] = *byte;
    }
    Hash256::from(fixed_bytes)
}
<commit_msg>Fix `fork_choice` tests<commit_after>#![cfg(not(debug_assertions))]
/// Tests the available fork-choice algorithms
pub use beacon_chain::BeaconChain;
use bls::Signature;
use store::MemoryStore;
use store::Store;
// use env_logger::{Builder, Env};
use fork_choice::{BitwiseLMDGhost, ForkChoice, LongestChain, OptimizedLMDGhost, SlowLMDGhost};
use std::collections::HashMap;
use std::sync::Arc;
use std::{fs::File, io::prelude::*, path::PathBuf};
use types::test_utils::TestingBeaconStateBuilder;
use types::{
    BeaconBlock, BeaconBlockBody, Eth1Data, EthSpec, FoundationEthSpec, Hash256, Keypair, Slot,
};
use yaml_rust::yaml;

// Note: We Assume the block Id's are hex-encoded.

#[test]
fn test_optimized_lmd_ghost() {
    // set up logging
    // Builder::from_env(Env::default().default_filter_or("trace")).init();

    test_yaml_vectors::<OptimizedLMDGhost<MemoryStore, FoundationEthSpec>>(
        "tests/lmd_ghost_test_vectors.yaml",
        100,
    );
}

#[test]
fn test_bitwise_lmd_ghost() {
    // set up logging
    //Builder::from_env(Env::default().default_filter_or("trace")).init();

    test_yaml_vectors::<BitwiseLMDGhost<MemoryStore, FoundationEthSpec>>(
        "tests/bitwise_lmd_ghost_test_vectors.yaml",
        100,
    );
}

#[test]
fn test_slow_lmd_ghost() {
    test_yaml_vectors::<SlowLMDGhost<MemoryStore, FoundationEthSpec>>(
        "tests/lmd_ghost_test_vectors.yaml",
        100,
    );
}

#[test]
fn test_longest_chain() {
    test_yaml_vectors::<LongestChain<MemoryStore>>("tests/longest_chain_test_vectors.yaml", 100);
}

// run a generic test over given YAML test vectors
fn test_yaml_vectors<T: ForkChoice<MemoryStore>>(
    yaml_file_path: &str,
    emulated_validators: usize, // the number of validators used to give weights.
) {
    // load test cases from yaml
    let test_cases = load_test_cases_from_yaml(yaml_file_path);

    // default vars
    let spec = FoundationEthSpec::spec();
    let zero_hash = Hash256::zero();
    let eth1_data = Eth1Data {
        deposit_root: zero_hash.clone(),
        block_hash: zero_hash.clone(),
    };
    let randao_reveal = Signature::empty_signature();
    let signature = Signature::empty_signature();
    let body = BeaconBlockBody {
        eth1_data,
        randao_reveal,
        proposer_slashings: vec![],
        attester_slashings: vec![],
        attestations: vec![],
        deposits: vec![],
        voluntary_exits: vec![],
        transfers: vec![],
    };

    // process the tests
    for test_case in test_cases {
        // setup a fresh test
        let (mut fork_choice, store, state_root) = setup_inital_state::<T>(emulated_validators);

        // keep a hashmap of block_id's to block_hashes (random hashes to abstract block_id)
        //let mut block_id_map: HashMap<String, Hash256> = HashMap::new();
        // keep a list of hash to slot
        let mut block_slot: HashMap<Hash256, Slot> = HashMap::new();
        // assume the block tree is given to us in order.
        let mut genesis_hash = None;
        for block in test_case["blocks"].clone().into_vec().unwrap() {
            let block_id = block["id"].as_str().unwrap().to_string();
            let parent_id = block["parent"].as_str().unwrap().to_string();

            // default params for genesis
            let block_hash = id_to_hash(&block_id);
            let mut slot = spec.genesis_slot;
            let previous_block_root = id_to_hash(&parent_id);

            // set the slot and parent based off the YAML. Start with genesis;
            // if not the genesis, update slot
            if parent_id != block_id {
                // find parent slot
                slot = *(block_slot
                    .get(&previous_block_root)
                    .expect("Parent should have a slot number"))
                    + 1;
            } else {
                genesis_hash = Some(block_hash);
            }

            // update slot mapping
            block_slot.insert(block_hash, slot);

            // build the BeaconBlock
            let beacon_block = BeaconBlock {
                slot,
                previous_block_root,
                state_root: state_root.clone(),
                signature: signature.clone(),
                body: body.clone(),
            };

            // Store the block.
            store.put(&block_hash, &beacon_block).unwrap();

            // run add block for fork choice if not genesis
            if parent_id != block_id {
                fork_choice
                    .add_block(&beacon_block, &block_hash, &spec)
                    .unwrap();
            }
        }

        // add the weights (attestations)
        let mut current_validator = 0;
        for id_map in test_case["weights"].clone().into_vec().unwrap() {
            // get the block id and weights
            for (map_id, map_weight) in id_map.as_hash().unwrap().iter() {
                let id = map_id.as_str().unwrap();
                let block_root = id_to_hash(&id.to_string());
                let weight = map_weight.as_i64().unwrap();
                // we assume a validator has a value 1 and add an attestation for to achieve the
                // correct weight
                for _ in 0..weight {
                    assert!(
                        current_validator <= emulated_validators,
                        "Not enough validators to emulate weights"
                    );
                    fork_choice
                        .add_attestation(current_validator as u64, &block_root, &spec)
                        .unwrap();
                    current_validator += 1;
                }
            }
        }

        // everything is set up, run the fork choice, using genesis as the head
        let head = fork_choice
            .find_head(&genesis_hash.unwrap(), &spec)
            .unwrap();

        // compare the result to the expected test
        let success = test_case["heads"]
            .clone()
            .into_vec()
            .unwrap()
            .iter()
            .find(|heads| id_to_hash(&heads["id"].as_str().unwrap().to_string()) == head)
            .is_some();

        println!("Head found: {}", head);
        assert!(success, "Did not find one of the possible heads");
    }
}

// loads the test_cases from the supplied yaml file
fn load_test_cases_from_yaml(file_path: &str) -> Vec<yaml_rust::Yaml> {
    // load the yaml
    let mut file = {
        let mut file_path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        file_path_buf.push(file_path);
        File::open(file_path_buf).unwrap()
    };
    let mut yaml_str = String::new();
    file.read_to_string(&mut yaml_str).unwrap();
    let docs = yaml::YamlLoader::load_from_str(&yaml_str).unwrap();
    let doc = &docs[0];
    doc["test_cases"].as_vec().unwrap().clone()
}

fn setup_inital_state<T>(
    // fork_choice_algo: &ForkChoiceAlgorithm,
    num_validators: usize
) -> (T, Arc<MemoryStore>, Hash256)
where
    T: ForkChoice<MemoryStore>,
{
    let store = Arc::new(MemoryStore::open());

    let fork_choice = ForkChoice::new(store.clone());
    let spec = FoundationEthSpec::spec();

    let mut state_builder: TestingBeaconStateBuilder<FoundationEthSpec> =
        TestingBeaconStateBuilder::from_single_keypair(num_validators, &Keypair::random(), &spec);
    state_builder.build_caches(&spec).unwrap();
    let (state, _keypairs) = state_builder.build();

    let state_root = state.canonical_root();
    store.put(&state_root, &state).unwrap();

    // return initialised vars
    (fork_choice, store, state_root)
}

// convert a block_id into a Hash256 -- assume input is hex encoded;
fn id_to_hash(id: &String) -> Hash256 {
    let bytes = hex::decode(id).expect("Block ID should be hex");

    let len = std::cmp::min(bytes.len(), 32);
    let mut fixed_bytes = [0u8; 32];
    for (index, byte) in bytes.iter().take(32).enumerate() {
        fixed_bytes[32 - len + index] = *byte;
    }
    Hash256::from(fixed_bytes)
}
<|endoftext|>


<commit_before>use std::cmp::max;
use std::fmt;
use std::ops::Deref;
use std::sync::{Mutex, MutexGuard};

use datetime::TimeZone;
use zoneinfo_compiled::{CompiledData, Result as TZResult};

use locale;

use users::UsersCache;

use output::cell::TextCell;
use output::colours::Colours;
use output::time::TimeFormat;

use fs::{File, Dir, fields as f};

/// Options for displaying a table.
pub struct Options {
    pub env: Environment,
    pub size_format: SizeFormat,
    pub time_format: TimeFormat,
    pub time_types: TimeTypes,
    pub inode: bool,
    pub links: bool,
    pub blocks: bool,
    pub group: bool,
    pub git: bool
}

impl fmt::Debug for Options {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        // I had to make other types derive Debug,
        // and Mutex<UsersCache> is not that!
        writeln!(f, "<table options>")
    }
}

impl Options {
    pub fn should_scan_for_git(&self) -> bool {
        self.git
    }

    pub fn for_dir(&self, dir: Option<&Dir>) -> Vec<Column> {
        let mut columns = vec![];

        if self.inode {
            columns.push(Column::Inode);
        }

        columns.push(Column::Permissions);

        if self.links {
            columns.push(Column::HardLinks);
        }

        columns.push(Column::FileSize(self.size_format));

        if self.blocks {
            columns.push(Column::Blocks);
        }

        columns.push(Column::User);

        if self.group {
            columns.push(Column::Group);
        }

        if self.time_types.modified {
            columns.push(Column::Timestamp(TimeType::Modified));
        }

        if self.time_types.created {
            columns.push(Column::Timestamp(TimeType::Created));
        }

        if self.time_types.accessed {
            columns.push(Column::Timestamp(TimeType::Accessed));
        }

        if cfg!(feature="git") {
            if let Some(d) = dir {
                if self.should_scan_for_git() && d.has_git_repo() {
                    columns.push(Column::GitStatus);
                }
            }
        }

        columns
    }
}

/// A table contains these.
#[derive(Debug)]
pub enum Column {
    Permissions,
    FileSize(SizeFormat),
    Timestamp(TimeType),
    Blocks,
    User,
    Group,
    HardLinks,
    Inode,
    GitStatus,
}

/// Each column can pick its own **Alignment**. Usually, numbers are
/// right-aligned, and text is left-aligned.
#[derive(Copy, Clone)]
pub enum Alignment {
    Left, Right,
}

impl Column {

    /// Get the alignment this column should use.
    pub fn alignment(&self) -> Alignment {
        match *self {
            Column::FileSize(_)
            | Column::HardLinks
            | Column::Inode
            | Column::Blocks
            | Column::GitStatus => Alignment::Right,
            _                   => Alignment::Left,
        }
    }

    /// Get the text that should be printed at the top, when the user elects
    /// to have a header row printed.
    pub fn header(&self) -> &'static str {
        match *self {
            Column::Permissions   => "Permissions",
            Column::FileSize(_)   => "Size",
            Column::Timestamp(t)  => t.header(),
            Column::Blocks        => "Blocks",
            Column::User          => "User",
            Column::Group         => "Group",
            Column::HardLinks     => "Links",
            Column::Inode         => "inode",
            Column::GitStatus     => "Git",
        }
    }
}

/// Formatting options for file sizes.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum SizeFormat {

    /// Format the file size using **decimal** prefixes, such as “kilo”,
    /// “mega”, or “giga”.
    DecimalBytes,

    /// Format the file size using **binary** prefixes, such as “kibi”,
    /// “mebi”, or “gibi”.
    BinaryBytes,

    /// Do no formatting and just display the size as a number of bytes.
    JustBytes,
}

impl Default for SizeFormat {
    fn default() -> SizeFormat {
        SizeFormat::DecimalBytes
    }
}

/// The types of a file’s time fields. These three fields are standard
/// across most (all?) operating systems.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum TimeType {

    /// The file’s accessed time (`st_atime`).
    Accessed,

    /// The file’s modified time (`st_mtime`).
    Modified,

    /// The file’s creation time (`st_ctime`).
    Created,
}

impl TimeType {

    /// Returns the text to use for a column’s heading in the columns output.
    pub fn header(&self) -> &'static str {
        match *self {
            TimeType::Accessed  => "Date Accessed",
            TimeType::Modified  => "Date Modified",
            TimeType::Created   => "Date Created",
        }
    }
}

/// Fields for which of a file’s time fields should be displayed in the
/// columns output.
///
/// There should always be at least one of these--there's no way to disable
/// the time columns entirely (yet).
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct TimeTypes {
    pub accessed: bool,
    pub modified: bool,
    pub created:  bool,
}

impl Default for TimeTypes {

    /// By default, display just the ‘modified’ time. This is the most
    /// common option, which is why it has this shorthand.
    fn default() -> TimeTypes {
        TimeTypes { accessed: false, modified: true, created: false }
    }
}

/// The **environment** struct contains any data that could change between
/// running instances of exa, depending on the user's computer's configuration.
///
/// Any environment field should be able to be mocked up for test runs.
pub struct Environment {

    /// Localisation rules for formatting numbers.
    numeric: locale::Numeric,

    /// The computer's current time zone. This gets used to determine how to
    /// offset files' timestamps.
    tz: Option<TimeZone>,

    /// Mapping cache of user IDs to usernames.
    users: Mutex<UsersCache>,
}

impl Environment {
    pub fn lock_users(&self) -> MutexGuard<UsersCache> {
        self.users.lock().unwrap()
    }

    pub fn load_all() -> Self {
        let tz = match determine_time_zone() {
            Ok(t) => Some(t),
            Err(ref e) => {
                println!("Unable to determine time zone: {}", e);
                None
            }
        };

        let numeric = locale::Numeric::load_user_locale()
                          .unwrap_or_else(|_| locale::Numeric::english());

        let users = Mutex::new(UsersCache::new());

        Environment { tz, numeric, users }
    }
}

fn determine_time_zone() -> TZResult<TimeZone> {
    TimeZone::from_file("/etc/localtime")
}

pub struct Table<'a> {
    columns: Vec<Column>,
    colours: &'a Colours,
    env: &'a Environment,
    widths: TableWidths,
    time_format: &'a TimeFormat,
}

#[derive(Clone)]
pub struct Row {
    cells: Vec<TextCell>,
}

impl<'a, 'f> Table<'a> {
    pub fn new(options: &'a Options, dir: Option<&'a Dir>, colours: &'a Colours) -> Table<'a> {
        let colz = options.for_dir(dir);
        let widths = TableWidths::zero(colz.len());
        Table { columns: colz, colours, env: &options.env, widths, time_format: &options.time_format }
    }

    pub fn widths(&self) -> &TableWidths {
        &self.widths
    }

    pub fn header_row(&self) -> Row {
        let cells = self.columns.iter()
                        .map(|c| TextCell::paint_str(self.colours.header, c.header()))
                        .collect();

        Row { cells }
    }

    pub fn row_for_file(&self, file: &File, xattrs: bool) -> Row {
        let cells = self.columns.iter()
                        .map(|c| self.display(file, c, xattrs))
                        .collect();

        Row { cells }
    }

    pub fn add_widths(&mut self, row: &Row) {
        self.widths.add_widths(row)
    }

    fn permissions_plus(&self, file: &File, xattrs: bool) -> f::PermissionsPlus {
        f::PermissionsPlus {
            file_type: file.type_char(),
            permissions: file.permissions(),
            xattrs: xattrs,
        }
    }

    fn display(&self, file: &File, column: &Column, xattrs: bool) -> TextCell {
        use output::table::TimeType::*;

        match *column {
            Column::Permissions    => self.permissions_plus(file, xattrs).render(&self.colours),
            Column::FileSize(fmt)  => file.size().render(&self.colours, fmt, &self.env.numeric),
            Column::HardLinks      => file.links().render(&self.colours, &self.env.numeric),
            Column::Inode          => file.inode().render(&self.colours),
            Column::Blocks         => file.blocks().render(&self.colours),
            Column::User           => file.user().render(&self.colours, &*self.env.lock_users()),
            Column::Group          => file.group().render(&self.colours, &*self.env.lock_users()),
            Column::GitStatus      => file.git_status().render(&self.colours),

            Column::Timestamp(Modified)  => file.modified_time().render(&self.colours, &self.env.tz, &self.time_format),
            Column::Timestamp(Created)   => file.created_time().render( &self.colours, &self.env.tz, &self.time_format),
            Column::Timestamp(Accessed)  => file.accessed_time().render(&self.colours, &self.env.tz, &self.time_format),
        }
    }

    pub fn render(&self, row: Row) -> TextCell {
        let mut cell = TextCell::default();

        for (n, (this_cell, width)) in row.cells.into_iter().zip(self.widths.iter()).enumerate() {
            let padding = width - *this_cell.width;

            match self.columns[n].alignment() {
                Alignment::Left  => { cell.append(this_cell); cell.add_spaces(padding); }
                Alignment::Right => { cell.add_spaces(padding); cell.append(this_cell); }
            }

            cell.add_spaces(1);
        }

        cell
    }
}

pub struct TableWidths(Vec<usize>);

impl Deref for TableWidths {
    type Target = [usize];

    fn deref<'a>(&'a self) -> &'a Self::Target {
        &self.0
    }
}

impl TableWidths {
    pub fn zero(count: usize) -> TableWidths {
        TableWidths(vec![ 0; count ])
    }

    pub fn add_widths(&mut self, row: &Row) {
        for (old_width, cell) in self.0.iter_mut().zip(row.cells.iter()) {
            *old_width = max(*old_width, *cell.width);
        }
    }

    pub fn total(&self) -> usize {
        self.0.len() + self.0.iter().sum::<usize>()
    }
}
<commit_msg>Move size format out of Column<commit_after>use std::cmp::max;
use std::fmt;
use std::ops::Deref;
use std::sync::{Mutex, MutexGuard};

use datetime::TimeZone;
use zoneinfo_compiled::{CompiledData, Result as TZResult};

use locale;

use users::UsersCache;

use output::cell::TextCell;
use output::colours::Colours;
use output::time::TimeFormat;

use fs::{File, Dir, fields as f};

/// Options for displaying a table.
pub struct Options {
    pub env: Environment,
    pub size_format: SizeFormat,
    pub time_format: TimeFormat,
    pub time_types: TimeTypes,
    pub inode: bool,
    pub links: bool,
    pub blocks: bool,
    pub group: bool,
    pub git: bool
}

impl fmt::Debug for Options {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        // I had to make other types derive Debug,
        // and Mutex<UsersCache> is not that!
        writeln!(f, "<table options>")
    }
}

impl Options {
    pub fn should_scan_for_git(&self) -> bool {
        self.git
    }

    pub fn for_dir(&self, dir: Option<&Dir>) -> Vec<Column> {
        let mut columns = vec![];

        if self.inode {
            columns.push(Column::Inode);
        }

        columns.push(Column::Permissions);

        if self.links {
            columns.push(Column::HardLinks);
        }

        columns.push(Column::FileSize);

        if self.blocks {
            columns.push(Column::Blocks);
        }

        columns.push(Column::User);

        if self.group {
            columns.push(Column::Group);
        }

        if self.time_types.modified {
            columns.push(Column::Timestamp(TimeType::Modified));
        }

        if self.time_types.created {
            columns.push(Column::Timestamp(TimeType::Created));
        }

        if self.time_types.accessed {
            columns.push(Column::Timestamp(TimeType::Accessed));
        }

        if cfg!(feature="git") {
            if let Some(d) = dir {
                if self.should_scan_for_git() && d.has_git_repo() {
                    columns.push(Column::GitStatus);
                }
            }
        }

        columns
    }
}

/// A table contains these.
#[derive(Debug)]
pub enum Column {
    Permissions,
    FileSize,
    Timestamp(TimeType),
    Blocks,
    User,
    Group,
    HardLinks,
    Inode,
    GitStatus,
}

/// Each column can pick its own **Alignment**. Usually, numbers are
/// right-aligned, and text is left-aligned.
#[derive(Copy, Clone)]
pub enum Alignment {
    Left, Right,
}

impl Column {

    /// Get the alignment this column should use.
    pub fn alignment(&self) -> Alignment {
        match *self {
            Column::FileSize
            | Column::HardLinks
            | Column::Inode
            | Column::Blocks
            | Column::GitStatus => Alignment::Right,
            _                   => Alignment::Left,
        }
    }

    /// Get the text that should be printed at the top, when the user elects
    /// to have a header row printed.
    pub fn header(&self) -> &'static str {
        match *self {
            Column::Permissions   => "Permissions",
            Column::FileSize      => "Size",
            Column::Timestamp(t)  => t.header(),
            Column::Blocks        => "Blocks",
            Column::User          => "User",
            Column::Group         => "Group",
            Column::HardLinks     => "Links",
            Column::Inode         => "inode",
            Column::GitStatus     => "Git",
        }
    }
}

/// Formatting options for file sizes.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum SizeFormat {

    /// Format the file size using **decimal** prefixes, such as “kilo”,
    /// “mega”, or “giga”.
    DecimalBytes,

    /// Format the file size using **binary** prefixes, such as “kibi”,
    /// “mebi”, or “gibi”.
    BinaryBytes,

    /// Do no formatting and just display the size as a number of bytes.
    JustBytes,
}

impl Default for SizeFormat {
    fn default() -> SizeFormat {
        SizeFormat::DecimalBytes
    }
}

/// The types of a file’s time fields. These three fields are standard
/// across most (all?) operating systems.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum TimeType {

    /// The file’s accessed time (`st_atime`).
    Accessed,

    /// The file’s modified time (`st_mtime`).
    Modified,

    /// The file’s creation time (`st_ctime`).
    Created,
}

impl TimeType {

    /// Returns the text to use for a column’s heading in the columns output.
    pub fn header(&self) -> &'static str {
        match *self {
            TimeType::Accessed  => "Date Accessed",
            TimeType::Modified  => "Date Modified",
            TimeType::Created   => "Date Created",
        }
    }
}

/// Fields for which of a file’s time fields should be displayed in the
/// columns output.
///
/// There should always be at least one of these--there's no way to disable
/// the time columns entirely (yet).
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct TimeTypes {
    pub accessed: bool,
    pub modified: bool,
    pub created:  bool,
}

impl Default for TimeTypes {

    /// By default, display just the ‘modified’ time. This is the most
    /// common option, which is why it has this shorthand.
    fn default() -> TimeTypes {
        TimeTypes { accessed: false, modified: true, created: false }
    }
}

/// The **environment** struct contains any data that could change between
/// running instances of exa, depending on the user's computer's configuration.
///
/// Any environment field should be able to be mocked up for test runs.
pub struct Environment {

    /// Localisation rules for formatting numbers.
    numeric: locale::Numeric,

    /// The computer's current time zone. This gets used to determine how to
    /// offset files' timestamps.
    tz: Option<TimeZone>,

    /// Mapping cache of user IDs to usernames.
    users: Mutex<UsersCache>,
}

impl Environment {
    pub fn lock_users(&self) -> MutexGuard<UsersCache> {
        self.users.lock().unwrap()
    }

    pub fn load_all() -> Self {
        let tz = match determine_time_zone() {
            Ok(t) => Some(t),
            Err(ref e) => {
                println!("Unable to determine time zone: {}", e);
                None
            }
        };

        let numeric = locale::Numeric::load_user_locale()
                          .unwrap_or_else(|_| locale::Numeric::english());

        let users = Mutex::new(UsersCache::new());

        Environment { tz, numeric, users }
    }
}

fn determine_time_zone() -> TZResult<TimeZone> {
    TimeZone::from_file("/etc/localtime")
}

pub struct Table<'a> {
    columns: Vec<Column>,
    colours: &'a Colours,
    env: &'a Environment,
    widths: TableWidths,
    time_format: &'a TimeFormat,
    size_format: SizeFormat,
}

#[derive(Clone)]
pub struct Row {
    cells: Vec<TextCell>,
}

impl<'a, 'f> Table<'a> {
    pub fn new(options: &'a Options, dir: Option<&'a Dir>, colours: &'a Colours) -> Table<'a> {
        let colz = options.for_dir(dir);
        let widths = TableWidths::zero(colz.len());
        Table { colours, widths,
            columns: colz,
            env:         &options.env,
            time_format: &options.time_format,
            size_format:  options.size_format,
        }
    }

    pub fn widths(&self) -> &TableWidths {
        &self.widths
    }

    pub fn header_row(&self) -> Row {
        let cells = self.columns.iter()
                        .map(|c| TextCell::paint_str(self.colours.header, c.header()))
                        .collect();

        Row { cells }
    }

    pub fn row_for_file(&self, file: &File, xattrs: bool) -> Row {
        let cells = self.columns.iter()
                        .map(|c| self.display(file, c, xattrs))
                        .collect();

        Row { cells }
    }

    pub fn add_widths(&mut self, row: &Row) {
        self.widths.add_widths(row)
    }

    fn permissions_plus(&self, file: &File, xattrs: bool) -> f::PermissionsPlus {
        f::PermissionsPlus {
            file_type: file.type_char(),
            permissions: file.permissions(),
            xattrs: xattrs,
        }
    }

    fn display(&self, file: &File, column: &Column, xattrs: bool) -> TextCell {
        use output::table::TimeType::*;

        match *column {
            Column::Permissions    => self.permissions_plus(file, xattrs).render(&self.colours),
            Column::FileSize       => file.size().render(&self.colours, self.size_format, &self.env.numeric),
            Column::HardLinks      => file.links().render(&self.colours, &self.env.numeric),
            Column::Inode          => file.inode().render(&self.colours),
            Column::Blocks         => file.blocks().render(&self.colours),
            Column::User           => file.user().render(&self.colours, &*self.env.lock_users()),
            Column::Group          => file.group().render(&self.colours, &*self.env.lock_users()),
            Column::GitStatus      => file.git_status().render(&self.colours),

            Column::Timestamp(Modified)  => file.modified_time().render(&self.colours, &self.env.tz, &self.time_format),
            Column::Timestamp(Created)   => file.created_time().render( &self.colours, &self.env.tz, &self.time_format),
            Column::Timestamp(Accessed)  => file.accessed_time().render(&self.colours, &self.env.tz, &self.time_format),
        }
    }

    pub fn render(&self, row: Row) -> TextCell {
        let mut cell = TextCell::default();

        for (n, (this_cell, width)) in row.cells.into_iter().zip(self.widths.iter()).enumerate() {
            let padding = width - *this_cell.width;

            match self.columns[n].alignment() {
                Alignment::Left  => { cell.append(this_cell); cell.add_spaces(padding); }
                Alignment::Right => { cell.add_spaces(padding); cell.append(this_cell); }
            }

            cell.add_spaces(1);
        }

        cell
    }
}

pub struct TableWidths(Vec<usize>);

impl Deref for TableWidths {
    type Target = [usize];

    fn deref<'a>(&'a self) -> &'a Self::Target {
        &self.0
    }
}

impl TableWidths {
    pub fn zero(count: usize) -> TableWidths {
        TableWidths(vec![ 0; count ])
    }

    pub fn add_widths(&mut self, row: &Row) {
        for (old_width, cell) in self.0.iter_mut().zip(row.cells.iter()) {
            *old_width = max(*old_width, *cell.width);
        }
    }

    pub fn total(&self) -> usize {
        self.0.len() + self.0.iter().sum::<usize>()
    }
}
<|endoftext|>