1use std::sync::{Arc, OnceLock, RwLock};
4use std::time::{Duration, SystemTime};
5use std::fmt::{self, Debug, Display};
6use std::task::{Context, Poll};
7use std::hash::{Hash, Hasher};
8use std::future::Future;
9use std::cmp::Ordering;
10use std::str::FromStr;
11use std::borrow::Cow;
12use std::{env, mem};
13use std::pin::Pin;
14
15use futures::stream::{BoxStream, Stream, StreamExt};
16use futures::ready;
17use tracing::{debug, instrument, warn};
18use futures::future::{poll_fn, BoxFuture};
19
20use crate::value::{AttrDataFormat, AttrDataKind, CommandDataKind, FromValue, IntoValue, Scalar, Spectrum, Value};
21use crate::database::{prop_params, set_prop_params, DatabaseProxy, GetPropSpec, PropCommands, SetPropSpec};
22use crate::backend::{default_backend, Backend, BackendDev};
23use crate::error::{Error, ErrorKind, Result, reason};
24use crate::name::{ArcTrl, IntoTrl, TrlObject};
25use crate::future::block_on_timeout;
26use crate::local;
27
28
29const DEFAULT_TIMEOUT: Duration = Duration::from_secs(3);
33
34pub(crate) const DATABASE_DEV_NAME: &str = "database";
36
37#[derive(Debug)]
45pub struct DevProxy {
46 inner: Box<ProxyInner>,
48 state: RwLock<ProxyState>,
50}
51
52#[derive(Debug, Clone)]
53struct ProxyInner {
54 backend: Arc<dyn Backend>,
56 source: DevSource,
58 timeout: Duration,
60 target: ProxyTarget,
62}
63
64#[derive(Debug, Clone)]
65struct ProxyState {
66 backend: Option<Arc<dyn BackendDev>>,
68 database_host_and_port: Option<(Arc<str>, u16)>,
71}
72
73#[derive(Debug, Clone, PartialEq, Eq, Hash)]
77pub(crate) enum ProxyTarget {
78 DatabaseFromEnv,
80 DatabaseAt {
82 host: Arc<str>,
83 port: u16,
84 },
85 RegularFromDatabase {
87 database: DatabaseProxy,
88 name: Arc<str>,
89 },
90 RegularAt {
93 host: Arc<str>,
94 port: u16,
95 name: Arc<str>,
96 },
97}
98
99impl DevProxy {
100
101 pub fn new<'a>(trl: impl IntoTrl<'a>) -> Self {
103 Self::new_with_backend(default_backend(), trl)
104 }
105
106 pub fn try_new<'a>(trl: impl IntoTrl<'a>) -> Option<Self> {
110 Self::try_new_with_backend(default_backend(), trl)
111 }
112
113 pub fn new_from_database(database: &DatabaseProxy, name: &str) -> Self {
115 Self::new_from_database_with_backend(default_backend(), database, name)
116 }
117
118 pub fn new_with_backend<'a>(backend: Arc<dyn Backend>, trl: impl IntoTrl<'a>) -> Self {
120 Self::try_new_with_backend(backend, trl).expect("The given TRL does not target a device")
121 }
122
123 pub fn try_new_with_backend<'a>(backend: Arc<dyn Backend>, trl: impl IntoTrl<'a>) -> Option<Self> {
127
128 let trl = trl.into_trl().ok()?;
129
130 if trl.property().is_some() {
132 return None;
133 }
134
135 let name = match trl.object() {
136 TrlObject::Alias(name) => name,
137 TrlObject::Dev(name) => name,
138 TrlObject::Attr(_, _) => return None,
139 };
140
141 let target = match (trl.db(), trl.host_and_port()) {
142 (true, Some((host, port))) => ProxyTarget::RegularFromDatabase {
143 database: DatabaseProxy::new_with_backend(Arc::clone(&backend), host, port),
144 name: Arc::from(name),
145 },
146 (true, None) => ProxyTarget::RegularFromDatabase {
147 database: DatabaseProxy::new_from_env_with_backend(Arc::clone(&backend)),
148 name: Arc::from(name),
149 },
150 (false, Some((host, port))) => ProxyTarget::RegularAt {
151 host: Arc::from(host),
152 port,
153 name: Arc::from(name),
154 },
155 (false, None) => return None,
156 };
157
158 Some(Self::new_from_target_with_backend(backend, target))
159
160 }
161
162 pub fn new_from_database_with_backend(backend: Arc<dyn Backend>, database: &DatabaseProxy, name: &str) -> Self {
164 Self::new_from_target_with_backend(backend, ProxyTarget::RegularFromDatabase {
165 database: database.clone(),
166 name: Arc::from(name),
167 })
168 }
169
170 pub(crate) fn new_from_target_with_backend(backend: Arc<dyn Backend>, target: ProxyTarget) -> Self {
172 Self {
173 inner: Box::new(ProxyInner {
174 backend,
175 target,
176 source: DevSource::CacheDev,
177 timeout: DEFAULT_TIMEOUT,
178 }),
179 state: RwLock::new(ProxyState {
180 backend: None,
181 database_host_and_port: None,
182 }),
183 }
184 }
185
186 pub fn backend(&self) -> &Arc<dyn Backend> {
187 &self.inner.backend
188 }
189
190 pub fn timeout(&self) -> Duration {
192 self.inner.timeout
193 }
194
195 pub fn set_timeout(&mut self, timeout: Duration) -> Duration {
197 mem::replace(&mut self.inner.timeout, timeout)
198 }
199
200 pub fn source(&self) -> DevSource {
202 self.inner.source
203 }
204
205 pub fn set_source(&mut self, source: DevSource) -> DevSource {
207 mem::replace(&mut self.inner.source, source)
208 }
209
210 fn connect_backend(&self) -> BoxFuture<'_, Result<(Arc<dyn BackendDev>, Option<(Arc<str>, u16)>)>> {
215 Box::pin(async move {
216
217 match self.inner.target {
218 ProxyTarget::DatabaseFromEnv => {
219
220 debug!("Connecting to the database of TANGO_HOST");
221
222 let databases = env_databases();
223 if databases.is_empty() {
224 return Err(Error::new(ErrorKind::TangoHostNotSet)
225 .with_append_description(match env::var("TANGO_HOST") {
226 Ok(var) => format!("TANGO_HOST is {var:?}"),
227 Err(_) => "TANGO_HOST is not defined in the environment".to_string(),
228 }));
229 }
230
231 let mut last_err = None;
232 for (host, port) in databases {
233
234 if let Some(dev_backend) = local::find_at(host, *port, DATABASE_DEV_NAME) {
235 debug!(%host, port, "Connected to the database, served by this process");
236 return Ok((dev_backend, Some((host.clone(), *port))));
237 }
238
239 match self.inner.backend.connect_at(host, *port, DATABASE_DEV_NAME).await {
240 Ok(dev_backend) => {
241 debug!(%host, port, "Connected to the database");
242 return Ok((dev_backend, Some((host.clone(), *port))));
243 }
244 Err(err) => {
245 warn!(%host, port, %err, "Failed to connect to the database, trying the next one");
246 last_err = Some(err);
247 }
248 }
249 }
250
251 let mut err = Error::new(ErrorKind::DatabaseNotConnected)
255 .with_append_description(format_args!("Tried, from TANGO_HOST: {}", DisplayHostAndPorts(databases)));
256 if let Some(last_err) = last_err {
257 err.set_parent(last_err);
258 }
259 Err(err)
260
261 }
262 ProxyTarget::DatabaseAt { ref host, port } => {
263
264 debug!(%host, port, "Connecting to the database");
265
266 if let Some(dev_backend) = local::find_at(host, port, DATABASE_DEV_NAME) {
267 debug!(%host, port, "Connected to the database, served by this process");
268 return Ok((dev_backend, Some((host.clone(), port))));
269 }
270
271 match self.inner.backend.connect_at(&host, port, DATABASE_DEV_NAME).await {
272 Ok(dev_backend) => {
273 debug!(%host, port, "Connected to the database");
274 Ok((dev_backend, Some((host.clone(), port))))
275 }
276 Err(err) => Err(Error::new(ErrorKind::DatabaseNotConnected)
277 .with_append_description(format_args!("Tried: {host}:{port}"))
278 .with_parent(err)),
279 }
280
281 }
282 ProxyTarget::RegularFromDatabase { ref database, ref name } => {
283
284 debug!(dev = %name, "Connecting to the device, through the database");
285
286 let info = match database.import_dev_async(name).await {
287 Ok(info) if !info.exported =>
288 return Err(Error::new(ErrorKind::DevNotFound)
289 .with_description("The device is defined in the database but no running server exports it")
290 .with_append_description(format_args!("Device {name:?} of the server {:?}", info.server))),
291 Ok(info) => info,
292 Err(err) if let ErrorKind::DevNotConnected = err.kind() =>
293 return Err(Error::new(ErrorKind::DatabaseNotConnected)
294 .with_parent(err)),
295 Err(err) => return Err(err),
296 };
297
298 if let Some(dev_backend) = local::find(info.itr.value()) {
301 debug!(dev = %name, "Connected to the device, served by this process");
302 return Ok((dev_backend, None));
303 }
304
305 match self.inner.backend.connect(info.itr.value()).await {
308 Ok(dev_backend) => {
309 debug!(dev = %name, "Connected to the device");
310 Ok((dev_backend, None))
311 }
312 Err(err) => Err(err.with_append_description(format_args!(
313 "While connecting to the device {name:?} of the server {:?}, exported by the database on host {:?}",
314 info.server, info.host))),
315 }
316
317 }
318 ProxyTarget::RegularAt { ref host, port, ref name } => {
319
320 debug!(%host, port, dev = %name, "Connecting to the device");
321
322 if let Some(dev_backend) = local::find_at(host, port, name) {
323 debug!(%host, port, dev = %name, "Connected to the device, served by this process");
324 return Ok((dev_backend, None));
325 }
326
327 match self.inner.backend.connect_at(&host, port, &name).await {
328 Ok(dev_backend) => {
329 debug!(%host, port, dev = %name, "Connected to the device");
330 Ok((dev_backend, None))
331 }
332 Err(err) => Err(err.with_append_description(format_args!(
333 "While connecting to the device {name:?} at {host}:{port}"))),
334 }
335
336 }
337 }
338
339 })
340 }
341
342 async fn ensure_backend(&self) -> Result<Arc<dyn BackendDev>> {
351
352 if let Some(backend) = self.state.read().unwrap().backend.as_ref() {
353 return Ok(Arc::clone(backend));
354 }
355
356 let (backend, database_host_and_port) = self.connect_backend().await?;
357
358 let mut state = self.state.write().unwrap();
359 match state.backend {
360 Some(ref backend) => Ok(Arc::clone(backend)),
361 None => {
362 state.backend = Some(Arc::clone(&backend));
363 state.database_host_and_port = database_host_and_port;
364 Ok(backend)
365 }
366 }
367
368 }
369
370 fn reset_backend(&self) {
371 let mut state = self.state.write().unwrap();
372 state.backend = None;
373 state.database_host_and_port = None;
374 }
375
376 fn check_backend<T>(&self, res: Result<T>) -> Result<T> {
377 if let Err(err) = &res && err.kind() == ErrorKind::DevNotConnected {
378 self.reset_backend();
379 }
380 res
381 }
382
383 pub(crate) fn block_on<F: Future>(&self, fut: F) -> Result<F::Output> {
384 block_on_timeout(fut, self.inner.timeout)
385 }
386
387 fn database_host_and_port(&self) -> Result<Option<(Arc<str>, u16)>> {
388 self.block_on(self.database_host_and_port_async())?
389 }
390
391 pub(crate) async fn database_host_and_port_async(&self) -> Result<Option<(Arc<str>, u16)>> {
394 self.ensure_backend().await?;
395 Ok(self.state.read().unwrap().database_host_and_port.clone())
396 }
397
398 pub fn database(&self) -> Result<&DatabaseProxy> {
401 match self.inner.target {
402 ProxyTarget::DatabaseFromEnv |
403 ProxyTarget::DatabaseAt { .. } =>
404 Ok(DatabaseProxy::from_dev(self)),
405 ProxyTarget::RegularFromDatabase { ref database, .. } =>
406 Ok(database),
407 ProxyTarget::RegularAt { .. } =>
408 Err(Error::new(ErrorKind::DatabaseNeeded)),
409 }
410 }
411
412 #[instrument(skip(self), fields(target = %self.inner.target))]
414 pub fn trl(&self) -> Result<ArcTrl> {
415 match self.inner.target {
416 ProxyTarget::DatabaseFromEnv => {
417 let name = self.name()?;
418 let (host, port) = self.database_host_and_port()?
419 .expect("This should be defined if the target is a database");
420 Ok(format!("tango://{host}:{port}/{name}").parse::<ArcTrl>().unwrap())
421 }
422 ProxyTarget::DatabaseAt { ref host, port } => {
423 let name = self.name()?;
424 Ok(format!("tango://{host}:{port}/{name}").parse::<ArcTrl>().unwrap())
425 }
426 ProxyTarget::RegularFromDatabase { ref database, ref name } => {
427 let (host, port) = database.database_host_and_port()?
428 .expect("This should be defined if the target is a database");
429 Ok(format!("tango://{host}:{port}/{name}").parse::<ArcTrl>().unwrap())
430 }
431 ProxyTarget::RegularAt { ref host, port, ref name } => {
432 Ok(format!("tango://{host}:{port}/{name}#dbase=no").parse::<ArcTrl>().unwrap())
433 }
434 }
435 }
436
437 #[instrument(skip(self), fields(target = %self.inner.target))]
440 pub fn connect(&self) -> Result<()> {
441 self.block_on(self.connect_async())?
442 }
443
444 pub async fn connect_async(&self) -> Result<()> {
446 self.ensure_backend().await.map(drop)
447 }
448
449 #[instrument(skip(self), fields(target = %self.inner.target))]
451 pub fn name(&self) -> Result<Arc<str>> {
452 self.block_on(self.name_async())?
453 }
454
455 pub async fn name_async(&self) -> Result<Arc<str>> {
457 let backend = self.ensure_backend().await?;
458 self.check_backend(backend.name().await)
459 }
460
461 #[instrument(skip(self), fields(target = %self.inner.target))]
463 pub fn admin_dev_name(&self) -> Result<Arc<str>> {
464 self.block_on(self.admin_dev_name_async())?
465 }
466
467 pub async fn admin_dev_name_async(&self) -> Result<Arc<str>> {
469 let backend = self.ensure_backend().await?;
470 self.check_backend(backend.admin_dev_name().await)
471 }
472
473 #[instrument(skip(self), fields(target = %self.inner.target))]
475 pub fn admin_dev(&self) -> Result<DevProxy> {
476 Ok(self.admin_dev_of(self.admin_dev_name()?))
477 }
478
479 pub async fn admin_dev_async(&self) -> Result<DevProxy> {
481 Ok(self.admin_dev_of(self.admin_dev_name_async().await?))
482 }
483
484 fn admin_dev_of(&self, name: Arc<str>) -> DevProxy {
488
489 let backend = Arc::clone(&self.inner.backend);
490
491 DevProxy::new_from_target_with_backend(backend, match &self.inner.target {
492 ProxyTarget::DatabaseFromEnv => todo!(),
493 ProxyTarget::DatabaseAt { .. } => {
494 ProxyTarget::RegularFromDatabase {
496 database: DatabaseProxy::with_dev(self.clone()),
497 name,
498 }
499 }
500 ProxyTarget::RegularFromDatabase { database, .. } => {
501 ProxyTarget::RegularFromDatabase {
502 database: database.clone(),
503 name,
504 }
505 }
506 ProxyTarget::RegularAt { host, port, .. } => {
507 ProxyTarget::RegularAt {
508 host: Arc::clone(host),
509 port: *port,
510 name,
511 }
512 }
513 })
514
515 }
516
517 #[instrument(skip(self), fields(target = %self.inner.target))]
523 pub fn info(&self) -> Result<DevInfo> {
524 self.block_on(self.info_async())?
525 }
526
527 pub async fn info_async(&self) -> Result<DevInfo> {
529 let backend = self.ensure_backend().await?;
530 self.check_backend(backend.info().await)
531 }
532
533 #[instrument(skip(self), fields(target = %self.inner.target))]
535 pub fn ping(&self) -> Result<()> {
536 self.block_on(self.ping_async())?
537 }
538
539 pub async fn ping_async(&self) -> Result<()> {
541 let backend = self.ensure_backend().await?;
542 self.check_backend(backend.ping().await)
543 }
544
545 #[instrument(skip(self), fields(target = %self.inner.target))]
547 pub fn state(&self) -> Result<DevState> {
548 self.block_on(self.state_async())?
549 }
550
551 pub async fn state_async(&self) -> Result<DevState> {
553 let backend = self.ensure_backend().await?;
554 self.check_backend(backend.state().await)
555 }
556
557 #[instrument(skip(self), fields(target = %self.inner.target))]
559 pub fn status(&self) -> Result<String> {
560 self.block_on(self.status_async())?
561 }
562
563 pub async fn status_async(&self) -> Result<String> {
565 let backend = self.ensure_backend().await?;
566 self.check_backend(backend.status().await)
567 }
568
569 pub fn init(&self) -> Result<()> {
571 self.command("Init", ())
572 }
573
574 pub fn init_async(&self) -> impl Future<Output = Result<()>> + Send {
576 self.command_async("Init", ())
577 }
578
579 #[instrument(skip(self), fields(target = %self.inner.target))]
581 pub fn black_box(&self, max: Option<usize>) -> Result<Vec<String>> {
582 self.block_on(self.black_box_async(max))?
583 }
584
585 pub async fn black_box_async(&self, max: Option<usize>) -> Result<Vec<String>> {
587 let backend = self.ensure_backend().await?;
588 self.check_backend(backend.black_box(max).await)
589 }
590
591 #[instrument(skip(self, input), fields(target = %self.inner.target))]
598 pub fn command_async<I, O>(&self, name: &str, input: I) -> impl Future<Output = Result<O>> + Send
599 where
600 I: IntoValue,
601 O: FromValue,
602 {
603
604 let command = Scalar::from(name);
611 let input = input.into_value();
612 async move {
613 let backend = self.ensure_backend().await?;
614 let res = backend.command(command, input, self.inner.source).await;
615 O::from_value(self.check_backend(res).map_err(command_err)?)
616 }
617
618 }
619
620 pub fn command<I, O>(&self, name: &str, input: I) -> Result<O>
623 where
624 I: IntoValue,
625 O: FromValue,
626 {
627 self.block_on(self.command_async(name, input))?
628 }
629
630 #[instrument(skip(self, names), fields(target = %self.inner.target))]
657 pub fn read_attr_async<Spec, Names: ToNames>(&self, names: Names) -> impl Future<Output = Result<Spec::Output>> + Send
658 where
659 Spec: ReadAttrSpec<Names>,
660 {
661 let names = names.to_names();
662 async move {
663 let backend = self.ensure_backend().await?;
664 let res = backend.read_attr(names, self.inner.source).await;
665 Spec::collect(self.check_backend(res)?)
666 }
667 }
668
669 pub fn read_attr<Spec, Names: ToNames>(&self, names: Names) -> Result<Spec::Output>
671 where
672 Spec: ReadAttrSpec<Names>,
673 {
674 self.block_on(self.read_attr_async::<Spec, Names>(names))?
675 }
676
677 #[instrument(skip(self, spec), fields(target = %self.inner.target))]
694 pub fn write_attr_async<'a, Spec>(&self, spec: Spec) -> impl Future<Output = Result<Spec::Output>> + Send
695 where
696 Spec: WriteAttrSpec<'a>,
697 {
698
699 let values = spec.into_values();
700
701 async move {
702
703 let backend = self.ensure_backend().await?;
704 let res = backend.write_attr(values).await;
705
706 let outcomes = self.check_backend(res)?.into_iter()
709 .map(|res| res.map_err(write_attr_err))
710 .collect();
711
712 Spec::collect(outcomes)
713
714 }
715
716 }
717
718 pub fn write_attr<'a, Spec>(&self, spec: Spec) -> Result<Spec::Output>
720 where
721 Spec: WriteAttrSpec<'a>,
722 {
723 self.block_on(self.write_attr_async(spec))?
724 }
725
726 #[instrument(skip(self, spec), fields(target = %self.inner.target))]
742 pub fn write_read_attr_async<'a, Spec>(&self, spec: Spec) -> impl Future<Output = Result<<Spec::ReadSpec as ReadAttrSpec<Spec::ReadNames>>::Output>> + Send
743 where
744 Spec: WriteAttrSpec<'a>,
745 Spec::ReadSpec: ReadAttrSpec<Spec::ReadNames>,
746 {
747 let values = spec.into_values();
748 async move {
749 let backend = self.ensure_backend().await?;
750 let res = backend.write_read_attr(values).await;
751 Spec::ReadSpec::collect(self.check_backend(res)?)
752 }
753 }
754
755 pub fn write_read_attr<'a, Spec>(&self, spec: Spec) -> Result<<Spec::ReadSpec as ReadAttrSpec<Spec::ReadNames>>::Output>
757 where
758 Spec: WriteAttrSpec<'a>,
759 Spec::ReadSpec: ReadAttrSpec<Spec::ReadNames>,
760 {
761 self.block_on(self.write_read_attr_async(spec))?
762 }
763
764 #[instrument(skip(self, spec), fields(target = %self.inner.target))]
779 pub fn command_info_async<Spec: ToNamesSpec<CommandInfo>>(&self, spec: Spec) -> impl Future<Output = Result<Spec::Output>> + Send {
780 let names = spec.to_names();
781 async move {
782 let backend = self.ensure_backend().await?;
783 let res = backend.command_info(names).await;
784 Spec::collect(self.check_backend(res)?)
785 }
786 }
787
788 pub fn command_info<Spec: ToNamesSpec<CommandInfo>>(&self, spec: Spec) -> Result<Spec::Output> {
790 self.block_on(self.command_info_async(spec))?
791 }
792
793 #[instrument(skip(self, spec), fields(target = %self.inner.target))]
806 pub fn attr_info_async<Spec: ToNamesSpec<AttrInfo>>(&self, spec: Spec) -> impl Future<Output = Result<Spec::Output>> + Send {
807 let names = spec.to_names();
808 async move {
809 let backend = self.ensure_backend().await?;
810 let res = backend.attr_info(names).await;
811 Spec::collect(self.check_backend(res)?)
812 }
813 }
814
815 pub fn attr_info<Spec: ToNamesSpec<AttrInfo>>(&self, spec: Spec) -> Result<Spec::Output> {
817 self.block_on(self.attr_info_async(spec))?
818 }
819
820 #[instrument(skip(self, values), fields(target = %self.inner.target))]
835 pub async fn set_attr_config_async(&self, values: &[(&str, AttrConfig)]) -> Result<Vec<Result<()>>> {
836 let configs = values.iter()
837 .map(|(name, config)| Named::new(Scalar::from(*name), config.clone()))
838 .collect();
839 let backend = self.ensure_backend().await?;
840 let res = backend.set_attr_config(configs).await;
841 self.check_backend(res).map_err(write_attr_err)
842 }
843
844 pub fn set_attr_config(&self, values: &[(&str, AttrConfig)]) -> Result<Vec<Result<()>>> {
847 self.block_on(self.set_attr_config_async(values))?
848 }
849
850 #[instrument(skip(self), fields(target = %self.inner.target))]
858 pub async fn command_history_async<T: FromValue>(&self, name: &str, max: usize) -> Result<Vec<(SystemTime, Result<T>)>> {
859 let backend = self.ensure_backend().await?;
860 let res = backend.command_history(name, max).await;
861 Ok(self.check_backend(res).map_err(command_err)?.into_iter()
862 .map(|(time, value)| (time, value.and_then(T::from_value)))
863 .collect())
864 }
865
866 pub fn command_history<T: FromValue>(&self, name: &str, max: usize) -> Result<Vec<(SystemTime, Result<T>)>> {
868 self.block_on(self.command_history_async(name, max))?
869 }
870
871 #[instrument(skip(self), fields(target = %self.inner.target))]
874 pub async fn attr_history_async<T: FromValue>(&self, name: &str, max: usize) -> Result<Vec<Result<AttrRead<T>>>> {
875 let backend = self.ensure_backend().await?;
876 let res = backend.attr_history(name, max).await;
877 Ok(self.check_backend(res)?.into_iter()
878 .map(|read| read.and_then(AttrRead::parse))
879 .collect())
880 }
881
882 pub fn attr_history<T: FromValue>(&self, name: &str, max: usize) -> Result<Vec<Result<AttrRead<T>>>> {
884 self.block_on(self.attr_history_async(name, max))?
885 }
886
887 pub async fn list_prop_async(&self, filter: &str) -> Result<Spectrum<str>> {
893 let dev_name = self.name_async().await?;
894 self.database()?.list_dev_prop_async(&dev_name, filter).await
895 }
896
897 pub fn list_prop(&self, filter: &str) -> Result<Spectrum<str>> {
899 self.block_on(self.list_prop_async(filter))?
900 }
901
902 pub fn get_prop_async<Spec, Names: ToNames>(&self, names: Names) -> impl Future<Output = Result<Spec::Output>> + Send
914 where
915 Spec: GetPropSpec<Names>,
916 {
917 let names = names.to_names();
920 async move {
921 let dev_name = self.name_async().await?;
922 let params = names.map(|names| prop_params(&dev_name, names.iter()));
923 let mut reply = self.database()?.get_raw_prop_async(PropCommands::DEV, &dev_name, params).await?;
924 Spec::collect(&mut reply)
925 }
926 }
927
928 pub fn get_prop<Spec, Names: ToNames>(&self, names: Names) -> Result<Spec::Output>
930 where
931 Spec: GetPropSpec<Names>,
932 {
933 self.block_on(self.get_prop_async::<Spec, Names>(names))?
934 }
935
936 pub fn set_prop_async<'a, Spec>(&self, spec: Spec) -> impl Future<Output = Result<()>> + Send
945 where
946 Spec: SetPropSpec<'a>,
947 {
948 let params = set_prop_params(spec);
949 async move {
950 let dev_name = self.name_async().await?;
951 self.database()?.prop_command_async("DbPutDeviceProperty", prop_params(&dev_name, params.iter().map(|param| &**param))).await
952 }
953 }
954
955 pub fn set_prop<'a, Spec>(&self, spec: Spec) -> Result<()>
957 where
958 Spec: SetPropSpec<'a>,
959 {
960 self.block_on(self.set_prop_async(spec))?
961 }
962
963 pub fn del_prop_async<Names: ToNamesNotAll>(&self, names: Names) -> impl Future<Output = Result<()>> + Send {
972 let names = names.to_names_not_all();
973 async move {
974 let dev_name = self.name_async().await?;
975 self.database()?.prop_command_async("DbDeleteDeviceProperty", prop_params(&dev_name, names.iter())).await
976 }
977 }
978
979 pub fn del_prop<Names: ToNamesNotAll>(&self, names: Names) -> Result<()> {
981 self.block_on(self.del_prop_async(names))?
982 }
983
984 pub fn subscribe_attr_value<T: FromValue>(&self, name: &str, event: AttrValueEvent) -> DevChannel<AttrRead<T>> {
992 DevChannel::new(self.clone(), name, ChannelKind::Value(event), ChannelEvents::poll_value)
993 }
994
995 pub fn subscribe_attr_ready(&self, name: &str) -> DevChannel<()> {
999 DevChannel::new(self.clone(), name, ChannelKind::Ready, ChannelEvents::poll_ready)
1000 }
1001
1002 pub fn subscribe_attr_conf(&self, name: &str) -> DevChannel<AttrInfo> {
1005 DevChannel::new(self.clone(), name, ChannelKind::Conf, ChannelEvents::poll_conf)
1006 }
1007
1008 }
1017
1018impl Clone for DevProxy {
1022 fn clone(&self) -> Self {
1023 Self {
1024 inner: Box::new(ProxyInner {
1025 backend: self.inner.backend.clone(),
1026 target: self.inner.target.clone(),
1027 source: self.inner.source,
1028 timeout: self.inner.timeout,
1029 }),
1030 state: self.state.try_read().ok()
1031 .map(|guard| RwLock::new((*guard).clone()))
1032 .unwrap_or_else(|| RwLock::new(ProxyState {
1033 backend: None,
1034 database_host_and_port: None,
1035 })),
1036 }
1037 }
1038}
1039
1040impl PartialEq for DevProxy {
1041 #[inline]
1042 fn eq(&self, other: &Self) -> bool {
1043 Arc::ptr_eq(&self.inner.backend, &other.inner.backend) && self.inner.target == other.inner.target
1044 }
1045}
1046
1047impl Eq for DevProxy { }
1048
1049impl Hash for DevProxy {
1050 #[inline]
1051 fn hash<H: Hasher>(&self, state: &mut H) {
1052 Arc::as_ptr(&self.inner.backend).hash(state);
1053 self.inner.target.hash(state);
1054 }
1055}
1056
1057impl fmt::Display for ProxyTarget {
1060 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1061 match self {
1062 ProxyTarget::DatabaseFromEnv =>
1063 write!(f, "db"),
1064 ProxyTarget::DatabaseAt { host, port } =>
1065 write!(f, "db@{host}:{port}"),
1066 ProxyTarget::RegularFromDatabase { name, .. } =>
1067 write!(f, "{name}"),
1068 ProxyTarget::RegularAt { host, port, name } =>
1069 write!(f, "{name}@{host}:{port}"),
1070 }
1071 }
1072}
1073
1074pub trait ToNames: Sized {
1089
1090 fn iter_names(&self) -> Option<impl Iterator<Item = &str>>;
1094
1095 #[inline]
1097 fn to_names(self) -> Option<Spectrum<str>> {
1098 self.iter_names().map(names_buf)
1099 }
1100
1101}
1102
1103fn names_buf<'a>(names: impl Iterator<Item = &'a str>) -> Spectrum<str> {
1105 Spectrum::from_strs(names).expect("names of more than 4 GiB")
1106}
1107
1108pub trait ToNamesNotAll: ToNames {
1113
1114 #[inline]
1116 fn iter_names_not_all(&self) -> impl Iterator<Item = &str> {
1117 self.iter_names().expect("should not return the 'all' marker")
1118 }
1119
1120 #[inline]
1122 fn to_names_not_all(self) -> Spectrum<str> {
1123 self.to_names().expect("should not return the 'all' marker")
1124 }
1125
1126}
1127
1128#[derive(Debug, Clone, PartialEq)]
1136pub struct Named<T> {
1137 pub name: Scalar<str>,
1139 pub value: T,
1141}
1142
1143impl<T> Named<T> {
1144
1145 #[inline]
1146 pub fn new(name: Scalar<str>, value: T) -> Self {
1147 Self { name, value }
1148 }
1149
1150 #[inline]
1152 pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Named<U> {
1153 Named { name: self.name, value: f(self.value) }
1154 }
1155
1156}
1157
1158pub type NamedResult<T> = Named<Result<T>>;
1160
1161pub struct All;
1164impl ToNames for All {
1165 #[inline]
1166 fn iter_names(&self) -> Option<impl Iterator<Item = &str>> {
1167 None::<std::iter::Empty<&str>>
1168 }
1169}
1170
1171impl ToNames for &str {
1172 #[inline]
1173 fn iter_names(&self) -> Option<impl Iterator<Item = &str>> {
1174 Some(std::iter::once(*self))
1175 }
1176}
1177impl ToNamesNotAll for &str { }
1178
1179impl ToNames for &[&str] {
1180 #[inline]
1181 fn iter_names(&self) -> Option<impl Iterator<Item = &str>> {
1182 Some(self.iter().copied())
1183 }
1184}
1185impl ToNamesNotAll for &[&str] { }
1186
1187impl<const N: usize> ToNames for [&str; N] {
1188 #[inline]
1189 fn iter_names(&self) -> Option<impl Iterator<Item = &str>> {
1190 Some(self.iter().copied())
1191 }
1192}
1193impl<const N: usize> ToNamesNotAll for [&str; N] { }
1194
1195impl ToNames for Spectrum<str> {
1196 #[inline]
1197 fn iter_names(&self) -> Option<impl Iterator<Item = &str>> {
1198 Some(self.iter())
1199 }
1200 #[inline]
1201 fn to_names(self) -> Option<Spectrum<str>> {
1202 Some(self)
1203 }
1204}
1205impl ToNamesNotAll for Spectrum<str> { }
1206
1207impl ToNames for &Spectrum<str> {
1208 #[inline]
1209 fn iter_names(&self) -> Option<impl Iterator<Item = &str>> {
1210 Some(self.iter())
1211 }
1212 #[inline]
1213 fn to_names(self) -> Option<Spectrum<str>> {
1214 Some(self.clone())
1215 }
1216}
1217impl ToNamesNotAll for &Spectrum<str> { }
1218
1219macro_rules! impl_to_names_tuple {
1220 ( @name: $comp:ident ) => { &str };
1221 ( @one: $comp:ident ) => { 1 };
1222 ( $( $comp:ident )* ) => {
1223 #[allow(unused_variables)]
1224 impl ToNames for ( $(impl_to_names_tuple!(@name: $comp),)* ) {
1225 #[inline]
1226 fn iter_names(&self) -> Option<impl Iterator<Item = &str>> {
1227 let ( $($comp,)* ) = *self;
1228 Some(<[&str; 0 $( + impl_to_names_tuple!(@one: $comp) )*]>::into_iter([ $($comp,)* ]))
1229 }
1230 }
1231 impl ToNamesNotAll for ( $(impl_to_names_tuple!(@name: $comp),)* ) { }
1232 };
1233}
1234
1235impl_to_names_tuple!();
1236impl_to_names_tuple!(a);
1237impl_to_names_tuple!(a b);
1238impl_to_names_tuple!(a b c);
1239impl_to_names_tuple!(a b c d);
1240impl_to_names_tuple!(a b c d e);
1241impl_to_names_tuple!(a b c d e f);
1242
1243pub trait ToNamesSpec<T>: ToNames {
1250
1251 type Output;
1253
1254 fn collect(entries: Vec<NamedResult<T>>) -> Result<Self::Output>;
1259
1260}
1261
1262impl<T> ToNamesSpec<T> for &str {
1265
1266 type Output = T;
1267
1268 fn collect(entries: Vec<NamedResult<T>>) -> Result<Self::Output> {
1269 match entries.into_iter().next() {
1270 Some(entry) => entry.value,
1271 None => Err(unexpected_len_err(1)),
1272 }
1273 }
1274
1275}
1276
1277impl<T> ToNamesSpec<T> for &[&str] {
1278
1279 type Output = Vec<Result<T>>;
1280
1281 fn collect(entries: Vec<NamedResult<T>>) -> Result<Self::Output> {
1282 Ok(entries.into_iter().map(|entry| entry.value).collect())
1283 }
1284
1285}
1286
1287impl<T> ToNamesSpec<T> for Spectrum<str> {
1288
1289 type Output = Vec<Result<T>>;
1290
1291 fn collect(entries: Vec<NamedResult<T>>) -> Result<Self::Output> {
1292 <&[&str] as ToNamesSpec<T>>::collect(entries)
1293 }
1294
1295}
1296
1297impl<T> ToNamesSpec<T> for &Spectrum<str> {
1298
1299 type Output = Vec<Result<T>>;
1300
1301 fn collect(entries: Vec<NamedResult<T>>) -> Result<Self::Output> {
1302 <&[&str] as ToNamesSpec<T>>::collect(entries)
1303 }
1304
1305}
1306
1307impl<T, const N: usize> ToNamesSpec<T> for [&str; N] {
1308
1309 type Output = [Result<T>; N];
1310
1311 fn collect(entries: Vec<NamedResult<T>>) -> Result<Self::Output> {
1312 take_array(entries.into_iter().map(|entry| entry.value))
1313 }
1314
1315}
1316
1317impl<T> ToNamesSpec<T> for All {
1318
1319 type Output = Vec<NamedResult<T>>;
1322
1323 fn collect(entries: Vec<NamedResult<T>>) -> Result<Self::Output> {
1324 Ok(entries)
1325 }
1326
1327}
1328
1329pub(crate) fn take_array<T, const N: usize>(mut entries: impl Iterator<Item = T>) -> Result<[T; N]> {
1333 let mut slots = [const { None }; N];
1334 for slot in &mut slots {
1335 *slot = Some(entries.next().ok_or_else(|| unexpected_len_err(N))?);
1336 }
1337 if entries.next().is_some() {
1338 return Err(unexpected_len_err(N));
1339 }
1340 Ok(slots.map(|slot| slot.expect("every slot is filled")))
1341}
1342
1343macro_rules! impl_to_names_spec_tuple {
1344 ( @one: $index:tt ) => { 1 };
1345 ( @name: $index:tt ) => { &str };
1346 ( @t: $index:tt ) => { T };
1347 ( @next: $index:tt, $entries:ident ) => { $entries.next().ok_or_else(|| unexpected_len_err(LEN))?.value };
1348 ( $( $index:tt )* ) => {
1349
1350 impl<T> ToNamesSpec<T> for ( $( impl_to_names_spec_tuple!(@name: $index), )* ) {
1351
1352 type Output = ( $( Result< impl_to_names_spec_tuple!(@t: $index) >,)* );
1353
1354 fn collect(entries: Vec<NamedResult<T>>) -> Result<Self::Output> {
1355 const LEN: usize = 0 $( + impl_to_names_spec_tuple!(@one: $index) )*;
1356 let mut entries = entries.into_iter();
1359 let output = ( $( impl_to_names_spec_tuple!(@next: $index, entries), )* );
1360 if entries.next().is_some() {
1361 return Err(unexpected_len_err(LEN));
1362 }
1363 Ok(output)
1364 }
1365
1366 }
1367
1368 };
1369}
1370
1371impl_to_names_spec_tuple!(0);
1372impl_to_names_spec_tuple!(0 1);
1373impl_to_names_spec_tuple!(0 1 2);
1374impl_to_names_spec_tuple!(0 1 2 3);
1375impl_to_names_spec_tuple!(0 1 2 3 4);
1376impl_to_names_spec_tuple!(0 1 2 3 4 5);
1377
1378pub trait ReadAttrSpec<Names> {
1388
1389 type Output;
1391
1392 fn collect(reads: Vec<NamedResult<AttrRead<Value>>>) -> Result<Self::Output>;
1397
1398}
1399
1400impl<'name, T: FromValue> ReadAttrSpec<&'name str> for T {
1401
1402 type Output = AttrRead<T>;
1403
1404 fn collect(reads: Vec<NamedResult<AttrRead<Value>>>) -> Result<Self::Output> {
1405 match reads.into_iter().next() {
1406 Some(entry) => entry.value?.parse(),
1407 None => Err(unexpected_len_err(1)),
1408 }
1409 }
1410
1411}
1412
1413impl<'name, T: FromValue> ReadAttrSpec<&'_ [&'name str]> for T {
1414
1415 type Output = Vec<Result<AttrRead<T>>>;
1416
1417 fn collect(reads: Vec<NamedResult<AttrRead<Value>>>) -> Result<Self::Output> {
1418 Ok(reads.into_iter().map(|entry| entry.value.and_then(AttrRead::parse)).collect())
1419 }
1420
1421}
1422
1423impl<T: FromValue> ReadAttrSpec<Spectrum<str>> for T {
1424
1425 type Output = Vec<Result<AttrRead<T>>>;
1426
1427 fn collect(reads: Vec<NamedResult<AttrRead<Value>>>) -> Result<Self::Output> {
1428 <T as ReadAttrSpec<&[&str]>>::collect(reads)
1429 }
1430
1431}
1432
1433impl<T: FromValue> ReadAttrSpec<&Spectrum<str>> for T {
1434
1435 type Output = Vec<Result<AttrRead<T>>>;
1436
1437 fn collect(reads: Vec<NamedResult<AttrRead<Value>>>) -> Result<Self::Output> {
1438 <T as ReadAttrSpec<&[&str]>>::collect(reads)
1439 }
1440
1441}
1442
1443impl<'name, T: FromValue, const N: usize> ReadAttrSpec<[&'name str; N]> for T {
1444
1445 type Output = [Result<AttrRead<T>>; N];
1446
1447 fn collect(reads: Vec<NamedResult<AttrRead<Value>>>) -> Result<Self::Output> {
1448 take_array(reads.into_iter().map(|entry| entry.value.and_then(AttrRead::parse)))
1449 }
1450
1451}
1452
1453impl<T: FromValue> ReadAttrSpec<All> for T {
1454
1455 type Output = Vec<NamedResult<AttrRead<T>>>;
1457
1458 fn collect(reads: Vec<NamedResult<AttrRead<Value>>>) -> Result<Self::Output> {
1459 Ok(reads.into_iter().map(|entry| entry.map(|read| read.and_then(AttrRead::parse))).collect())
1460 }
1461
1462}
1463
1464macro_rules! impl_read_attr_spec_tuple {
1465 ( @one: $comp:ident ) => { 1 };
1466 ( @name: $comp:ident ) => { &'name str };
1467 ( $( $comp:ident )* ) => {
1468
1469 impl<'name, $($comp,)* > ReadAttrSpec<( $( impl_read_attr_spec_tuple!(@name: $comp), )* )> for ( $($comp,)* )
1470 where
1471 $( $comp: FromValue, )*
1472 {
1473
1474 type Output = ( $( Result<AttrRead<$comp>>,)* );
1475
1476 fn collect(reads: Vec<NamedResult<AttrRead<Value>>>) -> Result<Self::Output> {
1477 const LEN: usize = 0 $( + impl_read_attr_spec_tuple!(@one: $comp) )*;
1478 let mut reads = reads.into_iter();
1482 let output = ( $( reads.next().ok_or_else(|| unexpected_len_err(LEN))?.value.and_then(AttrRead::parse::<$comp>), )* );
1483 if reads.next().is_some() {
1484 return Err(unexpected_len_err(LEN));
1485 }
1486 Ok(output)
1487 }
1488
1489 }
1490
1491 };
1492}
1493
1494impl_read_attr_spec_tuple!(A);
1495impl_read_attr_spec_tuple!(A B);
1496impl_read_attr_spec_tuple!(A B C);
1497impl_read_attr_spec_tuple!(A B C D);
1498impl_read_attr_spec_tuple!(A B C D E);
1499impl_read_attr_spec_tuple!(A B C D E F);
1500
1501pub trait WriteAttrSpec<'a>: Sized {
1511
1512 fn into_values(self) -> Vec<Named<Value>>;
1515
1516 type Output;
1518
1519 fn collect(writes: Vec<Result<()>>) -> Result<Self::Output>;
1523
1524 type ReadNames;
1526 type ReadSpec;
1528
1529}
1530
1531impl<'a, T: IntoValue> WriteAttrSpec<'a> for (&'a str, T) {
1533
1534 #[inline]
1535 fn into_values(self) -> Vec<Named<Value>> {
1536 vec![Named::new(Scalar::from(self.0), self.1.into_value())]
1537 }
1538
1539 type Output = ();
1540
1541 fn collect(writes: Vec<Result<()>>) -> Result<Self::Output> {
1542 match writes.into_iter().next() {
1543 Some(res) => res,
1544 None => Err(unexpected_len_err(1)),
1545 }
1546 }
1547
1548 type ReadNames = &'a str;
1549 type ReadSpec = T;
1550
1551}
1552
1553impl<'a, T: IntoValue, const N: usize> WriteAttrSpec<'a> for [(&'a str, T); N] {
1555
1556 #[inline]
1557 fn into_values(self) -> Vec<Named<Value>> {
1558 named_values(self.into_iter().map(|(name, value)| (name, value.into_value())).collect())
1559 }
1560
1561 type Output = [Result<()>; N];
1562
1563 fn collect(writes: Vec<Result<()>>) -> Result<Self::Output> {
1564 take_array(writes.into_iter())
1565 }
1566
1567 type ReadNames = [&'a str; N];
1568 type ReadSpec = T;
1569
1570}
1571
1572impl<'a, T: IntoValue> WriteAttrSpec<'a> for Vec<(&'a str, T)> {
1574
1575 #[inline]
1576 fn into_values(self) -> Vec<Named<Value>> {
1577 named_values(self.into_iter().map(|(name, value)| (name, value.into_value())).collect())
1578 }
1579
1580 type Output = Vec<Result<()>>;
1581
1582 fn collect(writes: Vec<Result<()>>) -> Result<Self::Output> {
1583 Ok(writes)
1584 }
1585
1586 type ReadNames = &'a [&'a str];
1587 type ReadSpec = T;
1588
1589}
1590
1591fn named_values(values: Vec<(&str, Value)>) -> Vec<Named<Value>> {
1594 let names = names_buf(values.iter().map(|&(name, _)| name));
1595 values.into_iter()
1596 .enumerate()
1597 .map(|(index, (_, value))| Named::new(names.get_scalar([index]).expect("as many names as values"), value))
1598 .collect()
1599}
1600
1601macro_rules! impl_write_attr_spec_tuple {
1602 ( @name: $comp:ident ) => { &'a str };
1603 ( @one: $comp:ident ) => { 1 };
1604 ( @unit: $comp:ident ) => { Result<()> };
1605 ( @next: $comp:ident, $writes:ident ) => { $writes.next().ok_or_else(|| unexpected_len_err(LEN))? };
1606 ( $( $comp:ident )* ) => {
1607
1608 #[allow(non_snake_case)]
1609 impl<'a, $($comp: IntoValue, )*> WriteAttrSpec<'a> for ( $( (&'a str, $comp), )* ) {
1610
1611 #[inline]
1612 fn into_values(self) -> Vec<Named<Value>> {
1613 let ( $($comp,)* ) = self;
1614 named_values(vec![$(($comp.0, $comp.1.into_value()), )* ])
1615 }
1616
1617 type Output = ( $( impl_write_attr_spec_tuple!(@unit: $comp), )* );
1618
1619 fn collect(writes: Vec<Result<()>>) -> Result<Self::Output> {
1620 const LEN: usize = 0 $( + impl_write_attr_spec_tuple!(@one: $comp) )*;
1621 let mut writes = writes.into_iter();
1624 let output = ( $( impl_write_attr_spec_tuple!(@next: $comp, writes), )* );
1625 if writes.next().is_some() {
1626 return Err(unexpected_len_err(LEN));
1627 }
1628 Ok(output)
1629 }
1630
1631 type ReadNames = ( $(impl_write_attr_spec_tuple!(@name: $comp), )* );
1632 type ReadSpec = ( $($comp, )* );
1633
1634 }
1635
1636 };
1637}
1638
1639impl_write_attr_spec_tuple!(A);
1640impl_write_attr_spec_tuple!(A B);
1641impl_write_attr_spec_tuple!(A B C);
1642impl_write_attr_spec_tuple!(A B C D);
1643impl_write_attr_spec_tuple!(A B C D E);
1644impl_write_attr_spec_tuple!(A B C D E F);
1645
1646#[derive(Debug, Clone, PartialEq, Eq)]
1652pub struct CommandInfo {
1653 pub display_level: DisplayLevel,
1655 pub in_kind: CommandDataKind,
1657 pub out_kind: CommandDataKind,
1659 pub in_doc: Option<Cow<'static, str>>,
1661 pub out_doc: Option<Cow<'static, str>>,
1663}
1664
1665#[derive(Debug, Clone, PartialEq, Eq)]
1671pub struct AttrRead<T> {
1672 pub value: Option<T>,
1675 pub w_value: Option<T>,
1678 pub quality: AttrQuality,
1680 pub time: SystemTime,
1682}
1683
1684impl<T> AttrRead<T> {
1685
1686 pub fn new(value: T) -> Self {
1687 Self {
1688 value: Some(value),
1689 w_value: None,
1690 quality: AttrQuality::Valid,
1691 time: SystemTime::now(),
1692 }
1693 }
1694
1695 pub fn with_quality(mut self, quality: AttrQuality) -> Self {
1696 self.quality = quality;
1697 self
1698 }
1699
1700 pub fn with_time(mut self, time: SystemTime) -> Self {
1701 self.time = time;
1702 self
1703 }
1704
1705}
1706
1707impl AttrRead<Value> {
1708
1709 fn parse<T: FromValue>(self) -> Result<AttrRead<T>> {
1711 Ok(AttrRead {
1712 value: match self.value {
1713 Some(value) => Some(T::from_value(value)
1714 .map_err(|err| err.with_append_description("Failed to parse read value"))?),
1715 None => None,
1716 },
1717 w_value: match self.w_value {
1718 Some(value) => Some(T::from_value(value)
1719 .map_err(|err| err.with_append_description("Failed to parse write value"))?),
1720 None => None,
1721 },
1722 quality: self.quality,
1723 time: self.time,
1724 })
1725 }
1726
1727}
1728
1729#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1731pub enum AttrQuality {
1732 Valid,
1733 Invalid,
1734 Alarm,
1735 Changing,
1736 Warning,
1737}
1738
1739#[derive(Debug, Clone, PartialEq)]
1741pub struct AttrInfo {
1742 pub config: AttrConfig,
1746 pub mode: AttrMode,
1748 pub data_kind: AttrDataKind,
1750 pub data_format: AttrDataFormat,
1753 pub display_level: DisplayLevel,
1755 pub memorized: Option<AttrMemorized>,
1757}
1758
1759#[derive(Debug, Clone, PartialEq, Eq)]
1761pub enum AttrMode {
1762 Read,
1764 ReadWithWrite {
1767 write_attr_name: Cow<'static, str>,
1769 },
1770 Write,
1772 ReadWrite,
1774}
1775
1776#[derive(Debug, Clone, PartialEq, Default)]
1788pub struct AttrConfig {
1789 pub doc: Option<Cow<'static, str>>,
1792 pub label: Option<Cow<'static, str>>,
1794 pub unit: Option<Cow<'static, str>>,
1797 pub standard_unit: Option<f64>,
1800 pub display_unit: Option<Cow<'static, str>>,
1802 pub display_format: Option<Cow<'static, str>>,
1805 pub range: AttrRangeConfig,
1807 pub alarm: AttrRangeConfig,
1809 pub warning: AttrRangeConfig,
1811 pub delta: Option<AttrDeltaConfig>,
1813 pub change_event: AttrChangeEventConfig,
1815 pub periodic_event: AttrPeriodicEventConfig,
1817 pub archive_event: AttrArchiveEventConfig,
1819 pub enum_labels: Vec<Cow<'static, str>>,
1826 pub extensions: Vec<Cow<'static, str>>,
1828}
1829
1830#[derive(Debug, Clone, Copy)]
1854pub enum AttrNumber {
1855 Double(f64),
1856 Long(i64),
1857 ULong(u64),
1858}
1859
1860impl AttrNumber {
1861
1862 pub fn as_f64(self) -> f64 {
1866 match self {
1867 Self::Double(value) => value,
1868 Self::Long(value) => value as f64,
1869 Self::ULong(value) => value as f64,
1870 }
1871 }
1872
1873}
1874
1875impl PartialEq for AttrNumber {
1876 #[inline]
1877 fn eq(&self, other: &Self) -> bool {
1878 self.partial_cmp(other) == Some(Ordering::Equal)
1879 }
1880}
1881
1882impl PartialOrd for AttrNumber {
1883 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1884 match (*self, *other) {
1885 (Self::Long(a), Self::Long(b)) => Some(a.cmp(&b)),
1886 (Self::ULong(a), Self::ULong(b)) => Some(a.cmp(&b)),
1887 (Self::Long(a), Self::ULong(b)) => Some(i128::from(a).cmp(&i128::from(b))),
1888 (Self::ULong(a), Self::Long(b)) => Some(i128::from(a).cmp(&i128::from(b))),
1889 (a, b) => a.as_f64().partial_cmp(&b.as_f64()),
1890 }
1891 }
1892}
1893
1894impl fmt::Display for AttrNumber {
1895 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1897 match self {
1898 Self::Double(value) => Display::fmt(value, f),
1899 Self::Long(value) => Display::fmt(value, f),
1900 Self::ULong(value) => Display::fmt(value, f),
1901 }
1902 }
1903}
1904
1905macro_rules! impl_attr_number_from {
1908 ($variant:ident: $($ty:ty),* $(,)?) => { $(
1909 impl From<$ty> for AttrNumber {
1910 #[inline]
1911 fn from(value: $ty) -> Self {
1912 Self::$variant(value.into())
1913 }
1914 }
1915 )* };
1916}
1917
1918impl_attr_number_from!(Double: f32, f64);
1919impl_attr_number_from!(Long: i8, i16, i32, i64);
1920impl_attr_number_from!(ULong: u8, u16, u32, u64);
1921
1922#[derive(Debug, Clone, Copy, PartialEq, Default)]
1926pub struct AttrRangeConfig {
1927 pub min: Option<AttrNumber>,
1928 pub max: Option<AttrNumber>,
1929}
1930
1931impl AttrRangeConfig {
1932 #[inline]
1934 pub fn is_set(&self) -> bool {
1935 self.min.is_some() || self.max.is_some()
1936 }
1937}
1938
1939#[derive(Debug, Clone, Copy, PartialEq)]
1941pub struct AttrDeltaConfig {
1942 pub value: AttrNumber,
1945 pub time: Option<Duration>,
1947}
1948
1949#[derive(Debug, Clone, Copy, PartialEq, Default)]
1952pub struct AttrChangeEventConfig {
1953 pub rel_change: Option<f64>,
1955 pub abs_change: Option<f64>,
1957}
1958
1959impl AttrChangeEventConfig {
1960 #[inline]
1962 pub fn is_set(&self) -> bool {
1963 self.rel_change.is_some() || self.abs_change.is_some()
1964 }
1965}
1966
1967#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
1968pub struct AttrPeriodicEventConfig {
1969 pub period: Option<Duration>,
1970}
1971
1972impl AttrPeriodicEventConfig {
1973 #[inline]
1975 pub fn is_set(&self) -> bool {
1976 self.period.is_some()
1977 }
1978}
1979
1980#[derive(Debug, Clone, Copy, PartialEq, Default)]
1983pub struct AttrArchiveEventConfig {
1984 pub rel_change: Option<f64>,
1985 pub abs_change: Option<f64>,
1986 pub period: Option<Duration>,
1987}
1988
1989impl AttrArchiveEventConfig {
1990 #[inline]
1992 pub fn is_set(&self) -> bool {
1993 self.rel_change.is_some() || self.abs_change.is_some() || self.period.is_some()
1994 }
1995}
1996
1997#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2003pub enum AttrMemorized {
2004 RestoreSetPoint,
2009 RestoreAndWrite,
2012}
2013
2014#[derive(Debug, Clone, PartialEq, Eq, Default)]
2023pub struct DevInfo {
2024 pub class: String,
2026 pub server_id: String,
2029 pub server_host: String,
2031 pub server_version: i32,
2034 pub doc_url: Option<Cow<'static, str>>,
2036 pub dev_type: Option<Cow<'static, str>>,
2039 pub version_info: Vec<(Cow<'static, str>, Cow<'static, str>)>,
2043}
2044
2045#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2051#[repr(u8)]
2052pub enum DevState {
2053 On,
2054 Off,
2055 Close,
2056 Open,
2057 Insert,
2058 Extract,
2059 Moving,
2060 Standby,
2061 Fault,
2062 Init,
2063 Running,
2064 Alarm,
2065 Disable,
2066 Unknown,
2067}
2068
2069#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2071pub enum DevSource {
2072 Dev,
2074 Cache,
2076 CacheDev,
2078}
2079
2080#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
2081pub enum DisplayLevel {
2082 #[default]
2084 Operator,
2085 Expert,
2086}
2087
2088enum ChannelEvents {
2095 Value(BoxStream<'static, Result<AttrRead<Value>>>),
2096 Ready(BoxStream<'static, Result<()>>),
2097 Conf(BoxStream<'static, Result<AttrInfo>>),
2098}
2099
2100impl ChannelEvents {
2101
2102 fn poll_value<T: FromValue>(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<AttrRead<T>>>> {
2104 let Self::Value(stream) = self else { unreachable!("a value channel subscribes to values") };
2105 stream.poll_next_unpin(cx).map(|item| item.map(|read| read.and_then(AttrRead::parse)))
2106 }
2107
2108 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<()>>> {
2110 let Self::Ready(stream) = self else { unreachable!("a ready channel subscribes to readiness") };
2111 stream.poll_next_unpin(cx)
2112 }
2113
2114 fn poll_conf(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<AttrInfo>>> {
2116 let Self::Conf(stream) = self else { unreachable!("a configuration channel subscribes to configurations") };
2117 stream.poll_next_unpin(cx)
2118 }
2119
2120}
2121
2122#[derive(Debug, Clone, Copy)]
2124enum ChannelKind {
2125 Value(AttrValueEvent),
2126 Ready,
2127 Conf,
2128}
2129
2130pub struct DevChannel<T> {
2158 dev: Option<DevProxy>,
2161 name: Arc<str>,
2163 kind: ChannelKind,
2164 poll_events: fn(&mut ChannelEvents, &mut Context<'_>) -> Poll<Option<Result<T>>>,
2166 timeout: Duration,
2168 state: ChannelState,
2169}
2170
2171enum ChannelState {
2172 Subscribed(ChannelEvents),
2174 Subscribing(BoxFuture<'static, (DevProxy, Result<ChannelEvents>)>),
2177}
2178
2179impl ChannelState {
2180
2181 fn subscribing(dev: DevProxy, name: Arc<str>, kind: ChannelKind) -> Self {
2184 Self::Subscribing(Box::pin(async move {
2185 let res = async {
2186 let backend = dev.ensure_backend().await?;
2187 let admin = dev.admin_dev_async().await?;
2188 let admin = admin.ensure_backend().await?;
2189 let res = match kind {
2190 ChannelKind::Value(event) => backend.subscribe_attr_value(admin, &name, event).await.map(ChannelEvents::Value),
2191 ChannelKind::Ready => backend.subscribe_attr_ready(admin, &name).await.map(ChannelEvents::Ready),
2192 ChannelKind::Conf => backend.subscribe_attr_conf(admin, &name).await.map(ChannelEvents::Conf),
2193 };
2194 dev.check_backend(res)
2195 }.await;
2196 (dev, res)
2197 }))
2198 }
2199
2200}
2201
2202impl<T> DevChannel<T> {
2203
2204 fn new(
2207 dev: DevProxy,
2208 name: &str,
2209 kind: ChannelKind,
2210 poll_events: fn(&mut ChannelEvents, &mut Context<'_>) -> Poll<Option<Result<T>>>,
2211 ) -> Self {
2212 let timeout = dev.timeout();
2213 let name = Arc::<str>::from(name);
2214 let state = ChannelState::subscribing(dev, Arc::clone(&name), kind);
2215 Self { dev: None, name, kind, poll_events, timeout, state }
2216 }
2217
2218 fn resubscribe(&mut self) {
2220 let dev = self.dev.take().expect("the device is moved into one attempt at a time");
2221 self.state = ChannelState::subscribing(dev, Arc::clone(&self.name), self.kind);
2222 }
2223
2224 fn poll_subscribed(&mut self, cx: &mut Context<'_>) -> Poll<Result<()>> {
2227
2228 let ChannelState::Subscribing(attempt) = &mut self.state else {
2229 return Poll::Ready(Ok(()));
2230 };
2231
2232 let (dev, res) = ready!(attempt.as_mut().poll(cx));
2233 let dev = &*self.dev.insert(dev);
2234
2235 Poll::Ready(match res {
2236 Ok(events) => {
2237 debug!(dev = %dev.inner.target, attr = %self.name, kind = ?self.kind, "Subscribed to the events");
2238 self.state = ChannelState::Subscribed(events);
2239 Ok(())
2240 }
2241 Err(err) => {
2242 debug!(dev = %dev.inner.target, attr = %self.name, kind = ?self.kind, %err, "Failed to subscribe to the events, retrying on the next poll");
2243 self.resubscribe();
2244 Err(err)
2245 }
2246 })
2247
2248 }
2249
2250 pub async fn ensure_subscribed_async(&mut self) -> Result<()> {
2254 poll_fn(|cx| self.poll_subscribed(cx)).await
2255 }
2256
2257 pub fn ensure_subscribed(&mut self) -> Result<()> {
2260 let timeout = self.timeout;
2261 block_on_timeout(self.ensure_subscribed_async(), timeout)?
2262 }
2263
2264 pub fn wait(&mut self, timeout: Duration) -> Result<T> {
2267 match block_on_timeout(self.next(), timeout)? {
2268 Some(item) => item,
2269 None => unreachable!("a device channel never ends"),
2270 }
2271 }
2272
2273}
2274
2275impl<T> Stream for DevChannel<T> {
2276
2277 type Item = Result<T>;
2278
2279 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
2280
2281 let this = self.get_mut();
2282
2283 loop {
2284
2285 if let Err(err) = ready!(this.poll_subscribed(cx)) {
2286 return Poll::Ready(Some(Err(err)));
2287 }
2288
2289 let ChannelState::Subscribed(events) = &mut this.state else {
2290 unreachable!("subscribed once the attempt succeeded");
2291 };
2292
2293 match ready!((this.poll_events)(events, cx)) {
2294 Some(Err(err)) if err.kind() == ErrorKind::DevNotConnected => {
2295 debug!(attr = %this.name, kind = ?this.kind, %err, "Lost the subscription to the events, subscribing again on the next poll");
2296 if let Some(dev) = &this.dev {
2297 dev.reset_backend();
2298 }
2299 this.resubscribe();
2300 return Poll::Ready(Some(Err(err)));
2301 }
2302 Some(item) => return Poll::Ready(Some(item)),
2303 None => {
2305 debug!(attr = %this.name, kind = ?this.kind, "The subscription to the events ended, subscribing again");
2306 this.resubscribe();
2307 }
2308 }
2309
2310 }
2311
2312 }
2313
2314}
2315
2316impl<T> Debug for DevChannel<T> {
2317 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2318 f.debug_struct("DevChannel")
2319 .field("dev", &self.dev)
2320 .field("name", &self.name)
2321 .finish_non_exhaustive()
2322 }
2323}
2324
2325#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2327pub enum AttrValueEvent {
2328 Change,
2329 Periodic,
2330 Archive,
2331 User,
2332 Alarm,
2333}
2334
2335#[cold]
2343pub(crate) fn unexpected_len_err(expected: usize) -> Error {
2344 Error::new(ErrorKind::Internal)
2345 .with_description("The device did not answer with as many entries as were requested")
2346 .with_append_description(format_args!("{expected} were expected"))
2347}
2348
2349fn command_err(err: Error) -> Error {
2350 match err.kind() {
2351 ErrorKind::Reason(reason::API_CommandNotFound) => {
2352 err.with_kind(ErrorKind::CommandNotFound)
2353 }
2354 ErrorKind::Reason(reason::API_IncompatibleCmdArgumentType) => {
2355
2356 let expected_kind = expected_cpp_tango_type(err.description())
2357 .and_then(command_data_kind_from_cpp_tango);
2358
2359 let err = err.with_kind(ErrorKind::CommandArgumentKindInvalid);
2360 match expected_kind {
2361 Some(kind) => err.with_append_description(format_args!("Expected kind: {kind:?}")),
2362 None => err,
2363 }
2364
2365 }
2366 _ => err,
2367 }
2368}
2369
2370fn write_attr_err(err: Error) -> Error {
2372 match err.kind() {
2373 ErrorKind::Reason(reason::API_AttrNotFound) => {
2374 err.with_kind(ErrorKind::AttrNotFound)
2375 }
2376 ErrorKind::Reason(reason::API_AttrNotWritable) => {
2377 err.with_kind(ErrorKind::AttrNotWritable)
2378 }
2379 ErrorKind::Reason(reason::API_IncompatibleAttrDataType) => {
2380
2381 let expected_kind = expected_cpp_tango_type(err.description())
2382 .and_then(attr_data_kind_from_cpp_tango);
2383
2384 let err = err.with_kind(ErrorKind::AttrKindInvalid);
2385 match expected_kind {
2386 Some(kind) => err.with_append_description(format_args!("Expected kind: {kind:?}")),
2387 None => err,
2388 }
2389
2390 }
2391 _ => err,
2392 }
2393}
2394
2395fn expected_cpp_tango_type(description: &str) -> Option<&str> {
2408 description.split_ascii_whitespace()
2409 .find_map(|word| word.strip_prefix("Tango::"))
2410 .map(|ty| ty.trim_matches(|c: char| !c.is_ascii_alphanumeric()))
2411}
2412
2413fn command_data_kind_from_cpp_tango(cpp_type: &str) -> Option<CommandDataKind> {
2418 Some(match cpp_type {
2419 "DevVoid" => CommandDataKind::Void,
2420 "DevBoolean" => CommandDataKind::Bool,
2421 "DevUChar" => CommandDataKind::Octet,
2422 "DevShort" => CommandDataKind::Short,
2423 "DevUShort" => CommandDataKind::UShort,
2424 "DevLong" => CommandDataKind::Long,
2425 "DevULong" => CommandDataKind::ULong,
2426 "DevLong64" => CommandDataKind::Long64,
2427 "DevULong64" => CommandDataKind::ULong64,
2428 "DevFloat" => CommandDataKind::Float,
2429 "DevDouble" => CommandDataKind::Double,
2430 "DevState" => CommandDataKind::State,
2431 "DevString" |
2432 "ConstDevString" => CommandDataKind::String,
2433 "DevVarBooleanArray" => CommandDataKind::BoolArray,
2434 "DevVarCharArray" => CommandDataKind::OctetArray,
2435 "DevVarShortArray" => CommandDataKind::ShortArray,
2436 "DevVarUShortArray" => CommandDataKind::UShortArray,
2437 "DevVarLongArray" => CommandDataKind::LongArray,
2438 "DevVarULongArray" => CommandDataKind::ULongArray,
2439 "DevVarLong64Array" => CommandDataKind::Long64Array,
2440 "DevVarULong64Array" => CommandDataKind::ULong64Array,
2441 "DevVarFloatArray" => CommandDataKind::FloatArray,
2442 "DevVarDoubleArray" => CommandDataKind::DoubleArray,
2443 "DevVarStateArray" => CommandDataKind::StateArray,
2444 "DevVarStringArray" => CommandDataKind::StringArray,
2445 "DevVarLongStringArray" => CommandDataKind::LongStringArray,
2446 "DevVarDoubleStringArray" => CommandDataKind::DoubleStringArray,
2447 "DevEncoded" => CommandDataKind::Encoded,
2448 _ => return None,
2449 })
2450}
2451
2452fn attr_data_kind_from_cpp_tango(cpp_type: &str) -> Option<AttrDataKind> {
2463 Some(match cpp_type {
2464 "DevBoolean" | "DevVarBooleanArray" => AttrDataKind::Bool,
2465 "DevUChar" | "DevVarCharArray" => AttrDataKind::Octet,
2466 "DevShort" | "DevVarShortArray" => AttrDataKind::Short,
2467 "DevUShort" | "DevVarUShortArray" => AttrDataKind::UShort,
2468 "DevLong" | "DevVarLongArray" => AttrDataKind::Long,
2469 "DevULong" | "DevVarULongArray" => AttrDataKind::ULong,
2470 "DevLong64" | "DevVarLong64Array" => AttrDataKind::Long64,
2471 "DevULong64" | "DevVarULong64Array" => AttrDataKind::ULong64,
2472 "DevFloat" | "DevVarFloatArray" => AttrDataKind::Float,
2473 "DevDouble" | "DevVarDoubleArray" => AttrDataKind::Double,
2474 "DevState" | "DevVarStateArray" => AttrDataKind::State,
2475 "DevString" | "ConstDevString" | "DevVarStringArray" => AttrDataKind::String,
2476 "DevEncoded" | "DevVarEncodedArray" => AttrDataKind::Encoded,
2477 _ => return None,
2478 })
2479}
2480
2481struct DisplayHostAndPorts<'a>(&'a [(Arc<str>, u16)]);
2484
2485impl Display for DisplayHostAndPorts<'_> {
2486 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2487 for (i, (host, port)) in self.0.iter().enumerate() {
2488 if i != 0 {
2489 f.write_str(", ")?;
2490 }
2491 write!(f, "{host}:{port}")?;
2492 }
2493 Ok(())
2494 }
2495}
2496
2497fn env_databases() -> &'static [(Arc<str>, u16)] {
2499 static CACHE: OnceLock<Box<[(Arc<str>, u16)]>> = OnceLock::new();
2500 &**CACHE.get_or_init(|| {
2501
2502 let mut ret = Vec::new();
2503
2504 if let Ok(var) = env::var("TANGO_HOST") {
2505 for part in var.split(',') {
2506 if let Some((host, port_raw)) = part.rsplit_once(':')
2507 && let Ok(port) = u16::from_str(port_raw) {
2508 ret.push((host.into(), port));
2509 }
2510 }
2511 }
2512
2513 ret.into_boxed_slice()
2514
2515 })
2516}
2517
2518
2519#[cfg(test)]
2520mod tests {
2521
2522 use crate::value::Scalar;
2523
2524 use super::*;
2525
2526 #[allow(dead_code)]
2529 fn props_in_send_future(proxy: &DevProxy) -> BoxFuture<'_, Result<()>> {
2530 Box::pin(async move {
2531 let _speed = proxy.get_prop_async::<f64, _>("speed").await?;
2532 let _all = proxy.get_prop_async::<Spectrum<str>, _>(All).await?;
2533 proxy.set_prop_async(("speed", 2.5)).await?;
2534 proxy.del_prop_async(["speed", "accel"]).await
2535 })
2536 }
2537
2538 #[allow(dead_code)]
2541 fn requests_in_send_future(proxy: &DevProxy) -> BoxFuture<'_, Result<()>> {
2542 Box::pin(async move {
2543 let _: f64 = proxy.command_async("Add", 1.0).await?;
2544 let _: String = proxy.command_async("Echo", "text").await?;
2545 let _ = proxy.read_attr_async::<f64, _>("speed").await?;
2546 let _ = proxy.read_attr_async::<f64, _>(["speed", "accel"]).await?;
2547 let _ = proxy.read_attr_async::<Value, _>(All).await?;
2548 proxy.write_attr_async(("speed", 2.5)).await?;
2549 let _ = proxy.write_attr_async([("speed", 2.5), ("accel", 1.0)]).await?;
2550 let _ = proxy.write_read_attr_async(("speed", 2.5)).await?;
2551 let _ = proxy.command_info_async("Add").await?;
2552 let _ = proxy.attr_info_async(["speed"]).await?;
2553 let names = proxy.list_prop_async("*").await?;
2554 let _ = proxy.read_attr_async::<f64, _>(&names).await?;
2555 let _ = proxy.attr_info_async(names).await?;
2556 Ok(())
2557 })
2558 }
2559
2560 #[allow(dead_code)]
2563 fn props_of_any_names<'a, Names>(proxy: &'a DevProxy, names: Names) -> BoxFuture<'a, Result<<f64 as GetPropSpec<Names>>::Output>>
2564 where
2565 Names: ToNames + 'a,
2566 f64: GetPropSpec<Names>,
2567 {
2568 Box::pin(proxy.get_prop_async::<f64, Names>(names))
2569 }
2570
2571 #[test]
2572 fn expected_kind() {
2573
2574 let cmd = "Incompatible command argument type, expected type is : Tango::DevVarDoubleStringArray\0";
2577 assert_eq!(expected_cpp_tango_type(cmd), Some("DevVarDoubleStringArray"));
2578 assert_eq!(
2579 expected_cpp_tango_type(cmd).and_then(command_data_kind_from_cpp_tango),
2580 Some(CommandDataKind::DoubleStringArray),
2581 );
2582
2583 let attr = "Incompatible attribute type: expected Tango::DevVarDoubleArray (even for single value), found Tango::DevVarStringArray\0";
2586 assert_eq!(expected_cpp_tango_type(attr), Some("DevVarDoubleArray"));
2587 assert_eq!(
2588 expected_cpp_tango_type(attr).and_then(attr_data_kind_from_cpp_tango),
2589 Some(AttrDataKind::Double),
2590 );
2591
2592 assert_eq!(attr_data_kind_from_cpp_tango("DevVarEncodedArray"), Some(AttrDataKind::Encoded));
2593 assert_eq!(command_data_kind_from_cpp_tango("DevEncoded"), Some(CommandDataKind::Encoded));
2594
2595 assert_eq!(expected_cpp_tango_type("Attribute my_attr is not writable"), None);
2597 assert_eq!(command_data_kind_from_cpp_tango("DevVarEncodedArray"), None);
2598
2599 }
2600
2601 fn reads<const N: usize>(entries: [(&str, Value); N]) -> Vec<NamedResult<AttrRead<Value>>> {
2604 entries.into_iter()
2605 .map(|(name, value)| Named::new(Scalar::from(name), Ok(AttrRead::new(value))))
2606 .collect()
2607 }
2608
2609 #[test]
2612 fn single_reading() {
2613 let reads = reads([("double_scalar", Value::from(2.5))]);
2614 let read = <f64 as ReadAttrSpec<&str>>::collect(reads).unwrap();
2615 assert_eq!(read.value, Some(2.5));
2616 }
2617
2618 #[test]
2621 fn readings_in_order() {
2622 let reads = reads([("first", Value::from(1.0)), ("second", Value::from(2.0))]);
2623 let [first, second] = <f64 as ReadAttrSpec<[&str; 2]>>::collect(reads).unwrap();
2624 assert_eq!(first.unwrap().value, Some(1.0));
2625 assert_eq!(second.unwrap().value, Some(2.0));
2626 }
2627
2628 #[test]
2631 fn readings_of_their_own_types() {
2632 let reads = reads([("double_scalar", Value::from(2.5)), ("string_scalar", Value::from("hello"))]);
2633 let (double, string) = <(f64, String) as ReadAttrSpec<(&str, &str)>>::collect(reads).unwrap();
2634 assert_eq!(double.unwrap().value, Some(2.5));
2635 assert_eq!(string.unwrap().value.as_deref(), Some("hello"));
2636 }
2637
2638 #[test]
2641 fn missing_reading() {
2642 let reads = reads([("first", Value::from(1.0))]);
2643 let err = <f64 as ReadAttrSpec<[&str; 2]>>::collect(reads).unwrap_err();
2644 assert_eq!(err.kind(), ErrorKind::Internal);
2645 }
2646
2647 #[test]
2650 fn every_reading_named() {
2651 let reads = reads([("first", Value::from(1.0)), ("second", Value::from(2.0))]);
2652 let all = <Value as ReadAttrSpec<All>>::collect(reads).unwrap();
2653 let names = all.iter().map(|read| read.name.value()).collect::<Vec<_>>();
2654 assert_eq!(names, ["first", "second"]);
2655 }
2656
2657 #[test]
2660 fn write_outcomes() {
2661 let writes = vec![Ok(()), Err(Error::new(ErrorKind::AttrNotWritable))];
2662 let (first, second) = <((&str, f64), (&str, f64)) as WriteAttrSpec>::collect(writes).unwrap();
2663 assert!(first.is_ok());
2664 assert_eq!(second.unwrap_err().kind(), ErrorKind::AttrNotWritable);
2665 }
2666
2667 #[test]
2670 fn unexpected_outcome() {
2671 let writes = vec![Ok(())];
2672 let err = <((&str, f64), (&str, f64)) as WriteAttrSpec>::collect(writes).unwrap_err();
2673 assert_eq!(err.kind(), ErrorKind::Internal);
2674 }
2675
2676}