use futures::{
future::BoxFuture,
stream::{Stream, StreamExt},
};
mod typed;
pub use typed::{MatchDispatch, MatchDispatchFrom, TypeNotification};
pub(crate) fn redacted_error(code: impl Into<i32>) -> impl std::fmt::Debug {
struct RedactedError {
code: i32,
reason: &'static str,
}
impl std::fmt::Debug for RedactedError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Error")
.field("code", &self.code)
.field("reason", &self.reason)
.finish()
}
}
let code = code.into();
let reason = match code {
-32700 => "Parse error",
-32600 => "Invalid request",
-32601 => "Method not found",
-32602 => "Invalid params",
-32603 => "Internal error",
_ => "Protocol or application error",
};
RedactedError { code, reason }
}
fn redact_env_and_header_values(value: &mut serde_json::Value) {
match value {
serde_json::Value::Object(object) => {
for (key, field) in object.iter_mut() {
if key == "env" || key == "headers" {
redact_name_value_pairs(field);
}
redact_env_and_header_values(field);
}
}
serde_json::Value::Array(values) => {
values.iter_mut().for_each(redact_env_and_header_values);
}
_ => {}
}
}
fn redact_name_value_pairs(field: &mut serde_json::Value) {
let redacted = || serde_json::Value::String("[REDACTED]".to_owned());
match field {
serde_json::Value::Array(entries) => {
for entry in entries {
if let Some(value) = entry.get_mut("value") {
*value = redacted();
}
}
}
serde_json::Value::Object(entries) => {
for value in entries.values_mut() {
*value = redacted();
}
}
_ => {}
}
}
pub(crate) fn redacted_json_line(line: &str) -> impl std::fmt::Display + '_ {
struct RedactedJsonLine<'a>(&'a str);
impl std::fmt::Display for RedactedJsonLine<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match serde_json::from_str::<serde_json::Value>(self.0) {
Ok(mut value) => {
redact_env_and_header_values(&mut value);
write!(f, "{value}")
}
Err(_) => write!(f, "<{} bytes of invalid JSON omitted>", self.0.len()),
}
}
}
RedactedJsonLine(line)
}
pub fn json_cast<N, M>(params: N) -> Result<M, crate::Error>
where
N: serde::Serialize,
M: serde::de::DeserializeOwned,
{
let json = serde_json::to_value(params).map_err(|e| {
crate::Error::parse_error().data(serde_json::json!({
"error": e.to_string(),
"phase": "serialization"
}))
})?;
let m = serde_json::from_value(json.clone()).map_err(|e| {
crate::Error::parse_error().data(serde_json::json!({
"error": e.to_string(),
"json": json,
"phase": "deserialization"
}))
})?;
Ok(m)
}
pub fn json_cast_params<N, M>(params: N) -> Result<M, crate::Error>
where
N: serde::Serialize,
M: serde::de::DeserializeOwned,
{
let json = serde_json::to_value(params).map_err(|e| {
crate::Error::internal_error().data(serde_json::json!({
"error": e.to_string(),
"phase": "serialization"
}))
})?;
let m = serde_json::from_value(json.clone()).map_err(|e| {
crate::Error::invalid_params().data(serde_json::json!({
"error": e.to_string(),
"json": json,
"phase": "deserialization"
}))
})?;
Ok(m)
}
pub fn internal_error(message: impl ToString) -> crate::Error {
crate::Error::internal_error().data(message.to_string())
}
pub fn parse_error(message: impl ToString) -> crate::Error {
crate::Error::parse_error().data(message.to_string())
}
#[must_use]
pub fn unsupported_protocol_version(
requested: crate::schema::ProtocolVersion,
supported: &[crate::schema::ProtocolVersion],
) -> crate::Error {
crate::Error::new(-32022, "Unsupported protocol version")
.data(serde_json::json!({ "requested": requested, "supported": supported }))
}
pub(crate) fn instrumented_with_connection_name<F>(
name: String,
task: F,
) -> tracing::instrument::Instrumented<F> {
use tracing::Instrument;
task.instrument(tracing::info_span!("connection", name = name))
}
pub(crate) async fn instrument_with_connection_name<R>(
name: Option<String>,
task: impl Future<Output = R>,
) -> R {
if let Some(name) = name {
instrumented_with_connection_name(name.clone(), task).await
} else {
task.await
}
}
pub fn run_until<T, E>(
background: impl Future<Output = Result<(), E>>,
foreground: impl Future<Output = Result<T, E>>,
) -> impl Future<Output = Result<T, E>> {
use futures::future::{Either, select};
use std::pin::pin;
Box::pin(async move {
match select(pin!(background), pin!(foreground)).await {
Either::Left((bg_result, fg_future)) => {
bg_result?; fg_future.await
}
Either::Right((fg_result, _bg_future)) => {
fg_result
}
}
})
}
pub(crate) async fn process_stream_concurrently<T, F>(
stream: impl Stream<Item = T>,
process_fn: F,
process_fn_hack: impl for<'a> Fn(&'a F, T) -> BoxFuture<'a, Result<(), crate::Error>>,
) -> Result<(), crate::Error>
where
F: AsyncFn(T) -> Result<(), crate::Error>,
{
use std::pin::pin;
use futures::stream::{FusedStream, FuturesUnordered};
use futures_concurrency::future::Race;
enum Event<T> {
NewItem(Option<T>),
FutureCompleted(Option<Result<(), crate::Error>>),
}
let mut stream = pin!(stream.fuse());
let mut futures: FuturesUnordered<_> = FuturesUnordered::new();
loop {
if futures.is_empty() {
match stream.next().await {
Some(item) => futures.push(process_fn_hack(&process_fn, item)),
None => return Ok(()),
}
continue;
}
if stream.is_terminated() {
while let Some(result) = futures.next().await {
result?;
}
return Ok(());
}
let event = (async { Event::NewItem(stream.next().await) }, async {
Event::FutureCompleted(futures.next().await)
})
.race()
.await;
match event {
Event::NewItem(Some(item)) => {
futures.push(process_fn_hack(&process_fn, item));
}
Event::FutureCompleted(Some(result)) => {
result?;
}
Event::NewItem(None) | Event::FutureCompleted(None) => {
}
}
}
}