1#![allow(deprecated)]
23
24pub mod defaults;
25pub mod gateway;
26mod include;
27pub mod qos;
28pub mod wrappers;
29
30#[allow(unused_imports)]
31use std::convert::TryFrom;
32#[allow(unused_imports)]
33use std::str::FromStr;
34use std::{
36 any::Any,
37 collections::HashSet,
38 fmt,
39 io::Read,
40 net::SocketAddr,
41 num::{NonZeroU16, NonZeroUsize},
42 ops::{self, Bound, Deref, DerefMut, RangeBounds},
43 path::Path,
44 sync::{Arc, Weak},
45};
46
47use include::recursive_include;
48use nonempty_collections::NEVec;
49use qos::{PublisherQoSConfList, QosFilter, QosOverwriteMessage, QosOverwrites};
50use secrecy::{CloneableSecret, DebugSecret, Secret, SerializableSecret, Zeroize};
51use serde::{Deserialize, Serialize};
52use serde_json::{Map, Value};
53use validated_struct::ValidatedMapAssociatedTypes;
54pub use validated_struct::{GetError, ValidatedMap};
55pub use wrappers::ZenohId;
56pub use zenoh_protocol::core::{
57 whatami, EndPoint, EndPoints, Locator, WhatAmI, WhatAmIMatcher, WhatAmIMatcherVisitor,
58};
59use zenoh_protocol::{
60 core::{
61 key_expr::{OwnedKeyExpr, OwnedNonWildKeyExpr},
62 Bits, RegionName,
63 },
64 transport::{BatchSize, TransportSn},
65};
66use zenoh_result::{bail, zerror, ZResult};
67use zenoh_util::{LibLoader, LibSearchDirs};
68
69pub mod mode_dependent;
70pub use mode_dependent::*;
71
72pub mod connection_retry;
73pub use connection_retry::*;
74
75#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
77pub struct SecretString(String);
78
79impl ops::Deref for SecretString {
80 type Target = String;
81
82 fn deref(&self) -> &Self::Target {
83 &self.0
84 }
85}
86
87impl SerializableSecret for SecretString {}
88impl DebugSecret for SecretString {}
89impl CloneableSecret for SecretString {}
90impl Zeroize for SecretString {
91 fn zeroize(&mut self) {
92 self.0 = "".to_string();
93 }
94}
95
96pub type SecretValue = Secret<SecretString>;
97
98#[derive(Debug, Deserialize, Serialize, Clone)]
99pub struct TransportWeight {
100 pub dst_zid: ZenohId,
102 pub weight: NonZeroU16,
104}
105
106#[derive(Debug, Deserialize, Serialize, Clone, Copy, Eq, PartialEq)]
107#[serde(rename_all = "snake_case")]
108pub enum InterceptorFlow {
109 Egress,
110 Ingress,
111}
112
113#[derive(Clone, Copy, Debug, Serialize, Deserialize, Eq, Hash, PartialEq)]
117#[serde(rename_all = "snake_case")]
118pub enum DataMessage {
119 Put,
120 Delete,
121 Query,
122 Reply,
123}
124
125#[derive(Debug, Deserialize, Serialize, Clone)]
126#[serde(deny_unknown_fields)]
127pub struct DownsamplingRuleConf {
128 pub key_expr: OwnedKeyExpr,
131 pub freq: f64,
133}
134
135#[derive(Debug, Deserialize, Serialize, Clone)]
136#[serde(deny_unknown_fields)]
137pub struct DownsamplingItemConf {
138 pub id: Option<String>,
140 pub interfaces: Option<NEVec<String>>,
143 pub link_protocols: Option<NEVec<InterceptorLink>>,
146 pub messages: NEVec<DataMessage>,
148 pub rules: NEVec<DownsamplingRuleConf>,
150 pub flows: Option<NEVec<InterceptorFlow>>,
152}
153
154#[derive(Serialize, Debug, Deserialize, Clone)]
155#[serde(deny_unknown_fields)]
156pub struct LowPassFilterConf {
157 pub id: Option<String>,
158 pub interfaces: Option<NEVec<String>>,
159 pub link_protocols: Option<NEVec<InterceptorLink>>,
160 pub flows: Option<NEVec<InterceptorFlow>>,
161 pub messages: NEVec<DataMessage>,
162 pub key_exprs: NEVec<OwnedKeyExpr>,
163 pub size_limit: usize,
164}
165
166#[derive(Serialize, Debug, Deserialize, Clone)]
167#[serde(deny_unknown_fields)]
168pub struct AclConfigRule {
169 pub id: String,
170 pub key_exprs: NEVec<OwnedKeyExpr>,
171 pub messages: NEVec<AclMessage>,
172 pub flows: Option<NEVec<InterceptorFlow>>,
173 pub permission: Permission,
174}
175
176#[derive(Serialize, Debug, Deserialize, Clone)]
177#[serde(deny_unknown_fields)]
178pub struct AclConfigSubjects {
179 pub id: String,
180 pub interfaces: Option<NEVec<Interface>>,
181 pub cert_common_names: Option<NEVec<CertCommonName>>,
182 pub usernames: Option<NEVec<Username>>,
183 pub link_protocols: Option<NEVec<InterceptorLink>>,
184 pub zids: Option<NEVec<ZenohId>>,
185}
186
187#[derive(Debug, Clone, PartialEq, Eq)]
188pub struct ConfRange {
189 start: Option<u64>,
190 end: Option<u64>,
191}
192
193impl ConfRange {
194 pub fn new(start: Option<u64>, end: Option<u64>) -> Self {
195 Self { start, end }
196 }
197}
198
199impl RangeBounds<u64> for ConfRange {
200 fn start_bound(&self) -> Bound<&u64> {
201 match self.start {
202 Some(ref start) => Bound::Included(start),
203 None => Bound::Unbounded,
204 }
205 }
206 fn end_bound(&self) -> Bound<&u64> {
207 match self.end {
208 Some(ref end) => Bound::Included(end),
209 None => Bound::Unbounded,
210 }
211 }
212}
213
214impl serde::Serialize for ConfRange {
215 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
216 where
217 S: serde::Serializer,
218 {
219 serializer.serialize_str(&format!(
220 "{}..{}",
221 self.start.unwrap_or_default(),
222 self.end.unwrap_or_default()
223 ))
224 }
225}
226
227impl<'a> serde::Deserialize<'a> for ConfRange {
228 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
229 where
230 D: serde::Deserializer<'a>,
231 {
232 struct V;
233
234 impl serde::de::Visitor<'_> for V {
235 type Value = ConfRange;
236
237 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
238 formatter.write_str("range string")
239 }
240
241 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
242 where
243 E: serde::de::Error,
244 {
245 let (start, end) = v
246 .split_once("..")
247 .ok_or_else(|| serde::de::Error::custom("invalid range"))?;
248 let parse_bound = |bound: &str| {
249 (!bound.is_empty())
250 .then(|| bound.parse::<u64>())
251 .transpose()
252 .map_err(|_| serde::de::Error::custom("invalid range bound"))
253 };
254 Ok(ConfRange::new(parse_bound(start)?, parse_bound(end)?))
255 }
256 }
257 deserializer.deserialize_str(V)
258 }
259}
260
261#[derive(Debug, Deserialize, Serialize, Clone)]
262#[serde(deny_unknown_fields)]
263pub struct QosOverwriteItemConf {
264 pub id: Option<String>,
266 pub zids: Option<NEVec<ZenohId>>,
268 pub interfaces: Option<NEVec<String>>,
271 pub link_protocols: Option<NEVec<InterceptorLink>>,
274 pub messages: NEVec<QosOverwriteMessage>,
276 pub key_exprs: Option<NEVec<OwnedKeyExpr>>,
278 pub overwrite: QosOverwrites,
280 pub flows: Option<NEVec<InterceptorFlow>>,
282 pub qos: Option<QosFilter>,
284 pub payload_size: Option<ConfRange>,
286}
287
288#[derive(Serialize, Debug, Deserialize, Clone, PartialEq, Eq, Hash)]
289pub struct Interface(pub String);
290
291impl std::fmt::Display for Interface {
292 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
293 write!(f, "Interface({})", self.0)
294 }
295}
296
297#[derive(Serialize, Debug, Deserialize, Clone, PartialEq, Eq, Hash)]
298pub struct CertCommonName(pub String);
299
300impl std::fmt::Display for CertCommonName {
301 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
302 write!(f, "CertCommonName({})", self.0)
303 }
304}
305
306#[derive(Serialize, Debug, Deserialize, Clone, PartialEq, Eq, Hash)]
307pub struct Username(pub String);
308
309impl std::fmt::Display for Username {
310 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
311 write!(f, "Username({})", self.0)
312 }
313}
314
315#[derive(Serialize, Debug, Deserialize, Clone, PartialEq, Eq, Hash)]
316#[serde(rename_all = "kebab-case")]
317pub enum InterceptorLink {
318 Tcp,
319 Udp,
320 Tls,
321 Quic,
322 Serial,
323 Unixpipe,
324 UnixsockStream,
325 Vsock,
326 Ws,
327}
328
329impl std::fmt::Display for InterceptorLink {
330 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
331 write!(f, "Transport({self:?})")
332 }
333}
334
335#[derive(Serialize, Debug, Deserialize, Clone, PartialEq, Eq, Hash)]
336#[serde(deny_unknown_fields)]
337pub struct AclConfigPolicyEntry {
338 pub id: Option<String>,
339 pub rules: Vec<String>,
340 pub subjects: Vec<String>,
341}
342
343#[derive(Clone, Serialize, Debug, Deserialize)]
344#[serde(deny_unknown_fields)]
345pub struct PolicyRule {
346 pub subject_id: usize,
347 pub key_expr: OwnedKeyExpr,
348 pub message: AclMessage,
349 pub permission: Permission,
350 pub flow: InterceptorFlow,
351}
352
353#[derive(Clone, Copy, Debug, Serialize, Deserialize, Eq, Hash, PartialEq)]
354#[serde(rename_all = "snake_case")]
355pub enum AclMessage {
356 Put,
357 Delete,
358 DeclareSubscriber,
359 Query,
360 DeclareQueryable,
361 Reply,
362 LivelinessToken,
363 DeclareLivelinessSubscriber,
364 LivelinessQuery,
365}
366
367#[derive(Clone, Copy, Debug, Serialize, Deserialize, Eq, Hash, PartialEq)]
368#[serde(rename_all = "snake_case")]
369pub enum Permission {
370 Allow,
371 Deny,
372}
373
374#[derive(Default, Clone, Copy, Debug, Serialize, Deserialize, Eq, Hash, PartialEq)]
376#[serde(rename_all = "kebab-case")]
377pub enum AutoConnectStrategy {
378 #[default]
381 Always,
382 GreaterZid,
387}
388
389#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
390pub struct StatsFilterConfig {
391 pub key: OwnedKeyExpr,
392}
393
394pub trait ConfigValidator: Send + Sync {
395 fn check_config(
396 &self,
397 _plugin_name: &str,
398 _path: &str,
399 _current: &serde_json::Map<String, serde_json::Value>,
400 _new: &serde_json::Map<String, serde_json::Value>,
401 ) -> ZResult<Option<serde_json::Map<String, serde_json::Value>>> {
402 Ok(None)
403 }
404}
405
406impl ConfigValidator for () {}
409
410pub fn empty() -> Config {
412 Config::default()
413}
414
415pub fn default() -> Config {
417 peer()
418}
419
420pub fn peer() -> Config {
422 let mut config = Config::default();
423 config.set_mode(Some(WhatAmI::Peer)).unwrap();
424 config
425}
426
427pub fn client<I: IntoIterator<Item = T>, T: Into<EndPoint>>(peers: I) -> Config {
429 let mut config = Config::default();
430 config.set_mode(Some(WhatAmI::Client)).unwrap();
431 config.connect.endpoints = ModeDependentValue::Unique(
432 peers
433 .into_iter()
434 .map(|t| EndPoints::Single(t.into()))
435 .collect(),
436 );
437 config
438}
439
440#[test]
441fn config_keys() {
442 let c = Config::default();
443 dbg!(Vec::from_iter(c.keys()));
444}
445
446#[derive(Clone, Debug, Default)]
449struct DeprecatedPeersFailoverBrokering(Option<bool>);
450
451impl serde::Serialize for DeprecatedPeersFailoverBrokering {
452 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
453 self.0.serialize(serializer)
454 }
455}
456
457impl<'de> serde::Deserialize<'de> for DeprecatedPeersFailoverBrokering {
458 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
459 tracing::warn!(
460 "`routing.router.peers_failover_brokering` is deprecated and has no effect; \
461 please remove it from your configuration"
462 );
463 Option::<bool>::deserialize(deserializer).map(Self)
464 }
465}
466
467#[derive(Clone, Debug, Default)]
470struct DeprecatedRoutingPeer(Option<Value>);
471
472impl serde::Serialize for DeprecatedRoutingPeer {
473 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
474 self.0.serialize(serializer)
475 }
476}
477
478impl<'de> serde::Deserialize<'de> for DeprecatedRoutingPeer {
479 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
480 tracing::warn!(
481 "routing.peer.mode` and `routing.peer.linkstate` are deprecated and have no effect; \
482 please remove them from your configuration"
483 );
484 Option::<Value>::deserialize(deserializer).map(Self)
485 }
486}
487
488validated_struct::validator! {
489 #[derive(Default)]
490 #[recursive_attrs]
491 #[derive(serde::Deserialize, serde::Serialize, Clone, Debug)]
492 #[serde(default)]
493 #[serde(deny_unknown_fields)]
494 #[doc(hidden)]
495 Config {
496 id: Option<ZenohId>,
499 metadata: Value,
501 mode: Option<whatami::WhatAmI>,
503 region_name: Option<RegionName>,
504 pub gateway: gateway::GatewayConf,
505 pub connect:
507 ConnectConfig {
508 pub timeout_ms: Option<ModeDependentValue<i64>>,
510 pub endpoints: ModeDependentValue<Vec<EndPoints>>,
512 pub exit_on_failure: Option<ModeDependentValue<bool>>,
514 pub retry: Option<connection_retry::ConnectionRetryModeDependentConf>,
515 },
516 pub listen:
518 ListenConfig {
519 pub timeout_ms: Option<ModeDependentValue<i64>>,
521 pub endpoints: ModeDependentValue<Vec<EndPoint>>,
523 pub exit_on_failure: Option<ModeDependentValue<bool>>,
525 pub retry: Option<connection_retry::ConnectionRetryModeDependentConf>,
526 },
527 pub open: #[derive(Default)]
529 OpenConf {
530 pub return_conditions: #[derive(Default)]
532 ReturnConditionsConf {
533 connect_scouted: Option<bool>,
536 declares: Option<bool>,
539 },
540 },
541 pub scouting: #[derive(Default)]
542 ScoutingConf {
543 timeout: Option<u64>,
545 delay: Option<u64>,
547 pub multicast: #[derive(Default)]
549 ScoutingMulticastConf {
550 enabled: Option<bool>,
552 address: Option<SocketAddr>,
554 interface: Option<String>,
556 pub ttl: Option<u32>,
558 autoconnect: Option<ModeDependentValue<WhatAmIMatcher>>,
560 autoconnect_strategy: Option<ModeDependentValue<TargetDependentValue<AutoConnectStrategy>>>,
562 listen: Option<ModeDependentValue<bool>>,
564 },
565 pub gossip: #[derive(Default)]
567 GossipConf {
568 enabled: Option<bool>,
570 multihop: Option<bool>,
576 target: Option<ModeDependentValue<WhatAmIMatcher>>,
578 autoconnect: Option<ModeDependentValue<WhatAmIMatcher>>,
580 autoconnect_strategy: Option<ModeDependentValue<TargetDependentValue<AutoConnectStrategy>>>,
582 },
583 },
584
585 pub timestamping: #[derive(Default)]
587 TimestampingConf {
588 enabled: Option<ModeDependentValue<bool>>,
590 drop_future_timestamp: Option<bool>,
594 },
595
596 queries_default_timeout: Option<u64>,
598
599 pub routing: #[derive(Default)]
601 RoutingConf {
602 pub router: #[derive(Default)]
604 RouterRoutingConf {
605 #[serde(default, skip_serializing)]
607 peers_failover_brokering: DeprecatedPeersFailoverBrokering,
608 pub linkstate: #[derive(Default)]
610 LinkstateConf {
611 pub transport_weights: Vec<TransportWeight>,
616 },
617 },
618 #[serde(default, skip_serializing)]
620 peer: DeprecatedRoutingPeer,
621 pub interests: #[derive(Default)]
624 InterestsConf {
625 timeout: Option<u64>,
627 },
628 },
629
630 pub aggregation: #[derive(Default)]
632 AggregationConf {
633 subscribers: Vec<OwnedKeyExpr>,
635 publishers: Vec<OwnedKeyExpr>,
637 },
638
639 pub qos: #[derive(Default)]
641 QoSConfig {
642 publication: PublisherQoSConfList,
644 network: Vec<QosOverwriteItemConf>,
646 },
647
648 pub transport: #[derive(Default)]
649 TransportConf {
650 pub unicast: TransportUnicastConf {
651 open_timeout: u64,
653 accept_timeout: u64,
655 accept_pending: usize,
657 max_sessions: usize,
659 max_links: usize,
664 lowlatency: bool,
668 pub qos: QoSUnicastConf {
669 enabled: bool
672 },
673 pub compression: CompressionUnicastConf {
674 enabled: bool,
677 },
678 },
679 pub multicast: TransportMulticastConf {
680 join_interval: Option<u64>,
682 max_sessions: Option<usize>,
684 pub qos: QoSMulticastConf {
685 enabled: bool
688 },
689 pub compression: CompressionMulticastConf {
690 enabled: bool,
693 },
694 },
695 pub link: #[derive(Default)]
696 TransportLinkConf {
697 pub protocols: Option<Vec<String>>,
700 pub tx: LinkTxConf {
701 sequence_number_resolution: Bits where (sequence_number_resolution_validator),
705 lease: u64,
707 keep_alive: usize,
709 batch_size: BatchSize,
711 pub queue: #[derive(Default)]
712 QueueConf {
713 pub size: QueueSizeConf {
719 control: usize,
720 real_time: usize,
721 interactive_high: usize,
722 interactive_low: usize,
723 data_high: usize,
724 data: usize,
725 data_low: usize,
726 background: usize,
727 } where (queue_size_validator),
728 pub congestion_control: #[derive(Default)]
732 CongestionControlConf {
733 pub drop: CongestionControlDropConf {
735 wait_before_drop: i64,
738 max_wait_before_drop_fragments: i64,
740 },
741 pub block: CongestionControlBlockConf {
743 wait_before_close: i64,
746 },
747 },
748 pub batching: BatchingConf {
749 enabled: bool,
754 time_limit: u64,
756 },
757 pub allocation: #[derive(Default, Copy, PartialEq, Eq)]
761 QueueAllocConf {
762 pub mode: QueueAllocMode,
763 },
764 },
765 threads: usize,
767 },
768 pub rx: LinkRxConf {
769 buffer_size: usize,
775 max_message_size: usize,
778 },
779 pub tls: #[derive(Default)]
780 TLSConf {
781 root_ca_certificate: Option<String>,
782 listen_private_key: Option<String>,
783 listen_certificate: Option<String>,
784 enable_mtls: Option<bool>,
785 connect_private_key: Option<String>,
786 connect_certificate: Option<String>,
787 verify_name_on_connect: Option<bool>,
788 close_link_on_expiration: Option<bool>,
789 pub so_sndbuf: Option<u32>,
791 pub so_rcvbuf: Option<u32>,
793 #[serde(skip_serializing)]
795 root_ca_certificate_base64: Option<SecretValue>,
796 #[serde(skip_serializing)]
797 listen_private_key_base64: Option<SecretValue>,
798 #[serde(skip_serializing)]
799 listen_certificate_base64: Option<SecretValue>,
800 #[serde(skip_serializing)]
801 connect_private_key_base64 : Option<SecretValue>,
802 #[serde(skip_serializing)]
803 connect_certificate_base64 : Option<SecretValue>,
804 },
805 pub tcp: #[derive(Default)]
806 TcpConf {
807 pub so_sndbuf: Option<u32>,
809 pub so_rcvbuf: Option<u32>,
811 },
812 pub unixpipe: #[derive(Default)]
813 UnixPipeConf {
814 file_access_mask: Option<u32>
815 },
816 },
817 pub shared_memory:
818 ShmConf {
819 enabled: bool,
826 mode: ShmInitMode,
833 pub transport_optimization:
834 LargeMessageTransportOpt {
835 enabled: bool,
838 pool_size: NonZeroUsize,
840 message_size_threshold: usize,
842 messages: Vec<DataMessage>,
848 },
849 },
850 pub auth: #[derive(Default)]
851 AuthConf {
852 pub usrpwd: #[derive(Default)]
855 UsrPwdConf {
856 user: Option<String>,
857 password: Option<String>,
858 dictionary_file: Option<String>,
860 } where (user_conf_validator),
861 pub pubkey: #[derive(Default)]
862 PubKeyConf {
863 public_key_pem: Option<String>,
864 private_key_pem: Option<String>,
865 public_key_file: Option<String>,
866 private_key_file: Option<String>,
867 key_size: Option<usize>,
868 known_keys_file: Option<String>,
869 },
870 },
871
872 },
873 pub adminspace: #[derive(Default)]
875 AdminSpaceConf {
881 #[serde(default = "set_false")]
883 pub enabled: bool,
884 pub permissions:
886 PermissionsConf {
887 #[serde(default = "set_true")]
889 pub read: bool,
890 #[serde(default = "set_false")]
892 pub write: bool,
893 },
894
895 },
896
897 pub namespace: Option<OwnedNonWildKeyExpr>,
906
907 downsampling: Vec<DownsamplingItemConf>,
909
910 pub access_control: AclConfig {
912 pub enabled: bool,
913 pub default_permission: Permission,
914 pub rules: Option<Vec<AclConfigRule>>,
915 pub subjects: Option<Vec<AclConfigSubjects>>,
916 pub policies: Option<Vec<AclConfigPolicyEntry>>,
917 },
918
919 pub low_pass_filter: Vec<LowPassFilterConf>,
921
922 pub stats: #[derive(Default, PartialEq, Eq)] StatsConfig {
924 filters: Vec<StatsFilterConfig>,
925 },
926
927 pub plugins_loading: #[derive(Default)]
930 PluginsLoading {
931 pub enabled: bool,
932 pub search_dirs: LibSearchDirs,
933 },
934 #[validated(recursive_accessors)]
935 plugins: PluginsConfig,
939 }
940}
941
942#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
943#[serde(rename_all = "snake_case")]
944pub enum QueueAllocMode {
945 Init,
946 #[default]
947 Lazy,
948}
949
950#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
951#[serde(rename_all = "snake_case")]
952pub enum ShmInitMode {
953 Init,
954 #[default]
955 Lazy,
956}
957
958impl Default for PermissionsConf {
959 fn default() -> Self {
960 PermissionsConf {
961 read: true,
962 write: false,
963 }
964 }
965}
966
967fn set_true() -> bool {
968 true
969}
970fn set_false() -> bool {
971 false
972}
973
974#[test]
975fn config_deser() {
976 let config = Config::from_deserializer(
977 &mut json5::Deserializer::from_str(
978 r#"{
979 scouting: {
980 multicast: {
981 enabled: false,
982 autoconnect: ["peer", "router"]
983 }
984 }
985 }"#,
986 )
987 .unwrap(),
988 )
989 .unwrap();
990 assert_eq!(*config.scouting().multicast().enabled(), Some(false));
991 assert_eq!(
992 config.scouting().multicast().autoconnect().router(),
993 Some(&WhatAmIMatcher::empty().router().peer())
994 );
995 assert_eq!(
996 config.scouting().multicast().autoconnect().peer(),
997 Some(&WhatAmIMatcher::empty().router().peer())
998 );
999 assert_eq!(
1000 config.scouting().multicast().autoconnect().client(),
1001 Some(&WhatAmIMatcher::empty().router().peer())
1002 );
1003 let config = Config::from_deserializer(
1004 &mut json5::Deserializer::from_str(
1005 r#"{
1006 scouting: {
1007 multicast: {
1008 enabled: false,
1009 autoconnect: {router: [], peer: ["peer", "router"]}
1010 }
1011 }
1012 }"#,
1013 )
1014 .unwrap(),
1015 )
1016 .unwrap();
1017 assert_eq!(*config.scouting().multicast().enabled(), Some(false));
1018 assert_eq!(
1019 config.scouting().multicast().autoconnect().router(),
1020 Some(&WhatAmIMatcher::empty())
1021 );
1022 assert_eq!(
1023 config.scouting().multicast().autoconnect().peer(),
1024 Some(&WhatAmIMatcher::empty().router().peer())
1025 );
1026 assert_eq!(config.scouting().multicast().autoconnect().client(), None);
1027 let config = Config::from_deserializer(
1028 &mut json5::Deserializer::from_str(
1029 r#"{transport: { auth: { usrpwd: { user: null, password: null, dictionary_file: "file" }}}}"#,
1030 )
1031 .unwrap(),
1032 )
1033 .unwrap();
1034 assert_eq!(
1035 config
1036 .transport()
1037 .auth()
1038 .usrpwd()
1039 .dictionary_file()
1040 .as_ref()
1041 .map(|s| s.as_ref()),
1042 Some("file")
1043 );
1044 std::mem::drop(Config::from_deserializer(
1045 &mut json5::Deserializer::from_str(
1046 r#"{transport: { auth: { usrpwd: { user: null, password: null, user_password_dictionary: "file" }}}}"#,
1047 )
1048 .unwrap(),
1049 )
1050 .unwrap_err());
1051
1052 let config = Config::from_deserializer(
1053 &mut json5::Deserializer::from_str(
1054 r#"{
1055 qos: {
1056 network: [
1057 {
1058 messages: ["put"],
1059 overwrite: {
1060 priority: "foo",
1061 },
1062 },
1063 ],
1064 }
1065 }"#,
1066 )
1067 .unwrap(),
1068 );
1069 assert!(config.is_err());
1070
1071 let config = Config::from_deserializer(
1072 &mut json5::Deserializer::from_str(
1073 r#"{
1074 qos: {
1075 network: [
1076 {
1077 messages: ["put"],
1078 overwrite: {
1079 priority: +8,
1080 },
1081 },
1082 ],
1083 }
1084 }"#,
1085 )
1086 .unwrap(),
1087 );
1088 assert!(config.is_err());
1089
1090 let config = Config::from_deserializer(
1091 &mut json5::Deserializer::from_str(
1092 r#"{
1093 qos: {
1094 network: [
1095 {
1096 messages: ["put"],
1097 overwrite: {
1098 priority: "data_high",
1099 },
1100 },
1101 ],
1102 }
1103 }"#,
1104 )
1105 .unwrap(),
1106 )
1107 .unwrap();
1108 assert_eq!(
1109 config.qos().network().first().unwrap().overwrite.priority,
1110 Some(qos::PriorityUpdateConf::Priority(
1111 qos::PriorityConf::DataHigh
1112 ))
1113 );
1114
1115 let config = Config::from_deserializer(
1116 &mut json5::Deserializer::from_str(
1117 r#"{
1118 qos: {
1119 network: [
1120 {
1121 messages: ["put"],
1122 overwrite: {
1123 priority: +1,
1124 },
1125 },
1126 ],
1127 }
1128 }"#,
1129 )
1130 .unwrap(),
1131 )
1132 .unwrap();
1133 assert_eq!(
1134 config.qos().network().first().unwrap().overwrite.priority,
1135 Some(qos::PriorityUpdateConf::Increment(1))
1136 );
1137
1138 let config = Config::from_deserializer(
1139 &mut json5::Deserializer::from_str(
1140 r#"{
1141 qos: {
1142 network: [
1143 {
1144 messages: ["put"],
1145 payload_size: "0..99",
1146 overwrite: {},
1147 },
1148 ],
1149 }
1150 }"#,
1151 )
1152 .unwrap(),
1153 )
1154 .unwrap();
1155 assert_eq!(
1156 config
1157 .qos()
1158 .network()
1159 .first()
1160 .unwrap()
1161 .payload_size
1162 .as_ref()
1163 .map(|r| (r.start_bound(), r.end_bound())),
1164 Some((Bound::Included(&0), Bound::Included(&99)))
1165 );
1166
1167 let config = Config::from_deserializer(
1168 &mut json5::Deserializer::from_str(
1169 r#"{
1170 qos: {
1171 network: [
1172 {
1173 messages: ["put"],
1174 payload_size: "100..",
1175 overwrite: {},
1176 },
1177 ],
1178 }
1179 }"#,
1180 )
1181 .unwrap(),
1182 )
1183 .unwrap();
1184 assert_eq!(
1185 config
1186 .qos()
1187 .network()
1188 .first()
1189 .unwrap()
1190 .payload_size
1191 .as_ref()
1192 .map(|r| (r.start_bound(), r.end_bound())),
1193 Some((Bound::Included(&100), Bound::Unbounded))
1194 );
1195
1196 let config = Config::from_deserializer(
1197 &mut json5::Deserializer::from_str(
1198 r#"{
1199 qos: {
1200 network: [
1201 {
1202 messages: ["put"],
1203 qos: {
1204 congestion_control: "drop",
1205 priority: "data",
1206 express: true,
1207 reliability: "reliable",
1208 },
1209 overwrite: {},
1210 },
1211 ],
1212 }
1213 }"#,
1214 )
1215 .unwrap(),
1216 )
1217 .unwrap();
1218 assert_eq!(
1219 config.qos().network().first().unwrap().qos,
1220 Some(QosFilter {
1221 congestion_control: Some(qos::CongestionControlConf::Drop),
1222 priority: Some(qos::PriorityConf::Data),
1223 express: Some(true),
1224 reliability: Some(qos::ReliabilityConf::Reliable),
1225 })
1226 );
1227
1228 let config = Config::from_deserializer(
1229 &mut json5::Deserializer::from_str(
1230 r#"{
1231 mode: "client",
1232 connect: {
1233 endpoints: [
1234 { strategy: "allOf", locators: ["tcp/127.0.0.1:7447?rel=0", "tcp/127.0.0.1:7448?rel=1"] },
1235 ]
1236 }
1237 }"#,
1238 )
1239 .unwrap(),
1240 )
1241 .unwrap();
1242 assert_eq!(*config.mode(), Some(WhatAmI::Client));
1243 let endpoints = config.connect().endpoints().client().unwrap();
1244 assert_eq!(endpoints.len(), 1);
1245 assert_eq!(
1246 endpoints[0],
1247 EndPoints::Locators(zenoh_protocol::core::Locators {
1248 strategy: zenoh_protocol::core::LocatorsStrategy::AllOf,
1249 locators: vec![
1250 EndPoint::from_str("tcp/127.0.0.1:7447?rel=0").unwrap(),
1251 EndPoint::from_str("tcp/127.0.0.1:7448?rel=1").unwrap()
1252 ]
1253 })
1254 );
1255
1256 dbg!(Config::from_file("../../DEFAULT_CONFIG.json5").unwrap());
1257}
1258
1259impl Config {
1260 pub fn insert<'d, D: serde::Deserializer<'d>>(
1261 &mut self,
1262 key: &str,
1263 value: D,
1264 ) -> Result<(), validated_struct::InsertionError>
1265 where
1266 validated_struct::InsertionError: From<D::Error>,
1267 {
1268 <Self as ValidatedMap>::insert(self, key, value)
1269 }
1270
1271 pub fn get(
1272 &self,
1273 key: &str,
1274 ) -> Result<<Self as ValidatedMapAssociatedTypes<'_>>::Accessor, GetError> {
1275 <Self as ValidatedMap>::get(self, key)
1276 }
1277
1278 pub fn get_json(&self, key: &str) -> Result<String, GetError> {
1279 <Self as ValidatedMap>::get_json(self, key)
1280 }
1281
1282 pub fn insert_json5(
1283 &mut self,
1284 key: &str,
1285 value: &str,
1286 ) -> Result<(), validated_struct::InsertionError> {
1287 <Self as ValidatedMap>::insert_json5(self, key, value)
1288 }
1289
1290 pub fn try_insert_json5_array_item(
1307 &mut self,
1308 key: &str,
1309 value: &str,
1310 ) -> Result<bool, validated_struct::InsertionError> {
1311 let Some((prefix, field_value)) = key.split_once('=') else {
1312 return Ok(false);
1313 };
1314 let (array_key, field_name) =
1315 prefix
1316 .rsplit_once('/')
1317 .ok_or(validated_struct::InsertionError::Str(
1318 "missing field filter",
1319 ))?;
1320 let new_item = json5::from_str::<serde_json::Value>(value)?;
1321 if new_item
1322 .as_object()
1323 .and_then(|map| map.get(field_name))
1324 .and_then(|v| v.as_str())
1325 != Some(field_value)
1326 {
1327 return Err(validated_struct::InsertionError::String(format!(
1328 "field filter mismatch: value must be an object containing {field_name}=\"{field_value}\""
1329 )));
1330 }
1331 let current = serde_json::from_str::<serde_json::Value>(
1332 &self
1333 .get_json(array_key)
1334 .map_err(|err| validated_struct::InsertionError::String(err.to_string()))?,
1335 )?;
1336 let serde_json::Value::Array(mut list) = current else {
1337 return Err(validated_struct::InsertionError::Str("not an array"));
1338 };
1339 let mut new_item = Some(new_item);
1340 for item in list.iter_mut() {
1341 let serde_json::Value::Object(map) = item else {
1342 return Err(validated_struct::InsertionError::Str(
1343 "array item is not an object",
1344 ));
1345 };
1346
1347 if map.get(field_name).and_then(|v| v.as_str()) == Some(field_value) {
1348 *item = new_item.take().unwrap();
1349 break;
1350 }
1351 }
1352 if let Some(new_item) = new_item {
1353 list.push(new_item);
1354 }
1355 <Self as ValidatedMap>::insert_json5(self, array_key, &serde_json::to_string(&list)?)?;
1356 Ok(true)
1357 }
1358
1359 pub fn keys(&self) -> impl Iterator<Item = String> {
1360 <Self as ValidatedMap>::keys(self).into_iter()
1361 }
1362
1363 pub fn set_plugin_validator<T: ConfigValidator + 'static>(&mut self, validator: Weak<T>) {
1364 self.plugins.validator = validator;
1365 }
1366
1367 pub fn plugin(&self, name: &str) -> Option<&Value> {
1368 self.plugins.values.get(name)
1369 }
1370
1371 pub fn sift_privates(&self) -> Self {
1372 let mut copy = self.clone();
1373 copy.plugins.sift_privates();
1374 copy
1375 }
1376
1377 pub fn remove<K: AsRef<str>>(&mut self, key: K) -> ZResult<()> {
1378 let key = key.as_ref();
1379
1380 let key = key.strip_prefix('/').unwrap_or(key);
1381 if !key.starts_with("plugins/") {
1382 bail!(
1383 "Removal of values from Config is only supported for keys starting with `plugins/`"
1384 )
1385 }
1386 self.plugins.remove(&key["plugins/".len()..])
1387 }
1388
1389 pub fn try_remove_json5_array_item<K: AsRef<str>>(&mut self, key: K) -> ZResult<bool> {
1403 let key = key.as_ref();
1404 let Some((prefix, field_value)) = key.split_once('=') else {
1405 return Ok(false);
1406 };
1407 let (array_key, field_name) = prefix.rsplit_once('/').ok_or("missing field filter")?;
1408 let current = serde_json::from_str::<serde_json::Value>(
1409 &self.get_json(array_key).map_err(|err| zerror!("{err}"))?,
1410 )?;
1411 let serde_json::Value::Array(mut list) = current else {
1412 bail!("not an array")
1413 };
1414 let prev_len = list.len();
1415 list.retain(|item| match item {
1416 serde_json::Value::Object(map) => {
1417 map.get(field_name).and_then(|v| v.as_str()) != Some(field_value)
1418 }
1419 _ => true,
1420 });
1421 if list.len() != prev_len {
1422 self.insert_json5(array_key, &serde_json::to_string(&list)?)?;
1423 }
1424 Ok(true)
1425 }
1426
1427 pub fn get_retry_config(
1428 &self,
1429 endpoint: Option<&EndPoint>,
1430 listen: bool,
1431 ) -> ConnectionRetryConf {
1432 get_retry_config(self, endpoint, listen)
1433 }
1434}
1435
1436#[derive(Debug)]
1437pub enum ConfigOpenErr {
1438 IoError(std::io::Error),
1439 JsonParseErr(json5::Error),
1440 InvalidConfiguration(Box<Config>),
1441}
1442impl std::fmt::Display for ConfigOpenErr {
1443 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1444 match self {
1445 ConfigOpenErr::IoError(e) => write!(f, "Couldn't open file: {e}"),
1446 ConfigOpenErr::JsonParseErr(e) => write!(f, "JSON5 parsing error {e}"),
1447 ConfigOpenErr::InvalidConfiguration(c) => write!(
1448 f,
1449 "Invalid configuration {}",
1450 serde_json::to_string(c).unwrap()
1451 ),
1452 }
1453 }
1454}
1455impl std::error::Error for ConfigOpenErr {}
1456impl Config {
1457 pub fn from_file<P: AsRef<Path>>(path: P) -> ZResult<Self> {
1458 let path = path.as_ref();
1459 let mut config = Self::_from_file(path)?;
1460 config.plugins.load_external_configs()?;
1461 Ok(config)
1462 }
1463
1464 fn _from_file(path: &Path) -> ZResult<Config> {
1465 match std::fs::File::open(path) {
1466 Ok(mut f) => {
1467 let mut content = String::new();
1468 if let Err(e) = f.read_to_string(&mut content) {
1469 bail!(e)
1470 }
1471 if content.is_empty() {
1472 bail!("Empty config file");
1473 }
1474 match path
1475 .extension()
1476 .map(|s| s.to_str().unwrap())
1477 {
1478 Some("json") | Some("json5") => match json5::Deserializer::from_str(&content) {
1479 Ok(mut d) => Config::from_deserializer(&mut d).map_err(|e| match e {
1480 Ok(c) => zerror!("Invalid configuration: {}", c).into(),
1481 Err(e) => zerror!("JSON error: {:?}", e).into(),
1482 }),
1483 Err(e) => bail!(e),
1484 },
1485 Some("yaml") | Some("yml") => Config::from_deserializer(serde_yaml::Deserializer::from_str(&content)).map_err(|e| match e {
1486 Ok(c) => zerror!("Invalid configuration: {}", c).into(),
1487 Err(e) => zerror!("YAML error: {:?}", e).into(),
1488 }),
1489 #[cfg(feature = "unstable")]
1490 Some("toml") => {
1491 tracing::warn!("The TOML configuration format is unstable and may be removed in a future release");
1492 match toml::Deserializer::parse(&content) {
1493 Ok(de) => Config::from_deserializer(de).map_err(|e| match e {
1494 Ok(c) => zerror!("Invalid configuration: {}", c).into(),
1495 Err(e) => zerror!("TOML deserization error: {:?}", e).into(),
1496 }),
1497 Err(e) => bail!("TOML parsing error: {:?}", e),
1498 }
1499 },
1500 Some(other) => bail!("Unsupported file type '.{}' (.json, .json5 and .yaml are supported)", other),
1501 None => bail!("Unsupported file type. Configuration files must have an extension (.json, .json5 and .yaml supported)")
1502 }
1503 }
1504 Err(e) => bail!(e),
1505 }
1506 }
1507
1508 pub fn libloader(&self) -> LibLoader {
1509 if self.plugins_loading.enabled {
1510 LibLoader::new(self.plugins_loading.search_dirs().clone())
1511 } else {
1512 LibLoader::empty()
1513 }
1514 }
1515
1516 pub fn expanded(mut self) -> ExpandedConfig {
1524 if self.id.is_none() {
1525 self.set_id(Some(ZenohId::default())).unwrap();
1526 }
1527
1528 if self.mode.is_none() {
1529 self.set_mode(Some(WhatAmI::default())).unwrap();
1530 }
1531
1532 ExpandedConfig(self)
1533 }
1534}
1535
1536#[doc(hidden)]
1537#[derive(Debug, Clone)]
1538pub struct ExpandedConfig(Config);
1539
1540impl ExpandedConfig {
1541 pub fn id(&self) -> ZenohId {
1542 self.0.id.unwrap()
1543 }
1544
1545 pub fn mode(&self) -> WhatAmI {
1546 self.0.mode.unwrap()
1547 }
1548}
1549
1550impl Deref for ExpandedConfig {
1551 type Target = Config;
1552
1553 fn deref(&self) -> &Self::Target {
1554 &self.0
1555 }
1556}
1557
1558impl DerefMut for ExpandedConfig {
1559 fn deref_mut(&mut self) -> &mut Self::Target {
1560 &mut self.0
1561 }
1562}
1563
1564impl std::fmt::Display for Config {
1565 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1566 serde_json::to_value(self)
1567 .map(|mut json| {
1568 sift_privates(&mut json);
1569 write!(f, "{json}")
1570 })
1571 .map_err(|e| {
1572 _ = write!(f, "{e:?}");
1573 fmt::Error
1574 })?
1575 }
1576}
1577
1578#[test]
1579fn config_from_json() {
1580 let from_str = serde_json::Deserializer::from_str;
1581 let mut config = Config::from_deserializer(&mut from_str(r#"{}"#)).unwrap();
1582 config
1583 .insert("transport/link/tx/lease", &mut from_str("168"))
1584 .unwrap();
1585 dbg!(std::mem::size_of_val(&config));
1586 println!("{}", serde_json::to_string_pretty(&config).unwrap());
1587}
1588
1589fn sequence_number_resolution_validator(b: &Bits) -> bool {
1590 b <= &Bits::from(TransportSn::MAX)
1591}
1592
1593fn queue_size_validator(q: &QueueSizeConf) -> bool {
1594 fn check(size: &usize) -> bool {
1595 (QueueSizeConf::MIN..=QueueSizeConf::MAX).contains(size)
1596 }
1597
1598 let QueueSizeConf {
1599 control,
1600 real_time,
1601 interactive_low,
1602 interactive_high,
1603 data_high,
1604 data,
1605 data_low,
1606 background,
1607 } = q;
1608 check(control)
1609 && check(real_time)
1610 && check(interactive_low)
1611 && check(interactive_high)
1612 && check(data_high)
1613 && check(data)
1614 && check(data_low)
1615 && check(background)
1616}
1617
1618fn user_conf_validator(u: &UsrPwdConf) -> bool {
1619 (u.password().is_none() && u.user().is_none()) || (u.password().is_some() && u.user().is_some())
1620}
1621
1622#[derive(Clone)]
1645pub struct PluginsConfig {
1646 values: Value,
1647 validator: std::sync::Weak<dyn ConfigValidator>,
1648}
1649fn sift_privates(value: &mut serde_json::Value) {
1650 match value {
1651 Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) => {}
1652 Value::Array(a) => a.iter_mut().for_each(sift_privates),
1653 Value::Object(o) => {
1654 o.remove("private");
1655 o.values_mut().for_each(sift_privates);
1656 }
1657 }
1658}
1659
1660fn load_external_plugin_config(title: &str, value: &mut Value) -> ZResult<()> {
1661 let Some(values) = value.as_object_mut() else {
1662 bail!("{} must be object", title);
1663 };
1664 recursive_include(title, values, HashSet::new(), "__config__", ".")
1665}
1666
1667#[derive(Debug, Clone)]
1668pub struct PluginLoad {
1669 pub id: String,
1670 pub name: String,
1671 pub paths: Option<Vec<String>>,
1672 pub required: bool,
1673}
1674impl PluginsConfig {
1675 pub fn sift_privates(&mut self) {
1676 sift_privates(&mut self.values);
1677 }
1678 fn load_external_configs(&mut self) -> ZResult<()> {
1679 let Some(values) = self.values.as_object_mut() else {
1680 bail!("plugins configuration must be an object")
1681 };
1682 for (name, value) in values.iter_mut() {
1683 load_external_plugin_config(format!("plugins.{}", name.as_str()).as_str(), value)?;
1684 }
1685 Ok(())
1686 }
1687 pub fn load_requests(&'_ self) -> impl Iterator<Item = PluginLoad> + '_ {
1688 self.values.as_object().unwrap().iter().map(|(id, value)| {
1689 let value = value.as_object().expect("Plugin configurations must be objects");
1690 let required = match value.get("__required__") {
1691 None => false,
1692 Some(Value::Bool(b)) => *b,
1693 _ => panic!("Plugin '{id}' has an invalid '__required__' configuration property (must be a boolean)")
1694 };
1695 let name = match value.get("__plugin__") {
1696 Some(Value::String(p)) => p,
1697 _ => id,
1698 };
1699
1700 if let Some(paths) = value.get("__path__") {
1701 let paths = match paths {
1702 Value::String(s) => vec![s.clone()],
1703 Value::Array(a) => a.iter().map(|s| if let Value::String(s) = s { s.clone() } else { panic!("Plugin '{id}' has an invalid '__path__' configuration property (must be either string or array of strings)") }).collect(),
1704 _ => panic!("Plugin '{id}' has an invalid '__path__' configuration property (must be either string or array of strings)")
1705 };
1706 PluginLoad { id: id.clone(), name: name.clone(), paths: Some(paths), required }
1707 } else {
1708 PluginLoad { id: id.clone(), name: name.clone(), paths: None, required }
1709 }
1710 })
1711 }
1712 pub fn remove(&mut self, key: &str) -> ZResult<()> {
1713 let mut split = key.split('/');
1714 let plugin = split.next().unwrap();
1715 let mut current = match split.next() {
1716 Some(first_in_plugin) => first_in_plugin,
1717 None => {
1718 self.values.as_object_mut().unwrap().remove(plugin);
1719 return Ok(());
1720 }
1721 };
1722 let (old_conf, mut new_conf) = match self.values.get_mut(plugin) {
1723 Some(plugin) => {
1724 let clone = plugin.clone();
1725 (plugin, clone)
1726 }
1727 None => bail!("No plugin {} to edit", plugin),
1728 };
1729 let mut remove_from = &mut new_conf;
1730 for next in split {
1731 match remove_from {
1732 Value::Object(o) => match o.get_mut(current) {
1733 Some(v) => {
1734 remove_from = unsafe {
1735 std::mem::transmute::<&mut serde_json::Value, &mut serde_json::Value>(v)
1736 }
1737 }
1738 None => bail!("{:?} has no {} property", o, current),
1739 },
1740 Value::Array(a) => {
1741 let index: usize = current.parse()?;
1742 if a.len() <= index {
1743 bail!("{:?} cannot be indexed at {}", a, index)
1744 }
1745 remove_from = &mut a[index];
1746 }
1747 other => bail!("{} cannot be indexed", other),
1748 }
1749 current = next
1750 }
1751 match remove_from {
1752 Value::Object(o) => {
1753 if o.remove(current).is_none() {
1754 bail!("{:?} has no {} property", o, current)
1755 }
1756 }
1757 Value::Array(a) => {
1758 let index: usize = current.parse()?;
1759 if a.len() <= index {
1760 bail!("{:?} cannot be indexed at {}", a, index)
1761 }
1762 a.remove(index);
1763 }
1764 other => bail!("{} cannot be indexed", other),
1765 }
1766 let new_conf = if let Some(validator) = self.validator.upgrade() {
1767 match validator.check_config(
1768 plugin,
1769 &key[("plugins/".len() + plugin.len())..],
1770 old_conf.as_object().unwrap(),
1771 new_conf.as_object().unwrap(),
1772 )? {
1773 None => new_conf,
1774 Some(new_conf) => Value::Object(new_conf),
1775 }
1776 } else {
1777 new_conf
1778 };
1779 *old_conf = new_conf;
1780 Ok(())
1781 }
1782}
1783impl serde::Serialize for PluginsConfig {
1784 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1785 where
1786 S: serde::Serializer,
1787 {
1788 let mut value = self.values.clone();
1789 sift_privates(&mut value);
1790 value.serialize(serializer)
1791 }
1792}
1793impl Default for PluginsConfig {
1794 fn default() -> Self {
1795 Self {
1796 values: Value::Object(Default::default()),
1797 validator: std::sync::Weak::<()>::new(),
1798 }
1799 }
1800}
1801impl<'a> serde::Deserialize<'a> for PluginsConfig {
1802 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1803 where
1804 D: serde::Deserializer<'a>,
1805 {
1806 Ok(PluginsConfig {
1807 values: serde::Deserialize::deserialize(deserializer)?,
1808 validator: std::sync::Weak::<()>::new(),
1809 })
1810 }
1811}
1812
1813impl std::fmt::Debug for PluginsConfig {
1814 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1815 let mut values: Value = self.values.clone();
1816 sift_privates(&mut values);
1817 write!(f, "{values:?}")
1818 }
1819}
1820
1821trait PartialMerge: Sized {
1822 fn merge(self, path: &str, value: Self) -> Result<Self, validated_struct::InsertionError>;
1823}
1824impl PartialMerge for serde_json::Value {
1825 fn merge(
1826 mut self,
1827 path: &str,
1828 new_value: Self,
1829 ) -> Result<Self, validated_struct::InsertionError> {
1830 let mut value = &mut self;
1831 let mut key = path;
1832 let key_not_found = || {
1833 Err(validated_struct::InsertionError::String(format!(
1834 "{path} not found"
1835 )))
1836 };
1837 while !key.is_empty() {
1838 let (current, new_key) = validated_struct::split_once(key, '/');
1839 key = new_key;
1840 if current.is_empty() {
1841 continue;
1842 }
1843 value = match value {
1844 Value::Bool(_) | Value::Number(_) | Value::String(_) => return key_not_found(),
1845 Value::Null => match current {
1846 "0" | "+" => {
1847 *value = Value::Array(vec![Value::Null]);
1848 &mut value[0]
1849 }
1850 _ => {
1851 *value = Value::Object(Default::default());
1852 value
1853 .as_object_mut()
1854 .unwrap()
1855 .entry(current)
1856 .or_insert(Value::Null)
1857 }
1858 },
1859 Value::Array(a) => match current {
1860 "+" => {
1861 a.push(Value::Null);
1862 a.last_mut().unwrap()
1863 }
1864 "0" if a.is_empty() => {
1865 a.push(Value::Null);
1866 a.last_mut().unwrap()
1867 }
1868 _ => match current.parse::<usize>() {
1869 Ok(i) => match a.get_mut(i) {
1870 Some(r) => r,
1871 None => return key_not_found(),
1872 },
1873 Err(_) => return key_not_found(),
1874 },
1875 },
1876 Value::Object(v) => v.entry(current).or_insert(Value::Null),
1877 }
1878 }
1879 *value = new_value;
1880 Ok(self)
1881 }
1882}
1883impl<'a> validated_struct::ValidatedMapAssociatedTypes<'a> for PluginsConfig {
1884 type Accessor = &'a dyn Any;
1885}
1886impl validated_struct::ValidatedMap for PluginsConfig {
1887 fn insert<'d, D: serde::Deserializer<'d>>(
1888 &mut self,
1889 key: &str,
1890 deserializer: D,
1891 ) -> Result<(), validated_struct::InsertionError>
1892 where
1893 validated_struct::InsertionError: From<D::Error>,
1894 {
1895 let (plugin, key) = validated_struct::split_once(key, '/');
1896 let new_value: Value = serde::Deserialize::deserialize(deserializer)?;
1897 let value = self
1898 .values
1899 .as_object_mut()
1900 .unwrap()
1901 .entry(plugin)
1902 .or_insert(Value::Null);
1903 let new_value = value.clone().merge(key, new_value)?;
1904 *value = if let Some(validator) = self.validator.upgrade() {
1905 let Some(new_plugin_config) = new_value.as_object() else {
1910 return Err(format!(
1911 "Attempt to provide non-object value as configuration for plugin `{plugin}`"
1912 )
1913 .into());
1914 };
1915 let empty_config = Map::new();
1919 let current_plugin_config = value.as_object().unwrap_or(&empty_config);
1920 match validator.check_config(plugin, key, current_plugin_config, new_plugin_config) {
1921 Ok(Some(val)) => Value::Object(val),
1923 Ok(None) => new_value,
1925 Err(e) => return Err(format!("{e}").into()),
1927 }
1928 } else {
1929 new_value
1930 };
1931 Ok(())
1932 }
1933 fn get<'a>(&'a self, mut key: &str) -> Result<&'a dyn Any, GetError> {
1934 let (current, new_key) = validated_struct::split_once(key, '/');
1935 key = new_key;
1936 let mut value = match self.values.get(current) {
1937 Some(matched) => matched,
1938 None => return Err(GetError::NoMatchingKey),
1939 };
1940 while !key.is_empty() {
1941 let (current, new_key) = validated_struct::split_once(key, '/');
1942 key = new_key;
1943 let matched = match value {
1944 serde_json::Value::Null
1945 | serde_json::Value::Bool(_)
1946 | serde_json::Value::Number(_)
1947 | serde_json::Value::String(_) => return Err(GetError::NoMatchingKey),
1948 serde_json::Value::Array(a) => a.get(match current.parse::<usize>() {
1949 Ok(i) => i,
1950 Err(_) => return Err(GetError::NoMatchingKey),
1951 }),
1952 serde_json::Value::Object(v) => v.get(current),
1953 };
1954 value = match matched {
1955 Some(matched) => matched,
1956 None => return Err(GetError::NoMatchingKey),
1957 }
1958 }
1959 Ok(value)
1960 }
1961
1962 type Keys = Vec<String>;
1963 fn keys(&self) -> Self::Keys {
1964 self.values.as_object().unwrap().keys().cloned().collect()
1965 }
1966
1967 fn get_json(&self, mut key: &str) -> Result<String, GetError> {
1968 let (current, new_key) = validated_struct::split_once(key, '/');
1969 key = new_key;
1970 let mut value = match self.values.get(current) {
1971 Some(matched) => matched,
1972 None => return Err(GetError::NoMatchingKey),
1973 };
1974 while !key.is_empty() {
1975 let (current, new_key) = validated_struct::split_once(key, '/');
1976 key = new_key;
1977 let matched = match value {
1978 serde_json::Value::Null
1979 | serde_json::Value::Bool(_)
1980 | serde_json::Value::Number(_)
1981 | serde_json::Value::String(_) => return Err(GetError::NoMatchingKey),
1982 serde_json::Value::Array(a) => a.get(match current.parse::<usize>() {
1983 Ok(i) => i,
1984 Err(_) => return Err(GetError::NoMatchingKey),
1985 }),
1986 serde_json::Value::Object(v) => v.get(current),
1987 };
1988 value = match matched {
1989 Some(matched) => matched,
1990 None => return Err(GetError::NoMatchingKey),
1991 }
1992 }
1993 Ok(serde_json::to_string(value).unwrap())
1994 }
1995}
1996
1997#[macro_export]
1998macro_rules! unwrap_or_default {
1999 ($val:ident$(.$field:ident($($param:ident)?))*) => {
2000 $val$(.$field($($param)?))*.clone().unwrap_or(zenoh_config::defaults$(::$field$(($param))?)*.into())
2001 };
2002}
2003
2004pub trait IConfig: Send + Sync {
2005 fn get(&self, key: &str) -> ZResult<String>;
2006 fn queries_default_timeout_ms(&self) -> u64;
2007 fn insert_json5(&self, key: &str, value: &str) -> ZResult<()>;
2008 fn to_json(&self) -> String;
2009}
2010
2011pub struct GenericConfig(Arc<dyn IConfig>);
2012
2013impl std::fmt::Debug for GenericConfig {
2014 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2015 f.debug_tuple("GenericConfig").field(&"..").finish()
2016 }
2017}
2018
2019impl Deref for GenericConfig {
2020 type Target = Arc<dyn IConfig>;
2021
2022 fn deref(&self) -> &Self::Target {
2023 &self.0
2024 }
2025}
2026
2027impl GenericConfig {
2028 pub fn new(value: Arc<dyn IConfig>) -> Self {
2029 GenericConfig(value)
2030 }
2031
2032 pub fn get_typed<T: for<'a> Deserialize<'a>>(&self, key: &str) -> ZResult<T> {
2033 self.0
2034 .get(key)
2035 .and_then(|v| serde_json::from_str::<T>(&v).map_err(|e| e.into()))
2036 }
2037
2038 pub fn get_plugin_config(&self, plugin_name: &str) -> ZResult<Value> {
2039 self.get(&("plugins/".to_owned() + plugin_name))
2040 .and_then(|v| serde_json::from_str(&v).map_err(|e| e.into()))
2041 }
2042}
2043
2044impl fmt::Display for GenericConfig {
2045 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2046 f.write_str(&self.0.to_json())
2047 }
2048}
2049
2050#[cfg(test)]
2051mod tests {
2052 use std::{env, fs::File, io::Write, str::FromStr, time::SystemTime};
2053
2054 use zenoh_protocol::core::{EndPoint, WhatAmI};
2055
2056 use crate::{Config, ModeDependentValue, ZenohId};
2057
2058 #[test]
2059 fn test_toml_config_format() {
2060 const FILE_CONTENTS: &str = r#"
2061 id = "abc"
2062 mode = "router"
2063
2064 [listen]
2065 endpoints = ["tcp/localhost:7448"]
2066
2067 [adminspace]
2068 enabled = true
2069 "#;
2070
2071 let timestamp = SystemTime::now()
2072 .duration_since(SystemTime::UNIX_EPOCH)
2073 .unwrap()
2074 .as_secs();
2075
2076 let path = env::temp_dir().join(format!("{timestamp}.test.config.toml"));
2077
2078 {
2079 let mut tmp = File::create(&path).unwrap();
2080 tmp.write_all(FILE_CONTENTS.as_bytes()).unwrap();
2081 tmp.flush().unwrap();
2082 }
2083
2084 let expected_config = {
2085 let mut c = Config::default();
2086 c.set_id(Some(ZenohId::from_str("abc").unwrap())).unwrap();
2087 c.set_mode(Some(WhatAmI::Router)).unwrap();
2088 c.listen
2089 .set_endpoints(ModeDependentValue::Unique(vec![EndPoint::from_str(
2090 "tcp/localhost:7448",
2091 )
2092 .unwrap()]))
2093 .unwrap();
2094 c.adminspace.set_enabled(true).unwrap();
2095 c
2096 };
2097
2098 assert_eq!(
2099 Config::from_file(&path).unwrap().to_string(),
2100 expected_config.to_string()
2101 );
2102 }
2103
2104 #[test]
2105 fn insert_remove_json5_array_item_by_id() {
2106 let mut config = Config::default();
2107
2108 assert!(config
2109 .try_insert_json5_array_item(
2110 "qos/network/id=item1",
2111 r#"{
2112 id: "item1",
2113 messages: ["put"],
2114 key_exprs: ["**"],
2115 overwrite: { priority: "data" },
2116 flows: ["egress"]
2117 }"#,
2118 )
2119 .unwrap());
2120 assert!(config
2121 .try_insert_json5_array_item(
2122 "qos/network/id=item1",
2123 r#"{
2124 id: "item1",
2125 messages: ["put"],
2126 key_exprs: ["**"],
2127 overwrite: { priority: "data_high" },
2128 flows: ["egress"]
2129 }"#,
2130 )
2131 .unwrap());
2132
2133 let items: serde_json::Value =
2134 serde_json::from_str(&config.get_json("qos/network").unwrap()).unwrap();
2135 assert_eq!(items.as_array().unwrap().len(), 1);
2136 assert_eq!(items[0]["id"], "item1");
2137 assert_eq!(items[0]["overwrite"]["priority"], "data_high");
2138
2139 assert!(config
2140 .try_remove_json5_array_item("qos/network/id=item1")
2141 .unwrap());
2142 let items: serde_json::Value =
2143 serde_json::from_str(&config.get_json("qos/network").unwrap()).unwrap();
2144 assert_eq!(items.as_array().unwrap().len(), 0);
2145 }
2146}