Skip to content

Commit

Permalink
xxx: workaround due to a compiler bug
Browse files Browse the repository at this point in the history
- see rust issue (rust-lang/rust#41053)
  • Loading branch information
icorderi committed Apr 4, 2017
1 parent f6a4cd4 commit 2ade1f4
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 65 deletions.
82 changes: 39 additions & 43 deletions examples/time_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,44 @@ fn now() -> u64 {
.as_secs()
}

// Note: Moved to a separate function due to a compiler issue (rust-lang/rust#41053)
fn subscribe(server: &TimeServer, ctl: &ServerCtl, params: &Option<Params>)
-> Option<<TimeServer as Server>::RpcCallResult> {
debug!(server.1, "Subscribing");
// Some parsing and bailing out on errors
// XXX: why is params borrowed?
let params2 = params.clone();
let params = parse_params!(params2, { secs: u64,
#[serde(default)]
nsecs: u32, });
// We need to have a client to be able to send notifications
let client = ctl.client();
let handle = server.0.clone();
let logger = server.1.clone();
// Get a stream that „ticks“
let result = Interval::new(Duration::new(params.secs, params.nsecs), &server.0)
.or_else(|e| Err(RpcError::server_error(Some(format!("Interval: {}", e)))))
.map(move |interval| {
let logger_cloned = logger.clone();
// And send the notification on each tick (and pass the client through)
let notified = interval.fold(client, move |client, _| {
debug!(logger_cloned, "Tick");
client.notify("time".to_owned(), params!([now()]))
})
// So it can be spawned, spawn needs ().
.map(|_| ())
// TODO: This reports a „Shouldn't happen“ error ‒ do something
// about that
.map_err(move |e| {
error!(logger, "Error notifying about a time: {}", e);
});
handle.spawn(notified);
// We need some result, but we don't send any meaningful value
Value::Null
});
Some(result)
}

/// The server implementation
///
/// Future versions of the library will have some helpers to create them in an easier way.
Expand All @@ -68,49 +106,7 @@ impl Server for TimeServer {
Some(Ok(Value::Number(now().into())))
},
// Subscribe to receiving updates of time (we don't do unsubscription)
"subscribe" => {
debug!(self.1, "Subscribing");
// Some parsing and bailing out on errors
// XXX: why is params borrowed?
let params2 = params.clone();
let params = parse_params!(params2, { secs: u64,
#[serde(default)]
nsecs: u32, });
// XXX: this is not happy code
// `impl Carrier` might make it nicer, if/when it lands on stable
let params = match params {
/// XXX: this should not be here, we should not return option by default
None => return Some(Err(RpcError::invalid_params(None))),
Some(Err(err)) => return Some(Err(err)),
Some(Ok(params)) => params,
};
// We need to have a client to be able to send notifications
let client = ctl.client();
let handle = self.0.clone();
let logger = self.1.clone();
// Get a stream that „ticks“
let result = Interval::new(Duration::new(params.secs, params.nsecs), &self.0)
.or_else(|e| Err(RpcError::server_error(Some(format!("Interval: {}", e)))))
.map(move |interval| {
let logger_cloned = logger.clone();
// And send the notification on each tick (and pass the client through)
let notified = interval.fold(client, move |client, _| {
debug!(logger_cloned, "Tick");
client.notify("time".to_owned(), params!([now()]))
})
// So it can be spawned, spawn needs ().
.map(|_| ())
// TODO: This reports a „Shouldn't happen“ error ‒ do something
// about that
.map_err(move |e| {
error!(logger, "Error notifying about a time: {}", e);
});
handle.spawn(notified);
// We need some result, but we don't send any meaningful value
Value::Null
});
Some(result)
},
"subscribe" => subscribe(self, ctl, params),
// Method not known
_ => None,
}
Expand Down
77 changes: 55 additions & 22 deletions src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,28 @@ impl Server for ServerChain {
}
}

/// Returns a Result<T, RpcError>
// TODO: docs missing
#[macro_export]
macro_rules! try_parse_params {
($params:expr, $(#[$attr:meta])* { $($body:tt)* }) => {
{
let parsed = parse_params!(@parse [$params, $(#[$attr])*] {} [] { $($body)* });
match parsed {
None => Err(RpcError::invalid_params(Some("Expected parameters".to_owned()))),
Some(x) => x,
}
}
};

($params:expr, option $(#[$attr:meta])* { $($body:tt)* }) => {
parse_params!(@parse [$params, $(#[$attr])*] {} [] { $($body)* });
};
}

/// Will exit the function if the parsing fails
// TODO: docs missing
// TODO: add a tokio-jsonrpc-derive that will do similar code for an already
// Review: why are we returning Options?
// TODO: have the user opt-in on an optional result
// existing struct
#[macro_export]
macro_rules! parse_params {
//
Expand All @@ -236,7 +253,7 @@ macro_rules! parse_params {
// We can leverage any #[serde(default)] by letting serde do the work
let mut object = $crate::macro_exports::Map::new();

// Review: do we need to construct an array or can we just use the macro loop?
// Review: do we need to allocate an array or can we just use the macro loop?
let fields = vec![$( stringify!($field_name)),*];
for f in fields {
if ! xs.is_empty() {
Expand Down Expand Up @@ -305,12 +322,32 @@ macro_rules! parse_params {
};

//
// entry points
// entry points, must come last
//

// macro entry point, must come last
// Returns an Option<T>
($params:expr, option $(#[$attr:meta])* { $($body:tt)* }) => {
{
let parsed = parse_params!(@parse [$params, $(#[$attr])*] {} [] { $($body)* });
match parsed {
None => None
Some(Err(err)) => return Some(Err(err)),
Some(Ok(params)) => Some(params),
}
}
};

// Returns a T
($params:expr, $(#[$attr:meta])* { $($body:tt)* }) => {
parse_params!(@parse [$params, $(#[$attr])*] {} [] { $($body)* });
{
let parsed = parse_params!(@parse [$params, $(#[$attr])*] {} [] { $($body)* });
match parsed {
None => return Some(Err(RpcError::invalid_params(
Some("Expected parameters".to_owned())))),
Some(Err(err)) => return Some(Err(err)),
Some(Ok(params)) => params,
}
}
};
}

Expand All @@ -322,22 +359,21 @@ macro_rules! parse_params {
pub fn foobar_anonymous_structs() {
// By position
let params = params!([7, "hello world"]);
let x = parse_params!(params, { x: usize, name: String, }).unwrap().unwrap();
let x = try_parse_params!(params, { x: usize, name: String, }).unwrap();
assert_eq!(x.x, 7);
assert_eq!(x.name, "hello world");

// By name
let params = params!({"x": 7, "name": "hello world"});
let x = parse_params!(params, { x: usize, name: String, }).unwrap().unwrap();
let x = try_parse_params!(params, { x: usize, name: String, }).unwrap();
assert_eq!(x.x, 7);
assert_eq!(x.name, "hello world");

println!("cp::1");

// Missing optional args (named)
let params = params!({"x":7});
let x = parse_params!(params, #[derive(Debug)] { x: usize, name: Option<String>, })
.unwrap()
let x = try_parse_params!(params, #[derive(Debug)] { x: usize, name: Option<String>, })
.unwrap();
println!("{:?}", x);
assert_eq!(x.x, 7);
Expand All @@ -347,8 +383,7 @@ pub fn foobar_anonymous_structs() {

// Missing optional args (positional)
let params = params!([7]);
let x = parse_params!(params, #[derive(Debug)] { x: usize, name: Option<String>, })
.unwrap()
let x = try_parse_params!(params, #[derive(Debug)] { x: usize, name: Option<String>, })
.unwrap();
println!("{:?}", x);
assert_eq!(x.x, 7);
Expand All @@ -358,7 +393,7 @@ pub fn foobar_anonymous_structs() {

// Nada
let params = params!();
let x = parse_params!(params, { x: usize, name: Option<String>, });
let x = try_parse_params!(params, option { x: usize, name: Option<String>, });
assert!(x.is_none());

println!("cp::4");
Expand All @@ -377,10 +412,9 @@ pub fn foobar_anonymous_structs() {

// Complex parsing with metadata (named)
let params = params!({ "x": 8 });
let x = parse_params!(params, { x: usize,
#[serde(default)]
kind: Kind, })
.unwrap()
let x = try_parse_params!(params, { x: usize,
#[serde(default)]
kind: Kind, })
.unwrap();
assert_eq!(x.x, 8);
assert_eq!(x.kind, Kind::Bar);
Expand All @@ -389,10 +423,9 @@ pub fn foobar_anonymous_structs() {

// Complex parsing with metadata (positional)
let params = params!([8]);
let x = parse_params!(params, { x: usize,
#[serde(default)]
kind: Kind, })
.unwrap()
let x = try_parse_params!(params, { x: usize,
#[serde(default)]
kind: Kind, })
.unwrap();
assert_eq!(x.x, 8);
assert_eq!(x.kind, Kind::Bar);
Expand Down

0 comments on commit 2ade1f4

Please sign in to comment.