<commit_before>use std::io::{TempDir, MemWriter};
use std::str::from_utf8;
use schema;
use buffer;

#[deriving(Show, Eq, Hash)]
struct Register {
	record: schema::Record,
	datatype: schema::SqlType,
}

impl Register {
	fn new(rec: schema::Record, typ: schema::SqlType) -> Register {
		Register {
			record: rec,
			datatype: typ,
		}
	}

	fn set_int(&mut self, val: int) {
		// TODO
	}

	fn get_int(&self) -> int {
		self.record.to_int()
	}

	fn set_str(&mut self) {
		// TODO
	}

	fn get_str<'a>(&'a self) -> &'a str {
		let d = self.record.get_data();
		from_utf8(d).unwrap()
	}
}

impl Ord for Register {
	fn lt(&self, other: &Register) -> bool {
		// not the most robust implementation
		self.get_int() < other.get_int()
	}
}

trait Operatorish<T>: Iterator<T> {
	//fn open();
	//fn close(&self);
}

struct TableScan<'a, 'b> {
	relation: schema::Relation,
	segment: &'a mut schema::SPSegment<'b>,
	current: uint,
}

impl<'a, 'b> TableScan<'a, 'b> {
	fn new(rel: schema::Relation, seg: &'a mut schema::SPSegment<'b>) -> TableScan<'a, 'b> {
		TableScan {
			relation: rel,
			segment: seg,
			current: 0,
		}
	}
}

impl<'a, 'b> Operatorish<Vec<Register>> for TableScan<'a, 'b> {
}

impl<'a, 'b> Iterator<Vec<Register>> for TableScan<'a, 'b> {
	fn next(&mut self) -> Option<Vec<Register>> {
		if (self.current as u64) < self.relation.inserted {
			let tup = self.relation.get(self.segment, self.current);
			let mut res = tup.move_iter().map(|(v, t)| Register::new(v, t)).
				collect::<Vec<Register>>();
			debug!("TS: {}", res);
			self.current += 1;
			Some(res)
		} else {
			None
		}
	}
}

struct Print<'a, T, V> {
	input: T,
	output: &'a mut V,
}

impl<'a, T: Operatorish<Vec<Register>>, V: Writer> Print<'a, T, V> {
	fn new(input: T, output: &'a mut V) -> Print<'a, T, V> {
		Print {
			input: input,
			output: output
		}
	}
}

impl<'a, T: Operatorish<Vec<Register>>, V: Writer> Iterator<Vec<Register>> for Print<'a, T, V> {
	fn next(&mut self) -> Option<Vec<Register>> {
		let cur = self.input.next();
		match cur {
			Some(reg) => {
				for item in reg.iter() {
					match item.datatype {
						schema::Varchar(_) => self.output.write(item.get_str().as_bytes()),
						schema::Integer => self.output.write(
							format!("{}", item.get_int()).as_bytes())
					};
					self.output.write(", ".as_bytes());
				}
				self.output.write("\n".as_bytes());
				Some(reg)
			},
			None => None,
		}
	}
}

struct Project<T> {
	input: T,
	registerids: Vec<uint>,
}

impl<T: Operatorish<Vec<Register>>> Project<T> {
	fn new(input: T, regs: Vec<uint>) -> Project<T> {
		Project {
			input: input,
			registerids: regs,
		}
	}
}

impl<T: Operatorish<Vec<Register>>> Iterator<Vec<Register>> for Project<T> {
	fn next(&mut self) -> Option<Vec<Register>> {
		let cur = self.input.next();
		match cur {
			None => None,
			Some(mut reg) => {
				let mut res = Vec::with_capacity(self.registerids.len());
				let mut regids = self.registerids.clone();
				regids.reverse();
				for index in regids.move_iter() {
					let value = reg.remove(index);
					let v = match value {
						None => fail!("Projection doesn't have vield {}", index),
						Some(v) => v,
					};
					res.push(v);
				}
				Some(res)
			}
		}
	}
}

impl<T: Operatorish<Vec<Register>>> Operatorish<Vec<Register>> for Project<T> {
}

#[deriving(Show)]
enum Selectable {
	Varchar(~str),
	Integer(int),
}

struct Select<T> {
	input: T,
	index: uint,
	value: Selectable,
}

impl<T: Operatorish<Vec<Register>>> Select<T> {
	fn new(input: T, index: uint, value: Selectable) -> Select<T> {
		Select {
			input: input,
			index: index,
			value: value,
		}
	}
}

impl<T: Operatorish<Vec<Register>>> Iterator<Vec<Register>> for Select<T> {
	fn next(&mut self) -> Option<Vec<Register>> {
		let mut cur = self.input.next();
		while cur != None {
			match cur {
				None => break,
				Some(reg) => {
					match self.value {
						Varchar(ref v) => {
							println!("Comparing {} with {}",
								reg.get(self.index).get_str(),
								v);
							if reg.get(self.index).get_str() == *v {
								return Some(reg)
							} else {
								cur = self.input.next();
								continue
							}
						},
						Integer(v) => {
							if reg.get(self.index).get_int() == v {
								return Some(reg)
							} else {
								cur = self.input.next();
								continue
							}
						}
					}
				},
			}
		}
		None
	}
}

impl<T: Operatorish<Vec<Register>>> Operatorish<Vec<Register>> for Select<T> {
}

#[test]
fn simple_tablescan() {
	let dir = match TempDir::new("tablescan") {
		Some(temp_dir) => temp_dir,
		None => fail!("creation of temporary directory"),
	};

	//let p = dir.path();
	let p = Path::new(".");

	let mut manager = buffer::BufferManager::new(1024, p.clone());
	let mut seg = schema::SPSegment {id: 1, manager: &mut manager};

	let name = schema::Column::new(~"name", schema::Varchar(128), vec!(schema::NotNull));
	let age = schema::Column::new(~"age", schema::Integer, vec!(schema::NotNull));
	let mut relation = schema::Relation::new(~"Person");
	relation.add_column(name);
	relation.add_column(age);
	relation.insert(&mut seg, vec!(schema::Record::from_str(~"Alice"), schema::Record::from_int(20)));
	relation.insert(&mut seg, vec!(schema::Record::from_str(~"Bob"), schema::Record::from_int(40)));

	{
		let mut ts = TableScan::new(relation.clone(), &mut seg);
		for tuple in ts {
			println!("Got entry {}", tuple);
		}
	}

	{
		let mut mw = MemWriter::new();
		{
			let mut ts = TableScan::new(relation.clone(), &mut seg);
			let mut pr = Print::new(ts, &mut mw);
			// force write by iterating, strange API
			for _ in pr {}
		}
		println!("Saved: {}", from_utf8(mw.unwrap()).unwrap());
	}

	{
		let mut mw = MemWriter::new();
		{
			let mut ts = TableScan::new(relation.clone(), &mut seg);
			let mut pr = Project::new(ts, vec!(0));
			let mut pr = Print::new(pr, &mut mw);
			for _ in pr {}
		}
		println!("Saved: {}", from_utf8(mw.unwrap()).unwrap());
	}

	{
		let mut mw = MemWriter::new();
		{
			let mut ts = TableScan::new(relation.clone(), &mut seg);
			let mut se = Select::new(ts, 1, Integer(20));
			let mut pr = Print::new(se, &mut mw);
			for _ in pr {}
		}
		println!("Saved: {}", from_utf8(mw.unwrap()).unwrap());
	}

	{
		let mut mw = MemWriter::new();
		{
			let mut ts = TableScan::new(relation.clone(), &mut seg);
			let mut se = Select::new(ts, 0, Varchar(~"Bob"));
			let mut pr = Print::new(se, &mut mw);
			for _ in pr {}
		}
		println!("Saved: {}", from_utf8(mw.unwrap()).unwrap());
	}

	assert!(false);
}
<commit_msg>Saved some indent levels by unwrapping directly<commit_after>use std::io::{TempDir, MemWriter};
use std::str::from_utf8;
use schema;
use buffer;

#[deriving(Show, Eq, Hash)]
struct Register {
	record: schema::Record,
	datatype: schema::SqlType,
}

impl Register {
	fn new(rec: schema::Record, typ: schema::SqlType) -> Register {
		Register {
			record: rec,
			datatype: typ,
		}
	}

	fn set_int(&mut self, val: int) {
		// TODO
	}

	fn get_int(&self) -> int {
		self.record.to_int()
	}

	fn set_str(&mut self) {
		// TODO
	}

	fn get_str<'a>(&'a self) -> &'a str {
		let d = self.record.get_data();
		from_utf8(d).unwrap()
	}
}

impl Ord for Register {
	fn lt(&self, other: &Register) -> bool {
		// not the most robust implementation
		self.get_int() < other.get_int()
	}
}

trait Operatorish<T>: Iterator<T> {
	//fn open();
	//fn close(&self);
}

struct TableScan<'a, 'b> {
	relation: schema::Relation,
	segment: &'a mut schema::SPSegment<'b>,
	current: uint,
}

impl<'a, 'b> TableScan<'a, 'b> {
	fn new(rel: schema::Relation, seg: &'a mut schema::SPSegment<'b>) -> TableScan<'a, 'b> {
		TableScan {
			relation: rel,
			segment: seg,
			current: 0,
		}
	}
}

impl<'a, 'b> Operatorish<Vec<Register>> for TableScan<'a, 'b> {
}

impl<'a, 'b> Iterator<Vec<Register>> for TableScan<'a, 'b> {
	fn next(&mut self) -> Option<Vec<Register>> {
		if (self.current as u64) < self.relation.inserted {
			let tup = self.relation.get(self.segment, self.current);
			let mut res = tup.move_iter().map(|(v, t)| Register::new(v, t)).
				collect::<Vec<Register>>();
			debug!("TS: {}", res);
			self.current += 1;
			Some(res)
		} else {
			None
		}
	}
}

struct Print<'a, T, V> {
	input: T,
	output: &'a mut V,
}

impl<'a, T: Operatorish<Vec<Register>>, V: Writer> Print<'a, T, V> {
	fn new(input: T, output: &'a mut V) -> Print<'a, T, V> {
		Print {
			input: input,
			output: output
		}
	}
}

impl<'a, T: Operatorish<Vec<Register>>, V: Writer> Iterator<Vec<Register>> for Print<'a, T, V> {
	fn next(&mut self) -> Option<Vec<Register>> {
		let cur = self.input.next();
		match cur {
			Some(reg) => {
				for item in reg.iter() {
					match item.datatype {
						schema::Varchar(_) => self.output.write(item.get_str().as_bytes()),
						schema::Integer => self.output.write(
							format!("{}", item.get_int()).as_bytes())
					};
					self.output.write(", ".as_bytes());
				}
				self.output.write("\n".as_bytes());
				Some(reg)
			},
			None => None,
		}
	}
}

struct Project<T> {
	input: T,
	registerids: Vec<uint>,
}

impl<T: Operatorish<Vec<Register>>> Project<T> {
	fn new(input: T, regs: Vec<uint>) -> Project<T> {
		Project {
			input: input,
			registerids: regs,
		}
	}
}

impl<T: Operatorish<Vec<Register>>> Iterator<Vec<Register>> for Project<T> {
	fn next(&mut self) -> Option<Vec<Register>> {
		let cur = self.input.next();
		match cur {
			None => None,
			Some(mut reg) => {
				let mut res = Vec::with_capacity(self.registerids.len());
				let mut regids = self.registerids.clone();
				regids.reverse();
				for index in regids.move_iter() {
					let value = reg.remove(index);
					let v = match value {
						None => fail!("Projection doesn't have vield {}", index),
						Some(v) => v,
					};
					res.push(v);
				}
				Some(res)
			}
		}
	}
}

impl<T: Operatorish<Vec<Register>>> Operatorish<Vec<Register>> for Project<T> {
}

#[deriving(Show)]
enum Selectable {
	Varchar(~str),
	Integer(int),
}

struct Select<T> {
	input: T,
	index: uint,
	value: Selectable,
}

impl<T: Operatorish<Vec<Register>>> Select<T> {
	fn new(input: T, index: uint, value: Selectable) -> Select<T> {
		Select {
			input: input,
			index: index,
			value: value,
		}
	}
}

impl<T: Operatorish<Vec<Register>>> Iterator<Vec<Register>> for Select<T> {
	fn next(&mut self) -> Option<Vec<Register>> {
		// initial value
		let mut cur = self.input.next();
		while cur.is_some() {
			// we made sure cur is Some(…), so we can unwrap safely
			let reg = cur.unwrap();

			// check what we got to select for and retrieve values
			// accordingly
			match self.value {
				// comparing to a string
				Varchar(ref v) => {
					println!("Comparing {} with {}",
						reg.get(self.index).get_str(),
						v);
					// getting the register value as string
					if reg.get(self.index).get_str() == *v {
						return Some(reg)
					} else {
						// not equal, skip to next entry
						cur = self.input.next();
						continue
					}
				},
				// comparing to a number
				Integer(v) => {
					// compare against reg as integer
					if reg.get(self.index).get_int() == v {
						return Some(reg)
					} else {
						// not equal, skip to next entry
						cur = self.input.next();
						continue
					}
				}
			}
		}
		// no more entries, signal iterator exhaustion
		None
	}
}

impl<T: Operatorish<Vec<Register>>> Operatorish<Vec<Register>> for Select<T> {
}

#[test]
fn simple_tablescan() {
	let dir = match TempDir::new("tablescan") {
		Some(temp_dir) => temp_dir,
		None => fail!("creation of temporary directory"),
	};

	//let p = dir.path();
	let p = Path::new(".");

	let mut manager = buffer::BufferManager::new(1024, p.clone());
	let mut seg = schema::SPSegment {id: 1, manager: &mut manager};

	let name = schema::Column::new(~"name", schema::Varchar(128), vec!(schema::NotNull));
	let age = schema::Column::new(~"age", schema::Integer, vec!(schema::NotNull));
	let mut relation = schema::Relation::new(~"Person");
	relation.add_column(name);
	relation.add_column(age);
	relation.insert(&mut seg, vec!(schema::Record::from_str(~"Alice"), schema::Record::from_int(20)));
	relation.insert(&mut seg, vec!(schema::Record::from_str(~"Bob"), schema::Record::from_int(40)));

	{
		let mut ts = TableScan::new(relation.clone(), &mut seg);
		for tuple in ts {
			println!("Got entry {}", tuple);
		}
	}

	{
		let mut mw = MemWriter::new();
		{
			let mut ts = TableScan::new(relation.clone(), &mut seg);
			let mut pr = Print::new(ts, &mut mw);
			// force write by iterating, strange API
			for _ in pr {}
		}
		println!("Saved: {}", from_utf8(mw.unwrap()).unwrap());
	}

	{
		let mut mw = MemWriter::new();
		{
			let mut ts = TableScan::new(relation.clone(), &mut seg);
			let mut pr = Project::new(ts, vec!(0));
			let mut pr = Print::new(pr, &mut mw);
			for _ in pr {}
		}
		println!("Saved: {}", from_utf8(mw.unwrap()).unwrap());
	}

	{
		let mut mw = MemWriter::new();
		{
			let mut ts = TableScan::new(relation.clone(), &mut seg);
			let mut se = Select::new(ts, 1, Integer(20));
			let mut pr = Print::new(se, &mut mw);
			for _ in pr {}
		}
		println!("Saved: {}", from_utf8(mw.unwrap()).unwrap());
	}

	{
		let mut mw = MemWriter::new();
		{
			let mut ts = TableScan::new(relation.clone(), &mut seg);
			let mut se = Select::new(ts, 0, Varchar(~"Bob"));
			let mut pr = Print::new(se, &mut mw);
			for _ in pr {}
		}
		println!("Saved: {}", from_utf8(mw.unwrap()).unwrap());
	}

	assert!(false);
}
<|endoftext|>


<commit_before>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! write docs here

use std::task::spawn;
use std::comm::channel;

/// write docs here
pub type WorkUnit<ARG, RV> = (ARG, Sender<RV>);

/// write docs here
pub enum MessageToWorker<ARG, RV> {
    /// A work unit.
    Work(WorkUnit<ARG, RV>),
    /// Shut down the worker task.
    Halt,
}

/// write docs here
pub enum MessageToDispatcher<ARG, RV> {
    /// A work unit that needs to be dispatched to a worker task.
    Dispatch(WorkUnit<ARG, RV>),
    /// Halt all worker tasks.
    HaltAll,
    /// Register a new worker task.
    RegisterWorker(Sender<Sender<Sender<MessageToWorker<ARG, RV>>>>),
}

/// A queue that distributes work items to worker tasks.
pub struct WorkQueue<ARG, RV> {
    /// write docs here
    dispatcher: Sender<MessageToDispatcher<ARG, RV>>,
}

/// A proxy to a `WorkQueue`. It can be freely cloned to use from multiple tasks.
pub struct WorkQueueProxy<ARG, RV> {
    /// write docs here
    dispatcher: Sender<MessageToDispatcher<ARG, RV>>,
}

/// A worker that executes tasks from its parent queue.
pub struct Worker<ARG, RV> {
    priv ask_for_work: Sender<Sender<MessageToWorker<ARG, RV>>>,
}

impl<ARG:Send, RV:Send> WorkQueue<ARG, RV> {
    /// Create a new work queue.
    pub fn create() -> WorkQueue<ARG, RV> {
        let (dispatcher, dispatcher_inbox) = channel::<MessageToDispatcher<ARG, RV>>();
        spawn(proc() {
            let (want_work, idle_worker) = channel::<Sender<MessageToWorker<ARG, RV>>>();
            let mut worker_count = 0;
            let inbox = dispatcher_inbox;
            let idle_worker = idle_worker;
            loop {
                match inbox.recv() {
                    Dispatch(work_item) => {
                        idle_worker.recv().send(Work(work_item));
                    },
                    RegisterWorker(want_idle_sender) => {
                        worker_count += 1;
                        want_idle_sender.send(want_work.clone());
                    }
                    HaltAll => {
                        while worker_count > 0 {
                            idle_worker.recv().send(Halt);
                            worker_count -= 1;
                        }
                        return;
                    },
                };
            }
        });
        return WorkQueue{dispatcher: dispatcher};
    }

    /// Create a copyable proxy that can be used to push work units.
    pub fn proxy(&self) -> WorkQueueProxy<ARG, RV> {
        return WorkQueueProxy{dispatcher: self.dispatcher.clone()};
    }

    /// Register a new worker. It will receive a sender where it can ask for tasks.
    pub fn register_worker(&self) -> Sender<Sender<MessageToWorker<ARG, RV>>> {
        let (reg_s, reg_r) = channel::<Sender<Sender<MessageToWorker<ARG, RV>>>>();
        self.dispatcher.send(RegisterWorker(reg_s));
        return reg_r.recv();
    }

    /// Create a new worker.
    pub fn worker(&self) -> Worker<ARG, RV> {
        return Worker{ask_for_work: self.register_worker()};
    }

    /// Push a work item to this queue.
    pub fn push(&self, arg: ARG) -> Receiver<RV> {
        let (rv, wait_for_rv) = channel::<RV>();
        self.dispatcher.send(Dispatch((arg, rv)));
        return wait_for_rv;
    }
}

// rustc complais "cannot implement a destructor on a structure with
// type parameters", but our destruction is safe, we only send
// a simple message on a channel.
#[unsafe_destructor]
impl<ARG:Send, RV:Send> Drop for WorkQueue<ARG, RV> {
    fn drop(&mut self) {
        self.dispatcher.send(HaltAll);
    }
}

impl<ARG:Send, RV:Send> WorkQueueProxy<ARG, RV> {
    /// Push a work item to this queue.
    pub fn push(&self, arg: ARG) -> Receiver<RV> {
        let (rv, wait_for_rv) = channel::<RV>();
        self.dispatcher.send(Dispatch((arg, rv)));
        return wait_for_rv;
    }
}

impl<ARG:Send, RV:Send> Clone for WorkQueueProxy<ARG, RV> {
    fn clone(&self) -> WorkQueueProxy<ARG, RV> {
        return WorkQueueProxy{dispatcher: self.dispatcher.clone()};
    }
}

impl<ARG:Send, RV:Send> Worker<ARG, RV> {
    pub fn run(&self, fun: |arg: ARG| -> RV) {
        loop {
            let (idle, work_unit) = channel::<MessageToWorker<ARG, RV>>();
            self.ask_for_work.send(idle);
            match work_unit.recv() {
                Work((arg, rv)) => rv.send((fun)(arg)),
                Halt            => return
            };
        }
    }
}

#[cfg(test)]
mod test {
    use std::task::spawn;
    use super::WorkQueue;

    #[test]
    fn test_queue() {
        let queue = WorkQueue::<int, int>::create();
        for _ in range(0, 3) {
            let worker = queue.worker();
            spawn(proc() { worker.run(|arg| arg * 2); });
        }

        let return_list: ~[int] =
            range(0, 10)
            .map(|c| queue.push(c))
            .map(|rv| rv.recv())
            .collect();

        assert_eq!(return_list, ~[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]);
    }

    #[test]
    fn test_enqueue_from_tasks() {
        let queue = WorkQueue::<int, int>::create();
        for _ in range(0, 3) {
            let worker = queue.worker();
            spawn(proc() { worker.run(|arg| arg * 2); });
        }
        let mut promise_list: ~[Receiver<int>] = ~[];
        let queue_proxy = queue.proxy();
        for c in range(0, 10) {
            let queue_proxy_clone = queue_proxy.clone();
            let (done, promise) = channel::<int>();
            promise_list.push(promise);
            spawn(proc() {
                let done = done;
                let queue = queue_proxy_clone;
                let rv = queue.push(c);
                done.send(rv.recv());
            });
        }

        let return_list: ~[int] =
            promise_list
            .iter()
            .map(|promise| promise.recv())
            .collect();
        assert_eq!(return_list, ~[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]);
    }
}

#[cfg(test)]
mod bench {
    extern crate test;

    use self::test::BenchHarness;
    use super::WorkQueue;

    #[bench]
    fn bench_50_tasks_4_threads(b: &mut BenchHarness) {
        let queue = WorkQueue::<int, int>::create();
        for _ in range(0, 4) {
            let worker = queue.worker();
            spawn(proc() { worker.run(|arg| arg * 2); });
        }
        b.iter(|| {
            let _: ~[int] =
                range(0, 50)
                .map(|_| queue.push(1))
                .map(|rv| rv.recv())
                .collect();
        });
    }

    #[bench]
    fn bench_spawn_5_workers(b: &mut BenchHarness) {
        b.iter(|| {
            let queue = WorkQueue::<int, int>::create();
            for _ in range(0, 5) {
                let worker = queue.worker();
                spawn(proc() { worker.run(|arg| arg * 2); });
            }
        });
    }
}
<commit_msg>change queue constructor<commit_after>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! write docs here

use std::task::spawn;
use std::comm::channel;

/// write docs here
pub type WorkUnit<ARG, RV> = (ARG, Sender<RV>);

/// write docs here
pub enum MessageToWorker<ARG, RV> {
    /// A work unit.
    Work(WorkUnit<ARG, RV>),
    /// Shut down the worker task.
    Halt,
}

/// write docs here
pub enum MessageToDispatcher<ARG, RV> {
    /// A work unit that needs to be dispatched to a worker task.
    Dispatch(WorkUnit<ARG, RV>),
    /// Halt all worker tasks.
    HaltAll,
    /// Register a new worker task.
    RegisterWorker(Sender<Sender<Sender<MessageToWorker<ARG, RV>>>>),
}

/// A queue that distributes work items to worker tasks.
pub struct WorkQueue<ARG, RV> {
    /// write docs here
    dispatcher: Sender<MessageToDispatcher<ARG, RV>>,
}

/// A proxy to a `WorkQueue`. It can be freely cloned to use from multiple tasks.
pub struct WorkQueueProxy<ARG, RV> {
    /// write docs here
    dispatcher: Sender<MessageToDispatcher<ARG, RV>>,
}

/// A worker that executes tasks from its parent queue.
pub struct Worker<ARG, RV> {
    priv ask_for_work: Sender<Sender<MessageToWorker<ARG, RV>>>,
}

/// Create a new work queue.
pub fn WorkQueue<ARG:Send, RV:Send>() -> WorkQueue<ARG, RV> {
    let (dispatcher, dispatcher_inbox) = channel::<MessageToDispatcher<ARG, RV>>();
    spawn(proc() {
        let (want_work, idle_worker) = channel::<Sender<MessageToWorker<ARG, RV>>>();
        let mut worker_count = 0;
        let inbox = dispatcher_inbox;
        let idle_worker = idle_worker;
        loop {
            match inbox.recv() {
                Dispatch(work_item) => {
                    idle_worker.recv().send(Work(work_item));
                },
                RegisterWorker(want_idle_sender) => {
                    worker_count += 1;
                    want_idle_sender.send(want_work.clone());
                }
                HaltAll => {
                    while worker_count > 0 {
                        idle_worker.recv().send(Halt);
                        worker_count -= 1;
                    }
                    return;
                },
            };
        }
    });
    return WorkQueue{dispatcher: dispatcher};
}

impl<ARG:Send, RV:Send> WorkQueue<ARG, RV> {
    /// Create a copyable proxy that can be used to push work units.
    pub fn proxy(&self) -> WorkQueueProxy<ARG, RV> {
        return WorkQueueProxy{dispatcher: self.dispatcher.clone()};
    }

    /// Register a new worker. It will receive a sender where it can ask for tasks.
    pub fn register_worker(&self) -> Sender<Sender<MessageToWorker<ARG, RV>>> {
        let (reg_s, reg_r) = channel::<Sender<Sender<MessageToWorker<ARG, RV>>>>();
        self.dispatcher.send(RegisterWorker(reg_s));
        return reg_r.recv();
    }

    /// Create a new worker.
    pub fn worker(&self) -> Worker<ARG, RV> {
        return Worker{ask_for_work: self.register_worker()};
    }

    /// Push a work item to this queue.
    pub fn push(&self, arg: ARG) -> Receiver<RV> {
        let (rv, wait_for_rv) = channel::<RV>();
        self.dispatcher.send(Dispatch((arg, rv)));
        return wait_for_rv;
    }
}

// rustc complais "cannot implement a destructor on a structure with
// type parameters", but our destruction is safe, we only send
// a simple message on a channel.
#[unsafe_destructor]
impl<ARG:Send, RV:Send> Drop for WorkQueue<ARG, RV> {
    fn drop(&mut self) {
        self.dispatcher.send(HaltAll);
    }
}

impl<ARG:Send, RV:Send> WorkQueueProxy<ARG, RV> {
    /// Push a work item to this queue.
    pub fn push(&self, arg: ARG) -> Receiver<RV> {
        let (rv, wait_for_rv) = channel::<RV>();
        self.dispatcher.send(Dispatch((arg, rv)));
        return wait_for_rv;
    }
}

impl<ARG:Send, RV:Send> Clone for WorkQueueProxy<ARG, RV> {
    fn clone(&self) -> WorkQueueProxy<ARG, RV> {
        return WorkQueueProxy{dispatcher: self.dispatcher.clone()};
    }
}

impl<ARG:Send, RV:Send> Worker<ARG, RV> {
    pub fn run(&self, fun: |arg: ARG| -> RV) {
        loop {
            let (idle, work_unit) = channel::<MessageToWorker<ARG, RV>>();
            self.ask_for_work.send(idle);
            match work_unit.recv() {
                Work((arg, rv)) => rv.send((fun)(arg)),
                Halt            => return
            };
        }
    }
}

#[cfg(test)]
mod test {
    use std::task::spawn;
    use super::WorkQueue;

    #[test]
    fn test_queue() {
        let queue = WorkQueue::<int, int>();
        for _ in range(0, 3) {
            let worker = queue.worker();
            spawn(proc() { worker.run(|arg| arg * 2); });
        }

        let return_list: ~[int] =
            range(0, 10)
            .map(|c| queue.push(c))
            .map(|rv| rv.recv())
            .collect();

        assert_eq!(return_list, ~[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]);
    }

    #[test]
    fn test_enqueue_from_tasks() {
        let queue = WorkQueue::<int, int>();
        for _ in range(0, 3) {
            let worker = queue.worker();
            spawn(proc() { worker.run(|arg| arg * 2); });
        }
        let mut promise_list: ~[Receiver<int>] = ~[];
        let queue_proxy = queue.proxy();
        for c in range(0, 10) {
            let queue_proxy_clone = queue_proxy.clone();
            let (done, promise) = channel::<int>();
            promise_list.push(promise);
            spawn(proc() {
                let done = done;
                let queue = queue_proxy_clone;
                let rv = queue.push(c);
                done.send(rv.recv());
            });
        }

        let return_list: ~[int] =
            promise_list
            .iter()
            .map(|promise| promise.recv())
            .collect();
        assert_eq!(return_list, ~[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]);
    }
}

#[cfg(test)]
mod bench {
    extern crate test;

    use self::test::BenchHarness;
    use super::WorkQueue;

    #[bench]
    fn bench_50_tasks_4_threads(b: &mut BenchHarness) {
        let queue = WorkQueue::<int, int>();
        for _ in range(0, 4) {
            let worker = queue.worker();
            spawn(proc() { worker.run(|arg| arg * 2); });
        }
        b.iter(|| {
            let _: ~[int] =
                range(0, 50)
                .map(|_| queue.push(1))
                .map(|rv| rv.recv())
                .collect();
        });
    }

    #[bench]
    fn bench_spawn_5_workers(b: &mut BenchHarness) {
        b.iter(|| {
            let queue = WorkQueue::<int, int>();
            for _ in range(0, 5) {
                let worker = queue.worker();
                spawn(proc() { worker.run(|arg| arg * 2); });
            }
        });
    }
}
<|endoftext|>


<commit_before>use std::collections::VecDeque;
use std::io::Write;
use std::mem::zeroed;
use std::os::raw::{c_int, c_uchar, c_uint};
use std::rc::Rc;

use atoms;
use client::{ClientW, Rect};
use config::{Config, TAG_OVERVIEW};
use layout::Layout;
use util;

use x11::xlib;

pub enum FocusShift {
    Forward,
    Backward,
}

pub struct Workspace {
    anchor_window: xlib::Window,
    pub config: Rc<Config>,
    client_current: Option<ClientW>,
    clients_prev: VecDeque<ClientW>,
    clients_next: VecDeque<ClientW>,
    description: Option<String>,
    layout: Box<Layout + 'static>,
    pub rect: Rect,
    pub tag: c_uchar,
}

impl Workspace {
    pub fn new(config: Rc<Config>,
               anchor_window: xlib::Window,
               tag: c_uchar,
               description: Option<String>,
               layout: Box<Layout + 'static>,
               rect: Rect)
               -> Self {
        Workspace {
            anchor_window: anchor_window,
            client_current: None,
            clients_prev: VecDeque::new(),
            clients_next: VecDeque::new(),
            description: description,
            config: config,
            layout: layout,
            rect: rect,
            tag: tag,
        }
    }

    pub fn circle_focus(&mut self, direction: FocusShift) {
        match direction {
            FocusShift::Forward => {
                if self.clients_next.len() > 0 {
                    self.shift_focus(direction);
                } else {
                    while self.clients_prev.len() > 0 {
                        self.shift_focus(FocusShift::Backward);
                    }
                }
            }
            FocusShift::Backward => {
                if self.clients_prev.len() > 0 {
                    self.shift_focus(direction);
                } else {
                    while self.clients_next.len() > 0 {
                        self.shift_focus(FocusShift::Forward);
                    }
                }
            }
        }
        self.restack();
    }

    pub fn clear(&mut self) {
        self.clients_prev.clear();
        self.clients_next.clear();
        self.client_current = None;
    }

    pub fn detach_current(&mut self) -> Option<ClientW> {
        if self.client_current.is_some() {
            if self.clients_next.len() > 0 {
                self.shift_focus(FocusShift::Forward);
                return self.clients_prev.pop_back();
            } else if self.clients_prev.len() > 0 {
                self.shift_focus(FocusShift::Backward);
                return self.clients_next.pop_front();
            }
        }
        self.client_current.take()
    }

    pub fn get_client_by_window(&self, window: xlib::Window) -> Option<ClientW> {
        if let Some(c) = self.client_current.as_ref() {
            if c.window() == window {
                return Some(c.clone());
            }
        }
        self.clients_prev
            .iter()
            .find(|c| c.window() == window)
            .map(|c| c.clone())
            .or_else(|| {
                self.clients_next
                    .iter()
                    .find(|c| c.window() == window)
                    .map(|c| c.clone())
            })
    }

    pub fn get_current_focused(&self) -> Option<ClientW> {
        self.client_current.as_ref().map(|c| c.clone())
    }

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

    pub fn get_layout(&self, rect: Rect) -> Vec<(ClientW, Rect)> {
        self.layout.layout(self, rect)
    }

    pub fn kill_client(&mut self) {
        self.client_current.as_mut().map(|client| {
            let atom = atoms::wm_delete();
            if !client.send_event(atom) {
                x_disable_error_unsafe!(client.display(), {
                    xlib::XSetCloseDownMode(client.display(), xlib::DestroyAll);
                    xlib::XKillClient(client.display(), client.window());
                });
            }
        });
    }

    pub fn new_client(&mut self, client: ClientW, at_focus: bool) {
        if !at_focus {
            self.clients_prev.push_front(client);
            while self.clients_prev.len() > 0 {
                self.shift_focus(FocusShift::Backward);
            }
        } else {
            self.clients_prev.push_back(client);
            self.shift_focus(FocusShift::Backward);
        }
    }

    fn push_next(&mut self, client: Option<ClientW>) {
        if let Some(c) = client {
            self.clients_next.push_front(c);
        }
    }

    fn push_prev(&mut self, client: Option<ClientW>) {
        if let Some(c) = client {
            self.clients_prev.push_back(c);
        }
    }

    pub fn remove_client(&mut self, client: ClientW) {
        if let Some(position) = self.clients_prev
            .iter()
            .position(|c| c.window() == client.window()) {
            self.clients_prev.remove(position);
            return;
        }
        if let Some(position) = self.clients_next
            .iter()
            .position(|c| c.window() == client.window()) {
            self.clients_next.remove(position);
            return;
        }
        let cmp = self.client_current.clone().map(|c| c.window() == client.window());
        if cmp.is_some() && cmp.unwrap() {
            self.detach_current();
        }
    }

    pub fn restack(&mut self) {
        if let Some(focus) = self.get_current_focused() {
            focus.focus(true);
            if focus.is_floating() {
                focus.raise_window();
                return;
            }

            unsafe {
                let mut wc: xlib::XWindowChanges = zeroed();
                wc.stack_mode = xlib::Below;
                wc.sibling = self.anchor_window;
                xlib::XConfigureWindow(focus.display(),
                                       focus.window(),
                                       xlib::CWSibling as c_uint | xlib::CWStackMode as c_uint,
                                       &mut wc);
                let mut xevent: xlib::XEvent = zeroed();
                xlib::XSync(focus.display(), 0);
                while xlib::XCheckMaskEvent(focus.display(),
                                            xlib::EnterWindowMask,
                                            &mut xevent) != 0 {}
            }
        }
    }

    pub fn set_description<T: Into<String>>(&mut self, description: T) {
        self.description = Some(description.into());
    }

    pub fn set_focus(&mut self, client: ClientW) {
        if let &mut Some(ref mut c) = &mut self.client_current {
            if c.window() == client.window() {
                // c.focus(true);
                c.grab_buttons(true);
                return;
            }
        }
        if let Some(position) = self.clients_prev
            .iter()
            .position(|c| c.window() == client.window()) {
            for _ in 0..self.clients_prev.len() - position {
                self.shift_focus(FocusShift::Backward);
            }
            return;
        }
        if let Some(position) = self.clients_next
            .iter()
            .position(|c| c.window() == client.window()) {
            for _ in 0..position + 1 {
                self.shift_focus(FocusShift::Forward);
            }
            return;
        }
    }

    pub fn select_clients(&self, pred: &Fn(&ClientW) -> bool) -> Vec<ClientW> {
        self.iter().cloned().filter(pred).collect()
    }

    pub fn show(&mut self, visible: bool) {
        for c in self.iter_mut() {
            c.show(visible);
        }
    }

    pub fn shift_focus(&mut self, shift: FocusShift) {
        match shift {
            FocusShift::Forward => {
                if let Some(mut next_client) = self.clients_next.pop_front() {
                    let current = self.client_current.take();
                    next_client.grab_buttons(true);
                    self.client_current = Some(next_client);
                    self.push_prev(current.map(|c| {
                        c.focus(false);
                        c.clone().grab_buttons(false);
                        c
                    }));
                }
            }
            FocusShift::Backward => {
                if let Some(mut prev_client) = self.clients_prev.pop_back() {
                    let current = self.client_current.take();
                    prev_client.grab_buttons(true);
                    self.client_current = Some(prev_client);
                    self.push_next(current.map(|c| {
                        c.focus(false);
                        c.clone().grab_buttons(false);
                        c
                    }));
                }
            }
        }
    }

    pub fn zoom(&mut self) {
        self.detach_current().map(|c| self.new_client(c, false));
    }

    pub fn arrange(&mut self) {
        let bar_height = if self.rect.x == 0 {
            self.config.bar_height
        } else {
            self.rect.y
        };

        // TODO: 1) Handle sticky windows as well
        //       2) Handle other multiple screen layout
        let strategy = self.get_layout(Rect::new(self.rect.x,
                                                 bar_height,
                                                 self.rect.width - 2 * self.config.border_width,
                                                 self.rect.height - bar_height -
                                                 2 * self.config.border_width));
        for (mut c, r) in strategy {
            if self.tag == TAG_OVERVIEW {
                c.resize(r, true);
                continue;
            }
            let target_rect = if c.is_maximized() {
                Rect::new(self.rect.x,
                          bar_height,
                          self.rect.width - 2 * self.config.border_width,
                          self.rect.height - bar_height - 2 * self.config.border_width)
            } else {
                r
            };
            c.resize(target_rect, false);
        }

        if self.tag != TAG_OVERVIEW {
            let mut floating_clients = self.select_clients(&|c| c.is_floating() == true);
            for fc in floating_clients.iter_mut() {
                let rect = fc.get_rect();
                fc.resize(rect, false);
                fc.raise_window();
            }
        }

        for c in self.iter_mut() {
            if c.is_fullscreen() {
                c.raise_window();
            }
        }
    }

    pub fn iter(&self) -> WSIter {
        WSIter {
            current_client: self.client_current.as_ref(),
            prev_iter: Box::new(self.clients_prev.iter()),
            next_iter: Box::new(self.clients_next.iter()),
            current_returned: false,
        }
    }

    pub fn iter_mut(&mut self) -> WSIterMut {
        WSIterMut {
            current_client: self.client_current.as_mut(),
            prev_iter: Box::new(self.clients_prev.iter_mut()),
            next_iter: Box::new(self.clients_next.iter_mut()),
            current_returned: false,
        }
    }
}

pub struct WSIter<'a> {
    current_client: Option<&'a ClientW>,
    prev_iter: Box<Iterator<Item = &'a ClientW> + 'a>,
    next_iter: Box<Iterator<Item = &'a ClientW> + 'a>,
    current_returned: bool,
}

impl<'a> Iterator for WSIter<'a> {
    type Item = &'a ClientW;
    fn next(&mut self) -> Option<Self::Item> {
        if let Some(next_client) = self.prev_iter.next() {
            Some(next_client)
        } else if !self.current_returned && self.current_client.is_some() {
            self.current_returned = true;
            self.current_client.take()
        } else {
            self.next_iter.next()
        }
    }
}

pub struct WSIterMut<'a> {
    current_client: Option<&'a mut ClientW>,
    prev_iter: Box<Iterator<Item = &'a mut ClientW> + 'a>,
    next_iter: Box<Iterator<Item = &'a mut ClientW> + 'a>,
    current_returned: bool,
}

impl<'a> Iterator for WSIterMut<'a> {
    type Item = &'a mut ClientW;
    fn next(&mut self) -> Option<Self::Item> {
        if let Some(next_client) = self.prev_iter.next() {
            Some(next_client)
        } else if !self.current_returned && self.current_client.is_some() {
            self.current_returned = true;
            self.current_client.take()
        } else {
            self.next_iter.next()
        }
    }
}
<commit_msg>changed the iterator order to prevent a bug with on_enter_notify.<commit_after>use std::collections::VecDeque;
use std::io::Write;
use std::mem::zeroed;
use std::os::raw::{c_int, c_uchar, c_uint};
use std::rc::Rc;

use atoms;
use client::{ClientW, Rect};
use config::{Config, TAG_OVERVIEW};
use layout::Layout;
use util;

use x11::xlib;

pub enum FocusShift {
    Forward,
    Backward,
}

pub struct Workspace {
    anchor_window: xlib::Window,
    pub config: Rc<Config>,
    client_current: Option<ClientW>,
    clients_prev: VecDeque<ClientW>,
    clients_next: VecDeque<ClientW>,
    description: Option<String>,
    layout: Box<Layout + 'static>,
    pub rect: Rect,
    pub tag: c_uchar,
}

impl Workspace {
    pub fn new(config: Rc<Config>,
               anchor_window: xlib::Window,
               tag: c_uchar,
               description: Option<String>,
               layout: Box<Layout + 'static>,
               rect: Rect)
               -> Self {
        Workspace {
            anchor_window: anchor_window,
            client_current: None,
            clients_prev: VecDeque::new(),
            clients_next: VecDeque::new(),
            description: description,
            config: config,
            layout: layout,
            rect: rect,
            tag: tag,
        }
    }

    pub fn circle_focus(&mut self, direction: FocusShift) {
        match direction {
            FocusShift::Forward => {
                if self.clients_next.len() > 0 {
                    self.shift_focus(direction);
                } else {
                    while self.clients_prev.len() > 0 {
                        self.shift_focus(FocusShift::Backward);
                    }
                }
            }
            FocusShift::Backward => {
                if self.clients_prev.len() > 0 {
                    self.shift_focus(direction);
                } else {
                    while self.clients_next.len() > 0 {
                        self.shift_focus(FocusShift::Forward);
                    }
                }
            }
        }
        self.restack();
    }

    pub fn clear(&mut self) {
        self.clients_prev.clear();
        self.clients_next.clear();
        self.client_current = None;
    }

    pub fn detach_current(&mut self) -> Option<ClientW> {
        if self.client_current.is_some() {
            if self.clients_next.len() > 0 {
                self.shift_focus(FocusShift::Forward);
                return self.clients_prev.pop_back();
            } else if self.clients_prev.len() > 0 {
                self.shift_focus(FocusShift::Backward);
                return self.clients_next.pop_front();
            }
        }
        self.client_current.take()
    }

    pub fn get_client_by_window(&self, window: xlib::Window) -> Option<ClientW> {
        if let Some(c) = self.client_current.as_ref() {
            if c.window() == window {
                return Some(c.clone());
            }
        }
        self.clients_prev
            .iter()
            .find(|c| c.window() == window)
            .map(|c| c.clone())
            .or_else(|| {
                self.clients_next
                    .iter()
                    .find(|c| c.window() == window)
                    .map(|c| c.clone())
            })
    }

    pub fn get_current_focused(&self) -> Option<ClientW> {
        self.client_current.as_ref().map(|c| c.clone())
    }

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

    pub fn get_layout(&self, rect: Rect) -> Vec<(ClientW, Rect)> {
        self.layout.layout(self, rect)
    }

    pub fn kill_client(&mut self) {
        self.client_current.as_mut().map(|client| {
            let atom = atoms::wm_delete();
            if !client.send_event(atom) {
                x_disable_error_unsafe!(client.display(), {
                    xlib::XSetCloseDownMode(client.display(), xlib::DestroyAll);
                    xlib::XKillClient(client.display(), client.window());
                });
            }
        });
    }

    pub fn new_client(&mut self, client: ClientW, at_focus: bool) {
        if !at_focus {
            self.clients_prev.push_front(client);
            while self.clients_prev.len() > 0 {
                self.shift_focus(FocusShift::Backward);
            }
        } else {
            self.clients_prev.push_back(client);
            self.shift_focus(FocusShift::Backward);
        }
    }

    fn push_next(&mut self, client: Option<ClientW>) {
        if let Some(c) = client {
            self.clients_next.push_front(c);
        }
    }

    fn push_prev(&mut self, client: Option<ClientW>) {
        if let Some(c) = client {
            self.clients_prev.push_back(c);
        }
    }

    pub fn remove_client(&mut self, client: ClientW) {
        if let Some(position) = self.clients_prev
            .iter()
            .position(|c| c.window() == client.window()) {
            self.clients_prev.remove(position);
            return;
        }
        if let Some(position) = self.clients_next
            .iter()
            .position(|c| c.window() == client.window()) {
            self.clients_next.remove(position);
            return;
        }
        let cmp = self.client_current.clone().map(|c| c.window() == client.window());
        if cmp.is_some() && cmp.unwrap() {
            self.detach_current();
        }
    }

    pub fn restack(&mut self) {
        if let Some(focus) = self.get_current_focused() {
            focus.focus(true);
            if focus.is_floating() {
                focus.raise_window();
                return;
            }

            unsafe {
                let mut wc: xlib::XWindowChanges = zeroed();
                wc.stack_mode = xlib::Below;
                wc.sibling = self.anchor_window;
                xlib::XConfigureWindow(focus.display(),
                                       focus.window(),
                                       xlib::CWSibling as c_uint | xlib::CWStackMode as c_uint,
                                       &mut wc);
                let mut xevent: xlib::XEvent = zeroed();
                xlib::XSync(focus.display(), 0);
                while xlib::XCheckMaskEvent(focus.display(),
                                            xlib::EnterWindowMask,
                                            &mut xevent) != 0 {}
            }
        }
    }

    pub fn set_description<T: Into<String>>(&mut self, description: T) {
        self.description = Some(description.into());
    }

    pub fn set_focus(&mut self, client: ClientW) {
        if let &mut Some(ref mut c) = &mut self.client_current {
            if c.window() == client.window() {
                // c.focus(true);
                c.grab_buttons(true);
                return;
            }
        }
        if let Some(position) = self.clients_prev
            .iter()
            .position(|c| c.window() == client.window()) {
            for _ in 0..self.clients_prev.len() - position {
                self.shift_focus(FocusShift::Backward);
            }
            return;
        }
        if let Some(position) = self.clients_next
            .iter()
            .position(|c| c.window() == client.window()) {
            for _ in 0..position + 1 {
                self.shift_focus(FocusShift::Forward);
            }
            return;
        }
    }

    pub fn select_clients(&self, pred: &Fn(&ClientW) -> bool) -> Vec<ClientW> {
        self.iter().cloned().filter(pred).collect()
    }

    pub fn show(&mut self, visible: bool) {
        for c in self.iter_mut() {
            c.show(visible);
        }
    }

    pub fn shift_focus(&mut self, shift: FocusShift) {
        match shift {
            FocusShift::Forward => {
                if let Some(mut next_client) = self.clients_next.pop_front() {
                    let current = self.client_current.take();
                    next_client.grab_buttons(true);
                    self.client_current = Some(next_client);
                    self.push_prev(current.map(|c| {
                        c.focus(false);
                        c.clone().grab_buttons(false);
                        c
                    }));
                }
            }
            FocusShift::Backward => {
                if let Some(mut prev_client) = self.clients_prev.pop_back() {
                    let current = self.client_current.take();
                    prev_client.grab_buttons(true);
                    self.client_current = Some(prev_client);
                    self.push_next(current.map(|c| {
                        c.focus(false);
                        c.clone().grab_buttons(false);
                        c
                    }));
                }
            }
        }
    }

    pub fn zoom(&mut self) {
        self.detach_current().map(|c| self.new_client(c, false));
    }

    pub fn arrange(&mut self) {
        let bar_height = if self.rect.x == 0 {
            self.config.bar_height
        } else {
            self.rect.y
        };

        // TODO: 1) Handle sticky windows as well
        //       2) Handle other multiple screen layout
        let strategy = self.get_layout(Rect::new(self.rect.x,
                                                 bar_height,
                                                 self.rect.width - 2 * self.config.border_width,
                                                 self.rect.height - bar_height -
                                                 2 * self.config.border_width));
        for (mut c, r) in strategy {
            if self.tag == TAG_OVERVIEW {
                c.resize(r, true);
                continue;
            }
            let target_rect = if c.is_maximized() {
                Rect::new(self.rect.x,
                          bar_height,
                          self.rect.width - 2 * self.config.border_width,
                          self.rect.height - bar_height - 2 * self.config.border_width)
            } else {
                r
            };
            c.resize(target_rect, false);
        }

        if self.tag != TAG_OVERVIEW {
            let mut floating_clients = self.select_clients(&|c| c.is_floating() == true);
            for fc in floating_clients.iter_mut() {
                let rect = fc.get_rect();
                fc.resize(rect, false);
                fc.raise_window();
            }
        }

        for c in self.iter_mut() {
            if c.is_fullscreen() {
                c.raise_window();
            }
        }
    }

    pub fn iter(&self) -> WSIter {
        WSIter {
            current_client: self.client_current.as_ref(),
            prev_iter: Box::new(self.clients_prev.iter()),
            next_iter: Box::new(self.clients_next.iter()),
            current_returned: false,
        }
    }

    pub fn iter_mut(&mut self) -> WSIterMut {
        WSIterMut {
            current_client: self.client_current.as_mut(),
            prev_iter: Box::new(self.clients_prev.iter_mut()),
            next_iter: Box::new(self.clients_next.iter_mut()),
            current_returned: false,
        }
    }
}

pub struct WSIter<'a> {
    current_client: Option<&'a ClientW>,
    prev_iter: Box<Iterator<Item = &'a ClientW> + 'a>,
    next_iter: Box<Iterator<Item = &'a ClientW> + 'a>,
    current_returned: bool,
}

impl<'a> Iterator for WSIter<'a> {
    type Item = &'a ClientW;
    fn next(&mut self) -> Option<Self::Item> {
        if let Some(next_client) = self.prev_iter.next() {
            Some(next_client)
        } else if let Some(next_client) = self.next_iter.next() {
            Some(next_client)
        } else {
            self.current_client.take()
        }
    }
}

pub struct WSIterMut<'a> {
    current_client: Option<&'a mut ClientW>,
    prev_iter: Box<Iterator<Item = &'a mut ClientW> + 'a>,
    next_iter: Box<Iterator<Item = &'a mut ClientW> + 'a>,
    current_returned: bool,
}

impl<'a> Iterator for WSIterMut<'a> {
    type Item = &'a mut ClientW;
    fn next(&mut self) -> Option<Self::Item> {
        if let Some(next_client) = self.prev_iter.next() {
            Some(next_client)
        } else if let Some(next_client) = self.next_iter.next() {
            Some(next_client)
        } else {
            self.current_returned = true;
            self.current_client.take()
        }
    }
}
<|endoftext|>


<commit_before>use libc;
use std;

use baseline::map::CodeDescriptor;
use ctxt::{get_vm, VM};
use exception::stacktrace_from_es;
use os;
use os_cpu::*;
use safepoint;

#[cfg(target_family = "windows")]
use winapi::winnt::EXCEPTION_POINTERS;

#[cfg(target_family = "unix")]
pub fn register_signals() {
    unsafe {
        let mut sa: libc::sigaction = std::mem::uninitialized();

        sa.sa_sigaction = handler as usize;
        libc::sigemptyset(&mut sa.sa_mask as *mut libc::sigset_t);
        sa.sa_flags = libc::SA_SIGINFO;

        if libc::sigaction(
            libc::SIGSEGV,
            &sa as *const libc::sigaction,
            0 as *mut libc::sigaction,
        ) == -1
        {
            libc::perror("sigaction for SIGSEGV failed".as_ptr() as *const libc::c_char);
        }

        if libc::sigaction(
            libc::SIGILL,
            &sa as *const libc::sigaction,
            0 as *mut libc::sigaction,
        ) == -1
        {
            libc::perror("sigaction for SIGILL failed".as_ptr() as *const libc::c_char);
        }
    }
}

#[cfg(target_family = "windows")]
pub fn register_signals(vm: &VM) {
    use kernel32::AddVectoredExceptionHandler;

    unsafe {
        AddVectoredExceptionHandler(1, Some(handler));
    }
}

#[cfg(target_family = "windows")]
extern "system" fn handler(exception: *mut EXCEPTION_POINTERS) -> i32 {
    use winapi::excpt;

    if fault_handler(exception) {
        return excpt::ExceptionContinueExecution.0 as i32;
    }

    excpt::ExceptionContinueSearch.0 as i32
}

#[cfg(target_family = "windows")]
fn fault_handler(exception: *mut EXCEPTION_POINTERS) -> bool {
    unsafe {
        let record = (*exception).ExceptionRecord;
        let context = (*exception).ContextRecord;
    }

    false
}

#[cfg(target_family = "unix")]
fn handler(signo: libc::c_int, info: *const siginfo_t, ucontext: *const u8) {
    let es = read_execstate(ucontext);
    let vm = get_vm();

    let addr = unsafe { (*info).si_addr } as *const u8;

    if detect_nil_check(vm, es.pc, signo, addr) {
        println!("nil check failed");
        let stacktrace = stacktrace_from_es(vm, &es);
        stacktrace.dump(vm);
        unsafe {
            libc::_exit(104);
        }
    } else if detect_polling_page_check(vm, signo, addr) {
        // polling page read failed => enter safepoint
        safepoint::block(&es);

    // otherwise trap not dected => crash
    } else {
        println!(
            "error: trap not detected (signal {}, addr {:?}).",
            signo, addr
        );
        println!();
        println!("{:?}", &es);
        println!();
        println!("polling page = {}", vm.polling_page.addr());
        println!();

        let code_map = vm.code_map.lock();
        code_map.dump(vm);

        unsafe {
            libc::_exit(1);
        }
    }
}

fn detect_nil_check(vm: &VM, pc: usize, signo: libc::c_int, addr: *const u8) -> bool {
    if signo != libc::SIGSEGV || addr as usize >= os::page_size() as usize {
        return false;
    }

    let code_map = vm.code_map.lock();

    if let Some(CodeDescriptor::DoraFct(fid)) = code_map.get(pc.into()) {
        let jit_fct = vm.jit_fcts.idx(fid);
        let offset = pc - jit_fct.fct_ptr().to_usize();

        let jit_fct = jit_fct.to_base().expect("baseline expected");
        jit_fct.nil_check_for_offset(offset as i32)
    } else {
        false
    }
}

fn detect_polling_page_check(vm: &VM, signo: libc::c_int, addr: *const u8) -> bool {
    signo == libc::SIGSEGV && vm.polling_page.addr().to_ptr() == addr
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Trap {
    DIV0,
    ASSERT,
    INDEX_OUT_OF_BOUNDS,
    NIL,
    THROW,
    CAST,
    UNEXPECTED,
    OOM,
}

impl Trap {
    pub fn int(self) -> u32 {
        match self {
            Trap::DIV0 => 1,
            Trap::ASSERT => 2,
            Trap::INDEX_OUT_OF_BOUNDS => 3,
            Trap::NIL => 4,
            Trap::THROW => 5,
            Trap::CAST => 6,
            Trap::UNEXPECTED => 7,
            Trap::OOM => 8,
        }
    }

    pub fn from(value: u32) -> Option<Trap> {
        match value {
            1 => Some(Trap::DIV0),
            2 => Some(Trap::ASSERT),
            3 => Some(Trap::INDEX_OUT_OF_BOUNDS),
            4 => Some(Trap::NIL),
            5 => Some(Trap::THROW),
            6 => Some(Trap::CAST),
            7 => Some(Trap::UNEXPECTED),
            8 => Some(Trap::OOM),
            _ => None,
        }
    }
}

struct siginfo_t {
    pub si_signo: libc::c_int,
    pub si_errno: libc::c_int,
    pub si_code: libc::c_int,
    pub si_addr: *const libc::c_void,
}
<commit_msg>fix definition of siginfo_t<commit_after>use libc;
use std;

use baseline::map::CodeDescriptor;
use ctxt::{get_vm, VM};
use exception::stacktrace_from_es;
use os;
use os_cpu::*;
use safepoint;

#[cfg(target_family = "windows")]
use winapi::winnt::EXCEPTION_POINTERS;

#[cfg(target_family = "unix")]
pub fn register_signals() {
    unsafe {
        let mut sa: libc::sigaction = std::mem::uninitialized();

        sa.sa_sigaction = handler as usize;
        libc::sigemptyset(&mut sa.sa_mask as *mut libc::sigset_t);
        sa.sa_flags = libc::SA_SIGINFO;

        if libc::sigaction(
            libc::SIGSEGV,
            &sa as *const libc::sigaction,
            0 as *mut libc::sigaction,
        ) == -1
        {
            libc::perror("sigaction for SIGSEGV failed".as_ptr() as *const libc::c_char);
        }

        if libc::sigaction(
            libc::SIGILL,
            &sa as *const libc::sigaction,
            0 as *mut libc::sigaction,
        ) == -1
        {
            libc::perror("sigaction for SIGILL failed".as_ptr() as *const libc::c_char);
        }
    }
}

#[cfg(target_family = "windows")]
pub fn register_signals(vm: &VM) {
    use kernel32::AddVectoredExceptionHandler;

    unsafe {
        AddVectoredExceptionHandler(1, Some(handler));
    }
}

#[cfg(target_family = "windows")]
extern "system" fn handler(exception: *mut EXCEPTION_POINTERS) -> i32 {
    use winapi::excpt;

    if fault_handler(exception) {
        return excpt::ExceptionContinueExecution.0 as i32;
    }

    excpt::ExceptionContinueSearch.0 as i32
}

#[cfg(target_family = "windows")]
fn fault_handler(exception: *mut EXCEPTION_POINTERS) -> bool {
    unsafe {
        let record = (*exception).ExceptionRecord;
        let context = (*exception).ContextRecord;
    }

    false
}

#[cfg(target_family = "unix")]
fn handler(signo: libc::c_int, info: *const siginfo_t, ucontext: *const u8) {
    let es = read_execstate(ucontext);
    let vm = get_vm();

    let addr = unsafe { (*info).si_addr } as *const u8;

    if detect_nil_check(vm, es.pc, signo, addr) {
        println!("nil check failed");
        let stacktrace = stacktrace_from_es(vm, &es);
        stacktrace.dump(vm);
        unsafe {
            libc::_exit(104);
        }
    } else if detect_polling_page_check(vm, signo, addr) {
        // polling page read failed => enter safepoint
        safepoint::block(&es);

    // otherwise trap not dected => crash
    } else {
        println!(
            "error: trap not detected (signal {}, addr {:?}).",
            signo, addr
        );
        println!();
        println!("{:?}", &es);
        println!();
        println!("polling page = {}", vm.polling_page.addr());
        println!();

        let code_map = vm.code_map.lock();
        code_map.dump(vm);

        unsafe {
            libc::_exit(1);
        }
    }
}

fn detect_nil_check(vm: &VM, pc: usize, signo: libc::c_int, addr: *const u8) -> bool {
    if signo != libc::SIGSEGV || addr as usize >= os::page_size() as usize {
        return false;
    }

    let code_map = vm.code_map.lock();

    if let Some(CodeDescriptor::DoraFct(fid)) = code_map.get(pc.into()) {
        let jit_fct = vm.jit_fcts.idx(fid);
        let offset = pc - jit_fct.fct_ptr().to_usize();

        let jit_fct = jit_fct.to_base().expect("baseline expected");
        jit_fct.nil_check_for_offset(offset as i32)
    } else {
        false
    }
}

fn detect_polling_page_check(vm: &VM, signo: libc::c_int, addr: *const u8) -> bool {
    signo == libc::SIGSEGV && vm.polling_page.addr().to_ptr() == addr
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Trap {
    DIV0,
    ASSERT,
    INDEX_OUT_OF_BOUNDS,
    NIL,
    THROW,
    CAST,
    UNEXPECTED,
    OOM,
}

impl Trap {
    pub fn int(self) -> u32 {
        match self {
            Trap::DIV0 => 1,
            Trap::ASSERT => 2,
            Trap::INDEX_OUT_OF_BOUNDS => 3,
            Trap::NIL => 4,
            Trap::THROW => 5,
            Trap::CAST => 6,
            Trap::UNEXPECTED => 7,
            Trap::OOM => 8,
        }
    }

    pub fn from(value: u32) -> Option<Trap> {
        match value {
            1 => Some(Trap::DIV0),
            2 => Some(Trap::ASSERT),
            3 => Some(Trap::INDEX_OUT_OF_BOUNDS),
            4 => Some(Trap::NIL),
            5 => Some(Trap::THROW),
            6 => Some(Trap::CAST),
            7 => Some(Trap::UNEXPECTED),
            8 => Some(Trap::OOM),
            _ => None,
        }
    }
}

#[repr(C)]
struct siginfo_t {
    pub si_signo: libc::c_int,
    pub si_errno: libc::c_int,
    pub si_code: libc::c_int,
    pub _pad: libc::c_int,
    pub si_addr: *const libc::c_void,
}
<|endoftext|>


<commit_before>use clap::builder::ArgAction;
use clap::Arg;
use clap::Command;

#[test]
fn set_true() {
    let cmd =
        Command::new("test").arg(Arg::new("mammal").long("mammal").action(ArgAction::SetTrue));

    let matches = cmd.clone().try_get_matches_from(["test"]).unwrap();
    assert_eq!(matches.get_one::<bool>("mammal"), None);
    assert_eq!(matches.is_present("mammal"), false);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), None);

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal"])
        .unwrap();
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), true);
    assert_eq!(matches.is_present("mammal"), true);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), Some(1));

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal", "--mammal"])
        .unwrap();
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), true);
    assert_eq!(matches.is_present("mammal"), true);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), Some(2));
}

#[test]
fn set_true_with_default_value() {
    let cmd = Command::new("test").arg(
        Arg::new("mammal")
            .long("mammal")
            .action(ArgAction::SetTrue)
            .default_value("false"),
    );

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal"])
        .unwrap();
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), true);
    assert_eq!(matches.is_present("mammal"), true);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), Some(1));

    let matches = cmd.clone().try_get_matches_from(["test"]).unwrap();
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), false);
    assert_eq!(matches.is_present("mammal"), true);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), Some(1));
}

#[test]
fn set_true_with_default_value_if_present() {
    let cmd = Command::new("test")
        .arg(
            Arg::new("mammal")
                .long("mammal")
                .action(ArgAction::SetTrue)
                .default_value_if("dog", None, Some("true")),
        )
        .arg(Arg::new("dog").long("dog").action(ArgAction::SetTrue));

    let matches = cmd.clone().try_get_matches_from(["test", "--dog"]).unwrap();
    assert_eq!(*matches.get_one::<bool>("dog").unwrap(), true);
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), true);

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal"])
        .unwrap();
    assert_eq!(matches.get_one::<bool>("dog"), None);
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), true);
}

#[test]
fn set_true_with_default_value_if_value() {
    let cmd = Command::new("test")
        .arg(
            Arg::new("mammal")
                .long("mammal")
                .action(ArgAction::SetTrue)
                .default_value_if("dog", Some("true"), Some("true")),
        )
        .arg(Arg::new("dog").long("dog").action(ArgAction::SetTrue));

    let matches = cmd.clone().try_get_matches_from(["test", "--dog"]).unwrap();
    assert_eq!(*matches.get_one::<bool>("dog").unwrap(), true);
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), true);

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal"])
        .unwrap();
    assert_eq!(matches.get_one::<bool>("dog"), None);
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), true);
}

#[test]
fn set_false() {
    let cmd = Command::new("test").arg(
        Arg::new("mammal")
            .long("mammal")
            .action(ArgAction::SetFalse),
    );

    let matches = cmd.clone().try_get_matches_from(["test"]).unwrap();
    assert_eq!(matches.get_one::<bool>("mammal"), None);
    assert_eq!(matches.is_present("mammal"), false);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), None);

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal"])
        .unwrap();
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), false);
    assert_eq!(matches.is_present("mammal"), true);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), Some(1));

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal", "--mammal"])
        .unwrap();
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), false);
    assert_eq!(matches.is_present("mammal"), true);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), Some(2));
}

#[test]
fn set_false_with_default_value() {
    let cmd = Command::new("test").arg(
        Arg::new("mammal")
            .long("mammal")
            .action(ArgAction::SetFalse)
            .default_value("true"),
    );

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal"])
        .unwrap();
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), false);
    assert_eq!(matches.is_present("mammal"), true);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), Some(1));

    let matches = cmd.clone().try_get_matches_from(["test"]).unwrap();
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), true);
    assert_eq!(matches.is_present("mammal"), true);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), Some(1));
}

#[test]
fn set_false_with_default_value_if_present() {
    let cmd = Command::new("test")
        .arg(
            Arg::new("mammal")
                .long("mammal")
                .action(ArgAction::SetFalse)
                .default_value_if("dog", None, Some("false")),
        )
        .arg(Arg::new("dog").long("dog").action(ArgAction::SetFalse));

    let matches = cmd.clone().try_get_matches_from(["test", "--dog"]).unwrap();
    assert_eq!(*matches.get_one::<bool>("dog").unwrap(), false);
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), false);

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal"])
        .unwrap();
    assert_eq!(matches.get_one::<bool>("dog"), None);
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), false);
}

#[test]
fn set_false_with_default_value_if_value() {
    let cmd = Command::new("test")
        .arg(
            Arg::new("mammal")
                .long("mammal")
                .action(ArgAction::SetFalse)
                .default_value_if("dog", Some("false"), Some("false")),
        )
        .arg(Arg::new("dog").long("dog").action(ArgAction::SetFalse));

    let matches = cmd.clone().try_get_matches_from(["test", "--dog"]).unwrap();
    assert_eq!(*matches.get_one::<bool>("dog").unwrap(), false);
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), false);

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal"])
        .unwrap();
    assert_eq!(matches.get_one::<bool>("dog"), None);
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), false);
}

#[test]
fn count() {
    let cmd = Command::new("test").arg(Arg::new("mammal").long("mammal").action(ArgAction::Count));

    let matches = cmd.clone().try_get_matches_from(["test"]).unwrap();
    assert_eq!(matches.get_one::<u64>("mammal"), None);
    assert_eq!(matches.is_present("mammal"), false);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), None);

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal"])
        .unwrap();
    assert_eq!(*matches.get_one::<u64>("mammal").unwrap(), 1);
    assert_eq!(matches.is_present("mammal"), true);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), Some(1));

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal", "--mammal"])
        .unwrap();
    assert_eq!(*matches.get_one::<u64>("mammal").unwrap(), 2);
    assert_eq!(matches.is_present("mammal"), true);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), Some(2));
}

#[test]
fn count_with_default_value() {
    let cmd = Command::new("test").arg(
        Arg::new("mammal")
            .long("mammal")
            .action(ArgAction::Count)
            .default_value("10"),
    );

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal"])
        .unwrap();
    assert_eq!(*matches.get_one::<u64>("mammal").unwrap(), 1);
    assert_eq!(matches.is_present("mammal"), true);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), Some(1));

    let matches = cmd.clone().try_get_matches_from(["test"]).unwrap();
    assert_eq!(*matches.get_one::<u64>("mammal").unwrap(), 10);
    assert_eq!(matches.is_present("mammal"), true);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), Some(1));
}

#[test]
fn count_with_default_value_if_present() {
    let cmd = Command::new("test")
        .arg(
            Arg::new("mammal")
                .long("mammal")
                .action(ArgAction::Count)
                .default_value_if("dog", None, Some("10")),
        )
        .arg(Arg::new("dog").long("dog").action(ArgAction::Count));

    let matches = cmd.clone().try_get_matches_from(["test", "--dog"]).unwrap();
    assert_eq!(*matches.get_one::<u64>("dog").unwrap(), 1);
    assert_eq!(*matches.get_one::<u64>("mammal").unwrap(), 10);

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal"])
        .unwrap();
    assert_eq!(matches.get_one::<u64>("dog"), None);
    assert_eq!(*matches.get_one::<u64>("mammal").unwrap(), 1);
}

#[test]
fn count_with_default_value_if_value() {
    let cmd = Command::new("test")
        .arg(
            Arg::new("mammal")
                .long("mammal")
                .action(ArgAction::Count)
                .default_value_if("dog", Some("2"), Some("10")),
        )
        .arg(Arg::new("dog").long("dog").action(ArgAction::Count));

    let matches = cmd.clone().try_get_matches_from(["test", "--dog"]).unwrap();
    assert_eq!(*matches.get_one::<u64>("dog").unwrap(), 1);
    assert_eq!(matches.get_one::<u64>("mammal"), None);

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--dog", "--dog"])
        .unwrap();
    assert_eq!(*matches.get_one::<u64>("dog").unwrap(), 2);
    assert_eq!(*matches.get_one::<u64>("mammal").unwrap(), 10);

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal"])
        .unwrap();
    assert_eq!(matches.get_one::<u64>("dog"), None);
    assert_eq!(*matches.get_one::<u64>("mammal").unwrap(), 1);
}
<commit_msg>test(parser): Ensure conditional requirements work with new Actions<commit_after>use clap::builder::ArgAction;
use clap::Arg;
use clap::Command;

#[test]
fn set_true() {
    let cmd =
        Command::new("test").arg(Arg::new("mammal").long("mammal").action(ArgAction::SetTrue));

    let matches = cmd.clone().try_get_matches_from(["test"]).unwrap();
    assert_eq!(matches.get_one::<bool>("mammal"), None);
    assert_eq!(matches.is_present("mammal"), false);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), None);

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal"])
        .unwrap();
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), true);
    assert_eq!(matches.is_present("mammal"), true);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), Some(1));

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal", "--mammal"])
        .unwrap();
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), true);
    assert_eq!(matches.is_present("mammal"), true);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), Some(2));
}

#[test]
fn set_true_with_default_value() {
    let cmd = Command::new("test").arg(
        Arg::new("mammal")
            .long("mammal")
            .action(ArgAction::SetTrue)
            .default_value("false"),
    );

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal"])
        .unwrap();
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), true);
    assert_eq!(matches.is_present("mammal"), true);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), Some(1));

    let matches = cmd.clone().try_get_matches_from(["test"]).unwrap();
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), false);
    assert_eq!(matches.is_present("mammal"), true);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), Some(1));
}

#[test]
fn set_true_with_default_value_if_present() {
    let cmd = Command::new("test")
        .arg(
            Arg::new("mammal")
                .long("mammal")
                .action(ArgAction::SetTrue)
                .default_value_if("dog", None, Some("true")),
        )
        .arg(Arg::new("dog").long("dog").action(ArgAction::SetTrue));

    let matches = cmd.clone().try_get_matches_from(["test", "--dog"]).unwrap();
    assert_eq!(*matches.get_one::<bool>("dog").unwrap(), true);
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), true);

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal"])
        .unwrap();
    assert_eq!(matches.get_one::<bool>("dog"), None);
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), true);
}

#[test]
fn set_true_with_default_value_if_value() {
    let cmd = Command::new("test")
        .arg(
            Arg::new("mammal")
                .long("mammal")
                .action(ArgAction::SetTrue)
                .default_value_if("dog", Some("true"), Some("true")),
        )
        .arg(Arg::new("dog").long("dog").action(ArgAction::SetTrue));

    let matches = cmd.clone().try_get_matches_from(["test", "--dog"]).unwrap();
    assert_eq!(*matches.get_one::<bool>("dog").unwrap(), true);
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), true);

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal"])
        .unwrap();
    assert_eq!(matches.get_one::<bool>("dog"), None);
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), true);
}

#[test]
fn set_true_with_required_if_eq() {
    let cmd = Command::new("test")
        .arg(
            Arg::new("mammal")
                .long("mammal")
                .action(ArgAction::SetTrue)
                .required_if_eq("dog", "true"),
        )
        .arg(Arg::new("dog").long("dog").action(ArgAction::SetTrue));

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal"])
        .unwrap();
    assert_eq!(matches.get_one::<u64>("dog"), None);
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), true);

    cmd.clone()
        .try_get_matches_from(["test", "--dog"])
        .unwrap_err();

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--dog", "--mammal"])
        .unwrap();
    assert_eq!(*matches.get_one::<bool>("dog").unwrap(), true);
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), true);
}

#[test]
fn set_false() {
    let cmd = Command::new("test").arg(
        Arg::new("mammal")
            .long("mammal")
            .action(ArgAction::SetFalse),
    );

    let matches = cmd.clone().try_get_matches_from(["test"]).unwrap();
    assert_eq!(matches.get_one::<bool>("mammal"), None);
    assert_eq!(matches.is_present("mammal"), false);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), None);

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal"])
        .unwrap();
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), false);
    assert_eq!(matches.is_present("mammal"), true);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), Some(1));

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal", "--mammal"])
        .unwrap();
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), false);
    assert_eq!(matches.is_present("mammal"), true);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), Some(2));
}

#[test]
fn set_false_with_default_value() {
    let cmd = Command::new("test").arg(
        Arg::new("mammal")
            .long("mammal")
            .action(ArgAction::SetFalse)
            .default_value("true"),
    );

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal"])
        .unwrap();
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), false);
    assert_eq!(matches.is_present("mammal"), true);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), Some(1));

    let matches = cmd.clone().try_get_matches_from(["test"]).unwrap();
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), true);
    assert_eq!(matches.is_present("mammal"), true);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), Some(1));
}

#[test]
fn set_false_with_default_value_if_present() {
    let cmd = Command::new("test")
        .arg(
            Arg::new("mammal")
                .long("mammal")
                .action(ArgAction::SetFalse)
                .default_value_if("dog", None, Some("false")),
        )
        .arg(Arg::new("dog").long("dog").action(ArgAction::SetFalse));

    let matches = cmd.clone().try_get_matches_from(["test", "--dog"]).unwrap();
    assert_eq!(*matches.get_one::<bool>("dog").unwrap(), false);
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), false);

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal"])
        .unwrap();
    assert_eq!(matches.get_one::<bool>("dog"), None);
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), false);
}

#[test]
fn set_false_with_default_value_if_value() {
    let cmd = Command::new("test")
        .arg(
            Arg::new("mammal")
                .long("mammal")
                .action(ArgAction::SetFalse)
                .default_value_if("dog", Some("false"), Some("false")),
        )
        .arg(Arg::new("dog").long("dog").action(ArgAction::SetFalse));

    let matches = cmd.clone().try_get_matches_from(["test", "--dog"]).unwrap();
    assert_eq!(*matches.get_one::<bool>("dog").unwrap(), false);
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), false);

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal"])
        .unwrap();
    assert_eq!(matches.get_one::<bool>("dog"), None);
    assert_eq!(*matches.get_one::<bool>("mammal").unwrap(), false);
}

#[test]
fn count() {
    let cmd = Command::new("test").arg(Arg::new("mammal").long("mammal").action(ArgAction::Count));

    let matches = cmd.clone().try_get_matches_from(["test"]).unwrap();
    assert_eq!(matches.get_one::<u64>("mammal"), None);
    assert_eq!(matches.is_present("mammal"), false);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), None);

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal"])
        .unwrap();
    assert_eq!(*matches.get_one::<u64>("mammal").unwrap(), 1);
    assert_eq!(matches.is_present("mammal"), true);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), Some(1));

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal", "--mammal"])
        .unwrap();
    assert_eq!(*matches.get_one::<u64>("mammal").unwrap(), 2);
    assert_eq!(matches.is_present("mammal"), true);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), Some(2));
}

#[test]
fn count_with_default_value() {
    let cmd = Command::new("test").arg(
        Arg::new("mammal")
            .long("mammal")
            .action(ArgAction::Count)
            .default_value("10"),
    );

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal"])
        .unwrap();
    assert_eq!(*matches.get_one::<u64>("mammal").unwrap(), 1);
    assert_eq!(matches.is_present("mammal"), true);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), Some(1));

    let matches = cmd.clone().try_get_matches_from(["test"]).unwrap();
    assert_eq!(*matches.get_one::<u64>("mammal").unwrap(), 10);
    assert_eq!(matches.is_present("mammal"), true);
    assert_eq!(matches.occurrences_of("mammal"), 0);
    assert_eq!(matches.index_of("mammal"), Some(1));
}

#[test]
fn count_with_default_value_if_present() {
    let cmd = Command::new("test")
        .arg(
            Arg::new("mammal")
                .long("mammal")
                .action(ArgAction::Count)
                .default_value_if("dog", None, Some("10")),
        )
        .arg(Arg::new("dog").long("dog").action(ArgAction::Count));

    let matches = cmd.clone().try_get_matches_from(["test", "--dog"]).unwrap();
    assert_eq!(*matches.get_one::<u64>("dog").unwrap(), 1);
    assert_eq!(*matches.get_one::<u64>("mammal").unwrap(), 10);

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal"])
        .unwrap();
    assert_eq!(matches.get_one::<u64>("dog"), None);
    assert_eq!(*matches.get_one::<u64>("mammal").unwrap(), 1);
}

#[test]
fn count_with_default_value_if_value() {
    let cmd = Command::new("test")
        .arg(
            Arg::new("mammal")
                .long("mammal")
                .action(ArgAction::Count)
                .default_value_if("dog", Some("2"), Some("10")),
        )
        .arg(Arg::new("dog").long("dog").action(ArgAction::Count));

    let matches = cmd.clone().try_get_matches_from(["test", "--dog"]).unwrap();
    assert_eq!(*matches.get_one::<u64>("dog").unwrap(), 1);
    assert_eq!(matches.get_one::<u64>("mammal"), None);

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--dog", "--dog"])
        .unwrap();
    assert_eq!(*matches.get_one::<u64>("dog").unwrap(), 2);
    assert_eq!(*matches.get_one::<u64>("mammal").unwrap(), 10);

    let matches = cmd
        .clone()
        .try_get_matches_from(["test", "--mammal"])
        .unwrap();
    assert_eq!(matches.get_one::<u64>("dog"), None);
    assert_eq!(*matches.get_one::<u64>("mammal").unwrap(), 1);
}
<|endoftext|>