use serde_json::Value;
fn has_type(s: &Value, ty: &str) -> bool {
match s.get("type") {
Some(Value::String(t)) => t == ty,
Some(Value::Array(ts)) => ts.iter().any(|t| t == ty),
_ => false,
}
}
fn endpoint_uris(s: &Value) -> Vec<&str> {
fn one(v: &Value) -> Option<&str> {
match v {
Value::String(u) => Some(u.as_str()),
Value::Object(o) => o.get("uri")?.as_str(),
_ => None,
}
}
match s.get("serviceEndpoint") {
Some(Value::Array(vs)) => vs.iter().filter_map(one).collect(),
Some(v) => one(v).into_iter().collect(),
None => Vec::new(),
}
}
fn did_endpoint(doc: &Value, ty: &str) -> Option<String> {
doc.get("service")?
.as_array()?
.iter()
.filter(|s| has_type(s, ty))
.find_map(|s| {
endpoint_uris(s)
.into_iter()
.find(|u| u.starts_with("did:"))
.map(str::to_string)
})
}
pub fn registry_referral(doc: &Value) -> Option<String> {
did_endpoint(doc, "TrustRegistry")
}
pub fn messaging_mediator(doc: &Value) -> Option<String> {
did_endpoint(doc, "TSPTransport").or_else(|| did_endpoint(doc, "DIDCommMessaging"))
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn the_registry_referral_is_read_from_the_document() {
let doc = json!({
"service": [
{ "id": "#didcomm", "type": "DIDCommMessaging", "serviceEndpoint": "did:web:m" },
{ "id": "#tr", "type": "TrustRegistry",
"serviceEndpoint": { "uri": "did:webvh:Qm:registry.example" } }
]
});
assert_eq!(
registry_referral(&doc).as_deref(),
Some("did:webvh:Qm:registry.example")
);
let serving = json!({ "service": [
{ "type": ["TRQPRest", "TrustRegistry"], "serviceEndpoint": "https://r.example" }
]});
assert_eq!(registry_referral(&serving), None);
assert_eq!(registry_referral(&json!({})), None);
}
#[test]
fn the_mediator_prefers_tsp_and_takes_only_a_did() {
let both = json!({ "service": [
{ "type": "DIDCommMessaging",
"serviceEndpoint": [{ "uri": "did:web:didcomm.example", "accept": ["didcomm/v2"] }] },
{ "type": "TSPTransport", "serviceEndpoint": "did:web:tsp.example" }
]});
assert_eq!(
messaging_mediator(&both).as_deref(),
Some("did:web:tsp.example")
);
let didcomm = json!({ "service": [
{ "type": "DIDCommMessaging",
"serviceEndpoint": [{ "uri": "https://direct.example" }, { "uri": "did:web:m.example" }] }
]});
assert_eq!(
messaging_mediator(&didcomm).as_deref(),
Some("did:web:m.example")
);
let direct = json!({ "service": [
{ "type": "DIDCommMessaging", "serviceEndpoint": { "uri": "https://direct.example" } }
]});
assert_eq!(messaging_mediator(&direct), None);
assert_eq!(messaging_mediator(&json!({ "service": "nope" })), None);
}
}