Skip to main content

tango/
database.rs

1//! Proxy to database devices.
2
3use std::ops::{Deref, DerefMut};
4use std::future::Future;
5use std::borrow::Cow;
6use std::fmt::Debug;
7use std::sync::Arc;
8
9use crate::dev::{All, DevProxy, Named, NamedResult, ProxyTarget, ToNames, ToNamesNotAll, unexpected_len_err};
10use crate::error::{Error, ErrorKind, Result, reason};
11use crate::backend::{default_backend, Backend};
12use crate::prop::{FromProp, IntoProp};
13use crate::value::{Scalar, Spectrum};
14
15
16/// Proxy to a database with easy abstraction for commands that request devices.
17#[derive(Debug, Clone, PartialEq, Eq, Hash)]
18#[repr(transparent)]
19pub struct DatabaseProxy {
20    dev: DevProxy,
21}
22
23impl DatabaseProxy {
24
25    // -- Constructors --
26
27    /// New handle to the environment database.
28    pub fn new_from_env() -> Self {
29        Self::new_from_env_with_backend(default_backend())
30    }
31
32    /// New handle to specific host/port.
33    pub fn new(host: &str, port: u16) -> Self {
34        Self::new_with_backend(default_backend(), host, port)
35    }
36
37    /// New handle to the environment database, with backend.
38    pub fn new_from_env_with_backend(backend: Arc<dyn Backend>) -> Self {
39        Self {
40            dev: DevProxy::new_from_target_with_backend(backend, ProxyTarget::DatabaseFromEnv),
41        }
42    }
43
44    /// New handle to specific host/port, with backend.
45    pub fn new_with_backend(backend: Arc<dyn Backend>, host: &str, port: u16) -> Self {
46        Self {
47            dev: DevProxy::new_from_target_with_backend(backend, ProxyTarget::DatabaseAt { 
48                host: Arc::from(host), 
49                port,
50            }),
51        }
52    }
53
54    /// Internal function to reinterpret a device proxy reference as a database proxy.
55    pub(crate) fn from_dev(dev: &DevProxy) -> &DatabaseProxy {
56        // SAFETY: DatabaseProxy is transparent over DevProxy
57        unsafe { &*(dev as *const _ as *const DatabaseProxy) }
58    }
59
60    /// Internal function to manually create a database proxy by assuming that the given
61    /// device proxy is such device.
62    pub(crate) fn with_dev(dev: DevProxy) -> Self {
63        Self {
64            dev,
65        }
66    }
67
68    /// The host and port this database is reached at, connecting it if that has not
69    /// happened yet, which is the one of `TANGO_HOST` that answered when it is taken from
70    /// the environment.
71    pub(crate) async fn host_and_port_async(&self) -> Result<(Arc<str>, u16)> {
72        let host_and_port = self.dev.database_host_and_port_async().await?;
73        Ok(host_and_port.expect("This should be defined if the target is a database"))
74    }
75
76    // -- Info --
77
78    /// Get the description of the database server and of what it holds, line by line.
79    #[doc(alias = "DbInfo")]
80    pub async fn database_info_async(&self) -> Result<Spectrum<str>> {
81        self.command_async("DbInfo", ()).await
82    }
83
84    /// The blocking form of [`Self::database_info_async`].
85    #[doc(alias = "DbInfo")]
86    pub fn database_info(&self) -> Result<Spectrum<str>> {
87        self.block_on(self.database_info_async())?
88    }
89
90    // -- Dev Add/Delete --
91
92    /// Define a device of the given class in a server, named `exe/inst`.
93    #[doc(alias = "DbAddDevice")]
94    pub async fn add_dev_async(&self, name: &str, class: &str, server: &str) -> Result<()> {
95        self.command_async("DbAddDevice", [server, name, class]).await
96    }
97
98    /// The blocking form of [`Self::add_dev_async`].
99    #[doc(alias = "DbAddDevice")]
100    pub fn add_dev(&self, name: &str, class: &str, server: &str) -> Result<()> {
101        self.block_on(self.add_dev_async(name, class, server))?
102    }
103
104    /// Delete a device.
105    #[doc(alias = "DbDeleteDevice")]
106    pub async fn delete_dev_async(&self, name: &str) -> Result<()> {
107        self.command_async("DbDeleteDevice", name).await
108    }
109
110    /// The blocking form of [`Self::delete_dev_async`].
111    #[doc(alias = "DbDeleteDevice")]
112    pub fn delete_dev(&self, name: &str) -> Result<()> {
113        self.block_on(self.delete_dev_async(name))?
114    }
115
116    // -- Server --
117
118    /// Define a server, named `exe/inst`, along with its devices, each given with its class,
119    /// a device already defined in another server being moved to this one.
120    #[doc(alias = "DbAddServer")]
121    pub async fn add_server_async(&self, server: &str, devs: &[(&str, &str)]) -> Result<()> {
122        let mut args = Vec::with_capacity(1 + devs.len() * 2);
123        args.push(server);
124        args.extend(devs.iter().flat_map(|&(name, class)| [name, class]));
125        self.command_async("DbAddServer", &args[..]).await
126    }
127
128    /// The blocking form of [`Self::add_server_async`].
129    #[doc(alias = "DbAddServer")]
130    pub fn add_server(&self, server: &str, devs: &[(&str, &str)]) -> Result<()> {
131        self.block_on(self.add_server_async(server, devs))?
132    }
133
134    /// Delete a server, named `exe/inst`, along with every device of it.
135    #[doc(alias = "DbDeleteServer")]
136    pub async fn delete_server_async(&self, server: &str) -> Result<()> {
137        self.command_async("DbDeleteServer", server).await
138    }
139
140    /// The blocking form of [`Self::delete_server_async`].
141    #[doc(alias = "DbDeleteServer")]
142    pub fn delete_server(&self, server: &str) -> Result<()> {
143        self.block_on(self.delete_server_async(server))?
144    }
145
146    /// Rename a server, named `exe/inst`, its devices following it.
147    #[doc(alias = "DbRenameServer")]
148    pub async fn rename_server_async(&self, name: &str, new_name: &str) -> Result<()> {
149        self.command_async("DbRenameServer", [name, new_name]).await
150    }
151
152    /// The blocking form of [`Self::rename_server_async`].
153    #[doc(alias = "DbRenameServer")]
154    pub fn rename_server(&self, name: &str, new_name: &str) -> Result<()> {
155        self.block_on(self.rename_server_async(name, new_name))?
156    }
157
158    /// List the devices a server, named `exe/inst`, should run, the class each device is an
159    /// instance of being named by that device. This is what a server queries when booting
160    /// in order to know which devices to create and export.
161    #[doc(alias = "DbGetDeviceClassList")]
162    pub async fn list_server_dev_async(&self, server: &str) -> Result<Vec<Named<Scalar<str>>>> {
163
164        let flat: Spectrum<str> = self.command_async("DbGetDeviceClassList", server).await?;
165
166        let mut pairs = Vec::with_capacity(flat.len() / 2);
167        let mut index = 0;
168
169        while let Some(dev_name) = flat.get_scalar([index]) {
170            let Some(class_name) = flat.get_scalar([index + 1]) else {
171                return Err(Error::new(ErrorKind::InvalidData)
172                    .with_description(format!("Database answered device {:?} of server {server:?} without its class", dev_name.value()))
173                    .with_append_description("Expected: a string spectrum of device/class pairs"));
174            };
175            pairs.push(Named::new(dev_name, class_name));
176            index += 2;
177        }
178
179        Ok(pairs)
180
181    }
182
183    /// The blocking form of [`Self::list_server_dev_async`].
184    #[doc(alias = "DbGetDeviceClassList")]
185    pub fn list_server_dev(&self, server: &str) -> Result<Vec<Named<Scalar<str>>>> {
186        self.block_on(self.list_server_dev_async(server))?
187    }
188
189    // -- Dev List --
190
191    /// List all existing devices, filtering using a filter, using a pattern for 
192    /// matching with domain/family/member pattern, supporting one '*' per member.
193    #[doc(alias = "DbGetDeviceWideList")]
194    pub async fn list_dev_async(&self, filter: &str) -> Result<Spectrum<str>> {
195        self.command_async("DbGetDeviceWideList", filter).await
196    }
197
198    /// The blocking form of [`Self::list_dev_async`].
199    #[doc(alias = "DbGetDeviceWideList")]
200    pub fn list_dev(&self, filter: &str) -> Result<Spectrum<str>> {
201        self.block_on(self.list_dev_async(filter))?
202    }
203
204    /// Same as [`Self::list_dev`] but returns only the member part of the
205    /// device names (in domain/family/member).
206    #[doc(alias = "DbGetDeviceMemberList")]
207    pub async fn list_dev_member_async(&self, filter: &str) -> Result<Spectrum<str>> {
208        self.command_async("DbGetDeviceMemberList", filter).await
209    }
210
211    /// The blocking form of [`Self::list_dev_member_async`].
212    #[doc(alias = "DbGetDeviceMemberList")]
213    pub fn list_dev_member(&self, filter: &str) -> Result<Spectrum<str>> {
214        self.block_on(self.list_dev_member_async(filter))?
215    }
216
217    /// Same as [`Self::list_dev`] but returns only the family part of the
218    /// device names (in domain/family/member).
219    #[doc(alias = "DbGetDeviceFamilyList")]
220    pub async fn list_dev_family_async(&self, filter: &str) -> Result<Spectrum<str>> {
221        self.command_async("DbGetDeviceFamilyList", filter).await
222    }
223
224    /// The blocking form of [`Self::list_dev_family_async`].
225    #[doc(alias = "DbGetDeviceFamilyList")]
226    pub fn list_dev_family(&self, filter: &str) -> Result<Spectrum<str>> {
227        self.block_on(self.list_dev_family_async(filter))?
228    }
229
230    /// Same as [`Self::list_dev`] but returns only the domain part of the
231    /// device names (in domain/family/member).
232    #[doc(alias = "DbGetDeviceDomainList")]
233    pub async fn list_dev_domain_async(&self, filter: &str) -> Result<Spectrum<str>> {
234        self.command_async("DbGetDeviceDomainList", filter).await
235    }
236
237    /// The blocking form of [`Self::list_dev_domain_async`].
238    #[doc(alias = "DbGetDeviceDomainList")]
239    pub fn list_dev_domain(&self, filter: &str) -> Result<Spectrum<str>> {
240        self.block_on(self.list_dev_domain_async(filter))?
241    }
242
243    // -- Export --
244
245    /// Mark a device as exported, recording how to reach it.
246    #[doc(alias = "DbExportDevice")]
247    pub async fn export_dev_async(&self, info: ExportDevInfo<'_>) -> Result<()> {
248        self.command_async("DbExportDevice", data_from_export_dev_info(&[info][..])).await
249    }
250
251    /// The blocking form of [`Self::export_dev_async`].
252    #[doc(alias = "DbExportDevice")]
253    pub fn export_dev(&self, info: ExportDevInfo<'_>) -> Result<()> {
254        self.block_on(self.export_dev_async(info))?
255    }
256
257    /// Mark a device as no longer exported.
258    #[doc(alias = "DbUnExportDevice")]
259    pub async fn unexport_dev_async(&self, dev_name: &str) -> Result<()> {
260        self.command_async("DbUnExportDevice", dev_name).await
261    }
262
263    /// The blocking form of [`Self::unexport_dev_async`].
264    #[doc(alias = "DbUnExportDevice")]
265    pub fn unexport_dev(&self, dev_name: &str) -> Result<()> {
266        self.block_on(self.unexport_dev_async(dev_name))?
267    }
268
269    /// Mark the devices of a server as exported, see [`Self::export_dev_async`].
270    #[doc(alias = "DbExportServer")]
271    pub async fn export_server_async(&self, infos: &[ExportDevInfo<'_>]) -> Result<()> {
272        self.command_async("DbExportServer", data_from_export_dev_info(infos)).await
273    }
274
275    /// The blocking form of [`Self::export_server_async`].
276    #[doc(alias = "DbExportServer")]
277    pub fn export_server(&self, infos: &[ExportDevInfo<'_>]) -> Result<()> {
278        self.block_on(self.export_server_async(infos))?
279    }
280
281    /// Mark every device of a server, named `exe/inst`, as no longer exported.
282    #[doc(alias = "DbUnExportServer")]
283    pub async fn unexport_server_async(&self, server: &str) -> Result<()> {
284        self.command_async("DbUnExportServer", server).await
285    }
286
287    /// The blocking form of [`Self::unexport_server_async`].
288    #[doc(alias = "DbUnExportServer")]
289    pub fn unexport_server(&self, server: &str) -> Result<()> {
290        self.block_on(self.unexport_server_async(server))?
291    }
292
293    /// Same input as [`Self::list_dev`], but returns only devices that should
294    /// currently be exported.
295    #[doc(alias = "DbGetDeviceExportedList")]
296    pub async fn list_exported_dev_async(&self, filter: &str) -> Result<Spectrum<str>> {
297        self.command_async("DbGetDeviceExportedList", filter).await
298    }
299
300    /// The blocking form of [`Self::list_exported_dev_async`].
301    #[doc(alias = "DbGetDeviceExportedList")]
302    pub fn list_exported_dev(&self, filter: &str) -> Result<Spectrum<str>> {
303        self.block_on(self.list_exported_dev_async(filter))?
304    }
305
306    // -- Import --
307
308    /// Get how to reach a device, failing with [`ErrorKind::DevNotFound`] if not defined.
309    #[doc(alias = "DbImportDevice")]
310    pub async fn import_dev_async(&self, dev_name: &str) -> Result<ImportDevInfo> {
311
312        let (
313            [exported, pid],
314            svalue,
315        ) = match self.command_async("DbImportDevice", dev_name).await.and_then(long2_string) {
316            Ok(ret) => ret,
317            Err(err) => {
318                if let ErrorKind::Reason(reason::DB_DeviceNotDefined) = err.kind() {
319                    return Err(Error::new(ErrorKind::DevNotFound)
320                        .with_description(format!("Device {dev_name:?} is not defined in the database"))
321                        .with_parent(err));
322                } else {
323                    return Err(err);
324                }
325            }
326        };
327
328        if svalue.len() != 5 && svalue.len() != 6 {
329            return Err(Error::new(ErrorKind::InvalidData)
330                .with_description(format!("Database answered {} strings for device {dev_name:?} where 5 or 6 were expected", svalue.len()))
331                .with_append_description(format_args!("Got: {svalue:?}")));
332        }
333
334        // Each within the length just checked.
335        let field = |index| svalue.get_scalar([index]).unwrap();
336
337        Ok(ImportDevInfo {
338            name: field(0),
339            itr: field(1),
340            host: field(4),
341            version: field(2),
342            server: field(3),
343            class: svalue.get_scalar([5]),
344            exported: exported == 1,
345            pid,
346        })
347
348    }
349
350    /// The blocking form of [`Self::import_dev_async`].
351    #[doc(alias = "DbImportDevice")]
352    pub fn import_dev(&self, dev_name: &str) -> Result<ImportDevInfo> {
353        self.block_on(self.import_dev_async(dev_name))?
354    }
355
356    // -- Dev Info --
357
358    /// Get what the database knows of a device, failing with [`ErrorKind::DevNotFound`] if
359    /// not defined.
360    #[doc(alias = "DbGetDeviceInfo")]
361    pub async fn get_dev_info_async(&self, dev_name: &str) -> Result<DbDevInfo> {
362        
363        let (
364            [exported, pid],
365            svalue,
366        ) = match self.command_async("DbGetDeviceInfo", dev_name).await.and_then(long2_string) {
367            Ok(ret) => ret,
368            Err(err) => {
369                if let ErrorKind::Reason(reason::DB_DeviceNotDefined) = err.kind() {
370                    return Err(Error::new(ErrorKind::DevNotFound)
371                        .with_description(format!("Device {dev_name:?} is not defined in the database"))
372                        .with_parent(err));
373                } else {
374                    return Err(err);
375                }
376            }
377        };
378
379        if svalue.len() != 7 && svalue.len() != 8 {
380            return Err(Error::new(ErrorKind::InvalidData)
381                .with_description(format!("Database answered {} strings for device {dev_name:?} where 7 or 8 were expected", svalue.len()))
382                .with_append_description(format_args!("Got: {svalue:?}")));
383        }
384
385        // Each within the length just checked.
386        let field = |index| svalue.get_scalar([index]).unwrap();
387
388        Ok(DbDevInfo {
389            name: field(0),
390            itr: field(1),
391            host: Some(field(4)).filter(|host| host.value() != "nada"),
392            version: field(2),
393            server: field(3),
394            class: svalue.get_scalar([7]),
395            last_exported: Some(field(5)).filter(|time| time.value() != "?"),
396            last_unexported: Some(field(6)).filter(|time| time.value() != "?"),
397            exported: exported == 1,
398            pid: Some(pid).filter(|&pid| pid != 0),
399        })
400
401    }
402
403    /// The blocking form of [`Self::get_dev_info_async`].
404    #[doc(alias = "DbGetDeviceInfo")]
405    pub fn get_dev_info(&self, dev_name: &str) -> Result<DbDevInfo> {
406        self.block_on(self.get_dev_info_async(dev_name))?
407    }
408    
409    // -- Dev Alias --
410
411    /// Get alias for a given device.
412    #[doc(alias = "DbGetDeviceAlias")]
413    pub async fn get_dev_alias_async(&self, dev_name: &str) -> Result<Option<Scalar<str>>> {
414        match self.command_async::<_, Scalar<str>>("DbGetDeviceAlias", dev_name).await {
415            Ok(ret) => Ok(Some(ret)),
416            Err(err) => {
417                if let ErrorKind::Reason(reason::DB_AliasNotDefined) = err.kind() {
418                    Ok(None)
419                } else {
420                    Err(err)
421                }
422            }
423        }
424    }
425
426    /// The blocking form of [`Self::get_dev_alias_async`].
427    #[doc(alias = "DbGetDeviceAlias")]
428    pub fn get_dev_alias(&self, dev_name: &str) -> Result<Option<Scalar<str>>> {
429        self.block_on(self.get_dev_alias_async(dev_name))?
430    }
431
432    /// Resolve a device alias, failing with [`ErrorKind::AliasNotFound`] if not found.
433    #[doc(alias = "DbGetAliasDevice")]
434    pub async fn resolve_dev_alias_async(&self, alias: &str) -> Result<Scalar<str>> {
435        match self.command_async("DbGetAliasDevice", alias).await {
436            Ok(ret) => Ok(ret),
437            Err(err) => {
438                if let ErrorKind::Reason(reason::DB_AliasNotDefined) = err.kind() {
439                    Err(Error::new(ErrorKind::AliasNotFound)
440                        .with_description(format!("Device alias {alias:?} is not defined in the database"))
441                        .with_parent(err))
442                } else {
443                    Err(err)
444                }
445            }
446        }
447    }
448
449    /// The blocking form of [`Self::resolve_dev_alias_async`].
450    #[doc(alias = "DbGetAliasDevice")]
451    pub fn resolve_dev_alias(&self, alias: &str) -> Result<Scalar<str>> {
452        self.block_on(self.resolve_dev_alias_async(alias))?
453    }
454
455    /// List all device aliases matching the given filter (using at most one '*').
456    #[doc(alias = "DbGetDeviceAliasList")]
457    pub async fn list_dev_aliases_async(&self, filter: &str) -> Result<Spectrum<str>> {
458        self.command_async("DbGetDeviceAliasList", filter).await
459    }
460
461    /// The blocking form of [`Self::list_dev_aliases_async`].
462    #[doc(alias = "DbGetDeviceAliasList")]
463    pub fn list_dev_aliases(&self, filter: &str) -> Result<Spectrum<str>> {
464        self.block_on(self.list_dev_aliases_async(filter))?
465    }
466
467    // -- Attr Alias --
468
469    /// Get alias for a given attribute.
470    #[doc(alias = "DbGetAttributeAlias")]
471    pub async fn get_attr_alias_async(&self, attr_name: &str) -> Result<Option<Scalar<str>>> {
472        match self.command_async::<_, Scalar<str>>("DbGetAttributeAlias", attr_name).await {
473            Ok(ret) => Ok(Some(ret)),
474            Err(err) => {
475                if let ErrorKind::Reason(reason::DB_AliasNotDefined) = err.kind() {
476                    Ok(None)
477                } else {
478                    Err(err)
479                }
480            }
481        }
482    }
483
484    /// The blocking form of [`Self::get_attr_alias_async`].
485    #[doc(alias = "DbGetAttributeAlias")]
486    pub fn get_attr_alias(&self, attr_name: &str) -> Result<Option<Scalar<str>>> {
487        self.block_on(self.get_attr_alias_async(attr_name))?
488    }
489
490    /// Resolve an attribute alias, failing with [`ErrorKind::AliasNotFound`] if not found.
491    #[doc(alias = "DbGetAliasAttribute")]
492    pub async fn resolve_attr_alias_async(&self, alias: &str) -> Result<Scalar<str>> {
493        match self.command_async("DbGetAliasAttribute", alias).await {
494            Ok(ret) => Ok(ret),
495            Err(err) => {
496                if let ErrorKind::Reason(reason::DB_AliasNotDefined) = err.kind() {
497                    Err(Error::new(ErrorKind::AliasNotFound)
498                        .with_description(format!("Attribute alias {alias:?} is not defined in the database"))
499                        .with_parent(err))
500                } else {
501                    Err(err)
502                }
503            }
504        }
505    }
506
507    /// The blocking form of [`Self::resolve_attr_alias_async`].
508    #[doc(alias = "DbGetAliasAttribute")]
509    pub fn resolve_attr_alias(&self, alias: &str) -> Result<Scalar<str>> {
510        self.block_on(self.resolve_attr_alias_async(alias))?
511    }
512
513    /// List all attribute aliases matching the given filter (using at most one '*').
514    #[doc(alias = "DbGetAttributeAliasList")]
515    pub async fn list_attr_aliases_async(&self, filter: &str) -> Result<Spectrum<str>> {
516        self.command_async("DbGetAttributeAliasList", filter).await
517    }
518
519    /// The blocking form of [`Self::list_attr_aliases_async`].
520    #[doc(alias = "DbGetAttributeAliasList")]
521    pub fn list_attr_aliases(&self, filter: &str) -> Result<Spectrum<str>> {
522        self.block_on(self.list_attr_aliases_async(filter))?
523    }
524
525    // -- Free Prop List --
526
527    /// List the names of the free properties of an object matching the given filter.
528    #[doc(alias = "DbGetPropertyList")]
529    pub async fn list_free_prop_async(&self, obj_name: &str, filter: &str) -> Result<Spectrum<str>> {
530        self.command_async("DbGetPropertyList", [obj_name, filter]).await
531    }
532
533    /// The blocking form of [`Self::list_free_prop_async`].
534    #[doc(alias = "DbGetPropertyList")]
535    pub fn list_free_prop(&self, obj_name: &str, filter: &str) -> Result<Spectrum<str>> {
536        self.block_on(self.list_free_prop_async(obj_name, filter))?
537    }
538
539    // -- Class Prop List --
540
541    /// List the names of the properties of a class matching the given filter.
542    #[doc(alias = "DbGetClassPropertyList")]
543    #[doc(alias = "DbGetClassPropertyListWildcard")]
544    pub async fn list_class_prop_async(&self, obj_name: &str, filter: &str) -> Result<Spectrum<str>> {
545        if filter == "*" {
546            self.command_async("DbGetClassPropertyList", [obj_name]).await
547        } else {
548            self.command_async("DbGetClassPropertyListWildcard", [obj_name, filter]).await
549        }
550    }
551
552    /// The blocking form of [`Self::list_class_prop_async`].
553    #[doc(alias = "DbGetClassPropertyList")]
554    #[doc(alias = "DbGetClassPropertyListWildcard")]
555    pub fn list_class_prop(&self, obj_name: &str, filter: &str) -> Result<Spectrum<str>> {
556        self.block_on(self.list_class_prop_async(obj_name, filter))?
557    }
558
559    // -- Dev Prop List --
560
561    /// List the names of the properties of a device matching the given filter.
562    #[doc(alias = "DbGetDevicePropertyList")]
563    pub async fn list_dev_prop_async(&self, obj_name: &str, filter: &str) -> Result<Spectrum<str>> {
564        self.command_async("DbGetDevicePropertyList", [obj_name, filter]).await
565    }
566
567    /// The blocking form of [`Self::list_dev_prop_async`].
568    #[doc(alias = "DbGetDevicePropertyList")]
569    pub fn list_dev_prop(&self, obj_name: &str, filter: &str) -> Result<Spectrum<str>> {
570        self.block_on(self.list_dev_prop_async(obj_name, filter))?
571    }
572
573    // -- Common Prop Get --
574
575    /// The reply to a read of the properties of that object, which is what every property
576    /// read comes down to, see [`PropCommands`]: the parameters are the object and the
577    /// names of the properties, see [`prop_params`], or absent for every property it has.
578    ///
579    /// The names are what the answer is checked against once it comes back, so the reply
580    /// keeps them, sharing the parameters they were sent in.
581    pub(crate) async fn get_raw_prop_async(&self,
582        commands: PropCommands,
583        obj_name: &str,
584        params: Option<Spectrum<str>>,
585    ) -> Result<get_prop::PropReply> {
586
587        let params = match params {
588            Some(params) => params,
589            None => {
590                let list_params = [obj_name, "*"];
591                let list_params = if commands.list_wildcard { &list_params[..] } else { &list_params[0..1] };
592                let names: Spectrum<str> = self.command_async(commands.list, list_params).await?;
593                prop_params(obj_name, names.iter())
594            }
595        };
596
597        let names = params.slice(1..);
598
599        // The database rejects a request carrying no property name at all, answering that
600        // it needs at least the object and one property, so it is simply not sent: there is
601        // nothing to report, which is what an object having no property at all gives, the
602        // property list command having then answered with an empty list.
603        if names.is_empty() {
604            return Ok(get_prop::PropReply::empty());
605        }
606
607        let listing: Spectrum<str> = self.command_async(commands.get, params).await?;
608        get_prop::PropReply::new(obj_name, listing, names)
609
610    }
611
612    /// Read properties of that object, parsed into the shape the given names ask for.
613    fn get_any_prop_async<Spec, Names: ToNames>(&self,
614        commands: PropCommands,
615        obj_name: &str,
616        names: Names,
617    ) -> impl Future<Output = Result<Spec::Output>> + Send
618    where
619        Spec: GetPropSpec<Names>,
620    {
621        let params = names.iter_names().map(|names| prop_params(obj_name, names));
622        async move {
623            let mut reply = self.get_raw_prop_async(commands, obj_name, params).await?;
624            Spec::collect(&mut reply)
625        }
626    }
627
628    // -- Free Prop Get --
629
630    /// The properties of that free object, parsed into the shape the given names ask for,
631    /// see [`Self::get_dev_prop_async`] for the accepted signatures.
632    #[doc(alias = "DbGetProperty")]
633    pub fn get_free_prop_async<Spec, Names: ToNames>(&self, obj_name: &str, names: Names) -> impl Future<Output = Result<Spec::Output>> + Send
634    where
635        Spec: GetPropSpec<Names>,
636    {
637        self.get_any_prop_async::<Spec, Names>(PropCommands::FREE, obj_name, names)
638    }
639
640    /// The properties of that free object, see [`Self::get_free_prop_async`].
641    #[doc(alias = "DbGetProperty")]
642    pub fn get_free_prop<Spec, Names: ToNames>(&self, obj_name: &str, names: Names) -> Result<Spec::Output>
643    where
644        Spec: GetPropSpec<Names>,
645    {
646        self.block_on(self.get_free_prop_async::<Spec, Names>(obj_name, names))?
647    }
648
649    // -- Class Prop Get --
650
651    /// The properties of that class, parsed into the shape the given names ask for, see
652    /// [`Self::get_dev_prop_async`] for the accepted signatures.
653    #[doc(alias = "DbGetClassProperty")]
654    pub fn get_class_prop_async<Spec, Names: ToNames>(&self, class_name: &str, names: Names) -> impl Future<Output = Result<Spec::Output>> + Send
655    where
656        Spec: GetPropSpec<Names>,
657    {
658        self.get_any_prop_async::<Spec, Names>(PropCommands::CLASS, class_name, names)
659    }
660
661    /// The properties of that class, see [`Self::get_class_prop_async`].
662    #[doc(alias = "DbGetClassProperty")]
663    pub fn get_class_prop<Spec, Names: ToNames>(&self, class_name: &str, names: Names) -> Result<Spec::Output>
664    where
665        Spec: GetPropSpec<Names>,
666    {
667        self.block_on(self.get_class_prop_async::<Spec, Names>(class_name, names))?
668    }
669
670    // -- Dev Prop Get --
671
672    /// The properties of that device, parsed into the shape the given names ask for. This
673    /// specification is really flexible and allows various signatures for the function:
674    /// 1. `get_dev_prop<T>(&str, &str) -> Result<T>`
675    /// 2. `get_dev_prop<T>(&str, &[&str]) -> Result<Vec<Result<T>>>`
676    /// 3. `get_dev_prop<T>(&str, [&str; N]) -> Result<[Result<T>; N]>`
677    /// 4. `get_dev_prop<(A, B, ...)>(&str, (&str, &str, ...)) -> Result<(Result<A>, Result<B>, ...)>`
678    /// 5. `get_dev_prop<Spectrum<str>>(&str, All) -> Result<Vec<Named<Spectrum<str>>>>`
679    ///
680    /// Form 2 also takes the names as a [`Spectrum<str>`], owned or borrowed. Every value
681    /// is parsed with [`FromProp`]: a [`String`], a [`Vec<String>`], a [`Spectrum<str>`]
682    /// sharing the reply, a [`bool`] or a number. Like for [`DevProxy::read_attr`], the
683    /// type has to come from a turbofish, as in `get_dev_prop::<f64, _>(dev, "speed")`,
684    /// or from the binding the output is assigned to.
685    ///
686    /// A property the database holds nothing for has no value, which the text forms read
687    /// as empty and the others fail with [`ErrorKind::InvalidProp`]. Form 5 gives every
688    /// property the device has, each with its name, as the raw values it holds.
689    ///
690    /// All the properties are read within a single request. Note that, unlike the
691    /// many-properties forms, form 1 has no per-property slot in its output and therefore
692    /// returns that property's error directly.
693    #[doc(alias = "DbGetDeviceProperty")]
694    pub fn get_dev_prop_async<Spec, Names: ToNames>(&self, dev_name: &str, names: Names) -> impl Future<Output = Result<Spec::Output>> + Send
695    where
696        Spec: GetPropSpec<Names>,
697    {
698        self.get_any_prop_async::<Spec, Names>(PropCommands::DEV, dev_name, names)
699    }
700
701    /// The properties of that device, see [`Self::get_dev_prop_async`].
702    #[doc(alias = "DbGetDeviceProperty")]
703    pub fn get_dev_prop<Spec, Names: ToNames>(&self, dev_name: &str, names: Names) -> Result<Spec::Output>
704    where
705        Spec: GetPropSpec<Names>,
706    {
707        self.block_on(self.get_dev_prop_async::<Spec, Names>(dev_name, names))?
708    }
709
710    // -- Common Prop Command --
711
712    /// Send a property command with those parameters, which every property write and
713    /// deletion comes down to, see [`prop_params`].
714    pub(crate) async fn prop_command_async(&self, cmd_name: &'static str, params: Spectrum<str>) -> Result<()> {
715        self.command_async(cmd_name, params).await
716    }
717
718    // -- Free Prop Set --
719
720    /// Write the given properties of that free object, see [`Self::set_dev_prop_async`]
721    /// for the accepted signatures.
722    #[doc(alias = "DbPutProperty")]
723    pub fn set_free_prop_async<'a, Spec>(&self, obj_name: &'a str, spec: Spec) -> impl Future<Output = Result<()>> + Send
724    where
725        Spec: SetPropSpec<'a>,
726    {
727        self.prop_command_async("DbPutProperty", prop_params(obj_name, set_prop_params(spec).iter().map(|param| &**param)))
728    }
729
730    /// Write the given properties of that free object, see [`Self::set_free_prop_async`].
731    #[doc(alias = "DbPutProperty")]
732    pub fn set_free_prop<'a, Spec>(&self, obj_name: &'a str, spec: Spec) -> Result<()>
733    where
734        Spec: SetPropSpec<'a>,
735    {
736        self.block_on(self.set_free_prop_async(obj_name, spec))?
737    }
738
739    // -- Class Prop Set --
740
741    /// Write the given properties of that class, see [`Self::set_dev_prop_async`] for the
742    /// accepted signatures.
743    #[doc(alias = "DbPutClassProperty")]
744    pub fn set_class_prop_async<'a, Spec>(&self, class_name: &'a str, spec: Spec) -> impl Future<Output = Result<()>> + Send
745    where
746        Spec: SetPropSpec<'a>,
747    {
748        self.prop_command_async("DbPutClassProperty", prop_params(class_name, set_prop_params(spec).iter().map(|param| &**param)))
749    }
750
751    /// Write the given properties of that class, see [`Self::set_class_prop_async`].
752    #[doc(alias = "DbPutClassProperty")]
753    pub fn set_class_prop<'a, Spec>(&self, class_name: &'a str, spec: Spec) -> Result<()>
754    where
755        Spec: SetPropSpec<'a>,
756    {
757        self.block_on(self.set_class_prop_async(class_name, spec))?
758    }
759
760    // -- Dev Prop Set --
761
762    /// Write the given properties of that device, given as pairs of a name and a value.
763    /// Like for [`Self::get_dev_prop`], this specification is really flexible and allows
764    /// various signatures for the function:
765    /// 1. `set_dev_prop(&str, (&str, T)) -> Result<()>`
766    /// 2. `set_dev_prop(&str, [(&str, T); N]) -> Result<()>`
767    /// 3. `set_dev_prop(&str, Vec<(&str, T)>) -> Result<()>`
768    /// 4. `set_dev_prop(&str, ((&str, A), (&str, B), ...)) -> Result<()>`
769    ///
770    /// Each value only needs to implement [`IntoProp`], a [`bool`], a number or a string,
771    /// so the value types are inferred from the specification and never have to be named,
772    /// form 4 giving each property its own type, as in
773    /// `set_dev_prop(dev, (("speed", 2.5), ("unit", "mm")))`.
774    ///
775    /// All the properties are written within a single request, a property that was
776    /// already there being overwritten.
777    #[doc(alias = "DbPutDeviceProperty")]
778    pub fn set_dev_prop_async<'a, Spec>(&self, dev_name: &'a str, spec: Spec) -> impl Future<Output = Result<()>> + Send
779    where
780        Spec: SetPropSpec<'a>,
781    {
782        self.prop_command_async("DbPutDeviceProperty", prop_params(dev_name, set_prop_params(spec).iter().map(|param| &**param)))
783    }
784
785    /// Write the given properties of that device, see [`Self::set_dev_prop_async`].
786    #[doc(alias = "DbPutDeviceProperty")]
787    pub fn set_dev_prop<'a, Spec>(&self, dev_name: &'a str, spec: Spec) -> Result<()>
788    where
789        Spec: SetPropSpec<'a>,
790    {
791        self.block_on(self.set_dev_prop_async(dev_name, spec))?
792    }
793
794    // -- Free Prop Del --
795
796    /// Delete the given properties of that free object, see [`Self::delete_dev_prop_async`]
797    /// for the accepted signatures.
798    #[doc(alias = "DbDeleteProperty")]
799    pub fn delete_free_prop_async<Names: ToNamesNotAll>(&self, obj_name: &str, names: Names) -> impl Future<Output = Result<()>> + Send {
800        self.prop_command_async("DbDeleteProperty", prop_params(obj_name, names.iter_names_not_all()))
801    }
802
803    /// Delete the given properties of that free object, see [`Self::delete_free_prop_async`].
804    #[doc(alias = "DbDeleteProperty")]
805    pub fn delete_free_prop<Names: ToNamesNotAll>(&self, obj_name: &str, names: Names) -> Result<()> {
806        self.block_on(self.delete_free_prop_async(obj_name, names))?
807    }
808
809    // -- Class Prop Del --
810
811    /// Delete the given properties of that class, see [`Self::delete_dev_prop_async`] for the
812    /// accepted signatures.
813    #[doc(alias = "DbDeleteClassProperty")]
814    pub fn delete_class_prop_async<Names: ToNamesNotAll>(&self, class_name: &str, names: Names) -> impl Future<Output = Result<()>> + Send {
815        self.prop_command_async("DbDeleteClassProperty", prop_params(class_name, names.iter_names_not_all()))
816    }
817
818    /// Delete the given properties of that class, see [`Self::delete_class_prop_async`].
819    #[doc(alias = "DbDeleteClassProperty")]
820    pub fn delete_class_prop<Names: ToNamesNotAll>(&self, class_name: &str, names: Names) -> Result<()> {
821        self.block_on(self.delete_class_prop_async(class_name, names))?
822    }
823
824    // -- Dev Prop Del --
825
826    /// Delete the given properties of that device, which allows various signatures for the
827    /// function:
828    /// 1. `delete_dev_prop(&str, &str) -> Result<()>`
829    /// 2. `delete_dev_prop(&str, &[&str]) -> Result<()>`
830    /// 3. `delete_dev_prop(&str, [&str; N]) -> Result<()>`
831    /// 4. `delete_dev_prop(&str, (&str, &str, ...)) -> Result<()>`
832    ///
833    /// Form 2 also takes the names as a [`Spectrum<str>`], owned or borrowed. Every name
834    /// has to be given, [`All`] not being accepted, and deleting a property the database
835    /// holds nothing for is not an error.
836    #[doc(alias = "DbDeleteDeviceProperty")]
837    pub fn delete_dev_prop_async<Names: ToNamesNotAll>(&self, dev_name: &str, names: Names) -> impl Future<Output = Result<()>> + Send {
838        self.prop_command_async("DbDeleteDeviceProperty", prop_params(dev_name, names.iter_names_not_all()))
839    }
840
841    /// Delete the given properties of that device, see [`Self::delete_dev_prop_async`].
842    #[doc(alias = "DbDeleteDeviceProperty")]
843    pub fn delete_dev_prop<Names: ToNamesNotAll>(&self, dev_name: &str, names: Names) -> Result<()> {
844        self.block_on(self.delete_dev_prop_async(dev_name, names))?
845    }
846
847    // -- Common Attr Prop Get --
848
849    /// The reply to a read of the properties of attributes of that object, which is what
850    /// every attribute property read comes down to, see [`AttrPropCommands`]: the parameters
851    /// are the object and the names of the attributes, see [`prop_params`], or absent for
852    /// every attribute that has any property, and the properties are those of the given
853    /// names of every attribute, or absent for every property each one has.
854    ///
855    /// The database answers with every property it holds of each attribute, there being no
856    /// asking it for some of them only, so the properties asked for are looked up in what it
857    /// answered, and one it does not hold is given with no value, see
858    /// [`get_prop::PropReply::Attr`].
859    pub(crate) async fn get_raw_attr_prop_async(&self,
860        commands: AttrPropCommands,
861        obj_name: &str,
862        params: Option<Spectrum<str>>,
863        prop_names: Option<Spectrum<str>>,
864    ) -> Result<get_prop::AttrPropReply> {
865
866        let params = match params {
867            Some(params) => params,
868            None => {
869                let attr_names: Spectrum<str> = self.command_async(commands.list, [obj_name, "*"]).await?;
870                prop_params(obj_name, attr_names.iter())
871            }
872        };
873
874        // The names the answer is checked against, sharing the parameters.
875        let attr_names = params.slice(1..);
876
877        // A request carrying no attribute at all is not worth sending, there being nothing
878        // it could answer with.
879        if attr_names.is_empty() {
880            return Ok(get_prop::AttrPropReply::empty());
881        }
882
883        let listing: Spectrum<str> = self.command_async(commands.get, params).await?;
884        get_prop::AttrPropReply::new(obj_name, listing, attr_names, prop_names)
885
886    }
887
888    /// Read the properties of attributes of that object, parsed into the shape the names of
889    /// the attributes and the names of the properties ask for, see [`GetAttrPropSpec`].
890    fn get_any_attr_prop_async<Spec, AttrNames, Names>(&self,
891        commands: AttrPropCommands,
892        obj_name: &str,
893        attr_names: AttrNames,
894        prop_names: Names,
895    ) -> impl Future<Output = Result<Spec::Output>> + Send
896    where
897        Spec: GetAttrPropSpec<AttrNames, Names>,
898        AttrNames: ToNames,
899        Names: ToNames,
900    {
901        let params = attr_names.iter_names().map(|attr_names| prop_params(obj_name, attr_names));
902        let prop_names = prop_names.to_names();
903        async move {
904            let mut reply = self.get_raw_attr_prop_async(commands, obj_name, params, prop_names).await?;
905            Spec::collect(&mut reply)
906        }
907    }
908
909    // -- Class Attr Prop Get --
910
911    /// The properties of the given attributes of that class, parsed into the shape the names
912    /// of the attributes and of the properties ask for, see [`Self::get_dev_attr_prop_async`]
913    /// for the accepted signatures.
914    #[doc(alias = "DbGetClassAttributeProperty2")]
915    pub fn get_class_attr_prop_async<Spec, AttrNames, Names>(&self, class_name: &str, attr_names: AttrNames, prop_names: Names) -> impl Future<Output = Result<Spec::Output>> + Send
916    where
917        Spec: GetAttrPropSpec<AttrNames, Names>,
918        AttrNames: ToNames,
919        Names: ToNames,
920    {
921        self.get_any_attr_prop_async::<Spec, AttrNames, Names>(AttrPropCommands::CLASS, class_name, attr_names, prop_names)
922    }
923
924    /// The properties of the given attributes of that class, see
925    /// [`Self::get_class_attr_prop_async`].
926    #[doc(alias = "DbGetClassAttributeProperty2")]
927    pub fn get_class_attr_prop<Spec, AttrNames, Names>(&self, class_name: &str, attr_names: AttrNames, prop_names: Names) -> Result<Spec::Output>
928    where
929        Spec: GetAttrPropSpec<AttrNames, Names>,
930        AttrNames: ToNames,
931        Names: ToNames,
932    {
933        self.block_on(self.get_class_attr_prop_async::<Spec, AttrNames, Names>(class_name, attr_names, prop_names))?
934    }
935
936    // -- Dev Attr Prop Get --
937
938    /// The properties of the given attributes of that device, or of every attribute of it
939    /// that has any, parsed into the shape the names of the attributes and of the properties
940    /// ask for. The names of the properties are given the way they are to
941    /// [`Self::get_dev_prop`], and give the same output for every attribute, written `P`
942    /// below, which the names of the attributes then shape in the same way:
943    /// 1. `get_dev_attr_prop<T>(&str, &str, Names) -> Result<P>`
944    /// 2. `get_dev_attr_prop<T>(&str, &[&str], Names) -> Result<Vec<Result<P>>>`
945    /// 3. `get_dev_attr_prop<T>(&str, [&str; N], Names) -> Result<[Result<P>; N]>`
946    /// 4. `get_dev_attr_prop<T>(&str, (&str, &str, ...), Names) -> Result<(Result<P>, Result<P>, ...)>`
947    /// 5. `get_dev_attr_prop<T>(&str, All, Names) -> Result<Vec<NamedResult<P>>>`
948    ///
949    /// Form 2 also takes the names as a [`Spectrum<str>`], owned or borrowed, and form 5
950    /// gives every attribute the device holds any property for, each with its name. For
951    /// instance:
952    /// - `get_dev_attr_prop::<String, _, _>(dev, "Position", "unit") -> Result<String>`
953    /// - `get_dev_attr_prop::<String, _, _>(dev, ["Position", "Speed"], "unit") -> Result<[Result<String>; 2]>`
954    /// - `get_dev_attr_prop::<(String, f64), _, _>(dev, "Position", ("label", "__value")) -> Result<(Result<String>, Result<f64>)>`
955    /// - `get_dev_attr_prop::<Spectrum<str>, _, _>(dev, All, All) -> Result<Vec<NamedResult<Vec<Named<Spectrum<str>>>>>>`
956    ///
957    /// All the attributes are read within a single request. Note that form 1 has no
958    /// per-attribute slot in its output and therefore returns that attribute's error
959    /// directly, the same way form 1 of the properties does.
960    #[doc(alias = "DbGetDeviceAttributeProperty2")]
961    pub fn get_dev_attr_prop_async<Spec, AttrNames, Names>(&self, dev_name: &str, attr_names: AttrNames, prop_names: Names) -> impl Future<Output = Result<Spec::Output>> + Send
962    where
963        Spec: GetAttrPropSpec<AttrNames, Names>,
964        AttrNames: ToNames,
965        Names: ToNames,
966    {
967        self.get_any_attr_prop_async::<Spec, AttrNames, Names>(AttrPropCommands::DEV, dev_name, attr_names, prop_names)
968    }
969
970    /// The properties of the given attributes of that device, see
971    /// [`Self::get_dev_attr_prop_async`].
972    #[doc(alias = "DbGetDeviceAttributeProperty2")]
973    pub fn get_dev_attr_prop<Spec, AttrNames, Names>(&self, dev_name: &str, attr_names: AttrNames, prop_names: Names) -> Result<Spec::Output>
974    where
975        Spec: GetAttrPropSpec<AttrNames, Names>,
976        AttrNames: ToNames,
977        Names: ToNames,
978    {
979        self.block_on(self.get_dev_attr_prop_async::<Spec, AttrNames, Names>(dev_name, attr_names, prop_names))?
980    }
981
982    // -- Class Attr Prop Set --
983
984    /// Write the given properties of attributes of that class, see
985    /// [`Self::set_dev_attr_prop_async`] for the accepted signatures.
986    #[doc(alias = "DbPutClassAttributeProperty2")]
987    pub fn set_class_attr_prop_async<'a, Spec>(&self, class_name: &'a str, spec: Spec) -> impl Future<Output = Result<()>> + Send
988    where
989        Spec: SetAttrPropSpec<'a>,
990    {
991        self.prop_command_async("DbPutClassAttributeProperty2", attr_prop_params(class_name, spec))
992    }
993
994    /// Write the given properties of attributes of that class, see
995    /// [`Self::set_class_attr_prop_async`].
996    #[doc(alias = "DbPutClassAttributeProperty2")]
997    pub fn set_class_attr_prop<'a, Spec>(&self, class_name: &'a str, spec: Spec) -> Result<()>
998    where
999        Spec: SetAttrPropSpec<'a>,
1000    {
1001        self.block_on(self.set_class_attr_prop_async(class_name, spec))?
1002    }
1003
1004    // -- Dev Attr Prop Set --
1005
1006    /// Write the given properties of attributes of that device, each attribute given as a
1007    /// pair of its name and of its properties, the latter in any of the forms
1008    /// [`Self::set_dev_prop`] accepts, written `S` below:
1009    /// 1. `set_dev_attr_prop(&str, (&str, S)) -> Result<()>`
1010    /// 2. `set_dev_attr_prop(&str, [(&str, S); N]) -> Result<()>`
1011    /// 3. `set_dev_attr_prop(&str, Vec<(&str, S)>) -> Result<()>`
1012    ///
1013    /// Forms 2 and 3 give every attribute the same form of properties. For instance:
1014    /// - `set_dev_attr_prop(dev, ("Position", ("unit", "mm")))`
1015    /// - `set_dev_attr_prop(dev, ("Position", (("unit", "mm"), ("min_value", -5))))`
1016    /// - `set_dev_attr_prop(dev, [("Position", vec![("label", "Pos")]), ("Speed", vec![("rel_change", "1")])])`
1017    ///
1018    /// All the attributes are written within a single request.
1019    #[doc(alias = "DbPutDeviceAttributeProperty2")]
1020    pub fn set_dev_attr_prop_async<'a, Spec>(&self, dev_name: &'a str, spec: Spec) -> impl Future<Output = Result<()>> + Send
1021    where
1022        Spec: SetAttrPropSpec<'a>,
1023    {
1024        self.prop_command_async("DbPutDeviceAttributeProperty2", attr_prop_params(dev_name, spec))
1025    }
1026
1027    /// Write the given properties of attributes of that device, see
1028    /// [`Self::set_dev_attr_prop_async`].
1029    #[doc(alias = "DbPutDeviceAttributeProperty2")]
1030    pub fn set_dev_attr_prop<'a, Spec>(&self, dev_name: &'a str, spec: Spec) -> Result<()>
1031    where
1032        Spec: SetAttrPropSpec<'a>,
1033    {
1034        self.block_on(self.set_dev_attr_prop_async(dev_name, spec))?
1035    }
1036
1037    // -- Class Attr Prop Del --
1038
1039    /// Delete the given properties of one attribute of that class, see
1040    /// [`Self::delete_dev_attr_prop_async`] for the accepted signatures.
1041    #[doc(alias = "DbDeleteClassAttributeProperty")]
1042    pub fn delete_class_attr_prop_async<Names: ToNamesNotAll>(&self, class_name: &str, attr_name: &str, names: Names) -> impl Future<Output = Result<()>> + Send {
1043        let params = prop_params(class_name, Some(attr_name).into_iter().chain(names.iter_names_not_all()));
1044        self.prop_command_async("DbDeleteClassAttributeProperty", params)
1045    }
1046
1047    /// Delete the given properties of one attribute of that class, see
1048    /// [`Self::delete_class_attr_prop_async`].
1049    #[doc(alias = "DbDeleteClassAttributeProperty")]
1050    pub fn delete_class_attr_prop<Names: ToNamesNotAll>(&self, class_name: &str, attr_name: &str, names: Names) -> Result<()> {
1051        self.block_on(self.delete_class_attr_prop_async(class_name, attr_name, names))?
1052    }
1053
1054    // -- Dev Attr Prop Del --
1055
1056    /// Delete the given properties of one attribute of that device, the names of the
1057    /// properties being given the way they are to [`Self::delete_dev_prop`]:
1058    /// 1. `delete_dev_attr_prop(&str, &str, &str) -> Result<()>`
1059    /// 2. `delete_dev_attr_prop(&str, &str, &[&str]) -> Result<()>`
1060    /// 3. `delete_dev_attr_prop(&str, &str, [&str; N]) -> Result<()>`
1061    /// 4. `delete_dev_attr_prop(&str, &str, (&str, &str, ...)) -> Result<()>`
1062    ///
1063    /// Only one attribute is deleted from per request, the database having no command
1064    /// deleting from several at once.
1065    #[doc(alias = "DbDeleteDeviceAttributeProperty")]
1066    pub fn delete_dev_attr_prop_async<Names: ToNamesNotAll>(&self, dev_name: &str, attr_name: &str, names: Names) -> impl Future<Output = Result<()>> + Send {
1067        let params = prop_params(dev_name, Some(attr_name).into_iter().chain(names.iter_names_not_all()));
1068        self.prop_command_async("DbDeleteDeviceAttributeProperty", params)
1069    }
1070
1071    /// Delete the given properties of one attribute of that device, see
1072    /// [`Self::delete_dev_attr_prop_async`].
1073    #[doc(alias = "DbDeleteDeviceAttributeProperty")]
1074    pub fn delete_dev_attr_prop<Names: ToNamesNotAll>(&self, dev_name: &str, attr_name: &str, names: Names) -> Result<()> {
1075        self.block_on(self.delete_dev_attr_prop_async(dev_name, attr_name, names))?
1076    }
1077
1078}
1079
1080impl Deref for DatabaseProxy {
1081    type Target = DevProxy;
1082    fn deref(&self) -> &Self::Target {
1083        &self.dev
1084    }
1085}
1086
1087impl DerefMut for DatabaseProxy {
1088    fn deref_mut(&mut self) -> &mut Self::Target {
1089        &mut self.dev
1090    }
1091}
1092
1093// =========================== //
1094//    Device import/export     //
1095// =========================== //
1096
1097/// The information required to export a device.
1098#[derive(Debug, Clone)]
1099pub struct ExportDevInfo<'a> {
1100    /// The name of the device.
1101    pub name: Cow<'a, str>,
1102    /// The Interoperable Tango Reference to reach the device.
1103    pub itr: Cow<'a, str>,
1104    /// The host the device is served on.
1105    pub host: Cow<'a, str>,
1106    /// The version of the protocol the device is served with.
1107    pub version: Cow<'a, str>,
1108    /// The Process ID of the device's server.
1109    pub pid: i32,
1110}
1111
1112fn data_from_export_dev_info(infos: &[ExportDevInfo<'_>]) -> Spectrum<str> {
1113
1114    let mut vec = Vec::with_capacity(infos.len() * 5);
1115
1116    for info in infos {
1117        vec.push(Cow::Borrowed(&*info.name));
1118        vec.push(Cow::Borrowed(&*info.itr));
1119        vec.push(Cow::Borrowed(&*info.host));
1120        vec.push(Cow::Owned(format!("{}", info.pid)));
1121        vec.push(Cow::Borrowed(&*info.version));
1122    }
1123
1124    Spectrum::from(&vec[..])
1125
1126}
1127
1128/// The two members of what the database answers about a device with, which are the
1129/// exported flag and the process ID, and then its strings.
1130fn long2_string((longs, strings): (Spectrum<i32>, Spectrum<str>)) -> Result<([i32; 2], Spectrum<str>)> {
1131    let longs = <[i32; 2]>::try_from(longs.as_slice())
1132        .map_err(|_| Error::new(ErrorKind::InvalidData)
1133            .with_description(format!("Database answered {} longs where 2 were expected", longs.len()))
1134            .with_append_description(format_args!("Got: {longs:?}")))?;
1135    Ok((longs, strings))
1136}
1137
1138/// Information for how to import a device from the database.
1139#[derive(Debug, Clone)]
1140pub struct ImportDevInfo {
1141    /// The name of the device.
1142    pub name: Scalar<str>,
1143    /// The Interoperable Tango Reference to reach the device.
1144    pub itr: Scalar<str>,
1145    /// The device host.
1146    pub host: Scalar<str>,
1147    /// The device version.
1148    pub version: Scalar<str>,
1149    /// The device server.
1150    pub server: Scalar<str>,
1151    /// Class of this device.
1152    pub class: Option<Scalar<str>>,
1153    /// True if the device is currently exported by a server.
1154    pub exported: bool,
1155    /// The last known Process ID of the device's server.
1156    pub pid: i32,
1157}
1158
1159/// What the database knows of a device, see [`DatabaseProxy::get_dev_info`].
1160#[derive(Debug, Clone)]
1161pub struct DbDevInfo {
1162    /// The name of the device.
1163    pub name: Scalar<str>,
1164    /// The Interoperable Tango Reference to reach the device.
1165    pub itr: Scalar<str>,
1166    /// The device host.
1167    pub host: Option<Scalar<str>>,
1168    /// The device version.
1169    pub version: Scalar<str>,
1170    /// The device server.
1171    pub server: Scalar<str>,
1172    /// Class of this device.
1173    pub class: Option<Scalar<str>>,
1174    /// When the device was last exported, as the database writes it, if it ever was.
1175    pub last_exported: Option<Scalar<str>>,    // TODO: Convert to chrono DateTime
1176    /// When the device was last unexported, as the database writes it, if it ever was.
1177    pub last_unexported: Option<Scalar<str>>,  // TODO: Convert to chrono DateTime
1178    /// True if the device is currently exported by a server.
1179    pub exported: bool,
1180    /// The last known Process ID of the device's server.
1181    pub pid: Option<i32>,
1182}
1183
1184// =========================== //
1185//      Property requests      //
1186// =========================== //
1187
1188// The property requests give back futures declared `Send`, which hold nothing of what they
1189// were given but what is taken out of it before anything is awaited: the names, and the
1190// values to write, so that they are `Send` whatever the names and the values are, which is
1191// what an asynchronous handler boxing them needs.
1192
1193/// What a property write carries behind the object: how many properties, then each
1194/// property with how many values it has and those values, the names and the values staying
1195/// borrowed.
1196pub(crate) fn set_prop_params<'a, Spec: SetPropSpec<'a>>(spec: Spec) -> Vec<Cow<'a, str>> {
1197    let mut builder = set_prop::Builder::new();
1198    spec.push_to_builder(&mut builder);
1199    builder.finalize()
1200}
1201
1202/// The parameters of a property command, the object first, copied together into the one
1203/// buffer the command is given.
1204pub(crate) fn prop_params<'a>(obj_name: &'a str, rest: impl Iterator<Item = &'a str>) -> Spectrum<str> {
1205    Spectrum::from_strs(Some(obj_name).into_iter().chain(rest)).expect("property parameters of more than 4 GiB")
1206}
1207
1208// =========================== //
1209//       Get properties        //
1210// =========================== //
1211
1212/// The commands reading the properties of one kind of object.
1213#[derive(Debug, Clone, Copy)]
1214pub(crate) struct PropCommands {
1215    /// The command reading properties by name.
1216    get: &'static str,
1217    /// The command listing the names of the properties.
1218    list: &'static str,
1219    /// Whether that listing command takes a filter, the class one taking none.
1220    list_wildcard: bool,
1221}
1222
1223impl PropCommands {
1224    const FREE: Self = Self { get: "DbGetProperty", list: "DbGetPropertyList", list_wildcard: true };
1225    const CLASS: Self = Self { get: "DbGetClassProperty", list: "DbGetClassPropertyList", list_wildcard: false };
1226    pub(crate) const DEV: Self = Self { get: "DbGetDeviceProperty", list: "DbGetDevicePropertyList", list_wildcard: true };
1227}
1228
1229/// Sealing module for the reply structure.
1230mod get_prop {
1231
1232    use super::{Error, ErrorKind, Named, Result, Scalar, Spectrum};
1233
1234    /// The properties the database answered a request with, one entry per property and in
1235    /// the order they were requested, taken one at a time, see [`super::GetPropSpec::collect`].
1236    ///
1237    /// Every name and every value shares the octets the answer was decoded out of, so nothing
1238    /// of them is copied before a caller parses them.
1239    #[derive(Debug)]
1240    pub enum PropReply {
1241        /// The properties of an object, still in the flat sequence of strings the database
1242        /// speaks in, read one at a time.
1243        Listing {
1244            /// The listing, empty when nothing was asked: a request carrying no property name
1245            /// at all is one the database refuses, so it is not sent, and an object that has no
1246            /// property gives exactly that.
1247            listing: Spectrum<str>,
1248            /// The names that were asked for, in the order they were, which the order of the
1249            /// answer is checked against.
1250            names: Spectrum<str>,
1251            /// Which property is taken next.
1252            index: usize,
1253            /// Where in the listing that property starts.
1254            cursor: usize,
1255        },
1256        /// The properties of one attribute, read out of the listing of an attribute property
1257        /// request, see [`AttrPropReply`].
1258        ///
1259        /// The database answers with every property it holds of an attribute, there being no
1260        /// asking it for some of them only, so the ones asked for are looked up among those
1261        /// ignoring the case, which is how Tango compares them, and one it does not hold is
1262        /// given with no value, which is what it answers for a property of an object.
1263        Attr {
1264            /// The listing of the whole request, every attribute in it.
1265            listing: Spectrum<str>,
1266            /// Where the first property of the attribute starts.
1267            begin: usize,
1268            /// How many properties the attribute has.
1269            count: usize,
1270            /// The names that were asked for, or none for every property the attribute has,
1271            /// in the order the database gave them.
1272            names: Option<Spectrum<str>>,
1273            /// Which property is taken next.
1274            index: usize,
1275            /// Where in the listing that property starts, when every one is taken.
1276            cursor: usize,
1277        },
1278    }
1279
1280    impl PropReply {
1281
1282        /// The reply to a request that asked for nothing, which is not sent: it carries no
1283        /// property and never looks at a listing.
1284        pub(super) fn empty() -> Self {
1285            Self::Listing { listing: Spectrum::new(), names: Spectrum::new(), index: 0, cursor: 0 }
1286        }
1287
1288        /// The reply to a request for the properties of that object of the given names,
1289        /// answered with that listing, whose head is read here, see [`read_head`], so that
1290        /// what is left to take is the properties themselves.
1291        pub(super) fn new(obj_name: &str, listing: Spectrum<str>, names: Spectrum<str>) -> Result<Self> {
1292            let cursor = read_head(&listing, obj_name, names.len(), "property count", "properties")?;
1293            Ok(Self::Listing { listing, names, index: 0, cursor })
1294        }
1295
1296        /// How many properties are left to be taken: what was asked for, minus what has already
1297        /// been taken.
1298        pub fn len(&self) -> usize {
1299            match self {
1300                Self::Listing { names, index, .. } => names.len() - index,
1301                Self::Attr { names: Some(names), index, .. } => names.len() - index,
1302                Self::Attr { names: None, count, index, .. } => count - index,
1303            }
1304        }
1305
1306        /// Whether every property has been taken, or nothing was asked for at all.
1307        pub fn is_empty(&self) -> bool {
1308            self.len() == 0
1309        }
1310
1311        /// Take the next property, which is its name as the database gave it back and the
1312        /// values it holds, both sharing the listing.
1313        ///
1314        /// A property carries a list of values and not one value: what a single value looks
1315        /// like is what [`crate::prop::FromProp`] makes of that list, an empty one being a property that is
1316        /// defined and holds nothing.
1317        ///
1318        /// A listing that does not add up ends the iteration rather than failing that one
1319        /// property: where a property starts is where the one before it ended, so nothing
1320        /// behind a broken one can be read either.
1321        pub fn next(&mut self) -> Option<Result<Named<Spectrum<str>>>> {
1322
1323            let (listing, names, index, cursor) = match self {
1324                Self::Listing { listing, names, index, cursor } => (listing, names, index, cursor),
1325                Self::Attr { listing, begin, count, names, index, cursor } => {
1326                    return next_attr_prop(listing, *begin, *count, names.as_ref(), index, cursor);
1327                }
1328            };
1329
1330            let expected = names.get([*index])?;
1331            *index += 1;
1332
1333            match next_prop(listing, expected, cursor) {
1334                Ok(prop) => Some(Ok(prop)),
1335                Err(err) => {
1336                    *index = names.len();
1337                    Some(Err(err))
1338                }
1339            }
1340
1341        }
1342
1343    }
1344
1345    /// Read one property out of the listing, starting at `cursor` and leaving it where the next
1346    /// property starts, see [`PropReply::next`].
1347    ///
1348    /// This is where the shape the database speaks in is read: a property is its name, how many
1349    /// values it holds, and then those values.
1350    fn next_prop(listing: &Spectrum<str>, expected: &str, cursor: &mut usize) -> Result<Named<Spectrum<str>>> {
1351
1352        let (Some(name), Some(count_raw)) = (listing.get([*cursor]), listing.get([*cursor + 1])) else {
1353            return Err(Error::new(ErrorKind::InvalidData)
1354                .with_description(format!("Database answer is missing the name and values count of property {expected:?}"))
1355                .with_append_description(format_args!("Got: {listing:?}")));
1356        };
1357
1358        if !name.eq_ignore_ascii_case(expected) {
1359            return Err(Error::new(ErrorKind::InvalidData)
1360                .with_description(format!("Database answered property {name:?} where {expected:?} was requested")));
1361        }
1362
1363        let Ok(count) = count_raw.parse::<usize>() else {
1364            return Err(Error::new(ErrorKind::InvalidData)
1365                .with_description(format!("Database answered values count {count_raw:?} of property {name:?}, which is not a number")));
1366        };
1367
1368        let name_index = *cursor;
1369        let begin = *cursor + 2;
1370        *cursor = begin + count;
1371
1372        if *cursor > listing.len() {
1373            return Err(Error::new(ErrorKind::InvalidData)
1374                .with_description(format!("Database answer is missing values of property {name:?}"))
1375                .with_append_description(format_args!("Got: {listing:?}")));
1376        }
1377
1378        let name = listing.slice(name_index..name_index + 1).into_scalar().expect("one name");
1379        Ok(Named::new(name, listing.slice(begin..*cursor)))
1380
1381    }
1382
1383    /// Take the next property of an attribute, see [`PropReply::Attr`]: the next one of the
1384    /// names asked for, looked up among every property of the attribute, or the next one the
1385    /// attribute has when none was asked for in particular.
1386    ///
1387    /// The properties of the attribute were walked once already, by [`AttrPropReply::next`],
1388    /// so that the next attribute could be found behind them, which a broken one fails.
1389    fn next_attr_prop(
1390        listing: &Spectrum<str>,
1391        begin: usize,
1392        count: usize,
1393        names: Option<&Spectrum<str>>,
1394        index: &mut usize,
1395        cursor: &mut usize,
1396    ) -> Option<Result<Named<Spectrum<str>>>> {
1397
1398        let Some(names) = names else {
1399            if *index >= count {
1400                return None;
1401            }
1402            *index += 1;
1403            return Some(next_value_prop(listing, cursor));
1404        };
1405
1406        let name = names.get_scalar([*index])?;
1407        *index += 1;
1408
1409        let mut lookup = begin;
1410        for _ in 0..count {
1411            match next_value_prop(listing, &mut lookup) {
1412                Ok(prop) if prop.name.value().eq_ignore_ascii_case(name.value()) => {
1413                    return Some(Ok(prop));
1414                }
1415                Ok(_) => {}
1416                Err(err) => {
1417                    *index = names.len();
1418                    return Some(Err(err));
1419                }
1420            }
1421        }
1422
1423        Some(Ok(Named::new(name, Spectrum::new())))
1424
1425    }
1426
1427    /// Read one property of an attribute out of the listing, starting at `cursor` and leaving
1428    /// it where the next property starts: its name, how many values it holds, and then those
1429    /// values, a property holding no value being followed by a single placeholder all the
1430    /// same, which is not one of its values.
1431    fn next_value_prop(listing: &Spectrum<str>, cursor: &mut usize) -> Result<Named<Spectrum<str>>> {
1432
1433        let name = next_str(listing, cursor, "property name")?;
1434        let count = next_count(listing, cursor, "property values count")?;
1435
1436        let begin = *cursor;
1437        *cursor += count.max(1);
1438        if *cursor > listing.len() {
1439            return Err(Error::new(ErrorKind::InvalidData)
1440                .with_description(format!("Database answer is missing values of property {:?}", name.value()))
1441                .with_append_description(format_args!("Got: {listing:?}")));
1442        }
1443
1444        Ok(Named::new(name, listing.slice(begin..begin + count)))
1445
1446    }
1447
1448    /// The properties of attributes the database answered a request with, one entry per
1449    /// attribute and in the order they were requested, taken one at a time, each being the
1450    /// properties of that attribute, see [`PropReply::Attr`].
1451    ///
1452    /// The listing is the flat sequence of strings the database speaks in: every attribute is
1453    /// its name, how many properties it has, and then those properties.
1454    #[derive(Debug)]
1455    pub struct AttrPropReply {
1456        /// The listing, empty when nothing was asked.
1457        listing: Spectrum<str>,
1458        /// The names of the attributes that were asked for, in the order they were, which the
1459        /// order of the answer is checked against.
1460        attr_names: Spectrum<str>,
1461        /// The names of the properties asked for of every attribute, or none for every one.
1462        prop_names: Option<Spectrum<str>>,
1463        /// Which attribute is taken next.
1464        index: usize,
1465        /// Where in the listing that attribute starts.
1466        cursor: usize,
1467    }
1468
1469    impl AttrPropReply {
1470
1471        /// The reply to a request that asked for no attribute, which is not sent: it carries
1472        /// no attribute and never looks at a listing.
1473        pub(super) fn empty() -> Self {
1474            Self { listing: Spectrum::new(), attr_names: Spectrum::new(), prop_names: None, index: 0, cursor: 0 }
1475        }
1476
1477        /// The reply to a request for the properties of the given names of the attributes of
1478        /// that object of the given names, answered with that listing, whose head is read
1479        /// here, see [`read_head`], so that what is left to take is the attributes themselves.
1480        pub(super) fn new(
1481            obj_name: &str,
1482            listing: Spectrum<str>,
1483            attr_names: Spectrum<str>,
1484            prop_names: Option<Spectrum<str>>,
1485        ) -> Result<Self> {
1486            let cursor = read_head(&listing, obj_name, attr_names.len(), "attribute count", "attributes")?;
1487            Ok(Self { listing, attr_names, prop_names, index: 0, cursor })
1488        }
1489
1490        /// How many attributes are left to be taken.
1491        pub fn len(&self) -> usize {
1492            self.attr_names.len() - self.index
1493        }
1494
1495        /// Take the next attribute, which is its name as the database gave it back and its
1496        /// properties.
1497        ///
1498        /// A listing that does not add up ends the iteration, an attribute starting where the
1499        /// one before it ended.
1500        pub fn next(&mut self) -> Option<Result<Named<PropReply>>> {
1501
1502            let Self { listing, attr_names, prop_names, index, cursor } = self;
1503
1504            let expected = attr_names.get([*index])?;
1505            *index += 1;
1506
1507            let res = (|| {
1508
1509                let name = next_str(listing, cursor, "attribute name")?;
1510                if !name.value().eq_ignore_ascii_case(expected) {
1511                    return Err(Error::new(ErrorKind::InvalidData)
1512                        .with_description(format!("Database answered attribute {:?} where {expected:?} was requested",
1513                            name.value())));
1514                }
1515
1516                // The properties are walked here, so that the next attribute is found behind
1517                // them, and read again as they are taken.
1518                let count = next_count(listing, cursor, "property count")?;
1519                let begin = *cursor;
1520                for _ in 0..count {
1521                    next_value_prop(listing, cursor)?;
1522                }
1523
1524                Ok(Named::new(name, PropReply::Attr {
1525                    listing: listing.clone(),
1526                    begin,
1527                    count,
1528                    names: prop_names.clone(),
1529                    index: 0,
1530                    cursor: begin,
1531                }))
1532
1533            })();
1534
1535            if res.is_err() {
1536                *index = attr_names.len();
1537            }
1538
1539            Some(res)
1540
1541        }
1542
1543    }
1544
1545    /// Read the head of a listing, which is the object it is of and how many entries follow,
1546    /// checked against what was asked for, giving back where the first entry starts.
1547    fn read_head(listing: &Spectrum<str>, obj_name: &str, expected: usize, what: &str, entries: &str) -> Result<usize> {
1548
1549        let mut cursor = 0;
1550
1551        let output_obj_name = next_str(listing, &mut cursor, "object name")?;
1552        if !output_obj_name.value().eq_ignore_ascii_case(obj_name) {
1553            return Err(Error::new(ErrorKind::InvalidData)
1554                .with_description(format!("Database answered for object {:?} where {obj_name:?} was requested",
1555                    output_obj_name.value())));
1556        }
1557
1558        let count = next_count(listing, &mut cursor, what)?;
1559        if count != expected {
1560            return Err(Error::new(ErrorKind::InvalidData)
1561                .with_description(format!("Database answered {count} {entries} of object {obj_name:?} where {expected} were requested")));
1562        }
1563
1564        Ok(cursor)
1565
1566    }
1567
1568    /// The string at the cursor of a listing, moving the cursor past it.
1569    fn next_str(listing: &Spectrum<str>, cursor: &mut usize, what: &str) -> Result<Scalar<str>> {
1570        let Some(value) = listing.get_scalar([*cursor]) else {
1571            return Err(Error::new(ErrorKind::InvalidData)
1572                .with_description(format!("Database answer is missing the {what}"))
1573                .with_append_description(format_args!("Got: {listing:?}")));
1574        };
1575        *cursor += 1;
1576        Ok(value)
1577    }
1578
1579    /// The count at the cursor of a listing, moving the cursor past it, see [`next_str`].
1580    fn next_count(listing: &Spectrum<str>, cursor: &mut usize, what: &str) -> Result<usize> {
1581        let raw = next_str(listing, cursor, what)?;
1582        raw.value().trim().parse::<usize>().map_err(|_| Error::new(ErrorKind::InvalidData)
1583            .with_description(format!("Database answered {what} {:?}, which is not a number", raw.value())))
1584    }
1585
1586}
1587
1588/// A trait for property specifiers, which allows a great flexibility in how
1589/// [`DatabaseProxy::get_dev_prop`] works.
1590///
1591/// **Supporting trait**: it is only there to make this crate's API easier to use, it is
1592/// implemented for everything that API accepts and is not one to implement anywhere else.
1593pub trait GetPropSpec<Names> {
1594
1595    /// Kind of output type returned.
1596    type Output;
1597
1598    /// Build the output out of the properties, taking them one at a time.
1599    ///
1600    /// The shape of the output is what the names that were given say it is, and what every
1601    /// property is parsed into is what this is implemented on.
1602    fn collect(props: &mut get_prop::PropReply) -> Result<Self::Output>;
1603
1604}
1605
1606/// One name, one property: that property *is* the output, and its failure is the output's
1607/// own since there is no slot of its own to carry it in.
1608impl<T> GetPropSpec<&'_ str> for T
1609where
1610    T: FromProp,
1611{
1612
1613    type Output = T;
1614
1615    fn collect(props: &mut get_prop::PropReply) -> Result<Self::Output> {
1616        match props.next() {
1617            Some(entry) => T::from_prop(&entry?.value),
1618            None => Err(unexpected_len_err(1)),
1619        }
1620    }
1621
1622}
1623
1624impl<T> GetPropSpec<&'_ [&'_ str]> for T
1625where
1626    T: FromProp,
1627{
1628
1629    type Output = Vec<Result<T>>;
1630
1631    fn collect(props: &mut get_prop::PropReply) -> Result<Self::Output> {
1632
1633        // How many properties there are is known before the first one is taken, so the room
1634        // for them is made once.
1635        let mut values = Vec::with_capacity(props.len());
1636
1637        while let Some(entry) = props.next() {
1638            values.push(T::from_prop(&entry?.value));
1639        }
1640
1641        Ok(values)
1642
1643    }
1644
1645}
1646
1647impl<T> GetPropSpec<Spectrum<str>> for T
1648where
1649    T: FromProp,
1650{
1651
1652    type Output = Vec<Result<T>>;
1653
1654    fn collect(props: &mut get_prop::PropReply) -> Result<Self::Output> {
1655        <T as GetPropSpec<&[&str]>>::collect(props)
1656    }
1657
1658}
1659
1660impl<T> GetPropSpec<&Spectrum<str>> for T
1661where
1662    T: FromProp,
1663{
1664
1665    type Output = Vec<Result<T>>;
1666
1667    fn collect(props: &mut get_prop::PropReply) -> Result<Self::Output> {
1668        <T as GetPropSpec<&[&str]>>::collect(props)
1669    }
1670
1671}
1672
1673impl<T, const N: usize> GetPropSpec<[&'_ str; N]> for T
1674where
1675    T: FromProp,
1676{
1677
1678    type Output = [Result<T>; N];
1679
1680    fn collect(props: &mut get_prop::PropReply) -> Result<Self::Output> {
1681
1682        // One property per slot, a last one behind them being a broken reply as much as one
1683        // of them missing, see [`crate::dev::take_array`].
1684        let mut slots = [const { None }; N];
1685        for slot in &mut slots {
1686            let entry = props.next().ok_or_else(|| unexpected_len_err(N))?;
1687            *slot = Some(T::from_prop(&entry?.value));
1688        }
1689        if props.next().is_some() {
1690            return Err(unexpected_len_err(N));
1691        }
1692
1693        Ok(slots.map(|slot| slot.expect("every slot is filled")))
1694
1695    }
1696    
1697}
1698
1699impl GetPropSpec<All> for Spectrum<str> {
1700
1701    type Output = Vec<Named<Spectrum<str>>>;
1702
1703    fn collect(props: &mut get_prop::PropReply) -> Result<Self::Output> {
1704
1705        // Nothing was named here, so every property carries its own name, both sharing the
1706        // listing they were read out of.
1707        let mut values = Vec::with_capacity(props.len());
1708
1709        while let Some(entry) = props.next() {
1710            values.push(entry?);
1711        }
1712
1713        Ok(values)
1714
1715    }
1716
1717}
1718
1719macro_rules! impl_get_prop_spec_tuple {
1720    ( @one: $comp:ident ) => { 1 };
1721    ( @name: $comp:ident ) => { &'name str };
1722    ( $( $comp:ident )* ) => {
1723
1724        #[allow(non_snake_case)]
1725        impl<'name, $($comp,)* > GetPropSpec<( $( impl_get_prop_spec_tuple!(@name: $comp), )* )> for ( $($comp,)* )
1726        where
1727            $(
1728            $comp: FromProp,
1729            )*
1730        {
1731
1732            type Output = ( $( Result<$comp>,)* );
1733
1734            fn collect(props: &mut get_prop::PropReply) -> Result<Self::Output> {
1735
1736                const LEN: usize = 0 $( + impl_get_prop_spec_tuple!(@one: $comp) )*;
1737
1738                // The properties are taken in the order the names were given, which is the
1739                // order the components of a tuple are built in, and every one of them is
1740                // parsed into the type of its own component.
1741                let output = ( $( {
1742                    let entry = props.next().ok_or_else(|| unexpected_len_err(LEN))?;
1743                    $comp::from_prop(&entry?.value)
1744                }, )* );
1745                if props.next().is_some() {
1746                    return Err(unexpected_len_err(LEN));
1747                }
1748
1749                Ok(output)
1750
1751            }
1752
1753        }
1754
1755    };
1756}
1757
1758impl_get_prop_spec_tuple!(A);
1759impl_get_prop_spec_tuple!(A B);
1760impl_get_prop_spec_tuple!(A B C);
1761impl_get_prop_spec_tuple!(A B C D);
1762impl_get_prop_spec_tuple!(A B C D E);
1763impl_get_prop_spec_tuple!(A B C D E F);
1764
1765// =========================== //
1766//       Set properties        //
1767// =========================== //
1768
1769/// Sealing module for the Builder structure.
1770mod set_prop {
1771
1772    use super::{Cow, IntoProp, SetPropSpec};
1773
1774    pub struct Builder<'a> {
1775        array: Vec<Cow<'a, str>>,
1776        count: usize,
1777    }
1778
1779    impl<'a> Builder<'a> {
1780
1781        pub fn new() -> Self {
1782            Self {
1783                array: vec![Cow::Borrowed("")],  // Properties count is set when finalized.
1784                count: 0,
1785            }
1786        }
1787
1788        pub fn push<T>(&mut self, name: &'a str, value: T)
1789        where
1790            T: IntoProp<'a>,
1791        {
1792            self.array.push(Cow::Borrowed(name));
1793            self.array.push(Cow::Borrowed(""));  // Will be written just after...
1794            let len = self.array.len();
1795            self.array.extend(value.into_prop());
1796            let count = self.array.len() - len;
1797            self.array[len - 1] = Cow::Owned(format!("{count}"));  // Actually write count!
1798            self.count += 1;
1799        }
1800
1801        pub fn finalize(mut self) -> Vec<Cow<'a, str>> {
1802            // Write the number of properties ahead of the array.
1803            self.array[0] = Cow::Owned(format!("{}", self.count));
1804            self.array
1805        }
1806
1807    }
1808
1809    /// The counterpart of [`Builder`] for the properties of attributes: every attribute
1810    /// is its name followed by what a [`Builder`] makes of its properties.
1811    pub struct AttrBuilder<'a> {
1812        array: Vec<Cow<'a, str>>,
1813        count: usize,
1814    }
1815
1816    impl<'a> AttrBuilder<'a> {
1817
1818        pub fn new() -> Self {
1819            Self {
1820                array: vec![Cow::Borrowed("")],  // Attributes count is set when finalized.
1821                count: 0,
1822            }
1823        }
1824
1825        pub fn push<S>(&mut self, attr_name: &'a str, spec: S)
1826        where
1827            S: SetPropSpec<'a>,
1828        {
1829            let mut builder = Builder::new();
1830            spec.push_to_builder(&mut builder);
1831            self.array.push(Cow::Borrowed(attr_name));
1832            self.array.extend(builder.finalize());
1833            self.count += 1;
1834        }
1835
1836        pub fn finalize(mut self) -> Vec<Cow<'a, str>> {
1837            self.array[0] = Cow::Owned(format!("{}", self.count));
1838            self.array
1839        }
1840
1841    }
1842
1843}
1844
1845/// The properties to write, as name/value pairs, see [`DatabaseProxy::set_dev_prop`].
1846///
1847/// **Supporting trait**: it is only there to make this crate's API easier to use, it is
1848/// implemented for everything that API accepts and is not one to implement anywhere else.
1849pub trait SetPropSpec<'a>: Sized {
1850    /// Make this write specification into the params for the set property command.
1851    fn push_to_builder(self, builder: &mut set_prop::Builder<'a>);
1852}
1853
1854/// For a single property.
1855impl<'a, T> SetPropSpec<'a> for (&'a str, T)
1856where
1857    T: IntoProp<'a>,
1858{
1859
1860    #[inline]
1861    fn push_to_builder(self, builder: &mut set_prop::Builder<'a>) {
1862        builder.push(self.0, self.1);
1863    }
1864
1865}
1866
1867/// For an array of properties.
1868impl<'a, T, const N: usize> SetPropSpec<'a> for [(&'a str, T); N]
1869where
1870    T: IntoProp<'a>,
1871{
1872
1873    #[inline]
1874    fn push_to_builder(self, builder: &mut set_prop::Builder<'a>) {
1875        for (name, value) in self {
1876            builder.push(name, value);
1877        }
1878    }
1879
1880}
1881
1882/// For a vector of properties.
1883impl<'a, 'aa: 'a, T> SetPropSpec<'a> for Vec<(&'aa str, T)>
1884where
1885    T: IntoProp<'a>,
1886{
1887
1888    #[inline]
1889    fn push_to_builder(self, builder: &mut set_prop::Builder<'a>) {
1890        for (name, value) in self {
1891            builder.push(name, value);
1892        }
1893    }
1894
1895}
1896
1897macro_rules! impl_set_prop_spec_tuple {
1898    ( @name: $comp:ident ) => { &'a str };
1899    ( $( $comp:ident )* ) => {
1900
1901        #[allow(non_snake_case, unused_assignments)]
1902        impl<'a, $($comp,)*> SetPropSpec<'a> for ( $( (impl_set_prop_spec_tuple!(@name: $comp), $comp), )* )
1903        where
1904            $(
1905                $comp: IntoProp<'a>,
1906            )*
1907        {
1908
1909            #[inline]
1910            fn push_to_builder(self, builder: &mut set_prop::Builder<'a>) {
1911                let ( $($comp,)* ) = self;
1912                $(
1913                    builder.push($comp.0, $comp.1);
1914                )*
1915            }
1916
1917        }
1918
1919    };
1920}
1921
1922impl_set_prop_spec_tuple!(A);
1923impl_set_prop_spec_tuple!(A B);
1924impl_set_prop_spec_tuple!(A B C);
1925impl_set_prop_spec_tuple!(A B C D);
1926impl_set_prop_spec_tuple!(A B C D E);
1927impl_set_prop_spec_tuple!(A B C D E F);
1928
1929// =========================== //
1930//  Get attribute properties   //
1931// =========================== //
1932
1933/// The commands reading the properties of the attributes of one kind of object.
1934#[derive(Debug, Clone, Copy)]
1935pub(crate) struct AttrPropCommands {
1936    /// The command reading the properties of attributes by name.
1937    get: &'static str,
1938    /// The command listing the attributes that have any property.
1939    list: &'static str,
1940}
1941
1942impl AttrPropCommands {
1943    pub(crate) const CLASS: Self = Self { get: "DbGetClassAttributeProperty2", list: "DbGetClassAttributeList" };
1944    pub(crate) const DEV: Self = Self { get: "DbGetDeviceAttributeProperty2", list: "DbGetDeviceAttributeList" };
1945}
1946
1947/// A trait for attribute property specifiers, which allows a great flexibility in how
1948/// [`DatabaseProxy::get_dev_attr_prop`] works: the attributes are shaped by their names, the
1949/// way any request on names is, see [`crate::dev::ToNamesSpec`], and the properties of every
1950/// one of them by the names of the properties, see [`GetPropSpec`], which is what this is
1951/// implemented on.
1952///
1953/// **Supporting trait**: it is only there to make this crate's API easier to use, it is
1954/// implemented for everything that API accepts and is not one to implement anywhere else.
1955pub trait GetAttrPropSpec<AttrNames, Names> {
1956
1957    /// Kind of output type returned.
1958    type Output;
1959
1960    /// Build the output out of the attributes, taking them one at a time, and the properties
1961    /// of every one of them.
1962    fn collect(attrs: &mut get_prop::AttrPropReply) -> Result<Self::Output>;
1963
1964}
1965
1966/// The properties of the next attribute, parsed, the listing not adding up failing the
1967/// whole output, see [`get_prop::AttrPropReply::next`].
1968fn next_attr<T: GetPropSpec<Names>, Names>(attrs: &mut get_prop::AttrPropReply) -> Option<Result<NamedResult<T::Output>>> {
1969    Some(attrs.next()?.map(|attr| attr.map(|mut props| T::collect(&mut props))))
1970}
1971
1972/// One attribute: its properties *are* the output, and their failure is the output's own
1973/// since there is no slot of its own to carry it in.
1974impl<T: GetPropSpec<Names>, Names> GetAttrPropSpec<&'_ str, Names> for T {
1975
1976    type Output = T::Output;
1977
1978    fn collect(attrs: &mut get_prop::AttrPropReply) -> Result<Self::Output> {
1979        match next_attr::<T, Names>(attrs) {
1980            Some(attr) => attr?.value,
1981            None => Err(unexpected_len_err(1)),
1982        }
1983    }
1984
1985}
1986
1987/// Every attribute of a list, each with the outcome of its own properties.
1988fn collect_attr_list<T: GetPropSpec<Names>, Names>(attrs: &mut get_prop::AttrPropReply) -> Result<Vec<Result<T::Output>>> {
1989    let mut outputs = Vec::with_capacity(attrs.len());
1990    while let Some(attr) = next_attr::<T, Names>(attrs) {
1991        outputs.push(attr?.value);
1992    }
1993    Ok(outputs)
1994}
1995
1996impl<T: GetPropSpec<Names>, Names> GetAttrPropSpec<&'_ [&'_ str], Names> for T {
1997    type Output = Vec<Result<T::Output>>;
1998    fn collect(attrs: &mut get_prop::AttrPropReply) -> Result<Self::Output> {
1999        collect_attr_list::<T, Names>(attrs)
2000    }
2001}
2002
2003impl<T: GetPropSpec<Names>, Names> GetAttrPropSpec<Spectrum<str>, Names> for T {
2004    type Output = Vec<Result<T::Output>>;
2005    fn collect(attrs: &mut get_prop::AttrPropReply) -> Result<Self::Output> {
2006        collect_attr_list::<T, Names>(attrs)
2007    }
2008}
2009
2010impl<T: GetPropSpec<Names>, Names> GetAttrPropSpec<&'_ Spectrum<str>, Names> for T {
2011    type Output = Vec<Result<T::Output>>;
2012    fn collect(attrs: &mut get_prop::AttrPropReply) -> Result<Self::Output> {
2013        collect_attr_list::<T, Names>(attrs)
2014    }
2015}
2016
2017impl<T: GetPropSpec<Names>, Names, const N: usize> GetAttrPropSpec<[&'_ str; N], Names> for T {
2018
2019    type Output = [Result<T::Output>; N];
2020
2021    fn collect(attrs: &mut get_prop::AttrPropReply) -> Result<Self::Output> {
2022        // One attribute per slot, a last one behind them being a broken reply as much as one
2023        // of them missing, see [`crate::dev::take_array`].
2024        let mut slots = [const { None }; N];
2025        for slot in &mut slots {
2026            *slot = Some(next_attr::<T, Names>(attrs).ok_or_else(|| unexpected_len_err(N))??.value);
2027        }
2028        if attrs.next().is_some() {
2029            return Err(unexpected_len_err(N));
2030        }
2031        Ok(slots.map(|slot| slot.expect("every slot is filled")))
2032    }
2033
2034}
2035
2036/// Nothing was named here, so every attribute carries its own name.
2037impl<T: GetPropSpec<Names>, Names> GetAttrPropSpec<All, Names> for T {
2038
2039    type Output = Vec<NamedResult<T::Output>>;
2040
2041    fn collect(attrs: &mut get_prop::AttrPropReply) -> Result<Self::Output> {
2042        let mut outputs = Vec::with_capacity(attrs.len());
2043        while let Some(attr) = next_attr::<T, Names>(attrs) {
2044            outputs.push(attr?);
2045        }
2046        Ok(outputs)
2047    }
2048
2049}
2050
2051macro_rules! impl_get_attr_prop_spec_tuple {
2052    ( @one: $index:tt ) => { 1 };
2053    ( @name: $index:tt ) => { &'name str };
2054    ( @output: $index:tt ) => { Result<T::Output> };
2055    ( @next: $index:tt, $attrs:ident ) => { next_attr::<T, Names>($attrs).ok_or_else(|| unexpected_len_err(LEN))??.value };
2056    ( $( $index:tt )* ) => {
2057
2058        impl<'name, T: GetPropSpec<Names>, Names> GetAttrPropSpec<( $( impl_get_attr_prop_spec_tuple!(@name: $index), )* ), Names> for T {
2059
2060            type Output = ( $( impl_get_attr_prop_spec_tuple!(@output: $index), )* );
2061
2062            fn collect(attrs: &mut get_prop::AttrPropReply) -> Result<Self::Output> {
2063                const LEN: usize = 0 $( + impl_get_attr_prop_spec_tuple!(@one: $index) )*;
2064                // The attributes come in the order the names were given, which is the order
2065                // the components of a tuple are built in.
2066                let output = ( $( impl_get_attr_prop_spec_tuple!(@next: $index, attrs), )* );
2067                if attrs.next().is_some() {
2068                    return Err(unexpected_len_err(LEN));
2069                }
2070                Ok(output)
2071            }
2072
2073        }
2074
2075    };
2076}
2077
2078impl_get_attr_prop_spec_tuple!(0);
2079impl_get_attr_prop_spec_tuple!(0 1);
2080impl_get_attr_prop_spec_tuple!(0 1 2);
2081impl_get_attr_prop_spec_tuple!(0 1 2 3);
2082impl_get_attr_prop_spec_tuple!(0 1 2 3 4);
2083impl_get_attr_prop_spec_tuple!(0 1 2 3 4 5);
2084
2085// =========================== //
2086//  Set attribute properties   //
2087// =========================== //
2088
2089/// An abstract specification of the properties to write of one to many attributes, every
2090/// attribute being given what [`SetPropSpec`] takes for the properties of an object, see
2091/// [`DatabaseProxy::set_dev_attr_prop`].
2092///
2093/// **Supporting trait**: it is only there to make this crate's API easier to use, it is
2094/// implemented for everything that API accepts and is not one to implement anywhere else.
2095pub trait SetAttrPropSpec<'a>: Sized {
2096    /// Make this write specification into the params for the set property command.
2097    fn push_to_builder(self, builder: &mut set_prop::AttrBuilder<'a>);
2098}
2099
2100/// For the properties of a single attribute.
2101impl<'a, S> SetAttrPropSpec<'a> for (&'a str, S)
2102where
2103    S: SetPropSpec<'a>,
2104{
2105
2106    #[inline]
2107    fn push_to_builder(self, builder: &mut set_prop::AttrBuilder<'a>) {
2108        builder.push(self.0, self.1);
2109    }
2110
2111}
2112
2113/// For an array of attributes.
2114impl<'a, S, const N: usize> SetAttrPropSpec<'a> for [(&'a str, S); N]
2115where
2116    S: SetPropSpec<'a>,
2117{
2118
2119    #[inline]
2120    fn push_to_builder(self, builder: &mut set_prop::AttrBuilder<'a>) {
2121        for (attr_name, spec) in self {
2122            builder.push(attr_name, spec);
2123        }
2124    }
2125
2126}
2127
2128/// For a vector of attributes.
2129impl<'a, S> SetAttrPropSpec<'a> for Vec<(&'a str, S)>
2130where
2131    S: SetPropSpec<'a>,
2132{
2133
2134    #[inline]
2135    fn push_to_builder(self, builder: &mut set_prop::AttrBuilder<'a>) {
2136        for (attr_name, spec) in self {
2137            builder.push(attr_name, spec);
2138        }
2139    }
2140
2141}
2142
2143/// The parameters of an attribute property write, the object first.
2144fn attr_prop_params<'a, Spec: SetAttrPropSpec<'a>>(obj_name: &str, spec: Spec) -> Spectrum<str> {
2145    let mut builder = set_prop::AttrBuilder::new();
2146    spec.push_to_builder(&mut builder);
2147    prop_params(obj_name, builder.finalize().iter().map(|param| &**param))
2148}
2149
2150#[cfg(test)]
2151mod tests {
2152
2153    use futures::future::BoxFuture;
2154
2155    use super::*;
2156
2157    /// The attribute property reads, awaited within a future boxed as `Send`, which is what
2158    /// an asynchronous handler is: this only has to compile.
2159    #[allow(dead_code)]
2160    fn attr_props_in_send_future(db: &DatabaseProxy) -> BoxFuture<'_, Result<()>> {
2161        Box::pin(async move {
2162            let _ = db.get_dev_attr_prop_async::<f64, _, _>("motor/dev/1", "Position", "max_value").await?;
2163            let _ = db.get_class_attr_prop_async::<(String, f64), _, _>("Motor", ["Position"], ("label", "max_value")).await?;
2164            let _ = db.get_dev_attr_prop_async::<Spectrum<str>, _, _>("motor/dev/1", All, All).await?;
2165            Ok(())
2166        })
2167    }
2168
2169    /// The reply to a property request that was asked for the given names and answered
2170    /// with the given listing, which is the flat list the database speaks in: the object,
2171    /// how many properties follow, and then every property as its name, how many values it
2172    /// holds, and those values.
2173    fn reply(names: &[&str], listing: &[&str]) -> get_prop::PropReply {
2174        get_prop::PropReply::new(listing[0], Spectrum::from(listing), Spectrum::from(names)).unwrap()
2175    }
2176
2177    /// The reply to an attribute property request that was asked for the given attributes
2178    /// and properties, and answered with the given listing, the flat list the database
2179    /// speaks in.
2180    fn attr_reply(attr_names: &[&str], prop_names: Option<&[&str]>, listing: &[&str]) -> get_prop::AttrPropReply {
2181        get_prop::AttrPropReply::new(listing[0], Spectrum::from(listing), Spectrum::from(attr_names), prop_names.map(Spectrum::from)).unwrap()
2182    }
2183
2184    /// The database answering for another object than the one asked about, or with another
2185    /// count than what was asked for, fails the whole reply before anything is read out of
2186    /// it, and a request that asks for nothing carries nothing.
2187    #[test]
2188    fn listing_head() {
2189
2190        let names = || Spectrum::from(&["prop"][..]);
2191
2192        let err = get_prop::PropReply::new("obj", Spectrum::from(&["another", "1", "prop", "0"][..]), names()).unwrap_err();
2193        assert_eq!(err.kind(), ErrorKind::InvalidData);
2194        let err = get_prop::PropReply::new("obj", Spectrum::from(&["obj", "2", "prop", "0"][..]), names()).unwrap_err();
2195        assert_eq!(err.kind(), ErrorKind::InvalidData);
2196        let err = get_prop::AttrPropReply::new("obj", Spectrum::from(&["obj", "2", "prop", "0"][..]), names(), None).unwrap_err();
2197        assert_eq!(err.kind(), ErrorKind::InvalidData);
2198
2199        let mut props = get_prop::PropReply::empty();
2200        assert!(props.is_empty());
2201        assert!(props.next().is_none());
2202        let mut attrs = get_prop::AttrPropReply::empty();
2203        assert_eq!(attrs.len(), 0);
2204        assert!(attrs.next().is_none());
2205
2206    }
2207
2208    /// The properties of an attribute, as they are taken out of its reply.
2209    fn attr_props(props: &mut get_prop::PropReply) -> Vec<(String, Vec<String>)> {
2210        let mut taken = Vec::new();
2211        while let Some(prop) = props.next() {
2212            let prop = prop.unwrap();
2213            taken.push((prop.name.value().to_string(), prop.value.iter().map(str::to_string).collect()));
2214        }
2215        taken
2216    }
2217
2218    const ATTR_LISTING: &[&str] = &[
2219        "sys/tg_test/1", "3",
2220        "first", "2", "label", "1", "First", "rel_change", "2", "-1", "2",
2221        "empty", "1", "unit", "0", " ",
2222        "none", "0",
2223    ];
2224
2225    /// The attributes come back one entry per attribute asked for, in the order they were,
2226    /// an attribute the database holds nothing of included, every one with every property
2227    /// it has when none is asked for in particular, and a property holding no value
2228    /// followed by a placeholder that is not one of its values.
2229    #[test]
2230    fn attr_props_in_order() {
2231
2232        let mut reply = attr_reply(&["first", "empty", "none"], None, ATTR_LISTING);
2233        assert_eq!(reply.len(), 3);
2234
2235        let Named { name, value: mut props } = reply.next().unwrap().unwrap();
2236        assert_eq!(name.value(), "first");
2237        assert_eq!(props.len(), 2);
2238        assert_eq!(attr_props(&mut props), [
2239            ("label".to_string(), vec!["First".to_string()]),
2240            ("rel_change".to_string(), vec!["-1".to_string(), "2".to_string()]),
2241        ]);
2242
2243        let mut props = reply.next().unwrap().unwrap().value;
2244        assert_eq!(attr_props(&mut props), [("unit".to_string(), vec![])]);
2245
2246        let mut props = reply.next().unwrap().unwrap().value;
2247        assert!(attr_props(&mut props).is_empty());
2248
2249        assert!(reply.next().is_none());
2250
2251    }
2252
2253    /// The properties asked for are looked up among every one an attribute has ignoring the
2254    /// case, in the order they were asked for, one it does not have coming with no value.
2255    #[test]
2256    fn attr_prop_lookup() {
2257
2258        let mut reply = attr_reply(&["first", "empty", "none"], Some(&["REL_CHANGE", "unit"]), ATTR_LISTING);
2259
2260        let mut props = reply.next().unwrap().unwrap().value;
2261        assert_eq!(props.len(), 2);
2262        assert_eq!(attr_props(&mut props), [
2263            ("rel_change".to_string(), vec!["-1".to_string(), "2".to_string()]),
2264            ("unit".to_string(), vec![]),
2265        ]);
2266
2267        let mut props = reply.next().unwrap().unwrap().value;
2268        assert_eq!(attr_props(&mut props), [
2269            ("REL_CHANGE".to_string(), vec![]),
2270            ("unit".to_string(), vec![]),
2271        ]);
2272
2273    }
2274
2275    /// The database naming back another attribute than the one asked for fails that
2276    /// attribute, as does one announcing more values than it carries, and nothing is read
2277    /// behind either, an attribute starting where the one before it ended.
2278    #[test]
2279    fn incoherent_attr_props() {
2280
2281        let mut reply = attr_reply(&["asked", "behind"], None, &["obj", "2", "another", "0", "behind", "0"]);
2282        assert_eq!(reply.next().unwrap().unwrap_err().kind(), ErrorKind::InvalidData);
2283        assert!(reply.next().is_none());
2284
2285        let mut reply = attr_reply(&["asked"], None, &["obj", "1", "asked", "1", "label", "2", "one"]);
2286        assert_eq!(reply.next().unwrap().unwrap_err().kind(), ErrorKind::InvalidData);
2287
2288    }
2289
2290    /// What an attribute property write carries: the object, how many attributes, then
2291    /// every attribute with how many properties it has, and every property with how many
2292    /// values it has and those values.
2293    #[test]
2294    fn attr_prop_write() {
2295        let params = attr_prop_params("obj", [
2296            ("first", vec![("label", "First"), ("unit", "mm")]),
2297            ("second", vec![("label", "Second")]),
2298        ]);
2299        assert_eq!(params.iter().collect::<Vec<_>>(), [
2300            "obj", "2",
2301            "first", "2", "label", "1", "First", "unit", "1", "mm",
2302            "second", "1", "label", "1", "Second",
2303        ]);
2304    }
2305
2306    /// The properties come back in the order they were asked for, each with the values it
2307    /// holds and nothing of the ones around it.
2308    #[test]
2309    fn props_in_order() {
2310
2311        let mut props = reply(&["first", "second"],
2312            &["sys/tg_test/1", "2", "first", "1", "one", "second", "2", "two", "three"]);
2313
2314        assert_eq!(props.len(), 2);
2315
2316        let Named { name, value: values } = props.next().unwrap().unwrap();
2317        assert_eq!(name.value(), "first");
2318        assert_eq!(values.iter().collect::<Vec<_>>(), ["one"]);
2319
2320        let Named { name, value: values } = props.next().unwrap().unwrap();
2321        assert_eq!(name.value(), "second");
2322        assert_eq!(values.iter().collect::<Vec<_>>(), ["two", "three"]);
2323
2324        assert!(props.next().is_none());
2325
2326    }
2327
2328    /// A property that is defined and holds nothing comes back with no value at all, which
2329    /// is not the same as it not being there.
2330    #[test]
2331    fn prop_without_value() {
2332        let mut props = reply(&["empty"], &["obj", "1", "empty", "0"]);
2333        let Named { name, value: values } = props.next().unwrap().unwrap();
2334        assert_eq!(name.value(), "empty");
2335        assert!(values.is_empty());
2336    }
2337
2338    /// The database naming another property back than the one that was asked for is it
2339    /// breaking the order the answer is read in, which that one property fails with.
2340    #[test]
2341    fn incoherent_prop_name() {
2342        let mut props = reply(&["asked"], &["obj", "1", "another", "1", "one"]);
2343        let err = props.next().unwrap().unwrap_err();
2344        assert_eq!(err.kind(), ErrorKind::InvalidData);
2345    }
2346
2347    /// A property announcing more values than the listing carries fails: the values are a
2348    /// slice of the listing and there is nothing there to slice.
2349    ///
2350    /// Nothing is read behind it either, a property starting where the one before it ended.
2351    #[test]
2352    fn missing_prop_values() {
2353        let mut props = reply(&["prop", "behind"], &["obj", "2", "prop", "2", "one"]);
2354        let err = props.next().unwrap().unwrap_err();
2355        assert_eq!(err.kind(), ErrorKind::InvalidData);
2356        assert!(props.next().is_none());
2357    }
2358
2359    /// One name asks for one property, which is parsed into the type the caller expects of
2360    /// it and is the output itself.
2361    #[test]
2362    fn single_prop_parsed() {
2363        let mut props = reply(&["prop"], &["obj", "1", "prop", "1", "3.5"]);
2364        let value = <f64 as GetPropSpec<&str>>::collect(&mut props).unwrap();
2365        assert_eq!(value, 3.5);
2366    }
2367
2368}