blob: c6d9e4fe4d98a6ee73133ceac82af438722d001f [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! The `utils` module contains helper functions for common operations
//! throughout the setui client.
use anyhow::Error;
use futures::{TryFutureExt, TryStream, TryStreamExt};
use std::fmt::Debug;
use std::future::Future;
/// An abstraction over a stream result from a watch, or the string output
/// from a get or set call.
pub enum Either {
Watch(StringTryStream),
Set(String),
Get(String),
}
pub type StringTryStream =
Box<dyn TryStream<Ok = String, Error = Error, Item = Result<String, Error>> + Unpin>;
pub type WatchOrSetResult = Result<Either, Error>;
/// A utility function to convert a watch into a stream of responses. Relies
/// on the output type of the watch call supporting `Debug` formatting.
pub(crate) fn watch_to_stream<P, W, Fut, T, E>(proxy: P, watch_fn: W) -> StringTryStream
where
P: 'static,
W: Fn(&P) -> Fut + 'static,
Fut: Future<Output = Result<T, E>> + Unpin + 'static,
T: Debug,
E: Into<Error> + 'static,
{
formatted_watch_to_stream(proxy, watch_fn, |t| format!("{:#?}", t))
}
/// A utility function to convert a watch into a stream of Responses. This variant
/// allows specifying the formatting function based on the output type of the
/// `watch` call.
pub(crate) fn formatted_watch_to_stream<P, W, Fut, F, T, E>(
proxy: P,
watch_fn: W,
formatting_fn: F,
) -> StringTryStream
where
P: 'static,
W: Fn(&P) -> Fut + 'static,
Fut: Future<Output = Result<T, E>> + Unpin + 'static,
F: Fn(T) -> String + Clone + 'static,
E: Into<Error> + 'static,
{
Box::new(futures::stream::try_unfold(proxy, move |proxy| {
let formatting_fn = formatting_fn.clone();
watch_fn(&proxy)
.map_ok(move |result| Some((formatting_fn(result), proxy)))
.map_err(Into::into)
}))
}
/// A utility function to display every output that comes from a watch stream.
pub(crate) async fn print_results<S>(label: &str, mut stream: S) -> Result<(), Error>
where
S: TryStream<Ok = String, Error = Error> + Unpin,
{
println!("Watching `{}` in a loop. Press Ctrl+C to stop.", label);
while let Some(output) = stream.try_next().await? {
println!("{}", output);
}
Ok(())
}
/// A utility function to manage outputting the results of either a watch or set
/// call.
pub(crate) async fn handle_mixed_result(
label: &str,
result: WatchOrSetResult,
) -> Result<(), Error> {
Ok(match result? {
Either::Watch(stream) => print_results(label, stream).await?,
Either::Set(output) | Either::Get(output) => println!("{}: {}", label, output),
})
}