bacnet_server/server/
read_range_budget.rs1use super::*;
3
4#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9pub struct ReadRangeBudget {
10 pub max_returned_items: usize,
12 pub max_service_ack_bytes: usize,
14}
15
16impl Default for ReadRangeBudget {
17 fn default() -> Self {
18 Self {
19 max_returned_items: 256,
20 max_service_ack_bytes: 16384,
21 }
22 }
23}
24
25impl ReadRangeBudget {
26 pub fn validate(&self) -> Result<(), Error> {
28 for (name, value) in [
29 ("read_range_max_returned_items", self.max_returned_items),
30 (
31 "read_range_max_service_ack_bytes",
32 self.max_service_ack_bytes,
33 ),
34 ] {
35 if value == 0 {
36 return Err(Error::Encoding(format!("{name} must be positive")));
37 }
38 }
39 Ok(())
40 }
41}
42
43impl<T: TransportPort + 'static> ServerBuilder<T> {
44 pub fn read_range_budget(mut self, budget: ReadRangeBudget) -> Self {
46 self.config.read_range_budget = budget;
47 self
48 }
49}
50impl BipServerBuilder {
51 pub fn read_range_budget(mut self, budget: ReadRangeBudget) -> Self {
53 self.config.read_range_budget = budget;
54 self
55 }
56}
57#[cfg(feature = "sc-tls")]
58impl ScServerBuilder {
59 pub fn read_range_budget(mut self, budget: ReadRangeBudget) -> Self {
61 self.config.read_range_budget = budget;
62 self
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use crate::server::test_transport::TestTransport;
70
71 #[tokio::test]
72 async fn read_range_all_builders_validate_before_start_or_dial() {
73 let default = ReadRangeBudget::default();
74 assert_eq!(
75 (default.max_returned_items, default.max_service_ack_bytes),
76 (256, 16384)
77 );
78 assert_eq!(ServerConfig::default().read_range_budget, default);
79 assert!(format!("{:?}", ServerConfig::default()).contains("read_range_budget"));
80 ReadRangeBudget {
81 max_returned_items: usize::MAX,
82 max_service_ack_bytes: usize::MAX,
83 }
84 .validate()
85 .unwrap();
86 for budget in [
87 ReadRangeBudget {
88 max_returned_items: 0,
89 ..default
90 },
91 ReadRangeBudget {
92 max_service_ack_bytes: 0,
93 ..default
94 },
95 ] {
96 let direct = BACnetServer::start(
97 ServerConfig {
98 read_range_budget: budget,
99 ..Default::default()
100 },
101 ObjectDatabase::new(),
102 TestTransport::never_start(),
103 )
104 .await
105 .err();
106 let generic = BACnetServer::generic_builder()
107 .transport(TestTransport::never_start())
108 .read_range_budget(budget)
109 .build()
110 .await
111 .err();
112 let bip = BACnetServer::bip_builder()
113 .port(0)
114 .read_range_budget(budget)
115 .build()
116 .await
117 .err();
118 for error in [direct, generic, bip] {
119 assert!(matches!(error, Some(Error::Encoding(m)) if m.contains("read_range_max_")));
120 }
121 #[cfg(feature = "sc-tls")]
122 {
123 let error = BACnetServer::sc_builder()
124 .hub_url("not-a-websocket-url")
125 .tls_config(crate::server::sc_builder::test_tls_config())
126 .device_uuid(crate::server::sc_builder::TEST_DEVICE_UUID)
127 .read_range_budget(budget)
128 .build()
129 .await
130 .err();
131 assert!(matches!(error, Some(Error::Encoding(m)) if m.contains("read_range_max_")));
132 }
133 }
134 }
135}