use core::future::Future;
use core::pin::Pin;
use serde_json::{Map, Value};
use super::{ClientError, ClientTransport};
use crate::domain::ir::JsonObject;
pub trait InputResolver: Send + Sync {
fn resolve<'a>(
&'a self,
key: &'a str,
method: &'a str,
params: &'a JsonObject,
) -> Pin<Box<dyn Future<Output = Option<JsonObject>> + Send + 'a>>;
}
impl<F, Fut> InputResolver for F
where
F: Fn(String, String, JsonObject) -> Fut + Send + Sync,
Fut: Future<Output = Option<JsonObject>> + Send + 'static,
{
fn resolve<'a>(
&'a self,
key: &'a str,
method: &'a str,
params: &'a JsonObject,
) -> Pin<Box<dyn Future<Output = Option<JsonObject>> + Send + 'a>> {
Box::pin(self(key.to_owned(), method.to_owned(), params.clone()))
}
}
pub const MAX_INPUT_ROUNDS: u32 = 8;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct InputAnswers {
pub responses: Map<String, Value>,
pub state: Option<String>,
}
impl InputAnswers {
#[must_use]
pub fn applied_to(&self, params: &JsonObject) -> JsonObject {
let mut attempt: Map<String, Value> = match serde_json::from_str(params.as_str()) {
Ok(Value::Object(object)) => object,
_ => Map::new(),
};
attempt.insert(
"inputResponses".to_owned(),
Value::Object(self.responses.clone()),
);
if let Some(state) = &self.state {
attempt.insert("requestState".to_owned(), Value::from(state.clone()));
}
JsonObject::from_value(Value::Object(attempt)).unwrap_or_default()
}
}
pub async fn answer_input_required(
result: &JsonObject,
resolve: &(impl InputResolver + ?Sized),
) -> Result<Option<InputAnswers>, ClientError> {
let parsed: Value = serde_json::from_str(result.as_str()).unwrap_or_default();
if parsed.get("resultType").and_then(Value::as_str) != Some("input_required") {
return Ok(None);
}
let state = parsed
.get("requestState")
.and_then(Value::as_str)
.map(ToOwned::to_owned);
let requests = parsed
.get("inputRequests")
.and_then(Value::as_object)
.cloned()
.unwrap_or_default();
let mut responses = Map::new();
for (key, request) in requests {
let asked = request
.get("method")
.and_then(Value::as_str)
.unwrap_or_default()
.to_owned();
let asked_params =
JsonObject::from_value(request.get("params").cloned().unwrap_or_default())
.unwrap_or_default();
let Some(answer) = resolve.resolve(&key, &asked, &asked_params).await else {
return Err(ClientError::InputUnresolved { method: asked });
};
responses.insert(
key,
serde_json::from_str(answer.as_str()).unwrap_or_default(),
);
}
Ok(Some(InputAnswers { responses, state }))
}
pub async fn call_resolving(
transport: &(impl ClientTransport + ?Sized),
method: &str,
params: &JsonObject,
resolve: &(impl InputResolver + ?Sized),
) -> Result<JsonObject, ClientError> {
let mut attempt = params.clone();
for _ in 0..MAX_INPUT_ROUNDS {
let result = transport.send(method, &attempt).await?;
let Some(answers) = answer_input_required(&result, resolve).await? else {
return Ok(result);
};
attempt = answers.applied_to(params);
}
Err(ClientError::TooManyRounds(MAX_INPUT_ROUNDS))
}