From 579b9008108913c896f4bbe7d35ad7461b427a40 Mon Sep 17 00:00:00 2001 From: Kevin Walter Date: Mon, 22 Sep 2014 02:50:19 +0200 Subject: [PATCH] initial commit --- .gitignore | 3 + .travis.yml | 11 +++ Cargo.toml | 9 +++ README.md | 1 + src/lib.rs | 188 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 212 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46bf68e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +target +Cargo.lock + diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..9952ac1 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,11 @@ +language: rust +os: + - linux + - osx +before_script: + - rustc -v + - cargo -V +script: + - cargo build -v + - cargo test -v + - cargo doc -v diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b399f83 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] + +name = "promise" +version = "0.0.1" +authors = ["Kevin Walter "] + +[lib] + +name = "promise" diff --git a/README.md b/README.md new file mode 100644 index 0000000..63a7d70 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# rust-promise [![Build Status](https://travis-ci.org/lucidd/rust-promise.svg?branch=master)](https://travis-ci.org/lucidd/rust-promise) diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..5fd295d --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,188 @@ +extern crate test; + +use std::any::Any; +use std::io::timer; +use std::time::duration::Duration; +use std::task::try; + + +pub enum FutureError{ + TaskFailure(Box), + HungUp +} + +pub struct Promise { + sender: Sender> +} + +impl Promise { + + fn new(tx: Sender>) -> Promise{ + Promise{ sender: tx } + } + + pub fn resolve(self, value: T) { + self.sender.send(Ok(value)) + } + + fn fail(self, error: FutureError) { + self.sender.send(Err(error)) + } + +} + +pub struct Future { + receiver: Receiver> +} + +impl Future{ + + fn new(rx: Receiver>) -> Future { + Future{ receiver: rx } + } + + pub fn value(val: T) -> Future { + let (p, f) = promise::(); + p.resolve(val); + f + } + + pub fn from_fn(func: proc(): Send -> T) -> Future { + let (p, f) = promise::(); + spawn(proc() { + match try(func) { + Ok(val) => p.resolve(val), + Err(err) => p.fail(TaskFailure(err)), + }; + }); + f + } + + pub fn delay(func: proc(): Send -> T, duration: Duration) -> Future { + Future::from_fn(proc() { + timer::sleep(duration); + func() + }) + } + + pub fn map(self, func: proc(T): Send -> B) -> Future { + let (p ,f) = promise::(); + self.on_complete(proc(res) { + match res { + Ok(val) => { + match try(proc() func(val)) { + Ok(mapped) => p.resolve(mapped), + Err(err) => p.fail(TaskFailure(err)), + }; + }, + Err(err) => p.fail(err), + }; + }); + f + } + + pub fn get(self) -> Result { + match self.receiver.recv_opt() { + Ok(res) => res, + Err(_) => Err(HungUp), + } + } + + pub fn on_complete(self, f: proc(Result):Send) { + spawn(proc(){ + let result = self.get(); + f(result); + }); + } + +} + +pub fn promise() -> (Promise, Future) { + let (tx, rx) = channel(); + (Promise::new(tx), Future::new(rx)) +} + + +#[cfg(test)] +mod tests { + use super::{promise, Future, HungUp, TaskFailure}; + use std::any::AnyRefExt; + use std::boxed::BoxAny; + use std::time::duration::Duration; + use std::io::timer; + + + #[test] + fn test_future(){ + let (p, f) = promise(); + p.resolve(123u); + assert_eq!(f.get().ok(), Some(123u)); + } + + #[test] + fn test_future2(){ + let (p, f) = promise::(); + spawn(proc(){ + timer::sleep(Duration::seconds(1)); + p; + }); + match f.get() { + Err(HungUp) => (), + _ => fail!("should not happen"), + } + } + + #[test] + fn test_future_from_fn(){ + let f = Future::from_fn(proc() 123u); + assert_eq!(f.get().ok(), Some(123u)); + } + + #[test] + fn test_future_from_fn_fail(){ + let f = Future::from_fn(proc() { + fail!("ooops"); + 123u + }); + let err = match f.get() { + Err(TaskFailure(err)) => err, + _ => fail!("should not happen"), + }; + assert!(err.is::<&'static str>()); + assert_eq!(*err.downcast::<&'static str>().unwrap(), "ooops"); + } + + #[test] + fn test_future_delay(){ + let f = Future::delay(proc() 123u, Duration::seconds(3)); + //TODO: test delay + assert_eq!(f.get().ok(), Some(123u)); + } + + #[test] + fn test_future_value(){ + let f = Future::value(123u); + assert_eq!(f.get().ok(), Some(123u)); + } + + #[test] + fn test_future_on_complete(){ + let (tx, rx) = channel(); + let f = Future::delay(proc() 123u, Duration::seconds(3)); + f.on_complete(proc(x){ + tx.send(x); + }); + assert_eq!(rx.recv().ok(), Some(123u)) + } + + #[test] + fn test_future_map(){ + let (tx, rx) = channel(); + let f = Future::value(3u); + f.map(proc(x) x*x).on_complete(proc(x){ + tx.send(x); + }); + assert_eq!(rx.recv().ok(), Some(9u)); + } + +}