I am developing a cli app that makes http responses to an API server. Sometimes I wan't to show the user the body of the response and sometimes I want to transform that response into something printable I can show the user, a table for example.
The functions that execute the commands receive a closure: impl FnOnce(T) -> Result<()>
that is called to show the result to the user.
I came up with the following traits and classes to do this:
pub struct TableResponse {
pub headers: HeaderMap,
pub str: String
}
impl PrintableResponse for TableResponse {
fn headers(&mut self) -> HashMap<String, String> {
HashMap::new() // TODO: Implement
}
fn read(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
Ok(buf.write(self.str.as_bytes())?)
}
}
pub trait PrintableResponse {
fn headers(&mut self: Self) -> HashMap<String, String>;
fn read(&mut self: Self, buf: &mut Vec<u8>) -> Result<usize>;
}
impl PrintableResponse for Response {
fn headers(&mut self) -> HashMap<String, String> {
let mut res: HashMap<String, String> = HashMap::new();
for (k, v) in Response::headers(self).iter() {
res.insert(k.to_string(), v.to_str().unwrap_or("").to_owned());
}
res
}
fn read(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
Ok(self.read_to_end(buf)?)
}
}
But this is kind of a mess. I have to use Box<dyn PrintableResult>
everywhere in the signatures wherever I want to pass the closure, but also when converting a Result to a Box<dyn PrintableResult>
is not always pretty.
I tried to create a wrapper type around this:
pub struct PrintableWrapped {
inner: Box<dyn PrintableResponse>,
}
impl From<Response> for PrintableWrapped {
fn from(r: Response) -> Self {
PrintableWrapped { inner: Box::new(r) }
}
}
impl From<TableResponse> for PrintableWrapped {
fn from(r: TableResponse) -> Self {
PrintableH { inner: Box::new(r) }
}
}
Which is ok, I then use PrintableWrapped
in function definitions, and just call .into()
whenever I need a PrintableWrapped
from a Response
, but this looks like a lot of boilerplate for such a simple thing and I feel I am fighting the type system here.
Is there a simpler/more idiomatic/rusty way to do this?
Thanks.