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