qualia_client_core/
qdp_server.rs1use crate::front_door::FrontDoorRecord;
14
15pub const WELL_KNOWN_QDP_PATH: &str = "/.well-known/QDP";
17
18#[derive(Debug, Clone)]
20pub struct QdpResponse {
21 pub content_type: String,
23 pub body: Vec<u8>,
25}
26
27pub fn render_profile(rec: &FrontDoorRecord, accept: &str) -> Result<QdpResponse, String> {
37 if accept.contains("application/ld+json") {
38 let body = serde_json::to_vec(&rec.to_json_ld()).map_err(|e| e.to_string())?;
39 Ok(QdpResponse {
40 content_type: "application/ld+json".to_string(),
41 body,
42 })
43 } else if accept.contains("application/cbor") {
44 let body = rec.to_cbor_ld()?;
45 Ok(QdpResponse {
46 content_type: "application/cbor".to_string(),
47 body,
48 })
49 } else {
50 let body = rec.to_turtle().into_bytes();
51 Ok(QdpResponse {
52 content_type: "text/turtle".to_string(),
53 body,
54 })
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61 use crate::domains::AgentType;
62 use crate::front_door::FrontDoorRecord;
63
64 fn minimal_record() -> FrontDoorRecord {
66 FrontDoorRecord {
67 domain: "a.example".to_string(),
68 agent_type: AgentType::NaturalPerson,
69 front_door_did: "did:qdp:a".to_string(),
70 name: None,
71 webid: None,
72 services: vec![],
73 identity_pubkey_hex: None,
74 wireguard_pubkey_hex: None,
75 overlay_addr: None,
76 profile_url: None,
77 }
78 }
79
80 #[test]
81 fn well_known_path_is_stable() {
82 assert_eq!(WELL_KNOWN_QDP_PATH, "/.well-known/QDP");
83 }
84
85 #[test]
86 fn turtle_is_the_default() {
87 let rec = minimal_record();
88 let resp = render_profile(&rec, "text/turtle").expect("turtle renders");
89 assert_eq!(resp.content_type, "text/turtle");
90 assert!(!resp.body.is_empty());
91
92 let any = render_profile(&rec, "*/*").expect("wildcard renders");
94 assert_eq!(any.content_type, "text/turtle");
95 assert!(!any.body.is_empty());
96 }
97
98 #[test]
99 fn json_ld_negotiation_roundtrips() {
100 let rec = minimal_record();
101 let resp = render_profile(&rec, "application/ld+json").expect("json-ld renders");
102 assert_eq!(resp.content_type, "application/ld+json");
103
104 let parsed: serde_json::Value =
105 serde_json::from_slice(&resp.body).expect("body is valid JSON");
106 assert!(
107 parsed.get("@type").is_some(),
108 "JSON-LD node carries an @type"
109 );
110 }
111
112 #[test]
113 fn cbor_negotiation_produces_bytes() {
114 let rec = minimal_record();
115 let resp = render_profile(&rec, "application/cbor").expect("cbor renders");
116 assert_eq!(resp.content_type, "application/cbor");
117 assert!(!resp.body.is_empty());
118 }
119}