use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Clone, Default)]
pub struct HandlerContext {
pub sender_did: Option<String>,
}
pub struct Extension<T>(pub T);
#[derive(Debug, thiserror::Error)]
pub enum DIDCommServiceError {
#[error("Handler error: {0}")]
Handler(String),
}
#[derive(Debug)]
pub struct DIDCommResponse {
pub type_: String,
pub body: Value,
pub thid: Option<String>,
}
impl DIDCommResponse {
pub fn new(type_: impl Into<String>, body: Value) -> Self {
Self {
type_: type_.into(),
body,
thid: None,
}
}
pub fn problem_report(report: ProblemReport) -> Self {
Self::new(vta_sdk::protocols::PROBLEM_REPORT_TYPE, report.to_body())
}
pub fn thid(mut self, thid: impl Into<String>) -> Self {
self.thid = Some(thid.into());
self
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct ProblemReport {
pub code: String,
pub comment: String,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub args: Vec<String>,
#[serde(rename = "escalate_to", skip_serializing_if = "Option::is_none")]
pub escalate_to: Option<String>,
}
pub mod codes {
pub const ERROR_UNAUTHORIZED: &str = "e.p.msg.unauthorized";
pub const ERROR_BAD_REQUEST: &str = "e.p.msg.bad-request";
pub const ERROR_NOT_FOUND: &str = "e.p.msg.not-found";
pub const ERROR_CONFLICT: &str = "e.p.msg.conflict";
pub const ERROR_INTERNAL: &str = "e.p.msg.internal-error";
}
pub trait ServiceProblemReport {
fn unauthorized(comment: impl Into<String>) -> Self;
fn bad_request(comment: impl Into<String>) -> Self;
fn not_found(comment: impl Into<String>) -> Self;
fn conflict(comment: impl Into<String>) -> Self;
fn internal_error(comment: impl Into<String>) -> Self;
fn with_args(self, args: Vec<String>) -> Self;
fn with_escalate_to(self, escalate_to: String) -> Self;
fn to_body(&self) -> Value;
}
impl ProblemReport {
fn from_code(code: &str, comment: impl Into<String>) -> Self {
Self {
code: code.to_string(),
comment: comment.into(),
args: Vec::new(),
escalate_to: None,
}
}
}
impl ServiceProblemReport for ProblemReport {
fn unauthorized(comment: impl Into<String>) -> Self {
Self::from_code(codes::ERROR_UNAUTHORIZED, comment)
}
fn bad_request(comment: impl Into<String>) -> Self {
Self::from_code(codes::ERROR_BAD_REQUEST, comment)
}
fn not_found(comment: impl Into<String>) -> Self {
Self::from_code(codes::ERROR_NOT_FOUND, comment)
}
fn conflict(comment: impl Into<String>) -> Self {
Self::from_code(codes::ERROR_CONFLICT, comment)
}
fn internal_error(comment: impl Into<String>) -> Self {
Self::from_code(codes::ERROR_INTERNAL, comment)
}
fn with_args(mut self, args: Vec<String>) -> Self {
self.args = args;
self
}
fn with_escalate_to(mut self, escalate_to: String) -> Self {
self.escalate_to = Some(escalate_to);
self
}
fn to_body(&self) -> Value {
serde_json::to_value(self).unwrap_or_else(|e| {
tracing::warn!(error = %e, "Failed to serialize problem report");
serde_json::json!({
"code": codes::ERROR_INTERNAL,
"comment": "Failed to serialize problem report"
})
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn problem_report_serializes_like_the_framework() {
let body = ProblemReport::bad_request("nope").to_body();
assert_eq!(body["code"], codes::ERROR_BAD_REQUEST);
assert_eq!(body["comment"], "nope");
assert!(body.get("args").is_none());
assert!(body.get("escalate_to").is_none());
let body = ProblemReport::bad_request("x")
.with_args(vec!["a".into()])
.with_escalate_to("support".into())
.to_body();
assert_eq!(body["args"], serde_json::json!(["a"]));
assert_eq!(body["escalate_to"], "support");
}
}