1use std::collections::BTreeMap;
4
5use crate::codec::{
6 bebop::BebopCodec, bson::BsonCodec, capnp::CapnpCodec, cbor::CborCodec,
7 flatbuffers::FlatbuffersCodec, json::JsonCodec, protobuf::ProtobufCodec,
8};
9use crate::value::WireCodec;
10
11pub struct CodecRegistry {
19 by_content_type: BTreeMap<&'static str, Box<dyn WireCodec>>,
20}
21
22impl CodecRegistry {
23 #[must_use]
25 pub fn new() -> Self {
26 Self {
27 by_content_type: BTreeMap::new(),
28 }
29 }
30
31 #[must_use]
41 pub fn with_baseline() -> Self {
42 let mut r = Self::new();
43 r.register(JsonCodec::new());
44 r.register(CborCodec::new());
45 r.register(ProtobufCodec::new());
46 r.register(FlatbuffersCodec::new());
47 r.register(CapnpCodec::new());
48 r.register(BebopCodec::new());
49 r.register(BsonCodec::new());
50 r
51 }
52
53 pub fn register<C: WireCodec + 'static>(&mut self, codec: C) {
57 let ct = codec.content_type();
58 self.by_content_type.insert(ct, Box::new(codec));
59 }
60
61 #[must_use]
64 pub fn for_content_type(&self, ct: &str) -> Option<&dyn WireCodec> {
65 self.by_content_type
66 .get(ct)
67 .map(std::convert::AsRef::as_ref)
68 }
69
70 pub fn content_types(&self) -> impl Iterator<Item = &'static str> + '_ {
73 self.by_content_type.keys().copied()
74 }
75}
76
77impl Default for CodecRegistry {
78 fn default() -> Self {
79 Self::new()
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 use crate::codec::json::JsonCodec;
87 use crate::value::{WireTypeId, WireValue};
88 use serde::{Deserialize, Serialize};
89
90 #[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
91 struct Probe {
92 s: String,
93 }
94
95 impl WireValue for Probe {
96 fn wire_type_id() -> WireTypeId {
97 WireTypeId::new("test.Probe")
98 }
99 }
100
101 #[test]
102 fn register_and_lookup_by_content_type() {
103 let mut codec = JsonCodec::new();
104 codec.register::<Probe>();
105
106 let mut registry = CodecRegistry::new();
107 registry.register(codec);
108
109 let codec = registry
110 .for_content_type("application/json")
111 .expect("json codec installed");
112 assert_eq!(codec.content_type(), "application/json");
113 }
114
115 #[test]
116 fn unknown_content_type_returns_none() {
117 let registry = CodecRegistry::with_baseline();
118 assert!(registry.for_content_type("application/x-mystery").is_none());
119 }
120
121 #[test]
122 fn baseline_registers_all_seven_content_types() {
123 let r = CodecRegistry::with_baseline();
124 let cts: Vec<&'static str> = r.content_types().collect();
125 assert!(cts.contains(&"application/json"));
126 assert!(cts.contains(&"application/cbor"));
127 assert!(cts.contains(&"application/x-protobuf"));
128 assert!(cts.contains(&"application/octet-stream;schema=flatbuffers"));
129 assert!(cts.contains(&"application/capnproto"));
130 assert!(cts.contains(&"application/x-bebop"));
131 assert!(cts.contains(&"application/bson"));
132 assert_eq!(cts.len(), 7);
133 }
134
135 #[test]
136 fn register_replaces_existing_codec_for_same_content_type() {
137 let empty = JsonCodec::new();
144 let mut populated = JsonCodec::new();
145 populated.register::<Probe>();
146
147 let mut r = CodecRegistry::new();
148 r.register(empty);
149 r.register(populated);
150
151 let codec = r.for_content_type("application/json").expect("present");
152 let probe = Probe { s: "hello".into() };
153 let bytes = codec.encode(&probe).expect("populated codec encodes");
154 assert!(!bytes.is_empty());
155 }
156
157 #[test]
158 fn default_is_empty() {
159 let r = CodecRegistry::default();
160 assert_eq!(r.content_types().count(), 0);
161 assert!(r.for_content_type("application/json").is_none());
162 }
163}