request builder improvements
This commit is contained in:
parent
64342d200c
commit
f0a587d310
@ -82,7 +82,7 @@ pub trait Provider: Send + Sync {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let headers = (0u64..req.max as u64)
|
let headers: Vec<_> = (0u64..req.max as u64)
|
||||||
.map(|x: u64| x.saturating_mul(req.skip + 1))
|
.map(|x: u64| x.saturating_mul(req.skip + 1))
|
||||||
.take_while(|x| if req.reverse { x < &start_num } else { best_num.saturating_sub(start_num) >= *x })
|
.take_while(|x| if req.reverse { x < &start_num } else { best_num.saturating_sub(start_num) >= *x })
|
||||||
.map(|x| if req.reverse { start_num - x } else { start_num + x })
|
.map(|x| if req.reverse { start_num - x } else { start_num + x })
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use request::{
|
use request::{
|
||||||
IncompleteRequest, CompleteRequest, Request,
|
IncompleteRequest, CompleteRequest, Request,
|
||||||
OutputKind, Output, NoSuchOutput, Response,
|
OutputKind, Output, NoSuchOutput, Response, ResponseError,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Build chained requests. Push them onto the series with `push`,
|
/// Build chained requests. Push them onto the series with `push`,
|
||||||
@ -34,7 +34,7 @@ pub struct RequestBuilder {
|
|||||||
|
|
||||||
impl RequestBuilder {
|
impl RequestBuilder {
|
||||||
/// Attempt to push a request onto the request chain. Fails if the request
|
/// Attempt to push a request onto the request chain. Fails if the request
|
||||||
/// references a non-existant output of a prior request.
|
/// references a non-existent output of a prior request.
|
||||||
pub fn push(&mut self, request: Request) -> Result<(), NoSuchOutput> {
|
pub fn push(&mut self, request: Request) -> Result<(), NoSuchOutput> {
|
||||||
request.check_outputs(|req, idx, kind| {
|
request.check_outputs(|req, idx, kind| {
|
||||||
match self.output_kinds.get(&(req, idx)) {
|
match self.output_kinds.get(&(req, idx)) {
|
||||||
@ -48,13 +48,17 @@ impl RequestBuilder {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a reference to the output kinds map.
|
||||||
|
pub fn output_kinds(&self) -> &HashMap<(usize, usize), OutputKind> {
|
||||||
|
&self.output_kinds
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert this into a "requests" object.
|
/// Convert this into a "requests" object.
|
||||||
pub fn build(self) -> Requests {
|
pub fn build(self) -> Requests {
|
||||||
Requests {
|
Requests {
|
||||||
output_kinds: self.output_kinds,
|
|
||||||
outputs: HashMap::new(),
|
outputs: HashMap::new(),
|
||||||
requests: self.requests,
|
requests: self.requests,
|
||||||
offset: 0,
|
answered: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -62,49 +66,27 @@ impl RequestBuilder {
|
|||||||
/// Requests pending responses.
|
/// Requests pending responses.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct Requests {
|
pub struct Requests {
|
||||||
output_kinds: HashMap<(usize, usize), OutputKind>,
|
|
||||||
outputs: HashMap<(usize, usize), Output>,
|
outputs: HashMap<(usize, usize), Output>,
|
||||||
requests: Vec<Request>,
|
requests: Vec<Request>,
|
||||||
offset: usize, // offset for splitting.
|
answered: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Requests {
|
impl Requests {
|
||||||
/// For each request, produce responses for each.
|
/// For each request, produce responses for each.
|
||||||
/// The responses vector produced goes up to the point where the responder
|
/// The responses vector produced goes up to the point where the responder
|
||||||
/// first returns `None`, an invalid response, or until all requests have been responded to.
|
/// first returns `None`, an invalid response, or until all requests have been responded to.
|
||||||
pub fn respond_to_all<F>(self, responder: F) -> Vec<Response>
|
pub fn respond_to_all<F>(mut self, responder: F) -> Vec<Response>
|
||||||
where F: Fn(CompleteRequest) -> Option<Response>
|
where F: Fn(CompleteRequest) -> Option<Response>
|
||||||
{
|
{
|
||||||
let mut responses = Vec::new();
|
let mut responses = Vec::new();
|
||||||
let mut found_bad = false;
|
|
||||||
let offset = self.offset;
|
|
||||||
let output_kinds = self.output_kinds;
|
|
||||||
let mut outputs = self.outputs;
|
|
||||||
for (idx, req) in self.requests.into_iter().enumerate().map(|(idx, req)| (idx + offset, req)) {
|
|
||||||
let complete = req.fill(|req_idx, out_idx| outputs.get(&(req_idx, out_idx)).cloned().ok_or(NoSuchOutput))
|
|
||||||
.expect("All outputs checked as invariant of `Requests` object; qed");
|
|
||||||
|
|
||||||
match responder(complete) {
|
while let Some(response) = self.next_complete().and_then(&responder) {
|
||||||
Some(response) => {
|
match self.supply_response(&response) {
|
||||||
response.fill_outputs(|out_idx, output| {
|
Ok(()) => responses.push(response),
|
||||||
match output_kinds.get(&(idx, out_idx)) {
|
Err(e) => {
|
||||||
None => {},
|
debug!(target: "pip", "produced bad response to request: {:?}", e);
|
||||||
Some(out) => if out == &output.kind() {
|
|
||||||
outputs.insert((idx, out_idx), output);
|
|
||||||
} else {
|
|
||||||
// output kind doesn't match expected.
|
|
||||||
found_bad = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if found_bad {
|
|
||||||
return responses;
|
return responses;
|
||||||
}
|
}
|
||||||
|
|
||||||
responses.push(response);
|
|
||||||
}
|
|
||||||
None => return responses,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +94,44 @@ impl Requests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get access to the underlying slice of requests.
|
/// Get access to the underlying slice of requests.
|
||||||
|
// TODO: unimplemented -> Vec<Request>, // do we _have to_ allocate?
|
||||||
pub fn requests(&self) -> &[Request] { &self.requests }
|
pub fn requests(&self) -> &[Request] { &self.requests }
|
||||||
|
|
||||||
|
/// Get the number of answered requests.
|
||||||
|
pub fn num_answered(&self) -> usize { self.answered }
|
||||||
|
|
||||||
|
/// Get the next request as a filled request. Returns `None` when all requests answered.
|
||||||
|
pub fn next_complete(&self) -> Option<CompleteRequest> {
|
||||||
|
if self.answered == self.requests.len() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let outputs = &self.outputs;
|
||||||
|
Some(self.requests[self.answered].clone()
|
||||||
|
.fill(|req_idx, out_idx| outputs.get(&(req_idx, out_idx)).cloned().ok_or(NoSuchOutput))
|
||||||
|
.expect("All outputs checked as invariant of `Requests` object; qed"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Supply a response for the next request.
|
||||||
|
/// Fails on: wrong request kind, all requests answered already.
|
||||||
|
pub fn supply_response(&mut self, response: &Response) -> Result<(), ResponseError> {
|
||||||
|
let idx = self.answered;
|
||||||
|
|
||||||
|
// check validity.
|
||||||
|
if idx == self.requests.len() { return Err(ResponseError::Unexpected) }
|
||||||
|
if self.requests[idx].kind() != response.kind() { return Err(ResponseError::WrongKind) }
|
||||||
|
|
||||||
|
let outputs = &mut self.outputs;
|
||||||
|
response.fill_outputs(|out_idx, output| {
|
||||||
|
// we don't need to check output kinds here because all back-references
|
||||||
|
// are validated in the builder.
|
||||||
|
// TODO: optimization for only storing outputs we "care about"?
|
||||||
|
outputs.insert((idx, out_idx), output);
|
||||||
|
});
|
||||||
|
|
||||||
|
self.answered += 1;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -69,6 +69,15 @@ pub use self::builder::{RequestBuilder, Requests};
|
|||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct NoSuchOutput;
|
pub struct NoSuchOutput;
|
||||||
|
|
||||||
|
/// Error on processing a response.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum ResponseError {
|
||||||
|
/// Wrong kind of response.
|
||||||
|
WrongKind,
|
||||||
|
/// No responses expected.
|
||||||
|
Unexpected,
|
||||||
|
}
|
||||||
|
|
||||||
/// An input to a request.
|
/// An input to a request.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum Field<T> {
|
pub enum Field<T> {
|
||||||
|
Loading…
Reference in New Issue
Block a user