<commit_before>use std::kinds::marker::ContravariantLifetime;
use std::mem;

use runtime::Object;
use {class, Id, IdVector, IntoIdVector};
use super::{INSCopying, INSObject};

pub struct NSRange {
	pub location: uint,
	pub length: uint,
}

pub struct NSEnumerator<'a, T> {
	id: Id<Object>,
	marker: ContravariantLifetime<'a>,
}

impl<'a, T> NSEnumerator<'a, T> {
	pub unsafe fn from_ptr(ptr: *mut Object) -> NSEnumerator<'a, T> {
		NSEnumerator { id: Id::from_ptr(ptr), marker: ContravariantLifetime }
	}
}

impl<'a, T> Iterator<&'a T> for NSEnumerator<'a, T> {
	fn next(&mut self) -> Option<&'a T> {
		unsafe {
			let obj = msg_send![self.id nextObject] as *mut T;
			if obj.is_null() {
				None
			} else {
				Some(&*obj)
			}
		}
	}
}

pub trait INSArray<T: INSObject> : INSObject {
	fn count(&self) -> uint {
		let result = unsafe {
			msg_send![self count]
		};
		result as uint
	}

	fn object_at(&self, index: uint) -> &T {
		unsafe {
			let result = msg_send![self objectAtIndex:index] as *mut T;
			&*result
		}
	}

	fn object_enumerator(&self) -> NSEnumerator<T> {
		unsafe {
			let result = msg_send![self objectEnumerator];
			NSEnumerator::from_ptr(result)
		}
	}

	unsafe fn from_refs(refs: &[&T]) -> Id<Self> {
		let cls = class::<Self>();
		let obj = msg_send![cls alloc];
		let obj = msg_send![obj initWithObjects:refs.as_ptr() count:refs.len()];
		Id::from_retained_ptr(obj as *mut Self)
	}

	fn from_vec(vec: Vec<Id<T>>) -> Id<Self> {
		let refs = vec.as_refs_slice();
		unsafe {
			INSArray::from_refs(refs)
		}
	}

	fn objects_in_range(&self, start: uint, len: uint) -> Vec<&T> {
		let vec: Vec<*mut T> = Vec::from_elem(len, RawPtr::null());
		let range = NSRange { location: start, length: len };
		unsafe {
			msg_send![self getObjects:vec.as_ptr() range:range];
			mem::transmute(vec)
		}
	}

	fn to_vec(&self) -> Vec<&T> {
		self.objects_in_range(0, self.count())
	}

	fn into_vec(array: Id<Self>) -> Vec<Id<T>> {
		let vec = array.to_vec();
		unsafe {
			vec.into_id_vec()
		}
	}
}

object_struct!(NSArray<T>)

impl<T: INSObject> INSArray<T> for NSArray<T> { }

impl<T> INSCopying<NSArray<T>> for NSArray<T> { }

impl<T: INSObject> Collection for NSArray<T> {
	fn len(&self) -> uint {
		self.count()
	}
}

impl<T: INSObject> Index<uint, T> for NSArray<T> {
	fn index(&self, index: &uint) -> &T {
		self.object_at(*index)
	}
}

#[cfg(test)]
mod tests {
	use {Id};
	use foundation::{INSObject, NSObject};
	use super::{INSArray, NSArray};

	fn sample_array(len: uint) -> Id<NSArray<NSObject>> {
		let vec: Vec<Id<NSObject>> = Vec::from_fn(len, |_| INSObject::new());
		INSArray::from_vec(vec)
	}

	#[test]
	fn test_count() {
		let empty_array: Id<NSArray<NSObject>> = INSObject::new();
		assert!(empty_array.count() == 0);

		let array = sample_array(4);
		assert!(array.count() == 4);
	}

	#[test]
	fn test_object_at() {
		let array = sample_array(4);
		assert!(array.object_at(0) != array.object_at(3));
	}

	#[test]
	fn test_object_enumerator() {
		let array = sample_array(4);

		assert!(array.object_enumerator().count() == 4);
		assert!(array.object_enumerator()
		             .enumerate()
		             .all(|(i, obj)| obj == array.object_at(i)));
	}

	#[test]
	fn test_objects_in_range() {
		let array = sample_array(4);

		let middle_objs = array.objects_in_range(1, 2);
		assert!(middle_objs.len() == 2);
		assert!(*middle_objs.get(0) == array.object_at(1));
		assert!(*middle_objs.get(1) == array.object_at(2));

		let empty_objs = array.objects_in_range(1, 0);
		assert!(empty_objs.len() == 0);

		let all_objs = array.objects_in_range(0, 4);
		assert!(all_objs.len() == 4);
	}

	#[test]
	fn test_into_vec() {
		let array = sample_array(4);

		let vec = INSArray::into_vec(array);
		assert!(vec.len() == 4);
	}
}
<commit_msg>Added Ownership type param to INSArray.<commit_after>use std::kinds::marker::ContravariantLifetime;
use std::mem;

use runtime::Object;
use {class, Id, IdVector, IntoIdVector, Owned, Ownership};
use super::{INSCopying, INSObject};

pub struct NSRange {
	pub location: uint,
	pub length: uint,
}

pub struct NSEnumerator<'a, T> {
	id: Id<Object>,
	marker: ContravariantLifetime<'a>,
}

impl<'a, T> NSEnumerator<'a, T> {
	pub unsafe fn from_ptr(ptr: *mut Object) -> NSEnumerator<'a, T> {
		NSEnumerator { id: Id::from_ptr(ptr), marker: ContravariantLifetime }
	}
}

impl<'a, T> Iterator<&'a T> for NSEnumerator<'a, T> {
	fn next(&mut self) -> Option<&'a T> {
		unsafe {
			let obj = msg_send![self.id nextObject] as *mut T;
			if obj.is_null() {
				None
			} else {
				Some(&*obj)
			}
		}
	}
}

pub trait INSArray<T: INSObject, O: Ownership> : INSObject {
	fn count(&self) -> uint {
		let result = unsafe {
			msg_send![self count]
		};
		result as uint
	}

	fn object_at(&self, index: uint) -> &T {
		unsafe {
			let result = msg_send![self objectAtIndex:index] as *mut T;
			&*result
		}
	}

	fn object_enumerator(&self) -> NSEnumerator<T> {
		unsafe {
			let result = msg_send![self objectEnumerator];
			NSEnumerator::from_ptr(result)
		}
	}

	unsafe fn from_refs(refs: &[&T]) -> Id<Self> {
		let cls = class::<Self>();
		let obj = msg_send![cls alloc];
		let obj = msg_send![obj initWithObjects:refs.as_ptr() count:refs.len()];
		Id::from_retained_ptr(obj as *mut Self)
	}

	fn from_vec(vec: Vec<Id<T, O>>) -> Id<Self> {
		let refs = vec.as_refs_slice();
		unsafe {
			INSArray::from_refs(refs)
		}
	}

	fn objects_in_range(&self, start: uint, len: uint) -> Vec<&T> {
		let vec: Vec<*mut T> = Vec::from_elem(len, RawPtr::null());
		let range = NSRange { location: start, length: len };
		unsafe {
			msg_send![self getObjects:vec.as_ptr() range:range];
			mem::transmute(vec)
		}
	}

	fn to_vec(&self) -> Vec<&T> {
		self.objects_in_range(0, self.count())
	}

	fn into_vec(array: Id<Self>) -> Vec<Id<T, O>> {
		let vec = array.to_vec();
		unsafe {
			vec.into_id_vec()
		}
	}
}

object_struct!(NSArray<T>)

impl<T: INSObject> INSArray<T, Owned> for NSArray<T> { }

impl<T> INSCopying<NSArray<T>> for NSArray<T> { }

impl<T: INSObject> Collection for NSArray<T> {
	fn len(&self) -> uint {
		self.count()
	}
}

impl<T: INSObject> Index<uint, T> for NSArray<T> {
	fn index(&self, index: &uint) -> &T {
		self.object_at(*index)
	}
}

#[cfg(test)]
mod tests {
	use {Id};
	use foundation::{INSObject, NSObject};
	use super::{INSArray, NSArray};

	fn sample_array(len: uint) -> Id<NSArray<NSObject>> {
		let vec: Vec<Id<NSObject>> = Vec::from_fn(len, |_| INSObject::new());
		INSArray::from_vec(vec)
	}

	#[test]
	fn test_count() {
		let empty_array: Id<NSArray<NSObject>> = INSObject::new();
		assert!(empty_array.count() == 0);

		let array = sample_array(4);
		assert!(array.count() == 4);
	}

	#[test]
	fn test_object_at() {
		let array = sample_array(4);
		assert!(array.object_at(0) != array.object_at(3));
	}

	#[test]
	fn test_object_enumerator() {
		let array = sample_array(4);

		assert!(array.object_enumerator().count() == 4);
		assert!(array.object_enumerator()
		             .enumerate()
		             .all(|(i, obj)| obj == array.object_at(i)));
	}

	#[test]
	fn test_objects_in_range() {
		let array = sample_array(4);

		let middle_objs = array.objects_in_range(1, 2);
		assert!(middle_objs.len() == 2);
		assert!(*middle_objs.get(0) == array.object_at(1));
		assert!(*middle_objs.get(1) == array.object_at(2));

		let empty_objs = array.objects_in_range(1, 0);
		assert!(empty_objs.len() == 0);

		let all_objs = array.objects_in_range(0, 4);
		assert!(all_objs.len() == 4);
	}

	#[test]
	fn test_into_vec() {
		let array = sample_array(4);

		let vec = INSArray::into_vec(array);
		assert!(vec.len() == 4);
	}
}
<|endoftext|>


<commit_before>//! Built-in functions and macros.

use super::*;

pub fn add_builtins(env: @mut Rusp) {
    macro_rules! rust(
        (NoEnv: $ty:ident, $name:ident) => (
            @FnEnv(ExternFn(|params, env| $name(params, env), $ty), None)
        );
        ($ty:ident, $name:ident) => (
            @FnEnv(ExternFn(|params, env| $name(params, env), $ty), Some(env))
        );
    );

    for [
        // special forms

        // most of these need to capture the environment where they
        // are called (e.g. to create a lexically-scoped closure, or
        // to unquote local variables), *not* the global environment,
        // hence NoEnv.
        (~"fn",                     rust!(NoEnv: Macro, builtin_fn)),
        (~"macro",                  rust!(NoEnv: Macro, builtin_macro)),

        (~"if",                     rust!(NoEnv: Macro, builtin_if)),
        (~"do",                     rust!(NoEnv: Macro, builtin_do)),
        (~"quote",                  @FnEnv(RuspFn(~[~"a"], @Symbol(~"a"), Macro), None)),
        (~"quasiquote",             rust!(NoEnv: Macro, builtin_quasiquote)),

        // this changes the global scope always
        (~"def",                    rust!(Macro, builtin_def)),

        // builtin functions
        (~"eval",                   rust!(Fn, builtin_eval)),
        (~"parse",                  rust!(Fn, builtin_parse)),
        (~"list",                   rust!(Fn, builtin_list)),
        (~"print",                  rust!(Fn, builtin_print)),

        (~"bool?",                  rust!(Fn, builtin_is_bool)),
        (~"int?",                   rust!(Fn, builtin_is_int)),
        (~"float?",                 rust!(Fn, builtin_is_float)),
        (~"string?",                rust!(Fn, builtin_is_string)),
        (~"list?",                  rust!(Fn, builtin_is_list)),
        (~"symbol?",                rust!(Fn, builtin_is_symbol)),
        (~"fn?",                    rust!(Fn, builtin_is_fn)),
        (~"macro?",                 rust!(Fn, builtin_is_macro)),
        (~"extern-fn?",             rust!(Fn, builtin_is_extern_fn)),
        (~"extern-macro?",          rust!(Fn, builtin_is_extern_macro)),

        (~"+",                      rust!(Fn, builtin_add)),
        (~"-",                      rust!(Fn, builtin_sub)),
        (~"*",                      rust!(Fn, builtin_mul)),
        (~"/",                      rust!(Fn, builtin_div)),
        (~"%",                      rust!(Fn, builtin_rem)),
        (~"=",                      rust!(Fn, builtin_eq)),
    ].each |&(name, func)| {
        env.define(name, func);
    }
}

/// Define a new symbol in the environment
fn builtin_def(params: &[@Value], env: @mut Rusp) -> EvalResult {
    match params {
        [@Symbol(ref id), .. rest] => {
            match rest {
                [] => {
                    env.define(id.clone(), @List(~[]));
                    Ok(@List(~[]))
                }
                [ref v] => {
                    env.eval(*v).chain(|res| env.define(id.clone(), res))
                }
                _ => Err(~"`def` expects 1 or 2 arguments")
            }
        }
        _ => Err(~"first argument to `def` should be an identifier")
    }
}

/// Evaluate an S-expression in the current environment
fn builtin_eval(params: &[@Value], env: @mut Rusp) -> EvalResult {
    match params {
        [ref p] => env.eval(*p),
        _ => Err(~"`eval` expects 1 argument")
    }
}

/// Parse S-expressions from a string
fn builtin_parse(params: &[@Value], _: @mut Rusp) -> EvalResult {
    match params {
        [@Str(ref s)] => match parse(*s) {
            Ok(vals) => Ok(@List(vals)),
            Err(parser::ParseFailure { description, _  }) => Err(description)
        },
        _ => Err(~"`parse` expects 1 argument")
    }
}

/// Conditional expression
fn builtin_if(params: &[@Value], env: @mut Rusp) -> EvalResult {
    match params {
        [ref pred, ref conseq, ref alt] => {
            do env.eval(*pred).chain |val| {
                match *val {
                    Bool(true) => env.eval(*conseq),
                    Bool(false) => env.eval(*alt),
                    _ => Err(fmt!("Expected boolean expression, found: %s", val.to_str())),
                }
            }
        }
        _ => Err(~"`if` expects 3 arguments"), // TODO
    }
}

/// Evaluate each expression in turn, returning the value of the last expression
fn builtin_do(params: &[@Value], env: @mut Rusp) -> EvalResult {
    match params {
        [..fst, ref lst] => {
            for fst.each |val| {
                match env.eval(*val) {
                    Ok(@List(ref l)) if l.is_empty() => (),
                    Ok(v) => return Err(fmt!("Expected unit expression, found: %s", v.to_str())),
                    Err(e) => return Err(e),
                }
            }
            env.eval(*lst)
        }
        _ => Err(~""), // TODO
    }
}

/// Return the arguments as a list
fn builtin_list(params: &[@Value], _: @mut Rusp) -> EvalResult {
    Ok(@List(params.to_owned()))
}

/// Print to stdout
fn builtin_print(params: &[@Value], _: @mut Rusp) -> EvalResult {
    let elements = params.map(|v| v.to_str());
    println(str::connect(elements, " "));
    Ok(@List(~[]))
}

/// Shared functionality between creating functions and macros
fn fn_macro(params: &[@Value], callee_env: @mut Rusp, ty: FnMode) -> EvalResult {
    match params {
        [@List(ref symbols), ref val] => {
            let mut idents = ~[];
            for symbols.each |symbol| {
                match **symbol {
                    Symbol(ref ident) => idents.push(ident.clone()),
                    _ => return Err(fmt!("Expected symbol identifier, found: \
                                          %s", symbol.to_str())),
                }
            };
            Ok(@FnEnv(RuspFn(idents, *val, ty), Some(callee_env)))
        }
        _ => Err(fmt!("`%s` expects 2 arguments", ty.to_str())), // TODO
    }
}

/// Evaluate to an anonymous function
fn builtin_fn(params: &[@Value], callee_env: @mut Rusp) -> EvalResult {
    fn_macro(params, callee_env, Fn)
}

/// Evaluate to an anonymous macro
fn builtin_macro(params: &[@Value], callee_env: @mut Rusp) -> EvalResult {
    fn_macro(params, callee_env, Macro)
}

/// Quasiquotation, to allow substitutions inside a quoted structure, e.g.
/// `(def a 1) (quasiquote (a (unquote (list a a a))))`
/// expands to (a (1 1 1))
fn builtin_quasiquote(params: &[@Value], env: @mut Rusp) -> EvalResult {
    return match params {
        [ref p] => unquote(*p, env),
        _ => Err(~"`quasiquote` expects 1 argument")
    };

    // traverse the tree, checking for `(unquote ..)`
    fn unquote(v: @Value, env: @mut Rusp) -> EvalResult {
        match *v {
            List([@Symbol(~"unquote"), .. rest]) => {
                match rest {
                    [ref p] => env.eval(*p),
                    _ => Err(~"`unquote` expects 1 argument")
                }
            }
            List(ref vals) => {
                let mut new = ~[];

                for vals.each |v| {
                    match unquote(*v, env) {
                        Ok(v) => new.push(v),
                        Err(e) => return Err(e)
                    }
                }
                Ok(@List(new))
            }
            FnEnv(RuspFn(ref args, ref body, ref fn_type), ref subenv) => {
                match unquote(*body, subenv.get_or_default(env)) {
                    Ok(b) => Ok(@FnEnv(RuspFn(args.clone(), b, fn_type.clone()),
                                       subenv.clone())),
                    Err(e) => Err(e)
                }
            }
            _ => Ok(v)
        }
    }
}

macro_rules! type_pred(
    ($name:ident, $pattern:pat) => (
        fn $name(params: &[@Value], _: @mut Rusp) -> EvalResult {
            match do params.map |&val| {
                match *val {
                    $pattern => @Bool(true),
                    _ => @Bool(false),
                }
            } {
                [ref result] => Ok(*result),
                results => Ok(@List(results)),
            }
        }
    )
)

type_pred!(builtin_is_bool, Bool(*))
type_pred!(builtin_is_int, Int(*))
type_pred!(builtin_is_float, Float(*))
type_pred!(builtin_is_string, Str(*))
type_pred!(builtin_is_list, List(*))
type_pred!(builtin_is_symbol, Symbol(*))
type_pred!(builtin_is_fn, FnEnv(RuspFn(_, _, Fn), _))
type_pred!(builtin_is_macro, FnEnv(RuspFn(_, _, Macro), _))
type_pred!(builtin_is_extern_fn, FnEnv(ExternFn(_, Fn), _))
type_pred!(builtin_is_extern_macro, FnEnv(ExternFn(_, Macro), _))

macro_rules! arithmetic_op(
    ($name:ident, $symbol:expr, $method:ident) => (
        fn $name(params: &[@Value], _: @mut Rusp) -> EvalResult {
            if params.len() < 2 {
                return Err(fmt!("`%s` expects at least 2 arguments", $symbol));
            }
            let mut result = *params.head();
            for params.tail().each |&val| {
                match (result, val) {
                    // Basic operations between values of the same type
                    (@Int(prev), @Int(i)) => result = @Int(prev.$method(&i)),
                    (@Float(prev), @Float(f)) => result = @Float(prev.$method(&f)),
                    // Handle numeric promotions
                    (@Int(prev), @Float(f)) => result = @Float((prev as float).$method(&f)),
                    (@Float(prev), @Int(i)) => result = @Float(prev.$method(&(i as float))),
                    // Type errors
                    (@Float(_), ref v) => return Err(fmt!("Expected float but found: %s", v.to_str())),
                    (@Int(_), ref v) => return Err(fmt!("Expected integer but found: %s", v.to_str())),
                    (ref prev, _) => return Err(fmt!("Expected number but found: %s", prev.to_str())),
                }
            }
            Ok(result)
        }
    )
)

arithmetic_op!(builtin_add, "+", add)
arithmetic_op!(builtin_sub, "-", sub)
arithmetic_op!(builtin_mul, "*", mul)
arithmetic_op!(builtin_div, "/", div)
arithmetic_op!(builtin_rem, "%", rem)

fn builtin_eq(params: &[@Value], _: @mut Rusp) -> EvalResult {
    match params {
        [head, .. rest] => {
            for rest.each |&val| {
                // TODO handle numeric promotions? i.e. 1.0 == 1?
                if !(val == head) { return Ok(@Bool(false)); }
            }
            Ok(@Bool(true))
        }
        [] => Err(fmt!("`=` expects at least 1 argument"))
    }
}

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

    macro_rules! test_ok(
        ($src:expr, $expected:expr) => (
            assert_eq!(Rusp::new().eval(
                @Parser::new($src).parse().get()
            ).get().to_str(), $expected);
        )
    )

    macro_rules! test_err(
        ($src:expr) => (
            assert!(Rusp::new().eval(
                @Parser::new($src).parse().get()
            ).is_err());
        )
    )

    #[test]
    fn test_type_preds() {
        test_ok!("(bool? true)", ~"true");
        test_ok!("(bool? 1)", ~"false");
        test_ok!("(int? 1)", ~"true");
        test_ok!("(int? 1.0)", ~"false");
        test_ok!("(float? 1.0)", ~"true");
        test_ok!("(float? \"hi\")", ~"false");
        test_ok!("(string? \"hi\")", ~"true");
        test_ok!("(string? (list 1 2))", ~"false");
        test_ok!("(list? (list 1 2))", ~"true");
        test_ok!("(list? (list))", ~"true");
        test_ok!("(list? 1.0)", ~"false");
        test_ok!("(symbol? (quote x))", ~"true");
        test_ok!("(symbol? (fn () 1))", ~"false");
        test_ok!("(fn? (fn () 1))", ~"true");
        test_ok!("(fn? (macro () 1))", ~"false");
        test_ok!("(macro? (macro () 1))", ~"true");
        test_ok!("(macro? false)", ~"false");
    }

    #[test]
    fn test_arithmetic() {
        test_ok!("(+ 1 2)", ~"3");
        test_ok!("(+ 1 2 4)", ~"7");
        test_ok!("(+ 1.5 3 4)", ~"8.5");
        test_ok!("(+ 3 1.67)", ~"4.67");
        test_ok!("(- 3 0.5)", ~"2.5");
        test_ok!("(- 3 1.5 (+ 2 4))", ~"-4.5");

        test_err!("(+)");
        test_err!("(-)");
        test_err!("(*)");
        test_err!("(/)");
        test_err!("(%)");

        test_err!("(+ 1)");
        test_err!("(- 1)");
        test_err!("(* 1)");
        test_err!("(/ 1)");
        test_err!("(% 1)");

        test_err!("(+ 1 \"hi\")");
        test_err!("(- 1 \"hi\")");
        test_err!("(* 1 \"hi\")");
        test_err!("(/ 1 \"hi\")");
        test_err!("(% 1 \"hi\")");

        test_err!("(+ \"hi\" 1)");
        test_err!("(- \"hi\" 1)");
        test_err!("(* \"hi\" 1)");
        test_err!("(/ \"hi\" 1)");
        test_err!("(% \"hi\" 1)");
    }
}<commit_msg>Convert macros back to dynamic scoping.<commit_after>//! Built-in functions and macros.

use super::*;

pub fn add_builtins(env: @mut Rusp) {
    macro_rules! rust(
        (Macro, $name:ident) => (
            @FnEnv(ExternFn(|params, env| $name(params, env), Macro), None)
        );
        (Fn, $name:ident) => (
            @FnEnv(ExternFn(|params, env| $name(params, env), Fn), Some(env))
        );
        (Fn, $name:ident, NoEnv) => (
            @FnEnv(ExternFn(|params, env| $name(params, env), Fn), None)
        );
    );

    for [
        // special forms

        // most of these need to capture the environment where they
        // are called (e.g. to create a lexically-scoped closure, or
        // to unquote local variables), *not* the global environment,
        // hence NoEnv.
        (~"fn",                     rust!(Macro, builtin_fn)),
        (~"macro",                  rust!(Macro, builtin_macro)),

        (~"if",                     rust!(Macro, builtin_if)),
        (~"do",                     rust!(Macro, builtin_do)),
        (~"quote",                  @FnEnv(RuspFn(~[~"a"], @Symbol(~"a"), Macro), None)),
        (~"quasiquote",             rust!(Macro, builtin_quasiquote)),

        (~"def",                    rust!(Macro, builtin_def)),

        // builtin functions
        (~"eval",                   rust!(Fn, builtin_eval, NoEnv)),
        (~"parse",                  rust!(Fn, builtin_parse)),
        (~"list",                   rust!(Fn, builtin_list)),
        (~"print",                  rust!(Fn, builtin_print)),

        (~"bool?",                  rust!(Fn, builtin_is_bool)),
        (~"int?",                   rust!(Fn, builtin_is_int)),
        (~"float?",                 rust!(Fn, builtin_is_float)),
        (~"string?",                rust!(Fn, builtin_is_string)),
        (~"list?",                  rust!(Fn, builtin_is_list)),
        (~"symbol?",                rust!(Fn, builtin_is_symbol)),
        (~"fn?",                    rust!(Fn, builtin_is_fn)),
        (~"macro?",                 rust!(Fn, builtin_is_macro)),
        (~"extern-fn?",             rust!(Fn, builtin_is_extern_fn)),
        (~"extern-macro?",          rust!(Fn, builtin_is_extern_macro)),

        (~"+",                      rust!(Fn, builtin_add)),
        (~"-",                      rust!(Fn, builtin_sub)),
        (~"*",                      rust!(Fn, builtin_mul)),
        (~"/",                      rust!(Fn, builtin_div)),
        (~"%",                      rust!(Fn, builtin_rem)),
        (~"=",                      rust!(Fn, builtin_eq)),
    ].each |&(name, func)| {
        env.define(name, func);
    }
}

/// Define a new symbol in the environment
fn builtin_def(params: &[@Value], env: @mut Rusp) -> EvalResult {
    match params {
        [@Symbol(ref id), .. rest] => {
            match rest {
                [] => {
                    env.define(id.clone(), @List(~[]));
                    Ok(@List(~[]))
                }
                [ref v] => {
                    env.eval(*v).chain(|res| env.define(id.clone(), res))
                }
                _ => Err(~"`def` expects 1 or 2 arguments")
            }
        }
        _ => Err(~"first argument to `def` should be an identifier")
    }
}

/// Evaluate an S-expression in the current environment
fn builtin_eval(params: &[@Value], env: @mut Rusp) -> EvalResult {
    match params {
        [ref p] => env.eval(*p),
        _ => Err(~"`eval` expects 1 argument")
    }
}

/// Parse S-expressions from a string
fn builtin_parse(params: &[@Value], _: @mut Rusp) -> EvalResult {
    match params {
        [@Str(ref s)] => match parse(*s) {
            Ok(vals) => Ok(@List(vals)),
            Err(parser::ParseFailure { description, _  }) => Err(description)
        },
        _ => Err(~"`parse` expects 1 argument")
    }
}

/// Conditional expression
fn builtin_if(params: &[@Value], env: @mut Rusp) -> EvalResult {
    match params {
        [ref pred, ref conseq, ref alt] => {
            do env.eval(*pred).chain |val| {
                match *val {
                    Bool(true) => env.eval(*conseq),
                    Bool(false) => env.eval(*alt),
                    _ => Err(fmt!("Expected boolean expression, found: %s", val.to_str())),
                }
            }
        }
        _ => Err(~"`if` expects 3 arguments"), // TODO
    }
}

/// Evaluate each expression in turn, returning the value of the last expression
fn builtin_do(params: &[@Value], env: @mut Rusp) -> EvalResult {
    match params {
        [..fst, ref lst] => {
            for fst.each |val| {
                match env.eval(*val) {
                    Ok(@List(ref l)) if l.is_empty() => (),
                    Ok(v) => return Err(fmt!("Expected unit expression, found: %s", v.to_str())),
                    Err(e) => return Err(e),
                }
            }
            env.eval(*lst)
        }
        _ => Err(~""), // TODO
    }
}

/// Return the arguments as a list
fn builtin_list(params: &[@Value], _: @mut Rusp) -> EvalResult {
    Ok(@List(params.to_owned()))
}

/// Print to stdout
fn builtin_print(params: &[@Value], _: @mut Rusp) -> EvalResult {
    let elements = params.map(|v| v.to_str());
    println(str::connect(elements, " "));
    Ok(@List(~[]))
}

/// Shared functionality between creating functions and macros
fn fn_macro(params: &[@Value], callee_env: @mut Rusp, mode: FnMode) -> EvalResult {
    match params {
        [@List(ref symbols), ref val] => {
            let mut idents = ~[];
            for symbols.each |symbol| {
                match **symbol {
                    Symbol(ref ident) => idents.push(ident.clone()),
                    _ => return Err(fmt!("Expected symbol identifier, found: \
                                          %s", symbol.to_str())),
                }
            };
            Ok(@FnEnv(RuspFn(idents, *val, mode),
                      if mode == Fn {Some(callee_env)} else {None}))
        }
        _ => Err(fmt!("`%s` expects 2 arguments", mode.to_str())),
    }
}

/// Evaluate to an anonymous function
fn builtin_fn(params: &[@Value], callee_env: @mut Rusp) -> EvalResult {
    fn_macro(params, callee_env, Fn)
}

/// Evaluate to an anonymous macro
fn builtin_macro(params: &[@Value], callee_env: @mut Rusp) -> EvalResult {
    fn_macro(params, callee_env, Macro)
}

/// Quasiquotation, to allow substitutions inside a quoted structure, e.g.
/// `(def a 1) (quasiquote (a (unquote (list a a a))))`
/// expands to (a (1 1 1))
fn builtin_quasiquote(params: &[@Value], env: @mut Rusp) -> EvalResult {
    return match params {
        [ref p] => unquote(*p, env),
        _ => Err(~"`quasiquote` expects 1 argument")
    };

    // traverse the tree, checking for `(unquote ..)`
    fn unquote(v: @Value, env: @mut Rusp) -> EvalResult {
        match *v {
            List([@Symbol(~"unquote"), .. rest]) => {
                match rest {
                    [ref p] => env.eval(*p),
                    _ => Err(~"`unquote` expects 1 argument")
                }
            }
            List(ref vals) => {
                let mut new = ~[];

                for vals.each |v| {
                    match unquote(*v, env) {
                        Ok(v) => new.push(v),
                        Err(e) => return Err(e)
                    }
                }
                Ok(@List(new))
            }
            FnEnv(RuspFn(ref args, ref body, ref fn_type), ref subenv) => {
                match unquote(*body, subenv.get_or_default(env)) {
                    Ok(b) => Ok(@FnEnv(RuspFn(args.clone(), b, fn_type.clone()),
                                       subenv.clone())),
                    Err(e) => Err(e)
                }
            }
            _ => Ok(v)
        }
    }
}

macro_rules! type_pred(
    ($name:ident, $pattern:pat) => (
        fn $name(params: &[@Value], _: @mut Rusp) -> EvalResult {
            match do params.map |&val| {
                match *val {
                    $pattern => @Bool(true),
                    _ => @Bool(false),
                }
            } {
                [ref result] => Ok(*result),
                results => Ok(@List(results)),
            }
        }
    )
)

type_pred!(builtin_is_bool, Bool(*))
type_pred!(builtin_is_int, Int(*))
type_pred!(builtin_is_float, Float(*))
type_pred!(builtin_is_string, Str(*))
type_pred!(builtin_is_list, List(*))
type_pred!(builtin_is_symbol, Symbol(*))
type_pred!(builtin_is_fn, FnEnv(RuspFn(_, _, Fn), _))
type_pred!(builtin_is_macro, FnEnv(RuspFn(_, _, Macro), _))
type_pred!(builtin_is_extern_fn, FnEnv(ExternFn(_, Fn), _))
type_pred!(builtin_is_extern_macro, FnEnv(ExternFn(_, Macro), _))

macro_rules! arithmetic_op(
    ($name:ident, $symbol:expr, $method:ident) => (
        fn $name(params: &[@Value], _: @mut Rusp) -> EvalResult {
            if params.len() < 2 {
                return Err(fmt!("`%s` expects at least 2 arguments", $symbol));
            }
            let mut result = *params.head();
            for params.tail().each |&val| {
                match (result, val) {
                    // Basic operations between values of the same type
                    (@Int(prev), @Int(i)) => result = @Int(prev.$method(&i)),
                    (@Float(prev), @Float(f)) => result = @Float(prev.$method(&f)),
                    // Handle numeric promotions
                    (@Int(prev), @Float(f)) => result = @Float((prev as float).$method(&f)),
                    (@Float(prev), @Int(i)) => result = @Float(prev.$method(&(i as float))),
                    // Type errors
                    (@Float(_), ref v) => return Err(fmt!("Expected float but found: %s", v.to_str())),
                    (@Int(_), ref v) => return Err(fmt!("Expected integer but found: %s", v.to_str())),
                    (ref prev, _) => return Err(fmt!("Expected number but found: %s", prev.to_str())),
                }
            }
            Ok(result)
        }
    )
)

arithmetic_op!(builtin_add, "+", add)
arithmetic_op!(builtin_sub, "-", sub)
arithmetic_op!(builtin_mul, "*", mul)
arithmetic_op!(builtin_div, "/", div)
arithmetic_op!(builtin_rem, "%", rem)

/// Test equality
fn builtin_eq(params: &[@Value], _: @mut Rusp) -> EvalResult {
    match params {
        [head, .. rest] => {
            for rest.each |&val| {
                // TODO handle numeric promotions? i.e. 1.0 == 1?
                if !(val == head) { return Ok(@Bool(false)); }
            }
            Ok(@Bool(true))
        }
        [] => Err(fmt!("`=` expects at least 1 argument"))
    }
}

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

    macro_rules! test_ok(
        ($src:expr, $expected:expr) => (
            assert_eq!(Rusp::new().eval(
                @Parser::new($src).parse().get()
            ).get().to_str(), $expected);
        )
    )

    macro_rules! test_err(
        ($src:expr) => (
            assert!(Rusp::new().eval(
                @Parser::new($src).parse().get()
            ).is_err());
        )
    )

    #[test]
    fn test_type_preds() {
        test_ok!("(bool? true)", ~"true");
        test_ok!("(bool? 1)", ~"false");
        test_ok!("(int? 1)", ~"true");
        test_ok!("(int? 1.0)", ~"false");
        test_ok!("(float? 1.0)", ~"true");
        test_ok!("(float? \"hi\")", ~"false");
        test_ok!("(string? \"hi\")", ~"true");
        test_ok!("(string? (list 1 2))", ~"false");
        test_ok!("(list? (list 1 2))", ~"true");
        test_ok!("(list? (list))", ~"true");
        test_ok!("(list? 1.0)", ~"false");
        test_ok!("(symbol? (quote x))", ~"true");
        test_ok!("(symbol? (fn () 1))", ~"false");
        test_ok!("(fn? (fn () 1))", ~"true");
        test_ok!("(fn? (macro () 1))", ~"false");
        test_ok!("(macro? (macro () 1))", ~"true");
        test_ok!("(macro? false)", ~"false");
    }

    #[test]
    fn test_arithmetic() {
        test_ok!("(+ 1 2)", ~"3");
        test_ok!("(+ 1 2 4)", ~"7");
        test_ok!("(+ 1.5 3 4)", ~"8.5");
        test_ok!("(+ 3 1.67)", ~"4.67");
        test_ok!("(- 3 0.5)", ~"2.5");
        test_ok!("(- 3 1.5 (+ 2 4))", ~"-4.5");

        test_err!("(+)");
        test_err!("(-)");
        test_err!("(*)");
        test_err!("(/)");
        test_err!("(%)");

        test_err!("(+ 1)");
        test_err!("(- 1)");
        test_err!("(* 1)");
        test_err!("(/ 1)");
        test_err!("(% 1)");

        test_err!("(+ 1 \"hi\")");
        test_err!("(- 1 \"hi\")");
        test_err!("(* 1 \"hi\")");
        test_err!("(/ 1 \"hi\")");
        test_err!("(% 1 \"hi\")");

        test_err!("(+ \"hi\" 1)");
        test_err!("(- \"hi\" 1)");
        test_err!("(* \"hi\" 1)");
        test_err!("(/ \"hi\" 1)");
        test_err!("(% \"hi\" 1)");
    }

    #[test]
    fn test_eval() {
        test_ok!("(eval 1)", ~"1");
        test_ok!("(eval (quote (+ 1 2)))", ~"3");

        test_ok!("((fn (a) (eval (quote a))) 1)", ~"1");
    }
}<|endoftext|>


<commit_before>use crate::options::{NumfmtOptions, RoundMethod};
use crate::units::{DisplayableSuffix, RawSuffix, Result, Suffix, Unit, IEC_BASES, SI_BASES};

/// Iterate over a line's fields, where each field is a contiguous sequence of
/// non-whitespace, optionally prefixed with one or more characters of leading
/// whitespace. Fields are returned as tuples of `(prefix, field)`.
///
/// # Examples:
///
/// ```
/// let mut fields = uu_numfmt::format::WhitespaceSplitter { s: Some("    1234 5") };
///
/// assert_eq!(Some(("    ", "1234")), fields.next());
/// assert_eq!(Some((" ", "5")), fields.next());
/// assert_eq!(None, fields.next());
/// ```
///
/// Delimiters are included in the results; `prefix` will be empty only for
/// the first field of the line (including the case where the input line is
/// empty):
///
/// ```
/// let mut fields = uu_numfmt::format::WhitespaceSplitter { s: Some("first second") };
///
/// assert_eq!(Some(("", "first")), fields.next());
/// assert_eq!(Some((" ", "second")), fields.next());
///
/// let mut fields = uu_numfmt::format::WhitespaceSplitter { s: Some("") };
///
/// assert_eq!(Some(("", "")), fields.next());
/// ```
pub struct WhitespaceSplitter<'a> {
    pub s: Option<&'a str>,
}

impl<'a> Iterator for WhitespaceSplitter<'a> {
    type Item = (&'a str, &'a str);

    /// Yield the next field in the input string as a tuple `(prefix, field)`.
    fn next(&mut self) -> Option<Self::Item> {
        let haystack = self.s?;

        let (prefix, field) = haystack.split_at(
            haystack
                .find(|c: char| !c.is_whitespace())
                .unwrap_or_else(|| haystack.len()),
        );

        let (field, rest) = field.split_at(
            field
                .find(|c: char| c.is_whitespace())
                .unwrap_or_else(|| field.len()),
        );

        self.s = if !rest.is_empty() { Some(rest) } else { None };

        Some((prefix, field))
    }
}

fn parse_suffix(s: &str) -> Result<(f64, Option<Suffix>)> {
    if s.is_empty() {
        return Err("invalid number: ''".to_string());
    }

    let with_i = s.ends_with('i');
    let mut iter = s.chars();
    if with_i {
        iter.next_back();
    }
    let suffix = match iter.next_back() {
        Some('K') => Some((RawSuffix::K, with_i)),
        Some('M') => Some((RawSuffix::M, with_i)),
        Some('G') => Some((RawSuffix::G, with_i)),
        Some('T') => Some((RawSuffix::T, with_i)),
        Some('P') => Some((RawSuffix::P, with_i)),
        Some('E') => Some((RawSuffix::E, with_i)),
        Some('Z') => Some((RawSuffix::Z, with_i)),
        Some('Y') => Some((RawSuffix::Y, with_i)),
        Some('0'..='9') => None,
        _ => return Err(format!("invalid suffix in input: '{}'", s)),
    };

    let suffix_len = match suffix {
        None => 0,
        Some((_, false)) => 1,
        Some((_, true)) => 2,
    };

    let number = s[..s.len() - suffix_len]
        .parse::<f64>()
        .map_err(|_| format!("invalid number: '{}'", s))?;

    Ok((number, suffix))
}

fn remove_suffix(i: f64, s: Option<Suffix>, u: &Unit) -> Result<f64> {
    match (s, u) {
        (None, _) => Ok(i),
        (Some((raw_suffix, false)), &Unit::Auto) | (Some((raw_suffix, false)), &Unit::Si) => {
            match raw_suffix {
                RawSuffix::K => Ok(i * 1e3),
                RawSuffix::M => Ok(i * 1e6),
                RawSuffix::G => Ok(i * 1e9),
                RawSuffix::T => Ok(i * 1e12),
                RawSuffix::P => Ok(i * 1e15),
                RawSuffix::E => Ok(i * 1e18),
                RawSuffix::Z => Ok(i * 1e21),
                RawSuffix::Y => Ok(i * 1e24),
            }
        }
        (Some((raw_suffix, false)), &Unit::Iec(false))
        | (Some((raw_suffix, true)), &Unit::Auto)
        | (Some((raw_suffix, true)), &Unit::Iec(true)) => match raw_suffix {
            RawSuffix::K => Ok(i * IEC_BASES[1]),
            RawSuffix::M => Ok(i * IEC_BASES[2]),
            RawSuffix::G => Ok(i * IEC_BASES[3]),
            RawSuffix::T => Ok(i * IEC_BASES[4]),
            RawSuffix::P => Ok(i * IEC_BASES[5]),
            RawSuffix::E => Ok(i * IEC_BASES[6]),
            RawSuffix::Z => Ok(i * IEC_BASES[7]),
            RawSuffix::Y => Ok(i * IEC_BASES[8]),
        },
        (_, _) => Err("This suffix is unsupported for specified unit".to_owned()),
    }
}

fn transform_from(s: &str, opts: &Unit) -> Result<f64> {
    let (i, suffix) = parse_suffix(s)?;

    remove_suffix(i, suffix, opts).map(|n| if n < 0.0 { -n.abs().ceil() } else { n.ceil() })
}

/// Divide numerator by denominator, with rounding.
///
/// If the result of the division is less than 10.0, round to one decimal point.
///
/// Otherwise, round to an integer.
///
/// # Examples:
///
/// ```
/// use uu_numfmt::format::div_round;
/// use uu_numfmt::options::RoundMethod;
///
/// // Rounding methods:
/// assert_eq!(div_round(1.01, 1.0, RoundMethod::FromZero), 1.1);
/// assert_eq!(div_round(1.01, 1.0, RoundMethod::TowardsZero), 1.0);
/// assert_eq!(div_round(1.01, 1.0, RoundMethod::Up), 1.1);
/// assert_eq!(div_round(1.01, 1.0, RoundMethod::Down), 1.0);
/// assert_eq!(div_round(1.01, 1.0, RoundMethod::Nearest), 1.0);
///
/// // Division:
/// assert_eq!(div_round(999.1, 1000.0, RoundMethod::FromZero), 1.0);
/// assert_eq!(div_round(1001., 10., RoundMethod::FromZero), 101.);
/// assert_eq!(div_round(9991., 10., RoundMethod::FromZero), 1000.);
/// assert_eq!(div_round(-12.34, 1.0, RoundMethod::FromZero), -13.0);
/// assert_eq!(div_round(1000.0, -3.14, RoundMethod::FromZero), -319.0);
/// assert_eq!(div_round(-271828.0, -271.0, RoundMethod::FromZero), 1004.0);
/// ```
pub fn div_round(n: f64, d: f64, method: RoundMethod) -> f64 {
    let v = n / d;

    if v.abs() < 10.0 {
        method.round(10.0 * v) / 10.0
    } else {
        method.round(v)
    }
}

fn consider_suffix(n: f64, u: &Unit, round_method: RoundMethod) -> Result<(f64, Option<Suffix>)> {
    use crate::units::RawSuffix::*;

    let abs_n = n.abs();
    let suffixes = [K, M, G, T, P, E, Z, Y];

    let (bases, with_i) = match *u {
        Unit::Si => (&SI_BASES, false),
        Unit::Iec(with_i) => (&IEC_BASES, with_i),
        Unit::Auto => return Err("Unit 'auto' isn't supported with --to options".to_owned()),
        Unit::None => return Ok((n, None)),
    };

    let i = match abs_n {
        _ if abs_n <= bases[1] - 1.0 => return Ok((n, None)),
        _ if abs_n < bases[2] => 1,
        _ if abs_n < bases[3] => 2,
        _ if abs_n < bases[4] => 3,
        _ if abs_n < bases[5] => 4,
        _ if abs_n < bases[6] => 5,
        _ if abs_n < bases[7] => 6,
        _ if abs_n < bases[8] => 7,
        _ if abs_n < bases[9] => 8,
        _ => return Err("Number is too big and unsupported".to_string()),
    };

    let v = div_round(n, bases[i], round_method);

    // check if rounding pushed us into the next base
    if v.abs() >= bases[1] {
        Ok((v / bases[1], Some((suffixes[i], with_i))))
    } else {
        Ok((v, Some((suffixes[i - 1], with_i))))
    }
}

fn transform_to(s: f64, opts: &Unit, round_method: RoundMethod) -> Result<String> {
    let (i2, s) = consider_suffix(s, opts, round_method)?;
    Ok(match s {
        None => format!("{}", i2),
        Some(s) if i2.abs() < 10.0 => format!("{:.1}{}", i2, DisplayableSuffix(s)),
        Some(s) => format!("{:.0}{}", i2, DisplayableSuffix(s)),
    })
}

fn format_string(
    source: &str,
    options: &NumfmtOptions,
    implicit_padding: Option<isize>,
) -> Result<String> {
    let number = transform_to(
        transform_from(source, &options.transform.from)?,
        &options.transform.to,
        options.round,
    )?;

    Ok(match implicit_padding.unwrap_or(options.padding) {
        0 => number,
        p if p > 0 => format!("{:>padding$}", number, padding = p as usize),
        p => format!("{:<padding$}", number, padding = p.abs() as usize),
    })
}

fn format_and_print_delimited(s: &str, options: &NumfmtOptions) -> Result<()> {
    let delimiter = options.delimiter.as_ref().unwrap();

    for (n, field) in (1..).zip(s.split(delimiter)) {
        let field_selected = uucore::ranges::contain(&options.fields, n);

        // print delimiter before second and subsequent fields
        if n > 1 {
            print!("{}", delimiter);
        }

        if field_selected {
            print!("{}", format_string(field.trim_start(), options, None)?);
        } else {
            // print unselected field without conversion
            print!("{}", field);
        }
    }

    println!();

    Ok(())
}

fn format_and_print_whitespace(s: &str, options: &NumfmtOptions) -> Result<()> {
    for (n, (prefix, field)) in (1..).zip(WhitespaceSplitter { s: Some(s) }) {
        let field_selected = uucore::ranges::contain(&options.fields, n);

        if field_selected {
            let empty_prefix = prefix.is_empty();

            // print delimiter before second and subsequent fields
            let prefix = if n > 1 {
                print!(" ");
                &prefix[1..]
            } else {
                &prefix
            };

            let implicit_padding = if !empty_prefix && options.padding == 0 {
                Some((prefix.len() + field.len()) as isize)
            } else {
                None
            };

            print!("{}", format_string(field, options, implicit_padding)?);
        } else {
            // print unselected field without conversion
            print!("{}{}", prefix, field);
        }
    }

    println!();

    Ok(())
}

/// Format a line of text according to the selected options.
///
/// Given a line of text `s`, split the line into fields, transform and format
/// any selected numeric fields, and print the result to stdout. Fields not
/// selected for conversion are passed through unmodified.
pub fn format_and_print(s: &str, options: &NumfmtOptions) -> Result<()> {
    match &options.delimiter {
        Some(_) => format_and_print_delimited(s, options),
        None => format_and_print_whitespace(s, options),
    }
}
<commit_msg>format: formatting<commit_after>use crate::options::{NumfmtOptions, RoundMethod};
use crate::units::{DisplayableSuffix, RawSuffix, Result, Suffix, Unit, IEC_BASES, SI_BASES};

/// Iterate over a line's fields, where each field is a contiguous sequence of
/// non-whitespace, optionally prefixed with one or more characters of leading
/// whitespace. Fields are returned as tuples of `(prefix, field)`.
///
/// # Examples:
///
/// ```
/// let mut fields = uu_numfmt::format::WhitespaceSplitter { s: Some("    1234 5") };
///
/// assert_eq!(Some(("    ", "1234")), fields.next());
/// assert_eq!(Some((" ", "5")), fields.next());
/// assert_eq!(None, fields.next());
/// ```
///
/// Delimiters are included in the results; `prefix` will be empty only for
/// the first field of the line (including the case where the input line is
/// empty):
///
/// ```
/// let mut fields = uu_numfmt::format::WhitespaceSplitter { s: Some("first second") };
///
/// assert_eq!(Some(("", "first")), fields.next());
/// assert_eq!(Some((" ", "second")), fields.next());
///
/// let mut fields = uu_numfmt::format::WhitespaceSplitter { s: Some("") };
///
/// assert_eq!(Some(("", "")), fields.next());
/// ```
pub struct WhitespaceSplitter<'a> {
    pub s: Option<&'a str>,
}

impl<'a> Iterator for WhitespaceSplitter<'a> {
    type Item = (&'a str, &'a str);

    /// Yield the next field in the input string as a tuple `(prefix, field)`.
    fn next(&mut self) -> Option<Self::Item> {
        let haystack = self.s?;

        let (prefix, field) = haystack.split_at(
            haystack
                .find(|c: char| !c.is_whitespace())
                .unwrap_or_else(|| haystack.len()),
        );

        let (field, rest) = field.split_at(
            field
                .find(|c: char| c.is_whitespace())
                .unwrap_or_else(|| field.len()),
        );

        self.s = if !rest.is_empty() { Some(rest) } else { None };

        Some((prefix, field))
    }
}

fn parse_suffix(s: &str) -> Result<(f64, Option<Suffix>)> {
    if s.is_empty() {
        return Err("invalid number: ''".to_string());
    }

    let with_i = s.ends_with('i');
    let mut iter = s.chars();
    if with_i {
        iter.next_back();
    }
    let suffix = match iter.next_back() {
        Some('K') => Some((RawSuffix::K, with_i)),
        Some('M') => Some((RawSuffix::M, with_i)),
        Some('G') => Some((RawSuffix::G, with_i)),
        Some('T') => Some((RawSuffix::T, with_i)),
        Some('P') => Some((RawSuffix::P, with_i)),
        Some('E') => Some((RawSuffix::E, with_i)),
        Some('Z') => Some((RawSuffix::Z, with_i)),
        Some('Y') => Some((RawSuffix::Y, with_i)),
        Some('0'..='9') => None,
        _ => return Err(format!("invalid suffix in input: '{}'", s)),
    };

    let suffix_len = match suffix {
        None => 0,
        Some((_, false)) => 1,
        Some((_, true)) => 2,
    };

    let number = s[..s.len() - suffix_len]
        .parse::<f64>()
        .map_err(|_| format!("invalid number: '{}'", s))?;

    Ok((number, suffix))
}

fn remove_suffix(i: f64, s: Option<Suffix>, u: &Unit) -> Result<f64> {
    match (s, u) {
        (None, _) => Ok(i),
        (Some((raw_suffix, false)), &Unit::Auto) | (Some((raw_suffix, false)), &Unit::Si) => {
            match raw_suffix {
                RawSuffix::K => Ok(i * 1e3),
                RawSuffix::M => Ok(i * 1e6),
                RawSuffix::G => Ok(i * 1e9),
                RawSuffix::T => Ok(i * 1e12),
                RawSuffix::P => Ok(i * 1e15),
                RawSuffix::E => Ok(i * 1e18),
                RawSuffix::Z => Ok(i * 1e21),
                RawSuffix::Y => Ok(i * 1e24),
            }
        }
        (Some((raw_suffix, false)), &Unit::Iec(false))
        | (Some((raw_suffix, true)), &Unit::Auto)
        | (Some((raw_suffix, true)), &Unit::Iec(true)) => match raw_suffix {
            RawSuffix::K => Ok(i * IEC_BASES[1]),
            RawSuffix::M => Ok(i * IEC_BASES[2]),
            RawSuffix::G => Ok(i * IEC_BASES[3]),
            RawSuffix::T => Ok(i * IEC_BASES[4]),
            RawSuffix::P => Ok(i * IEC_BASES[5]),
            RawSuffix::E => Ok(i * IEC_BASES[6]),
            RawSuffix::Z => Ok(i * IEC_BASES[7]),
            RawSuffix::Y => Ok(i * IEC_BASES[8]),
        },
        (_, _) => Err("This suffix is unsupported for specified unit".to_owned()),
    }
}

fn transform_from(s: &str, opts: &Unit) -> Result<f64> {
    let (i, suffix) = parse_suffix(s)?;

    remove_suffix(i, suffix, opts).map(|n| if n < 0.0 { -n.abs().ceil() } else { n.ceil() })
}

/// Divide numerator by denominator, with rounding.
///
/// If the result of the division is less than 10.0, round to one decimal point.
///
/// Otherwise, round to an integer.
///
/// # Examples:
///
/// ```
/// use uu_numfmt::format::div_round;
/// use uu_numfmt::options::RoundMethod;
///
/// // Rounding methods:
/// assert_eq!(div_round(1.01, 1.0, RoundMethod::FromZero), 1.1);
/// assert_eq!(div_round(1.01, 1.0, RoundMethod::TowardsZero), 1.0);
/// assert_eq!(div_round(1.01, 1.0, RoundMethod::Up), 1.1);
/// assert_eq!(div_round(1.01, 1.0, RoundMethod::Down), 1.0);
/// assert_eq!(div_round(1.01, 1.0, RoundMethod::Nearest), 1.0);
///
/// // Division:
/// assert_eq!(div_round(999.1, 1000.0, RoundMethod::FromZero), 1.0);
/// assert_eq!(div_round(1001., 10., RoundMethod::FromZero), 101.);
/// assert_eq!(div_round(9991., 10., RoundMethod::FromZero), 1000.);
/// assert_eq!(div_round(-12.34, 1.0, RoundMethod::FromZero), -13.0);
/// assert_eq!(div_round(1000.0, -3.14, RoundMethod::FromZero), -319.0);
/// assert_eq!(div_round(-271828.0, -271.0, RoundMethod::FromZero), 1004.0);
/// ```
pub fn div_round(n: f64, d: f64, method: RoundMethod) -> f64 {
    let v = n / d;

    if v.abs() < 10.0 {
        method.round(10.0 * v) / 10.0
    } else {
        method.round(v)
    }
}

fn consider_suffix(n: f64, u: &Unit, round_method: RoundMethod) -> Result<(f64, Option<Suffix>)> {
    use crate::units::RawSuffix::*;

    let abs_n = n.abs();
    let suffixes = [K, M, G, T, P, E, Z, Y];

    let (bases, with_i) = match *u {
        Unit::Si => (&SI_BASES, false),
        Unit::Iec(with_i) => (&IEC_BASES, with_i),
        Unit::Auto => return Err("Unit 'auto' isn't supported with --to options".to_owned()),
        Unit::None => return Ok((n, None)),
    };

    let i = match abs_n {
        _ if abs_n <= bases[1] - 1.0 => return Ok((n, None)),
        _ if abs_n < bases[2] => 1,
        _ if abs_n < bases[3] => 2,
        _ if abs_n < bases[4] => 3,
        _ if abs_n < bases[5] => 4,
        _ if abs_n < bases[6] => 5,
        _ if abs_n < bases[7] => 6,
        _ if abs_n < bases[8] => 7,
        _ if abs_n < bases[9] => 8,
        _ => return Err("Number is too big and unsupported".to_string()),
    };

    let v = div_round(n, bases[i], round_method);

    // check if rounding pushed us into the next base
    if v.abs() >= bases[1] {
        Ok((v / bases[1], Some((suffixes[i], with_i))))
    } else {
        Ok((v, Some((suffixes[i - 1], with_i))))
    }
}

fn transform_to(s: f64, opts: &Unit, round_method: RoundMethod) -> Result<String> {
    let (i2, s) = consider_suffix(s, opts, round_method)?;
    Ok(match s {
        None => format!("{}", i2),
        Some(s) if i2.abs() < 10.0 => format!("{:.1}{}", i2, DisplayableSuffix(s)),
        Some(s) => format!("{:.0}{}", i2, DisplayableSuffix(s)),
    })
}

fn format_string(
    source: &str,
    options: &NumfmtOptions,
    implicit_padding: Option<isize>,
) -> Result<String> {
    let number = transform_to(
        transform_from(source, &options.transform.from)?,
        &options.transform.to,
        options.round,
    )?;

    Ok(match implicit_padding.unwrap_or(options.padding) {
        0 => number,
        p if p > 0 => format!("{:>padding$}", number, padding = p as usize),
        p => format!("{:<padding$}", number, padding = p.abs() as usize),
    })
}

fn format_and_print_delimited(s: &str, options: &NumfmtOptions) -> Result<()> {
    let delimiter = options.delimiter.as_ref().unwrap();

    for (n, field) in (1..).zip(s.split(delimiter)) {
        let field_selected = uucore::ranges::contain(&options.fields, n);

        // print delimiter before second and subsequent fields
        if n > 1 {
            print!("{}", delimiter);
        }

        if field_selected {
            print!("{}", format_string(field.trim_start(), options, None)?);
        } else {
            // print unselected field without conversion
            print!("{}", field);
        }
    }

    println!();

    Ok(())
}

fn format_and_print_whitespace(s: &str, options: &NumfmtOptions) -> Result<()> {
    for (n, (prefix, field)) in (1..).zip(WhitespaceSplitter { s: Some(s) }) {
        let field_selected = uucore::ranges::contain(&options.fields, n);

        if field_selected {
            let empty_prefix = prefix.is_empty();

            // print delimiter before second and subsequent fields
            let prefix = if n > 1 {
                print!(" ");
                &prefix[1..]
            } else {
                &prefix
            };

            let implicit_padding = if !empty_prefix && options.padding == 0 {
                Some((prefix.len() + field.len()) as isize)
            } else {
                None
            };

            print!("{}", format_string(field, options, implicit_padding)?);
        } else {
            // print unselected field without conversion
            print!("{}{}", prefix, field);
        }
    }

    println!();

    Ok(())
}

/// Format a line of text according to the selected options.
///
/// Given a line of text `s`, split the line into fields, transform and format
/// any selected numeric fields, and print the result to stdout. Fields not
/// selected for conversion are passed through unmodified.
pub fn format_and_print(s: &str, options: &NumfmtOptions) -> Result<()> {
    match &options.delimiter {
        Some(_) => format_and_print_delimited(s, options),
        None => format_and_print_whitespace(s, options),
    }
}
<|endoftext|>


<commit_before>extern crate mqtt;
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate clap;
extern crate uuid;

use std::net::TcpStream;
use std::io::{self, Write};
use std::thread;

use clap::{App, Arg};

use uuid::Uuid;

use mqtt::{Encodable, Decodable, QualityOfService};
use mqtt::packet::*;
use mqtt::control::variable_header::ConnectReturnCode;
use mqtt::{TopicFilter, TopicName};

fn generate_client_id() -> String {
    format!("/MQTT/rust/{}", Uuid::new_v4().to_simple_string())
}

fn main() {
    env_logger::init().unwrap();

    let matches = App::new("sub-client")
                      .author("Y. T. Chung <zonyitoo@gmail.com>")
                      .arg(Arg::with_name("SERVER")
                               .short("S")
                               .long("server")
                               .takes_value(true)
                               .required(true)
                               .help("MQTT server address (host:port)"))
                      .arg(Arg::with_name("SUBSCRIBE")
                               .short("s")
                               .long("subscribe")
                               .takes_value(true)
                               .multiple(true)
                               .required(true)
                               .help("Channel filter to subscribe"))
                      .arg(Arg::with_name("USER_NAME")
                               .short("u")
                               .long("username")
                               .takes_value(true)
                               .help("Login user name"))
                      .arg(Arg::with_name("PASSWORD")
                               .short("p")
                               .long("password")
                               .takes_value(true)
                               .help("Password"))
                      .arg(Arg::with_name("CLIENT_ID")
                               .short("i")
                               .long("client-identifier")
                               .takes_value(true)
                               .help("Client identifier"))
                      .get_matches();

    let server_addr = matches.value_of("SERVER").unwrap();
    let client_id = matches.value_of("CLIENT_ID")
                           .map(|x| x.to_owned())
                           .unwrap_or_else(generate_client_id);
    let channel_filters: Vec<(TopicFilter, QualityOfService)> =
        matches.values_of("SUBSCRIBE")
               .unwrap()
               .iter()
               .map(|c| {
                   (TopicFilter::new_checked(c.to_string()).unwrap(),
                    QualityOfService::Level0)
               })
               .collect();

    print!("Connecting to {:?} ... ", server_addr);
    let mut stream = TcpStream::connect(server_addr).unwrap();
    println!("Connected!");

    println!("Client identifier {:?}", client_id);
    let mut conn = ConnectPacket::new("MQTT".to_owned(), client_id.to_owned());
    conn.set_clean_session(true);
    let mut buf = Vec::new();
    conn.encode(&mut buf).unwrap();
    stream.write_all(&buf[..]).unwrap();

    let connack = ConnackPacket::decode(&mut stream).unwrap();
    trace!("CONNACK {:?}", connack);

    if connack.connect_return_code() != ConnectReturnCode::ConnectionAccepted {
        panic!("Failed to connect to server, return code {:?}",
               connack.connect_return_code());
    }

    println!("Applying channel filters {:?} ...", channel_filters);
    let sub = SubscribePacket::new(10, channel_filters);
    let mut buf = Vec::new();
    sub.encode(&mut buf).unwrap();
    stream.write_all(&buf[..]).unwrap();

    let channels: Vec<TopicName> = matches.values_of("SUBSCRIBE")
                                          .unwrap()
                                          .iter()
                                          .map(|c| TopicName::new(c.to_string()).unwrap())
                                          .collect();

    let user_name = matches.value_of("USER_NAME").unwrap_or("<anonym>");

    let mut cloned_stream = stream.try_clone().unwrap();
    thread::spawn(move || {
        loop {
            let packet = match VariablePacket::decode(&mut cloned_stream) {
                Ok(pk) => pk,
                Err(err) => {
                    error!("Error in receiving packet {:?}", err);
                    continue;
                }
            };
            trace!("PACKET {:?}", packet);

            match packet {
                VariablePacket::PingreqPacket(..) => {
                    let pingresp = PingrespPacket::new();
                    info!("Sending Ping response {:?}", pingresp);
                    pingresp.encode(&mut cloned_stream).unwrap();
                }
                VariablePacket::DisconnectPacket(..) => {
                    break;
                }
                _ => {
                    // Ignore other packets in pub client
                }
            }
        }
    });

    let stdin = io::stdin();
    loop {
        print!("{}: ", user_name);
        io::stdout().flush().unwrap();

        let mut line = String::new();
        stdin.read_line(&mut line).unwrap();

        if line.trim_right() == "" { continue }

        let message = format!("{}: {}", user_name, line.trim_right());

        for chan in channels.iter() {
            let publish_packet = PublishPacket::new(chan.clone(),
                                                    QoSWithPacketIdentifier::Level0,
                                                    message.as_bytes().to_vec());
            let mut buf = Vec::new();
            publish_packet.encode(&mut buf).unwrap();
            stream.write_all(&buf[..]).unwrap();
        }
    }
}
<commit_msg>Just use the reference instead of calling .iter()<commit_after>extern crate mqtt;
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate clap;
extern crate uuid;

use std::net::TcpStream;
use std::io::{self, Write};
use std::thread;

use clap::{App, Arg};

use uuid::Uuid;

use mqtt::{Encodable, Decodable, QualityOfService};
use mqtt::packet::*;
use mqtt::control::variable_header::ConnectReturnCode;
use mqtt::{TopicFilter, TopicName};

fn generate_client_id() -> String {
    format!("/MQTT/rust/{}", Uuid::new_v4().to_simple_string())
}

fn main() {
    env_logger::init().unwrap();

    let matches = App::new("sub-client")
                      .author("Y. T. Chung <zonyitoo@gmail.com>")
                      .arg(Arg::with_name("SERVER")
                               .short("S")
                               .long("server")
                               .takes_value(true)
                               .required(true)
                               .help("MQTT server address (host:port)"))
                      .arg(Arg::with_name("SUBSCRIBE")
                               .short("s")
                               .long("subscribe")
                               .takes_value(true)
                               .multiple(true)
                               .required(true)
                               .help("Channel filter to subscribe"))
                      .arg(Arg::with_name("USER_NAME")
                               .short("u")
                               .long("username")
                               .takes_value(true)
                               .help("Login user name"))
                      .arg(Arg::with_name("PASSWORD")
                               .short("p")
                               .long("password")
                               .takes_value(true)
                               .help("Password"))
                      .arg(Arg::with_name("CLIENT_ID")
                               .short("i")
                               .long("client-identifier")
                               .takes_value(true)
                               .help("Client identifier"))
                      .get_matches();

    let server_addr = matches.value_of("SERVER").unwrap();
    let client_id = matches.value_of("CLIENT_ID")
                           .map(|x| x.to_owned())
                           .unwrap_or_else(generate_client_id);
    let channel_filters: Vec<(TopicFilter, QualityOfService)> =
        matches.values_of("SUBSCRIBE")
               .unwrap()
               .iter()
               .map(|c| {
                   (TopicFilter::new_checked(c.to_string()).unwrap(),
                    QualityOfService::Level0)
               })
               .collect();

    print!("Connecting to {:?} ... ", server_addr);
    let mut stream = TcpStream::connect(server_addr).unwrap();
    println!("Connected!");

    println!("Client identifier {:?}", client_id);
    let mut conn = ConnectPacket::new("MQTT".to_owned(), client_id.to_owned());
    conn.set_clean_session(true);
    let mut buf = Vec::new();
    conn.encode(&mut buf).unwrap();
    stream.write_all(&buf[..]).unwrap();

    let connack = ConnackPacket::decode(&mut stream).unwrap();
    trace!("CONNACK {:?}", connack);

    if connack.connect_return_code() != ConnectReturnCode::ConnectionAccepted {
        panic!("Failed to connect to server, return code {:?}",
               connack.connect_return_code());
    }

    println!("Applying channel filters {:?} ...", channel_filters);
    let sub = SubscribePacket::new(10, channel_filters);
    let mut buf = Vec::new();
    sub.encode(&mut buf).unwrap();
    stream.write_all(&buf[..]).unwrap();

    let channels: Vec<TopicName> = matches.values_of("SUBSCRIBE")
                                          .unwrap()
                                          .iter()
                                          .map(|c| TopicName::new(c.to_string()).unwrap())
                                          .collect();

    let user_name = matches.value_of("USER_NAME").unwrap_or("<anonym>");

    let mut cloned_stream = stream.try_clone().unwrap();
    thread::spawn(move || {
        loop {
            let packet = match VariablePacket::decode(&mut cloned_stream) {
                Ok(pk) => pk,
                Err(err) => {
                    error!("Error in receiving packet {:?}", err);
                    continue;
                }
            };
            trace!("PACKET {:?}", packet);

            match packet {
                VariablePacket::PingreqPacket(..) => {
                    let pingresp = PingrespPacket::new();
                    info!("Sending Ping response {:?}", pingresp);
                    pingresp.encode(&mut cloned_stream).unwrap();
                }
                VariablePacket::DisconnectPacket(..) => {
                    break;
                }
                _ => {
                    // Ignore other packets in pub client
                }
            }
        }
    });

    let stdin = io::stdin();
    loop {
        print!("{}: ", user_name);
        io::stdout().flush().unwrap();

        let mut line = String::new();
        stdin.read_line(&mut line).unwrap();

        if line.trim_right() == "" { continue }

        let message = format!("{}: {}", user_name, line.trim_right());

        for chan in &channels {
            let publish_packet = PublishPacket::new(chan.clone(),
                                                    QoSWithPacketIdentifier::Level0,
                                                    message.as_bytes().to_vec());
            let mut buf = Vec::new();
            publish_packet.encode(&mut buf).unwrap();
            stream.write_all(&buf[..]).unwrap();
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2016  ParadoxSpiral
//
// This file is part of mpv-rs.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

extern crate crossbeam;

use mpv::*;

use std::process;
use std::path::Path;
use std::time::Duration;
use std::thread;

pub fn exec() {
    let mpv = Parent::new(true).unwrap();
    mpv.set_option(&mut Property::new("cache-initial", Data::new(1))).unwrap();
    mpv.set_option(&mut Property::new("volume", Data::new(20))).unwrap();
    mpv.set_option(&mut Property::new("no-video", Data::new(true))).unwrap();
    mpv.set_option(&mut Property::new("ytdl", Data::new(true))).unwrap();
    mpv.init().unwrap();

    crossbeam::scope(|scope| {
        scope.spawn(|| {
            let iter = mpv.observe_all(&[Event::FileLoaded,
                                         Event::StartFile,
                                         Event::Seek,
                                         Event::PlaybackRestart,
                                         Event::EndFile(None)])
                          .unwrap();

            for elem in iter {
                match elem {
                    Err(Error::NoAssociatedEvent) => {}
                    Err(v) => panic!("unexpected error: {:?}", v),
                    Ok(vec) => {
                        if let Some(&Ok(Event::EndFile(ref v))) = vec.iter().find(|e| {
                            if e.is_ok() {
                                if let &Event::EndFile(_) = e.as_ref().unwrap() {
                                    return true;
                                }
                            }
                            false
                        }) {
                            println!("File ended! Reason: {:?}", v);
                            process::exit(0);
                        } else {
                            println!("1: {:?}", vec);
                        };
                    }
                }
            }
        });
        scope.spawn(|| {
            let iter = mpv.observe_all(&[Event::PropertyChange(Property::new("volume",
                                                                             Data::new(0))),
                                         Event::PropertyChange(Property::new("pause",
                                                                             Data::new(false)))])
                          .unwrap();

            for elem in iter {
                match elem {
                    Err(Error::NoAssociatedEvent) => {}
                    Err(v) => panic!("unexpected error: {:?}", v),
                    Ok(v) => println!("2: {:?}", v),
                }
            }
        });

        mpv.playlist(&PlaylistOp::Loadfiles(&[File::new(Path::new("https://www.youtube.\
                                                                   com/watch?v=DLzxrzFCyOs"),
                                                        FileState::AppendPlay,
                                                        None)]))
           .unwrap();

        thread::sleep(Duration::from_secs(3));
        mpv.set_property(&mut Property::new("volume", Data::new(14))).unwrap();

        thread::sleep(Duration::from_secs(30));

        mpv.playlist(&PlaylistOp::NextForce).unwrap();
    });
}
<commit_msg>Obey clippy warning<commit_after>// Copyright (C) 2016  ParadoxSpiral
//
// This file is part of mpv-rs.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

extern crate crossbeam;

use mpv::*;

use std::process;
use std::path::Path;
use std::time::Duration;
use std::thread;

pub fn exec() {
    let mpv = Parent::new(true).unwrap();
    mpv.set_option(&mut Property::new("cache-initial", Data::new(1))).unwrap();
    mpv.set_option(&mut Property::new("volume", Data::new(20))).unwrap();
    mpv.set_option(&mut Property::new("no-video", Data::new(true))).unwrap();
    mpv.set_option(&mut Property::new("ytdl", Data::new(true))).unwrap();
    mpv.init().unwrap();

    crossbeam::scope(|scope| {
        scope.spawn(|| {
            let iter = mpv.observe_all(&[Event::FileLoaded,
                                         Event::StartFile,
                                         Event::Seek,
                                         Event::PlaybackRestart,
                                         Event::EndFile(None)])
                          .unwrap();

            for elem in iter {
                match elem {
                    Err(Error::NoAssociatedEvent) => {}
                    Err(v) => panic!("unexpected error: {:?}", v),
                    Ok(vec) => {
                        if let Some(&Ok(Event::EndFile(ref v))) = vec.iter().find(|e| {
                            if e.is_ok() {
                                if let Event::EndFile(_) = *e.as_ref().unwrap() {
                                    return true;
                                }
                            }
                            false
                        }) {
                            println!("File ended! Reason: {:?}", v);
                            process::exit(0);
                        } else {
                            println!("1: {:?}", vec);
                        };
                    }
                }
            }
        });
        scope.spawn(|| {
            let iter = mpv.observe_all(&[Event::PropertyChange(Property::new("volume",
                                                                             Data::new(0))),
                                         Event::PropertyChange(Property::new("pause",
                                                                             Data::new(false)))])
                          .unwrap();

            for elem in iter {
                match elem {
                    Err(Error::NoAssociatedEvent) => {}
                    Err(v) => panic!("unexpected error: {:?}", v),
                    Ok(v) => println!("2: {:?}", v),
                }
            }
        });

        mpv.playlist(&PlaylistOp::Loadfiles(&[File::new(Path::new("https://www.youtube.\
                                                                   com/watch?v=DLzxrzFCyOs"),
                                                        FileState::AppendPlay,
                                                        None)]))
           .unwrap();

        thread::sleep(Duration::from_secs(3));
        mpv.set_property(&mut Property::new("volume", Data::new(14))).unwrap();

        thread::sleep(Duration::from_secs(30));

        mpv.playlist(&PlaylistOp::NextForce).unwrap();
    });
}
<|endoftext|>


<commit_before>extern crate mqtt;
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate clap;
extern crate uuid;
extern crate time;

use std::io::Write;
use std::net::TcpStream;
use std::str;

use clap::{App, Arg};

use uuid::Uuid;

use mqtt::{Decodable, Encodable, QualityOfService};
use mqtt::TopicFilter;
use mqtt::control::variable_header::ConnectReturnCode;
use mqtt::packet::*;

use std::thread;
use std::time::Duration;

fn generate_client_id() -> String {
    format!("/MQTT/rust/{}", Uuid::new_v4().simple().to_string())
}

fn main() {
    env_logger::init().unwrap();

    let matches = App::new("sub-client")
        .author("Y. T. Chung <zonyitoo@gmail.com>")
        .arg(Arg::with_name("SERVER")
                 .short("S")
                 .long("server")
                 .takes_value(true)
                 .required(true)
                 .help("MQTT server address (host:port)"))
        .arg(Arg::with_name("SUBSCRIBE")
                 .short("s")
                 .long("subscribe")
                 .takes_value(true)
                 .multiple(true)
                 .required(true)
                 .help("Channel filter to subscribe"))
        .arg(Arg::with_name("USER_NAME")
                 .short("u")
                 .long("username")
                 .takes_value(true)
                 .help("Login user name"))
        .arg(Arg::with_name("PASSWORD")
                 .short("p")
                 .long("password")
                 .takes_value(true)
                 .help("Password"))
        .arg(Arg::with_name("CLIENT_ID")
                 .short("i")
                 .long("client-identifier")
                 .takes_value(true)
                 .help("Client identifier"))
        .get_matches();

    let server_addr = matches.value_of("SERVER").unwrap();
    let client_id = matches.value_of("CLIENT_ID")
                           .map(|x| x.to_owned())
                           .unwrap_or_else(generate_client_id);
    let channel_filters: Vec<(TopicFilter, QualityOfService)> =
        matches.values_of("SUBSCRIBE")
               .unwrap()
               .map(|c| (TopicFilter::new(c.to_string()).unwrap(), QualityOfService::Level0))
               .collect();

    let keep_alive = 10;

    print!("Connecting to {:?} ... ", server_addr);
    let mut stream = TcpStream::connect(server_addr).unwrap();
    println!("Connected!");

    println!("Client identifier {:?}", client_id);
    let mut conn = ConnectPacket::new("MQTT", client_id);
    conn.set_clean_session(true);
    conn.set_keep_alive(keep_alive);
    let mut buf = Vec::new();
    conn.encode(&mut buf).unwrap();
    stream.write_all(&buf[..]).unwrap();

    let connack = ConnackPacket::decode(&mut stream).unwrap();
    trace!("CONNACK {:?}", connack);

    if connack.connect_return_code() != ConnectReturnCode::ConnectionAccepted {
        panic!("Failed to connect to server, return code {:?}", connack.connect_return_code());
    }

    // const CHANNEL_FILTER: &'static str = "typing-speed-test.aoeu.eu";
    println!("Applying channel filters {:?} ...", channel_filters);
    let sub = SubscribePacket::new(10, channel_filters);
    let mut buf = Vec::new();
    sub.encode(&mut buf).unwrap();
    stream.write_all(&buf[..]).unwrap();

    loop {
        let packet = match VariablePacket::decode(&mut stream) {
            Ok(pk) => pk,
            Err(err) => {
                error!("Error in receiving packet {:?}", err);
                continue;
            }
        };
        trace!("PACKET {:?}", packet);

        if let VariablePacket::SubackPacket(ref ack) = packet {
            if ack.packet_identifier() != 10 {
                panic!("SUBACK packet identifier not match");
            }

            println!("Subscribed!");
            break;
        }
    }

    let mut stream_clone = stream.try_clone().unwrap();
    thread::spawn(move || {
        let mut last_ping_time = 0;
        let mut next_ping_time = last_ping_time + (keep_alive as f32 * 0.9) as i64;
        loop {
            let current_timestamp = time::get_time().sec;
            if keep_alive > 0 && current_timestamp >= next_ping_time {
                println!("Sending PINGREQ to broker");

                let pingreq_packet = PingreqPacket::new();

                let mut buf = Vec::new();
                pingreq_packet.encode(&mut buf).unwrap();
                stream_clone.write_all(&buf[..]).unwrap();

                last_ping_time = current_timestamp;
                next_ping_time = last_ping_time + (keep_alive as f32 * 0.9) as i64;
                thread::sleep(Duration::new((keep_alive / 2) as u64, 0));
            }
        }
    });

    loop {
        let packet = match VariablePacket::decode(&mut stream) {
            Ok(pk) => pk,
            Err(err) => {
                error!("Error in receiving packet {}", err);
                continue;
            }
        };
        trace!("PACKET {:?}", packet);

        match packet {
            VariablePacket::PingrespPacket(..) => {
                println!("Receiving PINGRESP from broker ..");
            }
            VariablePacket::PublishPacket(ref publ) => {
                let msg = match str::from_utf8(&publ.payload()[..]) {
                    Ok(msg) => msg,
                    Err(err) => {
                        error!("Failed to decode publish message {:?}", err);
                        continue;
                    }
                };
                println!("PUBLISH ({}): {}", publ.topic_name(), msg);
            }
            _ => {}
        }
    }
}
<commit_msg>fixed tests to acommodate new changes<commit_after>extern crate mqtt;
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate clap;
extern crate uuid;
extern crate time;

use std::io::Write;
use std::net::TcpStream;
use std::str;

use clap::{App, Arg};

use uuid::Uuid;

use mqtt::{Decodable, Encodable, QualityOfService};
use mqtt::TopicFilter;
use mqtt::control::variable_header::ConnectReturnCode;
use mqtt::packet::*;

use std::thread;
use std::time::Duration;

fn generate_client_id() -> String {
    format!("/MQTT/rust/{}", Uuid::new_v4().simple().to_string())
}

fn main() {
    env_logger::init().unwrap();

    let matches = App::new("sub-client")
        .author("Y. T. Chung <zonyitoo@gmail.com>")
        .arg(Arg::with_name("SERVER")
                 .short("S")
                 .long("server")
                 .takes_value(true)
                 .required(true)
                 .help("MQTT server address (host:port)"))
        .arg(Arg::with_name("SUBSCRIBE")
                 .short("s")
                 .long("subscribe")
                 .takes_value(true)
                 .multiple(true)
                 .required(true)
                 .help("Channel filter to subscribe"))
        .arg(Arg::with_name("USER_NAME")
                 .short("u")
                 .long("username")
                 .takes_value(true)
                 .help("Login user name"))
        .arg(Arg::with_name("PASSWORD")
                 .short("p")
                 .long("password")
                 .takes_value(true)
                 .help("Password"))
        .arg(Arg::with_name("CLIENT_ID")
                 .short("i")
                 .long("client-identifier")
                 .takes_value(true)
                 .help("Client identifier"))
        .get_matches();

    let server_addr = matches.value_of("SERVER").unwrap();
    let client_id = matches.value_of("CLIENT_ID")
                           .map(|x| x.to_owned())
                           .unwrap_or_else(generate_client_id);
    let channel_filters: Vec<(TopicFilter, QualityOfService)> =
        matches.values_of("SUBSCRIBE")
               .unwrap()
               .map(|c| (TopicFilter::new(c.to_string()).unwrap(), QualityOfService::Level0))
               .collect();

    let keep_alive = 10;

    print!("Connecting to {:?} ... ", server_addr);
    let mut stream = TcpStream::connect(server_addr).unwrap();
    println!("Connected!");

    println!("Client identifier {:?}", client_id);
    let mut conn = ConnectPacket::new("MQTT", client_id);
    conn.set_clean_session(true);
    conn.set_keep_alive(keep_alive);
    let mut buf = Vec::new();
    conn.encode(&mut buf).unwrap();
    stream.write_all(&buf[..]).unwrap();

    let connack = ConnackPacket::decode(&mut stream).unwrap();
    trace!("CONNACK {:?}", connack);

    if connack.connect_return_code() != ConnectReturnCode::ConnectionAccepted {
        panic!("Failed to connect to server, return code {:?}", connack.connect_return_code());
    }

    // const CHANNEL_FILTER: &'static str = "typing-speed-test.aoeu.eu";
    println!("Applying channel filters {:?} ...", channel_filters);
    let sub = SubscribePacket::new(10, channel_filters);
    let mut buf = Vec::new();
    sub.encode(&mut buf).unwrap();
    stream.write_all(&buf[..]).unwrap();

    loop {
        let packet = match VariablePacket::decode(&mut stream) {
            Ok(pk) => pk,
            Err(err) => {
                error!("Error in receiving packet {:?}", err);
                continue;
            }
        };
        trace!("PACKET {:?}", packet);

        if let VariablePacket::SubackPacket(ref ack) = packet {
            if ack.packet_identifier() != 10 {
                panic!("SUBACK packet identifier not match");
            }

            println!("Subscribed!");
            break;
        }
    }

    let mut stream_clone = stream.try_clone().unwrap();
    thread::spawn(move || {
        let mut last_ping_time = 0;
        let mut next_ping_time = last_ping_time + (keep_alive as f32 * 0.9) as i64;
        loop {
            let current_timestamp = time::get_time().sec;
            if keep_alive > 0 && current_timestamp >= next_ping_time {
                println!("Sending PINGREQ to broker");

                let pingreq_packet = PingreqPacket::new();

                let mut buf = Vec::new();
                pingreq_packet.encode(&mut buf).unwrap();
                stream_clone.write_all(&buf[..]).unwrap();

                last_ping_time = current_timestamp;
                next_ping_time = last_ping_time + (keep_alive as f32 * 0.9) as i64;
                thread::sleep(Duration::new((keep_alive / 2) as u64, 0));
            }
        }
    });

    loop {
        let packet = match VariablePacket::decode(&mut stream) {
            Ok(pk) => pk,
            Err(err) => {
                error!("Error in receiving packet {}", err);
                continue;
            }
        };
        trace!("PACKET {:?}", packet);

        match packet {
            VariablePacket::PingrespPacket(..) => {
                println!("Receiving PINGRESP from broker ..");
            }
            VariablePacket::PublishPacket(ref publ) => {
                let msg = match str::from_utf8(&publ.payload_ref()[..]) {
                    Ok(msg) => msg,
                    Err(err) => {
                        error!("Failed to decode publish message {:?}", err);
                        continue;
                    }
                };
                println!("PUBLISH ({}): {}", publ.topic_name(), msg);
            }
            _ => {}
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::io::{self, Read, Write};
use std::str::from_utf8;

use mio::event::Event;
use mio::net::{TcpListener, TcpStream};
use mio::{Events, Interests, Poll, Registry, Token};

// Setup some tokens to allow us to identify which event is for which socket.
const SERVER: Token = Token(0);

// Some data we'll send over the connection.
const DATA: &[u8] = b"Hello world!\n";

fn main() -> io::Result<()> {
    env_logger::init();

    // Create a poll instance.
    let mut poll = Poll::new()?;
    // Create storage for events.
    let mut events = Events::with_capacity(128);

    // Setup the TCP server socket.
    let addr = "127.0.0.1:13265".parse().unwrap();
    let server = TcpListener::bind(addr)?;

    // Register the server with poll we can receive events for it.
    poll.registry()
        .register(&server, SERVER, Interests::READABLE)?;

    // Map of `Token` -> `TcpStream`.
    let mut connections = HashMap::new();
    // Unique token for each incoming connection.
    let mut unique_token = Token(SERVER.0 + 1);

    println!("You can connect to the server using `nc`:");
    println!(" $ nc 127.0.0.1 13265");
    println!("You'll see our welcome message and anything you type we'll be printed here.");

    loop {
        poll.poll(&mut events, None)?;

        for event in events.iter() {
            match event.token() {
                SERVER => {
                    // Received an event for the TCP server socket.
                    // Accept an connection.
                    let (connection, address) = server.accept()?;
                    println!("Accepted connection from: {}", address);

                    let token = next(&mut unique_token);
                    poll.registry().register(
                        &connection,
                        token,
                        Interests::READABLE.add(Interests::WRITABLE),
                    )?;

                    connections.insert(token, connection);
                }
                token => {
                    // (maybe) received an event for a TCP connection.
                    let done = if let Some(connection) = connections.get_mut(&token) {
                        handle_connection_event(poll.registry(), connection, event)?
                    } else {
                        // Sporadic events happen.
                        false
                    };
                    if done {
                        connections.remove(&token);
                    }
                }
            }
        }
    }
}

fn next(current: &mut Token) -> Token {
    let next = current.0;
    current.0 += 1;
    Token(next)
}

/// Returns `true` if the connection is done.
fn handle_connection_event(
    registry: &Registry,
    connection: &mut TcpStream,
    event: &Event,
) -> io::Result<bool> {
    if event.is_writable() {
        // We can (maybe) write to the connection.
        match connection.write(DATA) {
            // We want to write the entire `DATA` buffer in a single go. If we
            // write less we'll return a short write error (same as
            // `io::Write::write_all` does).
            Ok(n) if n < DATA.len() => return Err(io::ErrorKind::WriteZero.into()),
            Ok(_) => {
                // After we've written something we'll reregister the connection
                // to only respond to readable events.
                registry.reregister(&connection, event.token(), Interests::READABLE)?
            }
            // Would block "errors" are the OS's way of saying that the
            // connection is not actually ready to perform this I/O operation.
            Err(ref err) if would_block(err) => {}
            // Got interrupted (how rude!), we'll try again.
            Err(ref err) if interrupted(err) => {
                return handle_connection_event(registry, connection, event)
            }
            // Other errors we'll consider fatal.
            Err(err) => return Err(err),
        }
    }

    if event.is_readable() {
        let mut connection_closed = false;
        let mut received_data = Vec::with_capacity(4096);
        // We can (maybe) read from the connection.
        loop {
            let mut buf = [0; 256];
            match connection.read(&mut buf) {
                Ok(0) => {
                    // Reading 0 bytes means the other side has closed the
                    // connection or is done writing, then so are we.
                    connection_closed = true;
                    break;
                }
                Ok(n) => received_data.extend_from_slice(&buf[..n]),
                // Would block "errors" are the OS's way of saying that the
                // connection is not actually ready to perform this I/O operation.
                Err(ref err) if would_block(err) => break,
                Err(ref err) if interrupted(err) => continue,
                // Other errors we'll consider fatal.
                Err(err) => return Err(err),
            }
        }

        if let Ok(str_buf) = from_utf8(&received_data) {
            println!("Received data: {}", str_buf.trim_end());
        } else {
            println!("Received (none UTF-8) data: {:?}", &received_data);
        }

        if connection_closed {
            println!("Connection closed");
            return Ok(true);
        }
    }

    Ok(false)
}

fn would_block(err: &io::Error) -> bool {
    err.kind() == io::ErrorKind::WouldBlock
}

fn interrupted(err: &io::Error) -> bool {
    err.kind() == io::ErrorKind::Interrupted
}
<commit_msg>Change TCP server example port to 9000<commit_after>use std::collections::HashMap;
use std::io::{self, Read, Write};
use std::str::from_utf8;

use mio::event::Event;
use mio::net::{TcpListener, TcpStream};
use mio::{Events, Interests, Poll, Registry, Token};

// Setup some tokens to allow us to identify which event is for which socket.
const SERVER: Token = Token(0);

// Some data we'll send over the connection.
const DATA: &[u8] = b"Hello world!\n";

fn main() -> io::Result<()> {
    env_logger::init();

    // Create a poll instance.
    let mut poll = Poll::new()?;
    // Create storage for events.
    let mut events = Events::with_capacity(128);

    // Setup the TCP server socket.
    let addr = "127.0.0.1:9000".parse().unwrap();
    let server = TcpListener::bind(addr)?;

    // Register the server with poll we can receive events for it.
    poll.registry()
        .register(&server, SERVER, Interests::READABLE)?;

    // Map of `Token` -> `TcpStream`.
    let mut connections = HashMap::new();
    // Unique token for each incoming connection.
    let mut unique_token = Token(SERVER.0 + 1);

    println!("You can connect to the server using `nc`:");
    println!(" $ nc 127.0.0.1 9000");
    println!("You'll see our welcome message and anything you type we'll be printed here.");

    loop {
        poll.poll(&mut events, None)?;

        for event in events.iter() {
            match event.token() {
                SERVER => {
                    // Received an event for the TCP server socket.
                    // Accept an connection.
                    let (connection, address) = server.accept()?;
                    println!("Accepted connection from: {}", address);

                    let token = next(&mut unique_token);
                    poll.registry().register(
                        &connection,
                        token,
                        Interests::READABLE.add(Interests::WRITABLE),
                    )?;

                    connections.insert(token, connection);
                }
                token => {
                    // (maybe) received an event for a TCP connection.
                    let done = if let Some(connection) = connections.get_mut(&token) {
                        handle_connection_event(poll.registry(), connection, event)?
                    } else {
                        // Sporadic events happen.
                        false
                    };
                    if done {
                        connections.remove(&token);
                    }
                }
            }
        }
    }
}

fn next(current: &mut Token) -> Token {
    let next = current.0;
    current.0 += 1;
    Token(next)
}

/// Returns `true` if the connection is done.
fn handle_connection_event(
    registry: &Registry,
    connection: &mut TcpStream,
    event: &Event,
) -> io::Result<bool> {
    if event.is_writable() {
        // We can (maybe) write to the connection.
        match connection.write(DATA) {
            // We want to write the entire `DATA` buffer in a single go. If we
            // write less we'll return a short write error (same as
            // `io::Write::write_all` does).
            Ok(n) if n < DATA.len() => return Err(io::ErrorKind::WriteZero.into()),
            Ok(_) => {
                // After we've written something we'll reregister the connection
                // to only respond to readable events.
                registry.reregister(&connection, event.token(), Interests::READABLE)?
            }
            // Would block "errors" are the OS's way of saying that the
            // connection is not actually ready to perform this I/O operation.
            Err(ref err) if would_block(err) => {}
            // Got interrupted (how rude!), we'll try again.
            Err(ref err) if interrupted(err) => {
                return handle_connection_event(registry, connection, event)
            }
            // Other errors we'll consider fatal.
            Err(err) => return Err(err),
        }
    }

    if event.is_readable() {
        let mut connection_closed = false;
        let mut received_data = Vec::with_capacity(4096);
        // We can (maybe) read from the connection.
        loop {
            let mut buf = [0; 256];
            match connection.read(&mut buf) {
                Ok(0) => {
                    // Reading 0 bytes means the other side has closed the
                    // connection or is done writing, then so are we.
                    connection_closed = true;
                    break;
                }
                Ok(n) => received_data.extend_from_slice(&buf[..n]),
                // Would block "errors" are the OS's way of saying that the
                // connection is not actually ready to perform this I/O operation.
                Err(ref err) if would_block(err) => break,
                Err(ref err) if interrupted(err) => continue,
                // Other errors we'll consider fatal.
                Err(err) => return Err(err),
            }
        }

        if let Ok(str_buf) = from_utf8(&received_data) {
            println!("Received data: {}", str_buf.trim_end());
        } else {
            println!("Received (none UTF-8) data: {:?}", &received_data);
        }

        if connection_closed {
            println!("Connection closed");
            return Ok(true);
        }
    }

    Ok(false)
}

fn would_block(err: &io::Error) -> bool {
    err.kind() == io::ErrorKind::WouldBlock
}

fn interrupted(err: &io::Error) -> bool {
    err.kind() == io::ErrorKind::Interrupted
}
<|endoftext|>