Skip to main content

tango/server/
def.rs

1//! Definition of a device model and of its interface: its commands and its attributes.
2
3use std::time::{Duration, SystemTime};
4use std::ops::{Bound, RangeBounds};
5use std::fmt::{self, Debug};
6use std::future::Future;
7use std::borrow::Cow;
8use std::sync::Arc;
9
10use futures::future::BoxFuture;
11
12use crate::dev::{AttrConfig, AttrDeltaConfig, AttrMemorized, AttrNumber, AttrQuality, AttrRangeConfig, AttrRead, DevState, DisplayLevel};
13use crate::value::{AttrData, AttrDataFormat, AttrDataKind, CommandData, CommandDataKind, FromValue, IntoValue, Scalar, Value};
14use crate::error::Result;
15use crate::name;
16
17use super::{reserved_name, Model, RESERVED_ATTRS, RESERVED_COMMANDS, STATE_NAME, STATUS_NAME};
18use super::attr::{unsupported, unsupported_prop};
19use super::dev::Dev;
20
21
22/// Merge a data kind being defined with the one already defined, if any, and give the
23/// kind to keep. Both are compatible when they are equal, which is what allows a kind to
24/// be both derived from a handler and given explicitly, in any order. Anything else is a
25/// contradiction in the definition and therefore panics.
26#[track_caller]
27fn merge_kind<K: PartialEq + Debug>(def: &str, what: &str, current: Option<K>, new: K) -> K {
28    match current {
29        Some(current) if current != new => panic!(
30            "{def}: the {what} is already defined as {current:?}, incompatible with {new:?}"),
31        _ => new,
32    }
33}
34
35/// Merge a data format being defined with the one already defined, if any, and give the
36/// format to keep. Both are compatible when they have the same layout, their maximum
37/// dimensions being merged with [`merge_max`], anything else panics.
38#[track_caller]
39fn merge_format(def: &str, current: Option<AttrDataFormat>, new: AttrDataFormat) -> AttrDataFormat {
40    let Some(current) = current else { return new };
41    match (&current, &new) {
42        (AttrDataFormat::Scalar, AttrDataFormat::Scalar) => AttrDataFormat::Scalar,
43        (AttrDataFormat::Spectrum(cur), AttrDataFormat::Spectrum(new)) => {
44            AttrDataFormat::Spectrum(merge_max(def, "maximum size", *cur, *new))
45        }
46        (AttrDataFormat::Image(cur_x, cur_y), AttrDataFormat::Image(new_x, new_y)) => {
47            AttrDataFormat::Image(
48                merge_max(def, "maximum width", *cur_x, *new_x),
49                merge_max(def, "maximum height", *cur_y, *new_y),
50            )
51        }
52        _ => panic!(
53            "{def}: the data format is already defined as {current:?}, incompatible with {new:?}"),
54    }
55}
56
57/// Merge two maximum dimensions of a data format, zero standing for an undefined maximum
58/// and therefore being compatible with any other value. Two different defined maximums
59/// are a contradiction in the definition and therefore panic.
60#[track_caller]
61fn merge_max(def: &str, what: &str, current: usize, new: usize) -> usize {
62    match (current, new) {
63        (0, new) => new,
64        (current, 0) => current,
65        (current, new) if current != new => panic!(
66            "{def}: the {what} is already defined as {current}, incompatible with {new}"),
67        (current, _) => current,
68    }
69}
70
71/// The two bounds of a range being defined, either of which is absent for a bound that is
72/// left open, which is how an attribute says it has no minimum or no maximum rather than
73/// having one it never reaches.
74#[track_caller]
75fn range_config<T, R>(def: &str, what: &str, range: R) -> AttrRangeConfig
76where
77    T: Into<AttrNumber> + Copy,
78    R: RangeBounds<T>,
79{
80    AttrRangeConfig {
81        min: bound_value(def, what, range.start_bound()),
82        max: bound_value(def, what, range.end_bound()),
83    }
84}
85
86/// A single bound of such a range, as the number the attribute holds: a range written with
87/// integer bounds gives integer limits, one written with floating bounds gives floating
88/// ones, and it is up to the definition to bound an attribute with its own kind of number.
89///
90/// An exclusive bound has no representation in Tango, the bounds being values a client
91/// compares against, so it is refused rather than silently taken for the inclusive one it
92/// is not.
93#[track_caller]
94fn bound_value<T>(def: &str, what: &str, bound: Bound<&T>) -> Option<AttrNumber>
95where
96    T: Into<AttrNumber> + Copy,
97{
98    match bound {
99        Bound::Included(value) => Some((*value).into()),
100        Bound::Unbounded => None,
101        Bound::Excluded(_) => panic!(
102            "{def}: the {what} cannot have an exclusive bound, Tango only has inclusive ones"),
103    }
104}
105
106/// A placeholder shown by [`Debug`] in place of a handler, a closure having nothing to
107/// show about itself.
108struct HandlerDebug;
109
110impl Debug for HandlerDebug {
111    #[inline]
112    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113        f.write_str("..")
114    }
115}
116
117/// Implement [`Debug`] for the handler enums, showing which access to the model the
118/// handler takes and whether it is asynchronous, which is all there is to show about it.
119macro_rules! impl_handler_debug {
120    ($($handler:ident),* $(,)?) => { $(
121        impl<M: Model> Debug for $handler<M> {
122            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123                f.write_str(match self {
124                    Self::SharedSync(_) => "SharedSync(..)",
125                    Self::ExclusiveSync(_) => "ExclusiveSync(..)",
126                    Self::SharedAsync(_) => "SharedAsync(..)",
127                    Self::ExclusiveAsync(_) => "ExclusiveAsync(..)",
128                })
129            }
130        }
131    )* };
132}
133
134impl_handler_debug!(StateFn, StatusFn, ExecFn, ReadFn, WriteFn);
135
136/// A builder for defining a device model and interface.
137pub struct ClassDef<M: Model> {
138    /// The name of the device class.
139    pub(super) name: Cow<'static, str>,
140    /// The description every device of that class gives of itself, which is what the
141    /// description request of a device answers.
142    pub(super) doc: Cow<'static, str>,
143    /// Where the documentation of that class is, which is what a client browsing the
144    /// control system opens when it is asked to show what a device is. Absent for a class
145    /// that documents itself nowhere, which is what a client is then told.
146    pub(super) doc_url: Option<Cow<'static, str>>,
147    /// The function building the model when the device is initialized.
148    pub(super) init: InitFn<M>,
149    /// The function called when the device is deleted, given the model back.
150    pub(super) delete: Option<DeleteFn<M>>,
151    /// The function giving the state of the device, absent to always give [`DevState::On`].
152    pub(super) state: Option<StateFn<M>>,
153    /// The function giving the status of the device, absent to always give "Device is on".
154    pub(super) status: Option<StatusFn<M>>,
155    /// The commands every device of that class has, which is the usual way of defining
156    /// one: a device is given all of them when it is initialized, and only adds to that
157    /// what its own properties decide, see [`Dev::add_command`].
158    ///
159    /// Behind an [`Arc`] because a definition is fixed once the class is built and shared
160    /// by every device of it, only the state the server keeps around it being per-device.
161    pub(super) commands: Vec<Arc<CommandDef<M>>>,
162    /// The attributes every device of that class has, see [`Self::commands`].
163    pub(super) attrs: Vec<Arc<AttrDef<M>>>,
164    /// The definition of the State attribute every device has, which is read through the
165    /// state function but configured like any other, see [`Self::state_config`].
166    pub(super) state_attr: Arc<AttrDef<M>>,
167    /// The definition of the Status attribute every device has, see [`Self::state_attr`].
168    pub(super) status_attr: Arc<AttrDef<M>>,
169}
170
171/// The function building the model of a device, either synchronous or asynchronous, see
172/// [`ClassDef::new`] and [`ClassDef::new_async`].
173pub enum InitFn<M: Model> {
174    Sync(Box<dyn for<'ctx> Fn(&'ctx Dev<M>) -> Result<M> + Send + Sync>),
175    Async(Box<dyn for<'a> Fn(&'a Dev<M>) -> BoxFuture<'a, Result<M>> + Send + Sync>),
176}
177/// The function called when the model of a device is deleted, see [`ClassDef::delete`],
178/// either synchronous or asynchronous.
179pub enum DeleteFn<M: Model> {
180    Sync(Box<dyn for<'ctx> Fn(M, &'ctx Dev<M>) + Send + Sync>),
181    Async(Box<dyn for<'a> Fn(M, &'a Dev<M>) -> BoxFuture<'a, ()> + Send + Sync>),
182}
183
184/// The state function of a device, with a shared or an exclusive access to the model,
185/// either synchronous or asynchronous.
186///
187/// An asynchronous function borrows the model, the device and, for those that take one,
188/// the argument for as long as its future runs, hence the single lifetime they share.
189pub enum StateFn<M: Model> {
190    SharedSync(Box<dyn for<'m, 'ctx> Fn(&'m M, &'ctx Dev<M>) -> Result<DevState> + Send + Sync>),
191    ExclusiveSync(Box<dyn for<'m, 'ctx> Fn(&'m mut M, &'ctx Dev<M>) -> Result<DevState> + Send + Sync>),
192    SharedAsync(Box<dyn for<'a> Fn(&'a M, &'a Dev<M>) -> BoxFuture<'a, Result<DevState>> + Send + Sync>),
193    ExclusiveAsync(Box<dyn for<'a> Fn(&'a mut M, &'a Dev<M>) -> BoxFuture<'a, Result<DevState>> + Send + Sync>),
194}
195
196/// The status function of a device, see [`StateFn`].
197pub enum StatusFn<M: Model> {
198    SharedSync(Box<dyn for<'m, 'ctx> Fn(&'m M, &'ctx Dev<M>) -> Result<Scalar<str>> + Send + Sync>),
199    ExclusiveSync(Box<dyn for<'m, 'ctx> Fn(&'m mut M, &'ctx Dev<M>) -> Result<Scalar<str>> + Send + Sync>),
200    SharedAsync(Box<dyn for<'a> Fn(&'a M, &'a Dev<M>) -> BoxFuture<'a, Result<Scalar<str>>> + Send + Sync>),
201    ExclusiveAsync(Box<dyn for<'a> Fn(&'a mut M, &'a Dev<M>) -> BoxFuture<'a, Result<Scalar<str>>> + Send + Sync>),
202}
203
204/// An asynchronous function of the model and of the device alone, whose result is given
205/// as-is, which is what the state function is. See [`ClassDef::state_async`].
206///
207/// The model is taken as `R`, being either `&'a M` or `&'a mut M` depending on the access
208/// the function asks for, and the future may borrow from both the model and the device,
209/// which is what an `async fn` method of the model does.
210pub trait HandlerAsync<'a, R, M: Model, O> {
211    /// Call the function, giving the future of its result.
212    fn call(&self, model: R, dev: &'a Dev<M>) -> BoxFuture<'a, Result<O>>;
213}
214
215impl<'a, R, M: Model, O, F, Fut> HandlerAsync<'a, R, M, O> for F
216where
217    F: Fn(R, &'a Dev<M>) -> Fut,
218    Fut: Future<Output = Result<O>> + Send + 'a,
219{
220    #[inline]
221    fn call(&self, model: R, dev: &'a Dev<M>) -> BoxFuture<'a, Result<O>> {
222        Box::pin(self(model, dev))
223    }
224}
225
226/// A status function, see [`ClassDef::status`].
227///
228/// The model is taken as `R`, being either `&M` or `&mut M` depending on the access the
229/// function asks for, and the status is anything that converts into a [`Scalar`], which
230/// may borrow from the model until it is converted, such as a `&str` the model holds.
231pub trait StatusSync<R, M: Model> {
232    /// Call the function and convert the status it gives.
233    fn call(&self, model: R, dev: &Dev<M>) -> Result<Scalar<str>>;
234}
235
236impl<R, M: Model, O, F> StatusSync<R, M> for F
237where
238    F: Fn(R, &Dev<M>) -> Result<O>,
239    O: Into<Scalar<str>>,
240{
241    #[inline]
242    fn call(&self, model: R, dev: &Dev<M>) -> Result<Scalar<str>> {
243        self(model, dev).map(Into::into)
244    }
245}
246
247/// An asynchronous status function, see [`ClassDef::status_async`].
248///
249/// The model is taken as `R`, being either `&'a M` or `&'a mut M`, see [`HandlerAsync`],
250/// and the status may borrow from the model, see [`StatusSync`].
251pub trait StatusAsync<'a, R, M: Model> {
252    /// Give the future of the status, converted.
253    fn call(&self, model: R, dev: &'a Dev<M>) -> BoxFuture<'a, Result<Scalar<str>>>;
254}
255
256impl<'a, R, M: Model, O, F, Fut> StatusAsync<'a, R, M> for F
257where
258    F: Fn(R, &'a Dev<M>) -> Fut,
259    Fut: Future<Output = Result<O>> + Send + 'a,
260    O: Into<Scalar<str>>,
261{
262    #[inline]
263    fn call(&self, model: R, dev: &'a Dev<M>) -> BoxFuture<'a, Result<Scalar<str>>> {
264        let fut = self(model, dev);
265        Box::pin(async move { fut.await.map(Into::into) })
266    }
267}
268
269/// An asynchronous function building the model of a device, see [`ClassDef::new_async`].
270///
271/// The future may borrow the device, which is what an `async fn` taking it does.
272pub trait InitAsync<'a, M: Model> {
273    /// Call the function, giving the future of the model.
274    fn call(&self, dev: &'a Dev<M>) -> BoxFuture<'a, Result<M>>;
275}
276
277impl<'a, M: Model, F, Fut> InitAsync<'a, M> for F
278where
279    F: Fn(&'a Dev<M>) -> Fut,
280    Fut: Future<Output = Result<M>> + Send + 'a,
281{
282    #[inline]
283    fn call(&self, dev: &'a Dev<M>) -> BoxFuture<'a, Result<M>> {
284        Box::pin(self(dev))
285    }
286}
287
288/// An asynchronous function deleting the model of a device, see [`ClassDef::delete_async`].
289///
290/// The future owns the model and may borrow the device, which is what an `async fn` taking
291/// them does.
292pub trait DeleteAsync<'a, M: Model> {
293    /// Call the function, giving the future of the deletion.
294    fn call(&self, model: M, dev: &'a Dev<M>) -> BoxFuture<'a, ()>;
295}
296
297impl<'a, M: Model, F, Fut> DeleteAsync<'a, M> for F
298where
299    F: Fn(M, &'a Dev<M>) -> Fut,
300    Fut: Future<Output = ()> + Send + 'a,
301{
302    #[inline]
303    fn call(&self, model: M, dev: &'a Dev<M>) -> BoxFuture<'a, ()> {
304        Box::pin(self(model, dev))
305    }
306}
307
308impl<M: Model> ClassDef<M> {
309
310    /// Create a new class definition, the given function building the model of each
311    /// device of that class when it is initialized.
312    pub fn new<N, I>(name: N, init: I) -> Self
313    where
314        N: Into<Cow<'static, str>>,
315        I: for<'ctx> Fn(&'ctx Dev<M>) -> Result<M> + Send + Sync + 'static,
316    {
317        Self::with_init(name.into(), InitFn::Sync(Box::new(init)))
318    }
319
320    /// Create a new class definition, the given function building the model of each
321    /// device of that class asynchronously when it is initialized. This is the raw form of
322    /// [`Self::new_async`], for a closure that boxes its future itself.
323    pub fn new_async_with<N, I>(name: N, init: I) -> Self
324    where
325        N: Into<Cow<'static, str>>,
326        I: for<'a> Fn(&'a Dev<M>) -> BoxFuture<'a, Result<M>> + Send + Sync + 'static,
327    {
328        Self::with_init(name.into(), InitFn::Async(Box::new(init)))
329    }
330
331    /// Create a new class definition, the given function building the model of each
332    /// device of that class asynchronously when it is initialized, typically an `async fn`
333    /// taking the device.
334    ///
335    /// The model of the device is locked exclusively until the future completes, as it is
336    /// for a synchronous one. The future is polled by the server itself when nothing else
337    /// of the initialization may block, so it must never block, see [`Self::state_async`].
338    pub fn new_async<N, I>(name: N, init: I) -> Self
339    where
340        N: Into<Cow<'static, str>>,
341        I: for<'a> InitAsync<'a, M> + Send + Sync + 'static,
342    {
343        Self::new_async_with(name, move |dev| init.call(dev))
344    }
345
346    fn with_init(name: Cow<'static, str>, init: InitFn<M>) -> Self {
347        Self {
348            name,
349            doc: Cow::Borrowed("A Tango device"),
350            doc_url: None,
351            init,
352            delete: None,
353            state: None,
354            status: None,
355            commands: Vec::new(),
356            attrs: Vec::new(),
357            state_attr: state_attr(AttrConfig {
358                doc: Some(Cow::Borrowed("The state of the device")),
359                label: Some(Cow::Borrowed(STATE_NAME)),
360                ..AttrConfig::default()
361            }),
362            status_attr: status_attr(AttrConfig {
363                doc: Some(Cow::Borrowed("The status of the device")),
364                label: Some(Cow::Borrowed(STATUS_NAME)),
365                ..AttrConfig::default()
366            }),
367        }
368    }
369
370    /// Create a new class definition whose model is built with its default value.
371    pub fn new_default<N>(name: N) -> Self
372    where
373        M: Default,
374        N: Into<Cow<'static, str>>,
375    {
376        Self::new(name, |_ctx| Ok(M::default()))
377    }
378
379    /// Set the description the devices of that class give of themselves, which is what a
380    /// client asking a device what it is gets back, and what a class defines once for all
381    /// of its devices.
382    pub fn doc<D>(mut self, doc: D) -> Self
383    where
384        D: Into<Cow<'static, str>>,
385    {
386        self.doc = doc.into();
387        self
388    }
389
390    /// Set where the documentation of that class is, which is what a client shows as the
391    /// documentation of every device of it: a page describing what the class does, not
392    /// what one device of it is doing, which is its status.
393    pub fn doc_url<U>(mut self, url: U) -> Self
394    where
395        U: Into<Cow<'static, str>>,
396    {
397        self.doc_url = Some(url.into());
398        self
399    }
400
401    /// Define the function called when the model of a device of that class is deleted,
402    /// which is given the model back so that it may release its resources: before every
403    /// initialization but the first, which is what the Init command of a device is, and
404    /// once the device is no longer served. This is the one place a model releases its
405    /// resources, its [`Drop`] being only relied upon to free memory, see [`Model`].
406    pub fn delete<F>(mut self, delete: F) -> Self
407    where
408        F: for<'ctx> Fn(M, &'ctx Dev<M>) + Send + Sync + 'static,
409    {
410        self.delete = Some(DeleteFn::Sync(Box::new(delete)));
411        self
412    }
413
414    /// Define the function called when the model of a device of that class is deleted,
415    /// asynchronously. This is the raw form of [`Self::delete_async`], for a closure that
416    /// boxes its future itself.
417    pub fn delete_async_with<F>(mut self, delete: F) -> Self
418    where
419        F: for<'a> Fn(M, &'a Dev<M>) -> BoxFuture<'a, ()> + Send + Sync + 'static,
420    {
421        self.delete = Some(DeleteFn::Async(Box::new(delete)));
422        self
423    }
424
425    /// Define the function called when the model of a device of that class is deleted,
426    /// asynchronously, typically an `async fn` taking the model and the device, see
427    /// [`Self::delete`].
428    ///
429    /// The model of the device is locked exclusively until the future completes, as it is
430    /// for a synchronous one. The future is polled by the server itself when nothing else
431    /// of the deletion may block, so it must never block, see [`Self::state_async`].
432    pub fn delete_async<F>(self, delete: F) -> Self
433    where
434        F: for<'a> DeleteAsync<'a, M> + Send + Sync + 'static,
435    {
436        self.delete_async_with(move |model, dev| delete.call(model, dev))
437    }
438
439    /// Define the function giving the state of the device, with a shared access to the
440    /// model, which allows parallel execution of such functions.
441    pub fn state<F>(mut self, state: F) -> Self
442    where
443        F: for<'m, 'ctx> Fn(&'m M, &'ctx Dev<M>) -> Result<DevState> + Send + Sync + 'static,
444    {
445        self.state = Some(StateFn::SharedSync(Box::new(state)));
446        self
447    }
448
449    /// Define the function giving the state of the device, with an exclusive access to
450    /// the model, therefore no other command or attribute can access it in parallel.
451    pub fn state_exclusive<F>(mut self, state: F) -> Self
452    where
453        F: for<'m, 'ctx> Fn(&'m mut M, &'ctx Dev<M>) -> Result<DevState> + Send + Sync + 'static,
454    {
455        self.state = Some(StateFn::ExclusiveSync(Box::new(state)));
456        self
457    }
458
459    /// Define the function giving the state of the device, asynchronously and with a
460    /// shared access to the model. This is the raw form of [`Self::state_async`], for a
461    /// closure that boxes its future itself.
462    pub fn state_async_with<F>(mut self, state: F) -> Self
463    where
464        F: for<'a> Fn(&'a M, &'a Dev<M>) -> BoxFuture<'a, Result<DevState>> + Send + Sync + 'static,
465    {
466        self.state = Some(StateFn::SharedAsync(Box::new(state)));
467        self
468    }
469
470    /// Define the function giving the state of the device, asynchronously and with a
471    /// shared access to the model, typically an `async fn` method of the model.
472    ///
473    /// The model stays borrowed until the future completes, and the future is polled by
474    /// the server itself, so it must never block: a function that has to wait on anything
475    /// else than another future offloads that wait to a thread of its own.
476    pub fn state_async<F>(self, state: F) -> Self
477    where
478        F: for<'a> HandlerAsync<'a, &'a M, M, DevState> + Send + Sync + 'static,
479    {
480        self.state_async_with(move |model, dev| state.call(model, dev))
481    }
482
483    /// Define the function giving the state of the device, asynchronously and with an
484    /// exclusive access to the model. This is the raw form of [`Self::state_exclusive_async`],
485    /// see [`Self::state_async_with`].
486    pub fn state_exclusive_async_with<F>(mut self, state: F) -> Self
487    where
488        F: for<'a> Fn(&'a mut M, &'a Dev<M>) -> BoxFuture<'a, Result<DevState>> + Send + Sync + 'static,
489    {
490        self.state = Some(StateFn::ExclusiveAsync(Box::new(state)));
491        self
492    }
493
494    /// Define the function giving the state of the device, asynchronously and with an
495    /// exclusive access to the model, which no other command or attribute can then access
496    /// until the future completes. See [`Self::state_async`].
497    pub fn state_exclusive_async<F>(self, state: F) -> Self
498    where
499        F: for<'a> HandlerAsync<'a, &'a mut M, M, DevState> + Send + Sync + 'static,
500    {
501        self.state_exclusive_async_with(move |model, dev| state.call(model, dev))
502    }
503
504    /// Define the function giving the status of the device, with a shared access to the
505    /// model. This is the raw form of [`Self::status`], for a closure that gives a
506    /// [`Scalar`] itself.
507    pub fn status_with<F>(mut self, status: F) -> Self
508    where
509        F: for<'m, 'ctx> Fn(&'m M, &'ctx Dev<M>) -> Result<Scalar<str>> + Send + Sync + 'static,
510    {
511        self.status = Some(StatusFn::SharedSync(Box::new(status)));
512        self
513    }
514
515    /// Define the function giving the status of the device, with a shared access to the
516    /// model.
517    ///
518    /// The status is anything that converts into a [`Scalar`], and may borrow from the
519    /// model: it is then written as a function item generic over the model lifetime,
520    /// typically an associated method of the model, which `StatusSync` describes.
521    pub fn status<F>(self, status: F) -> Self
522    where
523        F: for<'m> StatusSync<&'m M, M> + Send + Sync + 'static,
524    {
525        self.status_with(move |model, dev| status.call(model, dev))
526    }
527
528    /// Define the function giving the status of the device, with an exclusive access to
529    /// the model. This is the raw form of [`Self::status_exclusive`], see
530    /// [`Self::status_with`].
531    pub fn status_exclusive_with<F>(mut self, status: F) -> Self
532    where
533        F: for<'m, 'ctx> Fn(&'m mut M, &'ctx Dev<M>) -> Result<Scalar<str>> + Send + Sync + 'static,
534    {
535        self.status = Some(StatusFn::ExclusiveSync(Box::new(status)));
536        self
537    }
538
539    /// Define the function giving the status of the device, with an exclusive access to
540    /// the model, see [`Self::status`].
541    pub fn status_exclusive<F>(self, status: F) -> Self
542    where
543        F: for<'m> StatusSync<&'m mut M, M> + Send + Sync + 'static,
544    {
545        self.status_exclusive_with(move |model, dev| status.call(model, dev))
546    }
547
548    /// Define the function giving the status of the device, asynchronously and with a
549    /// shared access to the model. This is the raw form of [`Self::status_async`], see
550    /// [`Self::state_async_with`].
551    pub fn status_async_with<F>(mut self, status: F) -> Self
552    where
553        F: for<'a> Fn(&'a M, &'a Dev<M>) -> BoxFuture<'a, Result<Scalar<str>>> + Send + Sync + 'static,
554    {
555        self.status = Some(StatusFn::SharedAsync(Box::new(status)));
556        self
557    }
558
559    /// Define the function giving the status of the device, asynchronously and with a
560    /// shared access to the model, see [`Self::state_async`].
561    pub fn status_async<F>(self, status: F) -> Self
562    where
563        F: for<'a> StatusAsync<'a, &'a M, M> + Send + Sync + 'static,
564    {
565        self.status_async_with(move |model, dev| status.call(model, dev))
566    }
567
568    /// Define the function giving the status of the device, asynchronously and with an
569    /// exclusive access to the model. This is the raw form of
570    /// [`Self::status_exclusive_async`], see [`Self::state_async_with`].
571    pub fn status_exclusive_async_with<F>(mut self, status: F) -> Self
572    where
573        F: for<'a> Fn(&'a mut M, &'a Dev<M>) -> BoxFuture<'a, Result<Scalar<str>>> + Send + Sync + 'static,
574    {
575        self.status = Some(StatusFn::ExclusiveAsync(Box::new(status)));
576        self
577    }
578
579    /// Define the function giving the status of the device, asynchronously and with an
580    /// exclusive access to the model, see [`Self::state_exclusive_async`].
581    pub fn status_exclusive_async<F>(self, status: F) -> Self
582    where
583        F: for<'a> StatusAsync<'a, &'a mut M, M> + Send + Sync + 'static,
584    {
585        self.status_exclusive_async_with(move |model, dev| status.call(model, dev))
586    }
587
588    /// Define a command every device of that class has, which is what defining a command
589    /// usually means: the devices of a class have the same interface, and a device only
590    /// adds to its own the part of it that its properties decide, see [`Dev::add_command`].
591    ///
592    /// # Panics
593    ///
594    /// If the class already defines a command of that name, if the name is invalid, see
595    /// [`name::check_tango_command_name`], or if it is one of the commands the server
596    /// answers itself, being `State`, `Status` and `Init`.
597    #[track_caller]
598    pub fn command(mut self, def: CommandDef<M>) -> Self {
599        check_own_name(&self.name, "command", &def.name, name::check_tango_command_name, RESERVED_COMMANDS);
600        if self.commands.iter().any(|other| other.name.eq_ignore_ascii_case(&def.name)) {
601            panic!("{}: the command {} is already defined", self.name, def.name);
602        }
603        self.commands.push(Arc::new(def));
604        self
605    }
606
607    /// Define an attribute every device of that class has, see [`Self::command`].
608    ///
609    /// # Panics
610    ///
611    /// If the class already defines an attribute of that name, if the name is invalid, see
612    /// [`name::check_tango_attr_name`], if it is one of the attributes the server answers
613    /// itself, being `State` and `Status`, or if the definition is inconsistent: memorized
614    /// without being a writable scalar of a kind that can be memorized, see
615    /// [`AttrDef::memorized`], or with a configuration giving a value to a property its kind
616    /// does not support, see [`AttrConfig`].
617    #[track_caller]
618    pub fn attr(mut self, def: AttrDef<M>) -> Self {
619        check_own_name(&self.name, "attribute", &def.name, name::check_tango_attr_name, RESERVED_ATTRS);
620        if let Err(err) = def.check() {
621            panic!("{}: {err}", self.name);
622        }
623        if self.attrs.iter().any(|other| other.name.eq_ignore_ascii_case(&def.name)) {
624            panic!("{}: the attribute {} is already defined", self.name, def.name);
625        }
626        self.attrs.push(Arc::new(def));
627        self
628    }
629
630    /// Set the configuration the State attribute of every device of that class starts
631    /// from, which the database and clients then override like the one of any attribute.
632    /// The default one only gives it a label and a description.
633    ///
634    /// # Panics
635    ///
636    /// If the configuration gives a value to a property a state does not support, such as
637    /// a bound or an event criterion, see [`AttrConfig`].
638    #[track_caller]
639    pub fn state_config(mut self, config: AttrConfig) -> Self {
640        self.state_attr = state_attr(config);
641        self
642    }
643
644    /// Set the configuration the Status attribute of every device of that class starts
645    /// from, see [`Self::state_config`].
646    ///
647    /// # Panics
648    ///
649    /// If the configuration gives a value to a property a string does not support, see
650    /// [`Self::state_config`].
651    #[track_caller]
652    pub fn status_config(mut self, config: AttrConfig) -> Self {
653        self.status_attr = status_attr(config);
654        self
655    }
656
657    /// Declare that the devices of that class push the events of their State themselves,
658    /// change and archive, see [`AttrDef::push_change_event`]. For each, `Some(detect)`
659    /// declares the event pushed by the device, `detect` telling whether its criteria are
660    /// checked against what is pushed, and `None` declares it not pushed.
661    pub fn push_state_events(mut self, change: Option<bool>, archive: Option<bool>) -> Self {
662        builtin_pushed(&mut self.state_attr, change, archive);
663        self
664    }
665
666    /// Declare that the devices of that class push the events of their Status themselves,
667    /// see [`Self::push_state_events`].
668    pub fn push_status_events(mut self, change: Option<bool>, archive: Option<bool>) -> Self {
669        builtin_pushed(&mut self.status_attr, change, archive);
670        self
671    }
672
673}
674
675/// The definition of the State attribute, with that configuration.
676#[track_caller]
677fn state_attr<M: Model>(config: AttrConfig) -> Arc<AttrDef<M>> {
678    builtin_attr(STATE_NAME, AttrDataKind::State, config)
679}
680
681/// The definition of the Status attribute, with that configuration.
682#[track_caller]
683fn status_attr<M: Model>(config: AttrConfig) -> Arc<AttrDef<M>> {
684    builtin_attr(STATUS_NAME, AttrDataKind::String, config)
685}
686
687/// Declare the events pushed for one of the attributes the server answers itself, whose
688/// definition only its class holds while it is built.
689fn builtin_pushed<M: Model>(def: &mut Arc<AttrDef<M>>, change: Option<bool>, archive: Option<bool>) {
690    let def = Arc::get_mut(def).expect("a class being built is the only owner of its definitions");
691    def.pushed.change = change;
692    def.pushed.archive = archive;
693}
694
695/// The definition of one of the attributes the server answers itself, a read-only scalar
696/// of that kind with that configuration.
697#[track_caller]
698fn builtin_attr<M: Model>(name: &'static str, kind: AttrDataKind, config: AttrConfig) -> Arc<AttrDef<M>> {
699    let mut def = AttrDef::new(name).kind(kind).scalar();
700    def.display_level = DisplayLevel::Operator;
701    def.config = config;
702    if let Err(err) = def.check() {
703        panic!("{err}");
704    }
705    Arc::new(def)
706}
707
708/// Refuse a name that the given check finds invalid, or that belongs to the server rather
709/// than to the class: the state, the status and the initialization of a device are
710/// answered by the server itself, whatever its class, so a definition of that name would
711/// never be reached and is a mistake worth pointing out where it is written rather than
712/// leaving to be found at runtime.
713#[track_caller]
714fn check_own_name(class: &str, what: &str, name: &str, valid: fn(&str) -> bool, reserved: &[&str]) {
715    if !valid(name) {
716        panic!("{class}: the {what} name {name:?} is invalid");
717    }
718    if let Some(reserved) = reserved_name(name, reserved) {
719        panic!("{class}: the {what} {name} cannot be defined by a class, \
720            {reserved} is answered by the server itself");
721    }
722}
723
724impl<M: Model> Debug for ClassDef<M> {
725    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
726        f.debug_struct("ClassDef")
727            .field("name", &self.name)
728            .field("doc", &self.doc)
729            .field("doc_url", &self.doc_url)
730            .field("init", &HandlerDebug)
731            .field("delete", &self.delete.as_ref().map(|_| HandlerDebug))
732            .field("state", &self.state)
733            .field("status", &self.status)
734            .field("commands", &self.commands)
735            .field("attrs", &self.attrs)
736            .field("state_attr", &self.state_attr)
737            .field("status_attr", &self.status_attr)
738            .finish()
739    }
740}
741
742/// A builder for defining a command of a device.
743pub struct CommandDef<M: Model> {
744    /// The name of the command.
745    pub(super) name: Cow<'static, str>,
746    /// The function executing the command.
747    pub(super) exec: Option<ExecFn<M>>,
748    /// The advertised kind of the argument, derived from the execution function, absent
749    /// until it is defined and then defaulting to [`CommandDataKind::Void`].
750    pub(super) in_kind: Option<CommandDataKind>,
751    /// The documentation of the argument.
752    pub(super) in_doc: Cow<'static, str>,
753    /// The advertised kind of the result, derived from the execution function, absent
754    /// until it is defined and then defaulting to [`CommandDataKind::Void`].
755    pub(super) out_kind: Option<CommandDataKind>,
756    /// The documentation of the result.
757    pub(super) out_doc: Cow<'static, str>,
758    /// The display level of the command, which is all a command has of a configuration:
759    /// unlike an attribute, nothing about it can be changed once the class is defined.
760    pub(super) display_level: DisplayLevel,
761    /// The polling period this command asks for, absent for one that is not polled unless
762    /// the database says otherwise.
763    pub(super) poll_period: Option<Duration>,
764}
765
766/// The execution function of a command, with a shared or an exclusive access to the model,
767/// either synchronous or asynchronous, see [`StateFn`].
768pub enum ExecFn<M: Model> {
769    SharedSync(Box<dyn Fn(&M, &Dev<M>, Value) -> Result<Value> + Send + Sync>),
770    ExclusiveSync(Box<dyn Fn(&mut M, &Dev<M>, Value) -> Result<Value> + Send + Sync>),
771    SharedAsync(Box<dyn for<'a> Fn(&'a M, &'a Dev<M>, Value) -> BoxFuture<'a, Result<Value>> + Send + Sync>),
772    ExclusiveAsync(Box<dyn for<'a> Fn(&'a mut M, &'a Dev<M>, Value) -> BoxFuture<'a, Result<Value>> + Send + Sync>),
773}
774
775/// An execution function of a command with a shared access to the model, whose result may
776/// borrow from it until it is converted into a value. See [`CommandDef::exec`].
777pub trait ExecShared<'m, M: Model, I> {
778    /// The advertised kind of the argument, derived from its type.
779    const IN_KIND: CommandDataKind;
780    /// The advertised kind of the result, derived from its type.
781    const OUT_KIND: CommandDataKind;
782    /// Convert the given argument, execute the command and convert its result.
783    fn exec(&self, model: &'m M, dev: &Dev<M>, input: Value) -> Result<Value>;
784}
785
786impl<'m, M: Model, I, O, F> ExecShared<'m, M, I> for F
787where
788    I: FromValue + CommandData,
789    O: IntoValue + CommandData,
790    F: Fn(&'m M, &Dev<M>, I) -> Result<O>,
791{
792
793    const IN_KIND: CommandDataKind = I::DATA_KIND;
794    const OUT_KIND: CommandDataKind = O::DATA_KIND;
795
796    #[inline]
797    fn exec(&self, model: &'m M, dev: &Dev<M>, input: Value) -> Result<Value> {
798        Ok(self(model, dev, I::from_value(input)?)?.into_value())
799    }
800    
801}
802
803/// The exclusive counterpart of [`ExecShared`], see [`CommandDef::exec_exclusive`].
804pub trait ExecExclusive<'m, M: Model, I> {
805    /// The advertised kind of the argument, derived from its type.
806    const IN_KIND: CommandDataKind;
807    /// The advertised kind of the result, derived from its type.
808    const OUT_KIND: CommandDataKind;
809    /// Convert the given argument, execute the command and convert its result.
810    fn exec(&self, model: &'m mut M, dev: &Dev<M>, input: Value) -> Result<Value>;
811}
812
813impl<'m, M: Model, I, O, F> ExecExclusive<'m, M, I> for F
814where
815    I: FromValue + CommandData,
816    O: IntoValue + CommandData,
817    F: Fn(&'m mut M, &Dev<M>, I) -> Result<O>,
818{
819
820    const IN_KIND: CommandDataKind = I::DATA_KIND;
821    const OUT_KIND: CommandDataKind = O::DATA_KIND;
822
823    #[inline]
824    fn exec(&self, model: &'m mut M, dev: &Dev<M>, input: Value) -> Result<Value> {
825        Ok(self(model, dev, I::from_value(input)?)?.into_value())
826    }
827
828}
829
830/// An asynchronous execution function of a command, see [`CommandDef::exec_async`].
831///
832/// The model is taken as `R`, being either `&'a M` or `&'a mut M`, see [`HandlerAsync`],
833/// and the future may borrow from the model and the device, its result being allowed to
834/// borrow from both until it is converted into a value.
835pub trait ExecAsync<'a, R, M: Model, I> {
836    /// The advertised kind of the argument, derived from its type.
837    const IN_KIND: CommandDataKind;
838    /// The advertised kind of the result, derived from its type.
839    const OUT_KIND: CommandDataKind;
840    /// Convert the given argument and give the future executing the command and
841    /// converting its result.
842    fn exec(&self, model: R, dev: &'a Dev<M>, input: Value) -> BoxFuture<'a, Result<Value>>;
843}
844
845impl<'a, R, M: Model, I, O, F, Fut> ExecAsync<'a, R, M, I> for F
846where
847    I: FromValue + CommandData,
848    O: IntoValue + CommandData,
849    F: Fn(R, &'a Dev<M>, I) -> Fut,
850    Fut: Future<Output = Result<O>> + Send + 'a,
851{
852
853    const IN_KIND: CommandDataKind = I::DATA_KIND;
854    const OUT_KIND: CommandDataKind = O::DATA_KIND;
855
856    #[inline]
857    fn exec(&self, model: R, dev: &'a Dev<M>, input: Value) -> BoxFuture<'a, Result<Value>> {
858        // The function is called right away rather than on the first poll, so that
859        // whatever it starts before its first await is started with the request.
860        let fut = I::from_value(input).map(|input| self(model, dev, input));
861        Box::pin(async move { Ok(fut?.await?.into_value()) })
862    }
863
864}
865
866impl<M: Model> CommandDef<M> {
867
868    /// Create a new command definition, without any execution function.
869    pub fn new<N>(name: N) -> Self
870    where
871        N: Into<Cow<'static, str>>,
872    {
873        Self {
874            name: name.into(),
875            exec: None,
876            in_kind: None,
877            in_doc: Cow::Borrowed(""),
878            out_kind: None,
879            out_doc: Cow::Borrowed(""),
880            display_level: DisplayLevel::default(),
881            poll_period: None,
882        }
883    }
884
885    /// Set the documentation of the command argument (argin).
886    ///
887    /// A command has no description of its own, Tango only telling a client what each
888    /// side of it is, so what the command does has to be said in one of these two.
889    pub fn in_doc<D>(mut self, doc: D) -> Self
890    where
891        D: Into<Cow<'static, str>>,
892    {
893        self.in_doc = doc.into();
894        self
895    }
896
897    /// Set the documentation of the command result (argout).
898    pub fn out_doc<D>(mut self, doc: D) -> Self
899    where
900        D: Into<Cow<'static, str>>,
901    {
902        self.out_doc = doc.into();
903        self
904    }
905
906    /// Set the display level of the command, [`DisplayLevel::Expert`] keeping it out of
907    /// the panels an operator is shown without keeping it from anyone who asks for it.
908    pub fn display_level(mut self, level: DisplayLevel) -> Self {
909        self.display_level = level;
910        self
911    }
912
913    /// Ask for the command to be polled at that period, its result being recorded in its
914    /// history, which a request from the cache is answered with, see [`super::Command`].
915    ///
916    /// Only a command that takes no argument can be polled, there being no argument for
917    /// the server to invent.
918    ///
919    /// This period comes second: a command the `polled_cmd` property of the device lists
920    /// is polled at the period the property gives, and one it does not list at this one.
921    /// The admin device changes it while the device runs, writing the property along, but
922    /// a command removed from the polling this way is polled at this period again from the
923    /// next initialization of the device, the property having no way to say it is not.
924    pub fn polled(mut self, period: Duration) -> Self {
925        self.poll_period = Some(period);
926        self
927    }
928
929    /// Set the advertised kind of the command argument (argin). This is only needed for
930    /// the execution functions that take a raw [`Value`] argument, the typed ones derive
931    /// it from their input type.
932    ///
933    /// # Panics
934    ///
935    /// If the argument kind has already been defined, explicitly or by an execution
936    /// function, as a different kind.
937    #[track_caller]
938    pub fn in_kind(mut self, kind: CommandDataKind) -> Self {
939        self.in_kind = Some(merge_kind(&self.name, "argument kind", self.in_kind.take(), kind));
940        self
941    }
942
943    /// Set the advertised kind of the command result (argout). This is only needed for
944    /// the execution functions that return a raw [`Value`], the typed ones derive it from
945    /// their output type.
946    ///
947    /// # Panics
948    ///
949    /// If the result kind has already been defined, explicitly or by an execution
950    /// function, as a different kind.
951    #[track_caller]
952    pub fn out_kind(mut self, kind: CommandDataKind) -> Self {
953        self.out_kind = Some(merge_kind(&self.name, "result kind", self.out_kind.take(), kind));
954        self
955    }
956
957    /// Define the execution function of this command, with a shared access to the model.
958    ///
959    /// This is the raw form of [`Self::exec`]: the closure is given the argument
960    /// [`Value`] as-is and returns one, converting what it borrows from the model itself,
961    /// which is what makes a closure usable for such a result. Because no input and output
962    /// types are involved, the advertised kinds have to be given with [`Self::in_kind`]
963    /// and [`Self::out_kind`].
964    pub fn exec_with<F>(mut self, func: F) -> Self
965    where
966        F: Fn(&M, &Dev<M>, Value) -> Result<Value> + Send + Sync + 'static,
967    {
968        self.exec = Some(ExecFn::SharedSync(Box::new(func)));
969        self
970    }
971
972    /// Define the execution function of this command, with a shared access to the model,
973    /// which allows parallel execution of such functions.
974    ///
975    /// The argument and result types are taken from the `ExecShared` implementation of
976    /// the function. The result may borrow from the model, being converted into a value
977    /// before the model is released, which requires a function item generic over the
978    /// model lifetime, typically an associated method of the model.
979    ///
980    /// # Panics
981    ///
982    /// If either advertised kind has already been defined as a different kind, see
983    /// [`Self::in_kind`] and [`Self::out_kind`].
984    #[track_caller]
985    pub fn exec<F, I>(self, func: F) -> Self
986    where
987        F: for<'m> ExecShared<'m, M, I> + Send + Sync + 'static,
988    {
989        self.in_kind(F::IN_KIND)
990            .out_kind(F::OUT_KIND)
991            .exec_with(move |model, dev, value| func.exec(model, dev, value))
992    }
993
994    /// Define the execution function of this command, with an exclusive access to the
995    /// model. This is the raw form of [`Self::exec_exclusive`], see [`Self::exec_with`].
996    pub fn exec_exclusive_with<F>(mut self, func: F) -> Self
997    where
998        F: Fn(&mut M, &Dev<M>, Value) -> Result<Value> + Send + Sync + 'static,
999    {
1000        self.exec = Some(ExecFn::ExclusiveSync(Box::new(func)));
1001        self
1002    }
1003
1004    /// Define the execution function of this command, with an exclusive access to the
1005    /// model, therefore no other command or attribute can access it in parallel.
1006    ///
1007    /// See [`Self::exec`] for the constraints on the argument and result types, this one
1008    /// is only different in that its function item takes the model by exclusive reference.
1009    ///
1010    /// # Panics
1011    ///
1012    /// If either advertised kind has already been defined as a different kind, see
1013    /// [`Self::in_kind`] and [`Self::out_kind`].
1014    #[track_caller]
1015    pub fn exec_exclusive<F, I>(self, func: F) -> Self
1016    where
1017        F: for<'m> ExecExclusive<'m, M, I> + Send + Sync + 'static,
1018    {
1019        self.in_kind(F::IN_KIND)
1020            .out_kind(F::OUT_KIND)
1021            .exec_exclusive_with(move |model, dev, value| func.exec(model, dev, value))
1022    }
1023
1024    /// Define the execution function of this command, asynchronously and with a shared
1025    /// access to the model. This is the raw form of [`Self::exec_async`], for a closure
1026    /// that boxes its future itself, see [`Self::exec_with`].
1027    pub fn exec_async_with<F>(mut self, func: F) -> Self
1028    where
1029        F: for<'a> Fn(&'a M, &'a Dev<M>, Value) -> BoxFuture<'a, Result<Value>> + Send + Sync + 'static,
1030    {
1031        self.exec = Some(ExecFn::SharedAsync(Box::new(func)));
1032        self
1033    }
1034
1035    /// Define the execution function of this command, asynchronously and with a shared
1036    /// access to the model, typically an `async fn` method of the model.
1037    ///
1038    /// This is what a command forwarding its execution to other devices is written as:
1039    /// it holds no thread while it waits on them, and may wait on many of them at once.
1040    /// The future is polled by the server itself, so it must never block, see
1041    /// [`ClassDef::state_async`].
1042    ///
1043    /// # Panics
1044    ///
1045    /// If either advertised kind has already been defined as a different kind, see
1046    /// [`Self::in_kind`] and [`Self::out_kind`].
1047    #[track_caller]
1048    pub fn exec_async<F, I>(self, func: F) -> Self
1049    where
1050        F: for<'a> ExecAsync<'a, &'a M, M, I> + Send + Sync + 'static,
1051    {
1052        self.in_kind(F::IN_KIND)
1053            .out_kind(F::OUT_KIND)
1054            .exec_async_with(move |model, dev, value| func.exec(model, dev, value))
1055    }
1056
1057    /// Define the execution function of this command, asynchronously and with an
1058    /// exclusive access to the model. This is the raw form of [`Self::exec_exclusive_async`],
1059    /// see [`Self::exec_async_with`].
1060    pub fn exec_exclusive_async_with<F>(mut self, func: F) -> Self
1061    where
1062        F: for<'a> Fn(&'a mut M, &'a Dev<M>, Value) -> BoxFuture<'a, Result<Value>> + Send + Sync + 'static,
1063    {
1064        self.exec = Some(ExecFn::ExclusiveAsync(Box::new(func)));
1065        self
1066    }
1067
1068    /// Define the execution function of this command, asynchronously and with an
1069    /// exclusive access to the model, which no other command or attribute can then access
1070    /// until the future completes. See [`Self::exec_async`].
1071    ///
1072    /// # Panics
1073    ///
1074    /// If either advertised kind has already been defined as a different kind, see
1075    /// [`Self::in_kind`] and [`Self::out_kind`].
1076    #[track_caller]
1077    pub fn exec_exclusive_async<F, I>(self, func: F) -> Self
1078    where
1079        F: for<'a> ExecAsync<'a, &'a mut M, M, I> + Send + Sync + 'static,
1080    {
1081        self.in_kind(F::IN_KIND)
1082            .out_kind(F::OUT_KIND)
1083            .exec_exclusive_async_with(move |model, dev, value| func.exec(model, dev, value))
1084    }
1085
1086}
1087
1088impl<M: Model> Debug for CommandDef<M> {
1089    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1090        f.debug_struct("CommandDef")
1091            .field("name", &self.name)
1092            .field("exec", &self.exec)
1093            .field("in_kind", &self.in_kind)
1094            .field("in_doc", &self.in_doc)
1095            .field("out_kind", &self.out_kind)
1096            .field("out_doc", &self.out_doc)
1097            .field("display_level", &self.display_level)
1098            .field("poll_period", &self.poll_period)
1099            .finish()
1100    }
1101}
1102
1103/// A builder for defining an attribute of a device.
1104pub struct AttrDef<M: Model> {
1105    /// The name of the attribute.
1106    pub(super) name: Cow<'static, str>,
1107    /// The function reading the attribute value, absent for a write-only attribute.
1108    pub(super) read: Option<ReadFn<M>>,
1109    /// The function writing the attribute value, absent for a read-only attribute.
1110    pub(super) write: Option<WriteFn<M>>,
1111    /// The advertised data kind, derived from the read and write functions.
1112    pub(super) kind: Option<AttrDataKind>,
1113    /// The advertised data format, derived from the read and write functions.
1114    pub(super) format: Option<AttrDataFormat>,
1115    /// The display level the attribute is shown at, which the class declares and a client
1116    /// cannot change, unlike the configuration below.
1117    pub(super) display_level: DisplayLevel,
1118    /// What the attribute does with the value of a write when the device is initialized
1119    /// again, absent for one that remembers nothing, which the class declares as well.
1120    pub(super) memorized: Option<AttrMemorized>,
1121    /// Everything a client may read about the attribute and, for most of it, change: its
1122    /// documentation, its units, its bounds and its event criteria. Every device of the
1123    /// class starts from this one and owns its copy from then on, because the database
1124    /// overrides it per device and a client changes it per device.
1125    pub(super) config: AttrConfig,
1126    /// The polling period this attribute asks for, absent for one that is not polled
1127    /// unless the database says otherwise.
1128    pub(super) poll_period: Option<Duration>,
1129    /// The events the device pushes itself for this attribute, see
1130    /// [`Self::push_change_event`].
1131    pub(super) pushed: PushedEvents,
1132}
1133
1134/// The events a device declares it pushes itself for an attribute, each with whether the
1135/// criteria of its configuration are checked against what is pushed, see
1136/// [`AttrDef::push_change_event`], and whether it announces it ready to be read, see
1137/// [`AttrDef::push_data_ready_event`].
1138#[derive(Debug, Clone, Copy, Default)]
1139pub(super) struct PushedEvents {
1140    pub change: Option<bool>,
1141    pub archive: Option<bool>,
1142    pub data_ready: bool,
1143}
1144
1145/// The read function of an attribute, with a shared or an exclusive access to the model,
1146/// either synchronous or asynchronous, see [`StateFn`].
1147pub enum ReadFn<M: Model> {
1148    SharedSync(Box<dyn Fn(&M, &Dev<M>) -> Result<AttrRead<Value>> + Send + Sync>),
1149    ExclusiveSync(Box<dyn Fn(&mut M, &Dev<M>) -> Result<AttrRead<Value>> + Send + Sync>),
1150    SharedAsync(Box<dyn for<'a> Fn(&'a M, &'a Dev<M>) -> BoxFuture<'a, Result<AttrRead<Value>>> + Send + Sync>),
1151    ExclusiveAsync(Box<dyn for<'a> Fn(&'a mut M, &'a Dev<M>) -> BoxFuture<'a, Result<AttrRead<Value>>> + Send + Sync>),
1152}
1153
1154/// The write function of an attribute, with a shared or an exclusive access to the model,
1155/// either synchronous or asynchronous, see [`StateFn`].
1156pub enum WriteFn<M: Model> {
1157    SharedSync(Box<dyn Fn(&M, &Dev<M>, Value) -> Result<()> + Send + Sync>),
1158    ExclusiveSync(Box<dyn Fn(&mut M, &Dev<M>, Value) -> Result<()> + Send + Sync>),
1159    SharedAsync(Box<dyn for<'a> Fn(&'a M, &'a Dev<M>, Value) -> BoxFuture<'a, Result<()>> + Send + Sync>),
1160    ExclusiveAsync(Box<dyn for<'a> Fn(&'a mut M, &'a Dev<M>, Value) -> BoxFuture<'a, Result<()>> + Send + Sync>),
1161}
1162
1163/// A trait for abstracting what output types are accepted by read attribute methods: a
1164/// value alone, its quality and timestamp then being implicit, or a whole [`AttrRead`].
1165pub trait ReadOutput {
1166    /// The advertised kind of the value, derived from its type.
1167    const DATA_KIND: AttrDataKind;
1168    /// The advertised format of the value, without any maximum dimension.
1169    const DATA_FORMAT: AttrDataFormat;
1170    /// Convert this output into a read result, an absent write value asking the server
1171    /// to fill it with the latest one it has written.
1172    fn into_read(self) -> AttrRead<Value>;
1173}
1174
1175impl<O> ReadOutput for O
1176where
1177    O: IntoValue + AttrData,
1178{
1179    
1180    const DATA_KIND: AttrDataKind = O::DATA_KIND;
1181    const DATA_FORMAT: AttrDataFormat = O::DATA_FORMAT;
1182
1183    fn into_read(self) -> AttrRead<Value> {
1184        AttrRead {
1185            value: Some(self.into_value()),
1186            // An absent write value asks the server to fill it with the latest one it
1187            // has written, which is the usual behaviour of a read function.
1188            w_value: None,
1189            quality: AttrQuality::Valid,
1190            time: SystemTime::now(),
1191        }
1192    }
1193    
1194}
1195
1196impl<O> ReadOutput for AttrRead<O>
1197where
1198    O: IntoValue + AttrData,
1199{
1200
1201    const DATA_KIND: AttrDataKind = O::DATA_KIND;
1202    const DATA_FORMAT: AttrDataFormat = O::DATA_FORMAT;
1203
1204    fn into_read(self) -> AttrRead<Value> {
1205        AttrRead {
1206            value: self.value.map(IntoValue::into_value),
1207            // A write value given by the read function replaces the one the server keeps,
1208            // so a next read that gives none answers this one.
1209            w_value: self.w_value.map(IntoValue::into_value),
1210            quality: self.quality,
1211            time: self.time,
1212        }
1213    }
1214
1215}
1216
1217/// A read function with a shared access to the model, whose result may borrow from it
1218/// until it is converted into a value. See [`AttrDef::read`], the same constraints as
1219/// [`ExecShared`] apply to the result type.
1220pub trait ReadShared<'m, M: Model> {
1221    /// The advertised kind of the value, derived from its type.
1222    const DATA_KIND: AttrDataKind;
1223    /// The advertised format of the value, without any maximum dimension.
1224    const DATA_FORMAT: AttrDataFormat;
1225    /// Read the value of the attribute and convert it.
1226    fn read(&self, model: &'m M, dev: &Dev<M>) -> Result<AttrRead<Value>>;
1227}
1228
1229impl<'m, M: Model, O, F> ReadShared<'m, M> for F
1230where
1231    F: Fn(&'m M, &Dev<M>) -> Result<O>,
1232    O: ReadOutput,
1233{
1234    const DATA_KIND: AttrDataKind = O::DATA_KIND;
1235    const DATA_FORMAT: AttrDataFormat = O::DATA_FORMAT;
1236
1237    #[inline]
1238    fn read(&self, model: &'m M, dev: &Dev<M>) -> Result<AttrRead<Value>> {
1239        Ok(self(model, dev)?.into_read())
1240    }
1241}
1242
1243/// The exclusive counterpart of [`ReadShared`], see [`AttrDef::read_exclusive`].
1244pub trait ReadExclusive<'m, M: Model> {
1245    /// The advertised kind of the value, derived from its type.
1246    const DATA_KIND: AttrDataKind;
1247    /// The advertised format of the value, without any maximum dimension.
1248    const DATA_FORMAT: AttrDataFormat;
1249    /// Read the value of the attribute and convert it.
1250    fn read(&self, model: &'m mut M, dev: &Dev<M>) -> Result<AttrRead<Value>>;
1251}
1252
1253impl<'m, M: Model, O, F> ReadExclusive<'m, M> for F
1254where
1255    F: Fn(&'m mut M, &Dev<M>) -> Result<O>,
1256    O: ReadOutput,
1257{
1258    const DATA_KIND: AttrDataKind = O::DATA_KIND;
1259    const DATA_FORMAT: AttrDataFormat = O::DATA_FORMAT;
1260
1261    #[inline]
1262    fn read(&self, model: &'m mut M, dev: &Dev<M>) -> Result<AttrRead<Value>> {
1263        Ok(self(model, dev)?.into_read())
1264    }
1265}
1266
1267/// A write function with a shared access to the model. See [`AttrDef::write`].
1268pub trait WriteShared<M: Model, I> {
1269    /// The advertised kind of the value, derived from its type.
1270    const DATA_KIND: AttrDataKind;
1271    /// The advertised format of the value, without any maximum dimension.
1272    const DATA_FORMAT: AttrDataFormat;
1273    /// Convert the given value and write it to the attribute.
1274    fn write(&self, model: &M, dev: &Dev<M>, value: Value) -> Result<()>;
1275}
1276
1277impl<M: Model, I, F> WriteShared<M, I> for F
1278where
1279    F: Fn(&M, &Dev<M>, I) -> Result<()>,
1280    I: FromValue + AttrData,
1281{
1282    const DATA_KIND: AttrDataKind = I::DATA_KIND;
1283    const DATA_FORMAT: AttrDataFormat = I::DATA_FORMAT;
1284
1285    #[inline]
1286    fn write(&self, model: &M, dev: &Dev<M>, value: Value) -> Result<()> {
1287        self(model, dev, I::from_value(value)?)
1288    }
1289}
1290
1291/// The exclusive counterpart of [`WriteShared`], see [`AttrDef::write_exclusive`].
1292pub trait WriteExclusive<M: Model, I> {
1293    /// The advertised kind of the value, derived from its type.
1294    const DATA_KIND: AttrDataKind;
1295    /// The advertised format of the value, without any maximum dimension.
1296    const DATA_FORMAT: AttrDataFormat;
1297    /// Convert the given value and write it to the attribute.
1298    fn write(&self, model: &mut M, dev: &Dev<M>, value: Value) -> Result<()>;
1299}
1300
1301impl<M: Model, I, F> WriteExclusive<M, I> for F
1302where
1303    F: Fn(&mut M, &Dev<M>, I) -> Result<()>,
1304    I: FromValue + AttrData,
1305{
1306    const DATA_KIND: AttrDataKind = I::DATA_KIND;
1307    const DATA_FORMAT: AttrDataFormat = I::DATA_FORMAT;
1308
1309    #[inline]
1310    fn write(&self, model: &mut M, dev: &Dev<M>, value: Value) -> Result<()> {
1311        self(model, dev, I::from_value(value)?)
1312    }
1313}
1314
1315/// An asynchronous read function of an attribute, see [`AttrDef::read_async`].
1316///
1317/// The model is taken as `R`, being either `&'a M` or `&'a mut M`, see [`HandlerAsync`],
1318/// and the value may borrow from the model, with the same result types as [`ReadShared`].
1319pub trait ReadAsync<'a, R, M: Model> {
1320    /// The advertised kind of the value, derived from its type.
1321    const DATA_KIND: AttrDataKind;
1322    /// The advertised format of the value, without any maximum dimension.
1323    const DATA_FORMAT: AttrDataFormat;
1324    /// Give the future reading the value of the attribute and converting it.
1325    fn read(&self, model: R, dev: &'a Dev<M>) -> BoxFuture<'a, Result<AttrRead<Value>>>;
1326}
1327
1328impl<'a, R, M: Model, O, F, Fut> ReadAsync<'a, R, M> for F
1329where
1330    F: Fn(R, &'a Dev<M>) -> Fut,
1331    Fut: Future<Output = Result<O>> + Send + 'a,
1332    O: ReadOutput,
1333{
1334    const DATA_KIND: AttrDataKind = O::DATA_KIND;
1335    const DATA_FORMAT: AttrDataFormat = O::DATA_FORMAT;
1336
1337    #[inline]
1338    fn read(&self, model: R, dev: &'a Dev<M>) -> BoxFuture<'a, Result<AttrRead<Value>>> {
1339        let fut = self(model, dev);
1340        Box::pin(async move { Ok(fut.await?.into_read()) })
1341    }
1342}
1343
1344/// An asynchronous write function of an attribute, see [`AttrDef::write_async`].
1345///
1346/// The model is taken as `R`, being either `&'a M` or `&'a mut M`, see [`HandlerAsync`].
1347pub trait WriteAsync<'a, R, M: Model, I> {
1348    /// The advertised kind of the value, derived from its type.
1349    const DATA_KIND: AttrDataKind;
1350    /// The advertised format of the value, without any maximum dimension.
1351    const DATA_FORMAT: AttrDataFormat;
1352    /// Convert the given value and give the future writing it to the attribute.
1353    fn write(&self, model: R, dev: &'a Dev<M>, value: Value) -> BoxFuture<'a, Result<()>>;
1354}
1355
1356impl<'a, R, M: Model, I, F, Fut> WriteAsync<'a, R, M, I> for F
1357where
1358    F: Fn(R, &'a Dev<M>, I) -> Fut,
1359    Fut: Future<Output = Result<()>> + Send + 'a,
1360    I: FromValue + AttrData,
1361{
1362    const DATA_KIND: AttrDataKind = I::DATA_KIND;
1363    const DATA_FORMAT: AttrDataFormat = I::DATA_FORMAT;
1364
1365    #[inline]
1366    fn write(&self, model: R, dev: &'a Dev<M>, value: Value) -> BoxFuture<'a, Result<()>> {
1367        let fut = I::from_value(value).map(|input| self(model, dev, input));
1368        Box::pin(async move { fut?.await })
1369    }
1370}
1371
1372impl<M: Model> AttrDef<M> {
1373
1374    /// Create a new attribute definition, without any read or write function.
1375    pub fn new<N>(name: N) -> Self
1376    where
1377        N: Into<Cow<'static, str>>,
1378    {
1379        Self {
1380            name: name.into(),
1381            read: None,
1382            write: None,
1383            kind: None,
1384            format: None,
1385            display_level: DisplayLevel::default(),
1386            memorized: None,
1387            config: AttrConfig::default(),
1388            poll_period: None,
1389            pushed: PushedEvents::default(),
1390        }
1391    }
1392
1393    // --- Documentation --- //
1394
1395    /// Set the description of the attribute, which every device of the class starts from;
1396    /// like the rest of its configuration, the database and clients may override it per
1397    /// device.
1398    pub fn doc<D>(mut self, doc: D) -> Self
1399    where
1400        D: Into<Cow<'static, str>>,
1401    {
1402        self.config.doc = Some(doc.into());
1403        self
1404    }
1405
1406    /// Set the label of the attribute, the short name a client shows in place of its own,
1407    /// where "Position" reads better than "position" and "Δθ" than "delta_theta".
1408    pub fn label<L>(mut self, label: L) -> Self
1409    where
1410        L: Into<Cow<'static, str>>,
1411    {
1412        self.config.label = Some(label.into());
1413        self
1414    }
1415
1416    /// Set the unit the value is expressed in, as it is shown next to it.
1417    pub fn unit<U>(mut self, unit: U) -> Self
1418    where
1419        U: Into<Cow<'static, str>>,
1420    {
1421        self.config.unit = Some(unit.into());
1422        self
1423    }
1424
1425    /// Set the factor going from the value of the attribute to the same quantity in the
1426    /// international system, so that a client can convert without knowing the unit: 1e-3
1427    /// for a value in millimetres, the metre being the standard unit of a length.
1428    pub fn standard_unit(mut self, factor: f64) -> Self {
1429        self.config.standard_unit = Some(factor);
1430        self
1431    }
1432
1433    /// Set the unit the value is *displayed* in, when it is not the one it is expressed
1434    /// in: a value in metres a client should show in millimetres.
1435    pub fn display_unit<U>(mut self, unit: U) -> Self
1436    where
1437        U: Into<Cow<'static, str>>,
1438    {
1439        self.config.display_unit = Some(unit.into());
1440        self
1441    }
1442
1443    /// Set how the value is formatted for display, as a printf format such as `%8.3f`,
1444    /// which is what tells a client how many decimals of it are meaningful.
1445    pub fn display_format<F>(mut self, format: F) -> Self
1446    where
1447        F: Into<Cow<'static, str>>,
1448    {
1449        self.config.display_format = Some(format.into());
1450        self
1451    }
1452
1453    /// Set the display level of the attribute, [`DisplayLevel::Expert`] hiding it from the
1454    /// panels an operator is shown without hiding it from anyone who asks for it.
1455    pub fn display_level(mut self, level: DisplayLevel) -> Self {
1456        self.display_level = level;
1457        self
1458    }
1459
1460    // --- Bounds --- //
1461
1462    /// Set the range of the values the attribute accepts being written, outside of which a
1463    /// write is refused by the server rather than by the device.
1464    ///
1465    /// The range is given as a Rust range, either bound of which may be left open for an
1466    /// attribute that is only bounded on one side:
1467    ///
1468    /// ```ignore
1469    /// AttrDef::new("position").range(-100.0..=100.0)
1470    /// AttrDef::new("pressure").range(0.0..)
1471    /// ```
1472    ///
1473    /// # Panics
1474    ///
1475    /// If a bound is exclusive, which Tango has no way to express.
1476    #[track_caller]
1477    pub fn range<T, R>(mut self, range: R) -> Self
1478    where
1479        T: Into<AttrNumber> + Copy,
1480        R: RangeBounds<T>,
1481    {
1482        self.config.range = range_config(&self.name, "value range", range);
1483        self
1484    }
1485
1486    /// Set the range outside of which the attribute is in [`AttrQuality::Alarm`], and the
1487    /// device with it: an alarm is the reading being wrong, not the write being refused.
1488    ///
1489    /// # Panics
1490    ///
1491    /// If a bound is exclusive, see [`Self::range`].
1492    #[track_caller]
1493    pub fn alarm<T, R>(mut self, range: R) -> Self
1494    where
1495        T: Into<AttrNumber> + Copy,
1496        R: RangeBounds<T>,
1497    {
1498        self.config.alarm = range_config(&self.name, "alarm range", range);
1499        self
1500    }
1501
1502    /// Set the range outside of which the attribute is in [`AttrQuality::Warning`]. Like an
1503    /// alarm, a warning puts the device in [`DevState::Alarm`], in the states given to
1504    /// [`Dev::set_auto_alarm_states`], the way cppTango does.
1505    ///
1506    /// # Panics
1507    ///
1508    /// If a bound is exclusive, see [`Self::range`].
1509    #[track_caller]
1510    pub fn warning<T, R>(mut self, range: R) -> Self
1511    where
1512        T: Into<AttrNumber> + Copy,
1513        R: RangeBounds<T>,
1514    {
1515        self.config.warning = range_config(&self.name, "warning range", range);
1516        self
1517    }
1518
1519    /// Set the alarm on the read value drifting from the written one: how far apart the
1520    /// two may be, and for how long before it counts, absent for immediately.
1521    ///
1522    /// This is what says that a motor asked to go somewhere is not going there, which no
1523    /// bound on the value alone can express.
1524    pub fn delta<V, T>(mut self, value: V, time: T) -> Self
1525    where
1526        V: Into<AttrNumber>,
1527        T: Into<Option<Duration>>,
1528    {
1529        self.config.delta = Some(AttrDeltaConfig {
1530            value: value.into(),
1531            time: time.into(),
1532        });
1533        self
1534    }
1535
1536    // --- Events --- //
1537
1538    /// Set the criteria of the change event: how much the value has to move, absolutely
1539    /// and relatively, for the server to send one. Either may be left out with [`None`],
1540    /// and an attribute that declares neither only has the change events its device pushes
1541    /// by itself.
1542    ///
1543    /// ```ignore
1544    /// AttrDef::new("position").change_event(0.001, None)
1545    /// ```
1546    pub fn change_event<A, R>(mut self, abs: A, rel: R) -> Self
1547    where
1548        A: Into<Option<f64>>,
1549        R: Into<Option<f64>>,
1550    {
1551        self.config.change_event.abs_change = abs.into();
1552        self.config.change_event.rel_change = rel.into();
1553        self
1554    }
1555
1556    /// Set the period of the periodic event, which is sent whether the value moved or not
1557    /// and is how a client tells a device that has nothing to say from one that is gone.
1558    pub fn periodic_event(mut self, period: Duration) -> Self {
1559        self.config.periodic_event.period = Some(period);
1560        self
1561    }
1562
1563    /// Set the criteria of the archive event: the same two changes as
1564    /// [`Self::change_event`], and the longest an archiver may go without an event when
1565    /// the value does not move.
1566    pub fn archive_event<A, R, P>(mut self, abs: A, rel: R, period: P) -> Self
1567    where
1568        A: Into<Option<f64>>,
1569        R: Into<Option<f64>>,
1570        P: Into<Option<Duration>>,
1571    {
1572        self.config.archive_event.abs_change = abs.into();
1573        self.config.archive_event.rel_change = rel.into();
1574        self.config.archive_event.period = period.into();
1575        self
1576    }
1577
1578    /// Declare that the device pushes the change events of this attribute itself, see
1579    /// [`Dev::push_change_event`], which is what a client may then subscribe to them for:
1580    /// with no polling, a subscription to the change events of an attribute that does not
1581    /// declare them is refused, the way cppTango refuses it.
1582    ///
1583    /// With `detect`, the criteria of the change event are to be checked against what is
1584    /// pushed, see [`Self::change_event`], as cppTango does.
1585    ///
1586    /// TODO: Check the criteria, every push being sent for now.
1587    pub fn push_change_event(mut self, detect: bool) -> Self {
1588        self.pushed.change = Some(detect);
1589        self
1590    }
1591
1592    /// Declare that the device pushes the archive events of this attribute itself, see
1593    /// [`Self::push_change_event`].
1594    pub fn push_archive_event(mut self, detect: bool) -> Self {
1595        self.pushed.archive = Some(detect);
1596        self
1597    }
1598
1599    /// Declare that the device announces this attribute ready to be read, see
1600    /// [`Dev::push_data_ready_event`]: without it, both the announcement and a subscription
1601    /// to it are refused, the way cppTango refuses them.
1602    pub fn push_data_ready_event(mut self) -> Self {
1603        self.pushed.data_ready = true;
1604        self
1605    }
1606
1607    // --- Server behaviour --- //
1608
1609    /// Make the attribute memorized: the server stores the value of every write in the
1610    /// database and restores it when the device is initialized, the mode saying whether it
1611    /// is only given back as the write value or written again for real.
1612    ///
1613    /// Only a writable scalar can be memorized, there being nothing to remember about a
1614    /// value the device computes, and a state or an encoded value cannot, which a class
1615    /// is told when the attribute is given to it, see [`ClassDef::attr`].
1616    pub fn memorized(mut self, mode: AttrMemorized) -> Self {
1617        self.memorized = Some(mode);
1618        self
1619    }
1620
1621    /// Check what is only known once the whole definition is built: that this attribute
1622    /// can be memorized if it asks to be, see [`Self::memorized`], and that its kind
1623    /// supports every property its configuration gives a value to.
1624    pub(super) fn check(&self) -> std::result::Result<(), String> {
1625
1626        if let Some(prop) = unsupported_prop(&self.config, self.kind) {
1627            return Err(format!("the attribute {} cannot have the property {:?}, {}",
1628                self.name, prop.name(), unsupported(self.kind)));
1629        }
1630
1631        if self.memorized.is_none() {
1632            return Ok(());
1633        }
1634
1635        let reason = if self.write.is_none() {
1636            "it is not writable"
1637        } else if !matches!(self.format, None | Some(AttrDataFormat::Scalar)) {
1638            "it is not a scalar"
1639        } else if matches!(self.kind, Some(AttrDataKind::State | AttrDataKind::Encoded)) {
1640            "a value of its kind cannot be memorized"
1641        } else {
1642            return Ok(());
1643        };
1644
1645        Err(format!("the attribute {} cannot be memorized, {reason}", self.name))
1646        
1647    }
1648
1649    /// Ask for the attribute to be polled at that period, each reading being recorded in
1650    /// its history, which a read from the cache is answered with, and sent as a periodic
1651    /// event, see [`Self::periodic_event`].
1652    ///
1653    /// This period comes second: an attribute the `polled_attr` property of the device
1654    /// lists is polled at the period the property gives, and one it does not list at this
1655    /// one. The admin device changes it while the device runs, writing the property along,
1656    /// but an attribute removed from the polling this way is polled at this period again
1657    /// from the next initialization of the device, the property having no way to say it
1658    /// is not.
1659    pub fn polled(mut self, period: Duration) -> Self {
1660        self.poll_period = Some(period);
1661        self
1662    }
1663
1664    /// Set the advertised data kind of the attribute. This is only needed for the read
1665    /// and write functions that use a raw [`Value`], the typed ones derive it from their
1666    /// value type.
1667    ///
1668    /// # Panics
1669    ///
1670    /// If the data kind has already been defined, explicitly or by a read or a write
1671    /// function, as a different kind.
1672    #[track_caller]
1673    pub fn kind(mut self, kind: AttrDataKind) -> Self {
1674        self.kind = Some(merge_kind(&self.name, "data kind", self.kind.take(), kind));
1675        self
1676    }
1677
1678    /// Set the advertised data format of the attribute, which also gives the maximum
1679    /// dimensions of a spectrum or an image. This is only needed for the read and write
1680    /// functions that use a raw [`Value`], the typed ones derive a format without any
1681    /// maximum dimension from their value type.
1682    ///
1683    /// A maximum dimension of zero being undefined, it is compatible with any other one,
1684    /// so the maximum dimensions of a typed read or write function can be completed by
1685    /// this method, in any order.
1686    ///
1687    /// # Panics
1688    ///
1689    /// If the data format has already been defined, explicitly or by a read or a write
1690    /// function, with a different layout or a different maximum dimension.
1691    #[track_caller]
1692    pub fn format(mut self, format: AttrDataFormat) -> Self {
1693        self.format = Some(merge_format(&self.name, self.format.take(), format));
1694        self
1695    }
1696
1697    /// Declare the attribute to hold a single value, which is [`Self::format`] with the
1698    /// format written for you.
1699    ///
1700    /// # Panics
1701    ///
1702    /// If the format has already been defined as another layout, see [`Self::format`].
1703    #[track_caller]
1704    pub fn scalar(self) -> Self {
1705        self.format(AttrDataFormat::Scalar)
1706    }
1707
1708    /// Declare the attribute to hold a one dimensional array of at most that many values.
1709    ///
1710    /// A typed read or write function already gives the layout, so this is usually only
1711    /// there for the maximum size, which no value type carries; a maximum of zero leaves
1712    /// it undeclared, an array of any size then being accepted.
1713    ///
1714    /// # Panics
1715    ///
1716    /// If the format has already been defined as another layout, or as a spectrum of a
1717    /// different maximum size, see [`Self::format`].
1718    #[track_caller]
1719    pub fn spectrum(self, max_size: usize) -> Self {
1720        self.format(AttrDataFormat::Spectrum(max_size))
1721    }
1722
1723    /// Declare the attribute to hold a two dimensional array of at most that width and
1724    /// that height, see [`Self::spectrum`].
1725    ///
1726    /// # Panics
1727    ///
1728    /// If the format has already been defined as another layout, or as an image of
1729    /// different maximum dimensions, see [`Self::format`].
1730    #[track_caller]
1731    pub fn image(self, max_width: usize, max_height: usize) -> Self {
1732        self.format(AttrDataFormat::Image(max_width, max_height))
1733    }
1734
1735    /// Define the read function of this attribute, with a shared access to the model.
1736    ///
1737    /// This is the raw form of [`Self::read`]: the closure returns a [`Value`], converting
1738    /// what it borrows from the model itself, which is what makes a closure usable for
1739    /// such a result. The advertised kind and format then have to be given with
1740    /// [`Self::kind`] and [`Self::format`].
1741    pub fn read_with<F>(mut self, func: F) -> Self
1742    where
1743        F: Fn(&M, &Dev<M>) -> Result<AttrRead<Value>> + Send + Sync + 'static,
1744    {
1745        self.read = Some(ReadFn::SharedSync(Box::new(func)));
1746        self
1747    }
1748
1749    /// Define the read function of this attribute, with a shared access to the model,
1750    /// which allows parallel reading of such attributes.
1751    ///
1752    /// The value type is taken from the `ReadShared` implementation of the function,
1753    /// and may borrow from the model, with the same constraints as [`CommandDef::exec`]:
1754    /// it is then written as a function item generic over the model lifetime, typically
1755    /// an associated method of the model.
1756    ///
1757    /// # Panics
1758    ///
1759    /// If the advertised kind or format has already been defined incompatibly, see
1760    /// [`Self::kind`] and [`Self::format`].
1761    #[track_caller]
1762    pub fn read<F>(self, func: F) -> Self
1763    where
1764        F: for<'m> ReadShared<'m, M> + Send + Sync + 'static,
1765    {
1766        self.kind(F::DATA_KIND)
1767            .format(F::DATA_FORMAT)
1768            .read_with(move |model, dev| func.read(model, dev))
1769    }
1770
1771    /// Define the read function of this attribute, with an exclusive access to the model.
1772    /// This is the raw form of [`Self::read_exclusive`], see [`Self::read_with`].
1773    pub fn read_exclusive_with<F>(mut self, func: F) -> Self
1774    where
1775        F: Fn(&mut M, &Dev<M>) -> Result<AttrRead<Value>> + Send + Sync + 'static,
1776    {
1777        self.read = Some(ReadFn::ExclusiveSync(Box::new(func)));
1778        self
1779    }
1780
1781    /// Define the read function of this attribute, with an exclusive access to the model,
1782    /// therefore no other command or attribute can access it in parallel.
1783    ///
1784    /// # Panics
1785    ///
1786    /// If the advertised kind or format has already been defined incompatibly, see
1787    /// [`Self::kind`] and [`Self::format`].
1788    #[track_caller]
1789    pub fn read_exclusive<F>(self, func: F) -> Self
1790    where
1791        F: for<'m> ReadExclusive<'m, M> + Send + Sync + 'static,
1792    {
1793        self.kind(F::DATA_KIND)
1794            .format(F::DATA_FORMAT)
1795            .read_exclusive_with(move |model, dev| func.read(model, dev))
1796    }
1797
1798    /// Define the read function of this attribute, asynchronously and with a shared access
1799    /// to the model. This is the raw form of [`Self::read_async`], for a closure that
1800    /// boxes its future itself, see [`Self::read_with`].
1801    pub fn read_async_with<F>(mut self, func: F) -> Self
1802    where
1803        F: for<'a> Fn(&'a M, &'a Dev<M>) -> BoxFuture<'a, Result<AttrRead<Value>>> + Send + Sync + 'static,
1804    {
1805        self.read = Some(ReadFn::SharedAsync(Box::new(func)));
1806        self
1807    }
1808
1809    /// Define the read function of this attribute, asynchronously and with a shared access
1810    /// to the model, typically an `async fn` method of the model, see
1811    /// [`CommandDef::exec_async`] for what it is for and what it must not do.
1812    ///
1813    /// # Panics
1814    ///
1815    /// If the advertised kind or format has already been defined incompatibly, see
1816    /// [`Self::kind`] and [`Self::format`].
1817    #[track_caller]
1818    pub fn read_async<F>(self, func: F) -> Self
1819    where
1820        F: for<'a> ReadAsync<'a, &'a M, M> + Send + Sync + 'static,
1821    {
1822        self.kind(F::DATA_KIND)
1823            .format(F::DATA_FORMAT)
1824            .read_async_with(move |model, dev| func.read(model, dev))
1825    }
1826
1827    /// Define the read function of this attribute, asynchronously and with an exclusive
1828    /// access to the model. This is the raw form of [`Self::read_exclusive_async`], see
1829    /// [`Self::read_async_with`].
1830    pub fn read_exclusive_async_with<F>(mut self, func: F) -> Self
1831    where
1832        F: for<'a> Fn(&'a mut M, &'a Dev<M>) -> BoxFuture<'a, Result<AttrRead<Value>>> + Send + Sync + 'static,
1833    {
1834        self.read = Some(ReadFn::ExclusiveAsync(Box::new(func)));
1835        self
1836    }
1837
1838    /// Define the read function of this attribute, asynchronously and with an exclusive
1839    /// access to the model, which no other command or attribute can then access until the
1840    /// future completes. See [`Self::read_async`].
1841    ///
1842    /// # Panics
1843    ///
1844    /// If the advertised kind or format has already been defined incompatibly, see
1845    /// [`Self::kind`] and [`Self::format`].
1846    #[track_caller]
1847    pub fn read_exclusive_async<F>(self, func: F) -> Self
1848    where
1849        F: for<'a> ReadAsync<'a, &'a mut M, M> + Send + Sync + 'static,
1850    {
1851        self.kind(F::DATA_KIND)
1852            .format(F::DATA_FORMAT)
1853            .read_exclusive_async_with(move |model, dev| func.read(model, dev))
1854    }
1855
1856    /// Define the write function of this attribute, with a shared access to the model.
1857    /// This is the raw form of [`Self::write`], the written [`Value`] being given as-is
1858    /// and the advertised kind and format having to be given with [`Self::kind`] and
1859    /// [`Self::format`].
1860    pub fn write_with<F>(mut self, func: F) -> Self
1861    where
1862        F: Fn(&M, &Dev<M>, Value) -> Result<()> + Send + Sync + 'static,
1863    {
1864        self.write = Some(WriteFn::SharedSync(Box::new(func)));
1865        self
1866    }
1867
1868    /// Define the write function of this attribute, with a shared access to the model.
1869    ///
1870    /// The value type is taken from the `WriteShared` implementation of the function,
1871    /// any [`FromValue`] type, a [`Buf`](crate::buf::Buf) sharing the written value
1872    /// rather than copying it.
1873    ///
1874    /// # Panics
1875    ///
1876    /// If the advertised kind or format has already been defined incompatibly, which
1877    /// notably happens when the read function of the attribute does not use the same
1878    /// value type, see [`Self::kind`] and [`Self::format`].
1879    #[track_caller]
1880    pub fn write<F, I>(self, func: F) -> Self
1881    where
1882        F: WriteShared<M, I> + Send + Sync + 'static,
1883    {
1884        self.kind(F::DATA_KIND)
1885            .format(F::DATA_FORMAT)
1886            .write_with(move |model, dev, value| func.write(model, dev, value))
1887    }
1888
1889    /// Define the write function of this attribute, with an exclusive access to the
1890    /// model. This is the raw form of [`Self::write_exclusive`], see [`Self::write_with`].
1891    pub fn write_exclusive_with<F>(mut self, func: F) -> Self
1892    where
1893        F: Fn(&mut M, &Dev<M>, Value) -> Result<()> + Send + Sync + 'static,
1894    {
1895        self.write = Some(WriteFn::ExclusiveSync(Box::new(func)));
1896        self
1897    }
1898
1899    /// Define the write function of this attribute, with an exclusive access to the
1900    /// model, which is what a write usually needs in order to store the written value.
1901    ///
1902    /// # Panics
1903    ///
1904    /// If the advertised kind or format has already been defined incompatibly, which
1905    /// notably happens when the read function of the attribute does not use the same
1906    /// value type, see [`Self::kind`] and [`Self::format`].
1907    #[track_caller]
1908    pub fn write_exclusive<F, I>(self, func: F) -> Self
1909    where
1910        F: WriteExclusive<M, I> + Send + Sync + 'static,
1911    {
1912        self.kind(F::DATA_KIND)
1913            .format(F::DATA_FORMAT)
1914            .write_exclusive_with(move |model, dev, value| func.write(model, dev, value))
1915    }
1916
1917    /// Define the write function of this attribute, asynchronously and with a shared
1918    /// access to the model. This is the raw form of [`Self::write_async`], see
1919    /// [`Self::read_async_with`].
1920    pub fn write_async_with<F>(mut self, func: F) -> Self
1921    where
1922        F: for<'a> Fn(&'a M, &'a Dev<M>, Value) -> BoxFuture<'a, Result<()>> + Send + Sync + 'static,
1923    {
1924        self.write = Some(WriteFn::SharedAsync(Box::new(func)));
1925        self
1926    }
1927
1928    /// Define the write function of this attribute, asynchronously and with a shared
1929    /// access to the model, see [`Self::read_async`].
1930    ///
1931    /// # Panics
1932    ///
1933    /// If the advertised kind or format has already been defined incompatibly, see
1934    /// [`Self::write`].
1935    #[track_caller]
1936    pub fn write_async<F, I>(self, func: F) -> Self
1937    where
1938        F: for<'a> WriteAsync<'a, &'a M, M, I> + Send + Sync + 'static,
1939    {
1940        self.kind(F::DATA_KIND)
1941            .format(F::DATA_FORMAT)
1942            .write_async_with(move |model, dev, value| func.write(model, dev, value))
1943    }
1944
1945    /// Define the write function of this attribute, asynchronously and with an exclusive
1946    /// access to the model. This is the raw form of [`Self::write_exclusive_async`], see
1947    /// [`Self::read_async_with`].
1948    pub fn write_exclusive_async_with<F>(mut self, func: F) -> Self
1949    where
1950        F: for<'a> Fn(&'a mut M, &'a Dev<M>, Value) -> BoxFuture<'a, Result<()>> + Send + Sync + 'static,
1951    {
1952        self.write = Some(WriteFn::ExclusiveAsync(Box::new(func)));
1953        self
1954    }
1955
1956    /// Define the write function of this attribute, asynchronously and with an exclusive
1957    /// access to the model, which no other command or attribute can then access until the
1958    /// future completes. See [`Self::read_async`].
1959    ///
1960    /// # Panics
1961    ///
1962    /// If the advertised kind or format has already been defined incompatibly, see
1963    /// [`Self::write`].
1964    #[track_caller]
1965    pub fn write_exclusive_async<F, I>(self, func: F) -> Self
1966    where
1967        F: for<'a> WriteAsync<'a, &'a mut M, M, I> + Send + Sync + 'static,
1968    {
1969        self.kind(F::DATA_KIND)
1970            .format(F::DATA_FORMAT)
1971            .write_exclusive_async_with(move |model, dev, value| func.write(model, dev, value))
1972    }
1973
1974}
1975
1976impl<M: Model> Debug for AttrDef<M> {
1977    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1978        f.debug_struct("AttrDef")
1979            .field("name", &self.name)
1980            .field("read", &self.read)
1981            .field("write", &self.write)
1982            .field("kind", &self.kind)
1983            .field("format", &self.format)
1984            .field("config", &self.config)
1985            .field("poll_period", &self.poll_period)
1986            .field("pushed", &self.pushed)
1987            .finish()
1988    }
1989}
1990
1991
1992#[cfg(test)]
1993mod tests {
1994
1995    use super::*;
1996    use crate::value::Spectrum;
1997
1998    #[derive(Default)]
1999    struct Model { count: i32, label: String, values: Vec<f64>, labels: Vec<String> }
2000
2001    impl Model {
2002
2003        /// A result borrowed from the model, the lifetime elision binding it to the model
2004        /// borrow, see [`CommandDef::exec`].
2005        fn label(&self, _dev: &Dev<Self>, _arg: ()) -> Result<&str> {
2006            Ok(self.label.as_str())
2007        }
2008
2009        fn values(&self, _dev: &Dev<Self>, _arg: ()) -> Result<&[f64]> {
2010            Ok(&self.values)
2011        }
2012
2013        fn set_label(&mut self, _dev: &Dev<Self>, arg: String) -> Result<&str> {
2014            self.label = arg;
2015            Ok(self.label.as_str())
2016        }
2017
2018        fn add(&self, _dev: &Dev<Self>, arg: i32) -> Result<i32> {
2019            Ok(self.count + arg)
2020        }
2021
2022        fn incr(&mut self, _dev: &Dev<Self>, arg: i32) -> Result<i32> {
2023            self.count += arg;
2024            Ok(self.count)
2025        }
2026
2027        // --- Attribute handlers, which are only given the device --- //
2028
2029        fn count(&self, _dev: &Dev<Self>) -> Result<i32> {
2030            Ok(self.count)
2031        }
2032
2033        fn set_count(&mut self, _dev: &Dev<Self>, arg: i32) -> Result<()> {
2034            self.count = arg;
2035            Ok(())
2036        }
2037
2038        /// A read giving the quality and the timestamp of the value along with it.
2039        fn count_full(&self, _dev: &Dev<Self>) -> Result<AttrRead<i32>> {
2040            Ok(AttrRead {
2041                value: Some(self.count),
2042                w_value: None,
2043                quality: AttrQuality::Valid,
2044                time: SystemTime::now(),
2045            })
2046        }
2047
2048        fn read_label(&self, _dev: &Dev<Self>) -> Result<&str> {
2049            Ok(self.label.as_str())
2050        }
2051
2052        fn read_values(&self, _dev: &Dev<Self>) -> Result<&[f64]> {
2053            Ok(&self.values)
2054        }
2055
2056        /// The handler shared by the commands of a dynamic set, the index of the command
2057        /// being captured by the closure forwarding to it.
2058        fn label_n(&self, _dev: &Dev<Self>, index: usize) -> Result<&str> {
2059            Ok(self.labels[index].as_str())
2060        }
2061
2062        /// A read updating the model, therefore needing an exclusive access to it.
2063        fn tick(&mut self, _dev: &Dev<Self>) -> Result<i32> {
2064            self.count += 1;
2065            Ok(self.count)
2066        }
2067
2068    }
2069
2070    /// A result that does not borrow from the model at all.
2071    fn version(_model: &Model, _dev: &Dev<Model>, _arg: ()) -> Result<&'static str> {
2072        Ok("1.0.0")
2073    }
2074
2075    /// Check that all the intended signatures are accepted, and that both kinds are
2076    /// always derived from the argument and result types.
2077    #[test]
2078    fn exec_signatures() {
2079
2080        // Scalar in, scalar out, as an associated method of the model.
2081        let def = CommandDef::<Model>::new("Add").exec(Model::add);
2082        assert_eq!(def.in_kind, Some(CommandDataKind::Long));
2083        assert_eq!(def.out_kind, Some(CommandDataKind::Long));
2084
2085        // Exclusive access to the model.
2086        let _ = CommandDef::<Model>::new("Incr").exec_exclusive(Model::incr);
2087
2088        // A closure is still accepted as long as its result does not borrow from the
2089        // model, but it has to annotate the two reference parameters.
2090        let _ = CommandDef::<Model>::new("Status")
2091            .exec(|model: &Model, _dev: &Dev<Model>, (): ()| Ok(model.label.clone()));
2092
2093        // A closure whose result borrows from the model is only accepted by the raw
2094        // form, see `closures_borrowing`.
2095        let def = CommandDef::<Model>::new("Label")
2096            .out_kind(CommandDataKind::String)
2097            .exec_with(|model: &Model, dev, value| {
2098                Ok(model.label(dev, <()>::from_value(value)?)?.into())
2099            });
2100        assert_eq!(def.out_kind, Some(CommandDataKind::String));
2101
2102        // Spectrum in, scalar out.
2103        let def = CommandDef::<Model>::new("Sum")
2104            .exec(|_model: &Model, _dev: &Dev<Model>, arg: Vec<f64>| Ok(arg.iter().sum::<f64>()));
2105        assert_eq!(def.in_kind, Some(CommandDataKind::DoubleArray));
2106        assert_eq!(def.out_kind, Some(CommandDataKind::Double));
2107
2108        // Spectrum in and out, owned or borrowed from the argument.
2109        let _ = CommandDef::<Model>::new("Scale")
2110            .exec(|_model: &Model, _dev: &Dev<Model>, arg: Vec<f64>| Ok(arg.iter().map(|v| v * 2.0).collect::<Vec<f64>>()));
2111        let _ = CommandDef::<Model>::new("Reverse")
2112            .exec(|_model: &Model, _dev: &Dev<Model>, arg: Vec<String>| Ok(arg.into_iter().rev().collect::<Vec<String>>()));
2113        let _ = CommandDef::<Model>::new("Zeros")
2114            .exec(|_model: &Model, _dev: &Dev<Model>, (): ()| Ok(Spectrum::<f64>::from(vec![0.0; 4])));
2115
2116        // A `&'static str` result.
2117        let def = CommandDef::<Model>::new("Version").exec(version);
2118        assert_eq!(def.out_kind, Some(CommandDataKind::String));
2119
2120        // A result borrowed from the model, both kinds still being derived.
2121        let def = CommandDef::<Model>::new("Label")
2122            .out_doc("The label of the device")
2123            .exec(Model::label);
2124        assert_eq!(def.in_kind, Some(CommandDataKind::Void));
2125        assert_eq!(def.out_kind, Some(CommandDataKind::String));
2126
2127        let def = CommandDef::<Model>::new("Values").exec(Model::values);
2128        assert_eq!(def.out_kind, Some(CommandDataKind::DoubleArray));
2129
2130        // Exclusive access to the model, with a result borrowed from it.
2131        let def = CommandDef::<Model>::new("SetLabel").exec_exclusive(Model::set_label);
2132        assert_eq!(def.in_kind, Some(CommandDataKind::String));
2133        assert_eq!(def.out_kind, Some(CommandDataKind::String));
2134
2135        // Raw form, with a result borrowed from the model, the kinds being explicit
2136        // because they cannot be derived from a raw `Value`.
2137        let def = CommandDef::<Model>::new("Status")
2138            .out_kind(CommandDataKind::String)
2139            .exec_with(|model: &Model, _dev, _value| Ok(model.label.as_str().into()));
2140        assert_eq!(def.in_kind, None);
2141        assert_eq!(def.out_kind, Some(CommandDataKind::String));
2142
2143        // Raw form, borrowing the argument value itself.
2144        let _ = CommandDef::<Model>::new("Echo")
2145            .in_kind(CommandDataKind::String)
2146            .out_kind(CommandDataKind::String)
2147            .exec_with(|model: &Model, _dev, value| {
2148                let arg = <&str>::try_from(&value)?;
2149                Ok(format!("{}{arg}", model.label).into())
2150            });
2151
2152        // Raw form with an exclusive access to the model, returning a borrow of it.
2153        let _ = CommandDef::<Model>::new("SetLabel")
2154            .in_kind(CommandDataKind::String)
2155            .out_kind(CommandDataKind::String)
2156            .exec_exclusive_with(|model: &mut Model, _dev, value| {
2157                model.label = String::try_from(value)?;
2158                Ok(model.label.as_str().into())
2159            });
2160
2161    }
2162
2163    /// Check that all the intended read and write signatures are accepted, and that the
2164    /// kind and the format are always derived from the value type.
2165    #[test]
2166    fn attr_signatures() {
2167
2168        // Scalar read and write, as associated methods of the model.
2169        let def = AttrDef::<Model>::new("Count")
2170            .read(Model::count)
2171            .write_exclusive(Model::set_count);
2172        assert_eq!(def.kind, Some(AttrDataKind::Long));
2173        assert_eq!(def.format, Some(AttrDataFormat::Scalar));
2174
2175        // A value borrowed from the model, scalar or spectrum.
2176        let def = AttrDef::<Model>::new("Label").read(Model::read_label);
2177        assert_eq!(def.kind, Some(AttrDataKind::String));
2178        assert_eq!(def.format, Some(AttrDataFormat::Scalar));
2179
2180        let def = AttrDef::<Model>::new("Values").read(Model::read_values);
2181        assert_eq!(def.kind, Some(AttrDataKind::Double));
2182        assert_eq!(def.format, Some(AttrDataFormat::Spectrum(0)));
2183
2184        // A full read result, giving the quality and the timestamp.
2185        let def = AttrDef::<Model>::new("CountFull").read(Model::count_full);
2186        assert_eq!(def.kind, Some(AttrDataKind::Long));
2187
2188        // Exclusive access to the model, for a read updating it.
2189        let _ = AttrDef::<Model>::new("Ticks").read_exclusive(Model::tick);
2190
2191        // A closure is accepted as long as its result does not borrow from the model.
2192        let _ = AttrDef::<Model>::new("Double")
2193            .read(|model: &Model, _dev: &Dev<Model>| Ok(model.count * 2));
2194
2195        // Raw form, the kind and the format having to be explicit.
2196        let _ = AttrDef::<Model>::new("Raw")
2197            .kind(AttrDataKind::Double)
2198            .format(AttrDataFormat::Spectrum(4096))
2199            .read_with(|model: &Model, _dev| Ok(AttrRead {
2200                value: Some(model.values.as_slice().into()),
2201                w_value: None,
2202                quality: AttrQuality::Valid,
2203                time: SystemTime::now(),
2204            }));
2205
2206    }
2207
2208    /// Check that every configuration builder writes the field of [`AttrConfig`] it is
2209    /// named after, which is what a device of the class then starts from.
2210    #[test]
2211    fn attr_config() {
2212
2213        let def = AttrDef::<Model>::new("Position")
2214            .read(Model::count)
2215            .doc("The position of the axis")
2216            .label("Position")
2217            .unit("mm")
2218            .standard_unit(1e-3)
2219            .display_unit("mm")
2220            .display_format("%8.3f")
2221            .display_level(DisplayLevel::Expert)
2222            .delta(0.5, Duration::from_millis(200))
2223            .change_event(0.001, None)
2224            .periodic_event(Duration::from_secs(1))
2225            .archive_event(0.1, None, Duration::from_secs(10))
2226            .memorized(AttrMemorized::RestoreAndWrite)
2227            .polled(Duration::from_millis(100));
2228
2229        assert_eq!(def.config.doc.as_deref(), Some("The position of the axis"));
2230        assert_eq!(def.config.label.as_deref(), Some("Position"));
2231        assert_eq!(def.config.unit.as_deref(), Some("mm"));
2232        assert_eq!(def.config.standard_unit, Some(1e-3));
2233        assert_eq!(def.config.display_unit.as_deref(), Some("mm"));
2234        assert_eq!(def.config.display_format.as_deref(), Some("%8.3f"));
2235        assert_eq!(def.display_level, DisplayLevel::Expert);
2236
2237        // The delta is a limit like the bounds are, so it is the number of the attribute
2238        // and not a double the value would have to be converted to.
2239        assert_eq!(def.config.delta, Some(AttrDeltaConfig {
2240            value: AttrNumber::Double(0.5),
2241            time: Some(Duration::from_millis(200)),
2242        }));
2243
2244        // The criteria of the events are not limits: an absolute change is a magnitude and
2245        // a relative one a percentage, so both stay doubles, and either may be left out.
2246        assert_eq!(def.config.change_event.abs_change, Some(0.001));
2247        assert_eq!(def.config.change_event.rel_change, None);
2248        assert_eq!(def.config.periodic_event.period, Some(Duration::from_secs(1)));
2249        assert_eq!(def.config.archive_event.abs_change, Some(0.1));
2250        assert_eq!(def.config.archive_event.period, Some(Duration::from_secs(10)));
2251
2252        assert_eq!(def.memorized, Some(AttrMemorized::RestoreAndWrite));
2253        assert_eq!(def.poll_period, Some(Duration::from_millis(100)));
2254
2255        // What is not defined stays absent rather than being given a value nobody asked
2256        // for, an attribute with no bound being the usual case.
2257        let def = AttrDef::<Model>::new("Count").read(Model::count);
2258        assert_eq!(def.config, AttrConfig::default());
2259        assert_eq!(def.poll_period, None);
2260
2261    }
2262
2263    /// Check the three ways a range is bounded, that a bound left open stays open, and
2264    /// that a range is the kind of number it was written with.
2265    #[test]
2266    fn attr_ranges() {
2267
2268        let def = AttrDef::<Model>::new("Position")
2269            .read(Model::count)
2270            .range(-100.0..=100.0)
2271            .warning(-90.5..)
2272            .alarm(..=95.0);
2273
2274        assert_eq!(def.config.range, AttrRangeConfig {
2275            min: Some(AttrNumber::Double(-100.0)),
2276            max: Some(AttrNumber::Double(100.0)),
2277        });
2278
2279        // Only bounded below: an attribute that cannot go under that and has no ceiling.
2280        assert_eq!(def.config.warning, AttrRangeConfig {
2281            min: Some(AttrNumber::Double(-90.5)),
2282            max: None,
2283        });
2284
2285        // And only bounded above.
2286        assert_eq!(def.config.alarm, AttrRangeConfig {
2287            min: None,
2288            max: Some(AttrNumber::Double(95.0)),
2289        });
2290
2291        // An integer attribute is bounded with integers, which are held as such rather
2292        // than being rounded into a double: this is the whole point of the three kinds.
2293        let def = AttrDef::<Model>::new("Count")
2294            .read(Model::count)
2295            .range(-8..=8i32)
2296            .alarm(..=u64::MAX);
2297
2298        assert_eq!(def.config.range, AttrRangeConfig {
2299            min: Some(AttrNumber::Long(-8)),
2300            max: Some(AttrNumber::Long(8)),
2301        });
2302        assert_eq!(def.config.alarm.max, Some(AttrNumber::ULong(u64::MAX)));
2303
2304        // Two limits a double cannot tell apart, which is the whole reason they are not
2305        // doubles: kept as they are they differ, converted they are the same number.
2306        assert_ne!(AttrNumber::ULong(u64::MAX), AttrNumber::ULong(u64::MAX - 1));
2307        assert_eq!(AttrNumber::ULong(u64::MAX).as_f64(), AttrNumber::ULong(u64::MAX - 1).as_f64());
2308
2309        // Two limits of different kinds are equal when they stand for the same number.
2310        assert_eq!(AttrNumber::Long(2), AttrNumber::Double(2.0));
2311        assert!(AttrNumber::Long(-1) < AttrNumber::ULong(0));
2312        assert!(AttrNumber::ULong(u64::MAX) > AttrNumber::ULong(u64::MAX - 1));
2313        assert_ne!(AttrNumber::Double(f64::NAN), AttrNumber::Double(f64::NAN));
2314
2315    }
2316
2317    #[test]
2318    #[should_panic(expected = "the value range cannot have an exclusive bound")]
2319    fn exclusive_range_bound() {
2320        let _ = AttrDef::<Model>::new("Position").range(-100.0..100.0);
2321    }
2322
2323    /// Check that the three layout methods are the formats they stand for, and that they
2324    /// merge with what a handler derived the way [`AttrDef::format`] does.
2325    #[test]
2326    fn attr_layouts() {
2327
2328        let def = AttrDef::<Model>::new("Count").read(Model::count).scalar();
2329        assert_eq!(def.format, Some(AttrDataFormat::Scalar));
2330
2331        // The layout comes from the handler and only the maximum size from here, in
2332        // either order.
2333        let def = AttrDef::<Model>::new("Values").read(Model::read_values).spectrum(4096);
2334        assert_eq!(def.format, Some(AttrDataFormat::Spectrum(4096)));
2335
2336        let def = AttrDef::<Model>::new("Values").spectrum(4096).read(Model::read_values);
2337        assert_eq!(def.kind, Some(AttrDataKind::Double));
2338        assert_eq!(def.format, Some(AttrDataFormat::Spectrum(4096)));
2339
2340        // Nothing is derived from a raw handler, so both are stated outright.
2341        let def = AttrDef::<Model>::new("Frame")
2342            .kind(AttrDataKind::Double)
2343            .image(640, 480);
2344        assert_eq!(def.format, Some(AttrDataFormat::Image(640, 480)));
2345
2346        // A maximum of zero is the undefined one, which any size then matches.
2347        let def = AttrDef::<Model>::new("Values").read(Model::read_values).spectrum(0);
2348        assert_eq!(def.format, Some(AttrDataFormat::Spectrum(0)));
2349
2350    }
2351
2352    #[test]
2353    #[should_panic(expected = "the data format is already defined as Scalar")]
2354    fn incompatible_layout() {
2355        let _ = AttrDef::<Model>::new("Count").read(Model::count).spectrum(8);
2356    }
2357
2358    /// Check what a command has of a configuration, which is a good deal less than an
2359    /// attribute: Tango tells a client the display level and nothing else about it.
2360    #[test]
2361    fn command_config() {
2362
2363        let def = CommandDef::<Model>::new("Add")
2364            .exec(Model::add)
2365            .display_level(DisplayLevel::Expert)
2366            .polled(Duration::from_secs(1));
2367
2368        assert_eq!(def.display_level, DisplayLevel::Expert);
2369        assert_eq!(def.poll_period, Some(Duration::from_secs(1)));
2370
2371        // The level everything is at unless it says otherwise.
2372        let def = CommandDef::<Model>::new("Add").exec(Model::add);
2373        assert_eq!(def.display_level, DisplayLevel::Operator);
2374        assert_eq!(def.poll_period, None);
2375
2376    }
2377
2378    /// The description of a class is the one every device of it gives of itself.
2379    #[test]
2380    fn class_doc() {
2381        let def = ClassDef::<Model>::new_default("Motor").doc("A single axis");
2382        assert_eq!(def.doc, "A single axis");
2383        let def = ClassDef::<Model>::new_default("Motor");
2384        assert_eq!(def.doc, "A Tango device");
2385    }
2386
2387    /// Check that an explicit kind or format completes the one derived from a handler,
2388    /// in any order, as long as they do not contradict each other.
2389    #[test]
2390    fn compatible_kinds() {
2391
2392        // The same kind given twice, before or after the execution function.
2393        let def = CommandDef::<Model>::new("Add")
2394            .in_kind(CommandDataKind::Long)
2395            .exec(Model::add)
2396            .out_kind(CommandDataKind::Long);
2397        assert_eq!(def.in_kind, Some(CommandDataKind::Long));
2398        assert_eq!(def.out_kind, Some(CommandDataKind::Long));
2399
2400        // An undefined maximum size being completed by an explicit format, the derived
2401        // kind being kept in both orders.
2402        let def = AttrDef::<Model>::new("Values")
2403            .read(Model::read_values)
2404            .format(AttrDataFormat::Spectrum(4096));
2405        assert_eq!(def.kind, Some(AttrDataKind::Double));
2406        assert_eq!(def.format, Some(AttrDataFormat::Spectrum(4096)));
2407
2408        let def = AttrDef::<Model>::new("Values")
2409            .format(AttrDataFormat::Spectrum(4096))
2410            .read(Model::read_values);
2411        assert_eq!(def.format, Some(AttrDataFormat::Spectrum(4096)));
2412
2413    }
2414
2415    #[test]
2416    #[should_panic(expected = "the argument kind is already defined as String")]
2417    fn incompatible_in_kind() {
2418        let _ = CommandDef::<Model>::new("Add")
2419            .in_kind(CommandDataKind::String)
2420            .exec(Model::add);
2421    }
2422
2423    #[test]
2424    #[should_panic(expected = "the result kind is already defined as Long")]
2425    fn incompatible_out_kind() {
2426        let _ = CommandDef::<Model>::new("Add")
2427            .exec(Model::add)
2428            .out_kind(CommandDataKind::Long64);
2429    }
2430
2431    #[test]
2432    #[should_panic(expected = "the data kind is already defined as String")]
2433    fn incompatible_attr_kind() {
2434        let _ = AttrDef::<Model>::new("Label")
2435            .read(Model::read_label)
2436            .write_exclusive(Model::set_count);
2437    }
2438
2439    #[test]
2440    #[should_panic(expected = "the data format is already defined as Scalar")]
2441    fn incompatible_attr_format() {
2442        let _ = AttrDef::<Model>::new("Count")
2443            .read(Model::count)
2444            .format(AttrDataFormat::Spectrum(8));
2445    }
2446
2447    #[test]
2448    #[should_panic(expected = "the maximum size is already defined as 8")]
2449    fn incompatible_attr_max() {
2450        let _ = AttrDef::<Model>::new("Values")
2451            .format(AttrDataFormat::Spectrum(8))
2452            .format(AttrDataFormat::Spectrum(4096));
2453    }
2454
2455
2456    #[test]
2457    #[should_panic(expected = "Test: the attribute Label cannot have the property \"max_alarm\", not supported by an attribute of type String")]
2458    fn unsupported_attr_prop() {
2459        let _ = ClassDef::<Model>::new_default("Test")
2460            .attr(AttrDef::new("Label")
2461                .read(Model::read_label)
2462                .alarm(..=5));
2463    }
2464
2465    #[test]
2466    #[should_panic(expected = "Test: the command name \"Do it\" is invalid")]
2467    fn invalid_command_name() {
2468        let _ = ClassDef::<Model>::new_default("Test").command(CommandDef::new("Do it"));
2469    }
2470
2471    #[test]
2472    #[should_panic(expected = "Test: the attribute name \"_Speed\" is invalid")]
2473    fn invalid_attr_name() {
2474        let _ = ClassDef::<Model>::new_default("Test").attr(AttrDef::new("_Speed").kind(AttrDataKind::Double).scalar());
2475    }
2476
2477    #[test]
2478    #[should_panic(expected = "the attribute State cannot have the property \"min_value\", not supported by an attribute of type State")]
2479    fn unsupported_state_prop() {
2480        let _ = ClassDef::<Model>::new_default("Test")
2481            .state_config(AttrConfig { range: AttrRangeConfig { min: Some(AttrNumber::Long(0)), max: None }, ..AttrConfig::default() });
2482    }
2483
2484    #[test]
2485    #[should_panic(expected = "the attribute Count cannot be memorized, it is not writable")]
2486    fn memorized_read_only() {
2487        let _ = ClassDef::<Model>::new_default("Test")
2488            .attr(AttrDef::new("Count")
2489                .read(Model::count)
2490                .memorized(AttrMemorized::RestoreSetPoint));
2491    }
2492
2493    #[test]
2494    #[should_panic(expected = "the attribute Values cannot be memorized, it is not a scalar")]
2495    fn memorized_spectrum() {
2496        let _ = ClassDef::<Model>::new_default("Test")
2497            .attr(AttrDef::new("Values")
2498                .memorized(AttrMemorized::RestoreAndWrite)
2499                .kind(AttrDataKind::Double)
2500                .format(AttrDataFormat::Spectrum(8))
2501                .write_with(|_model: &Model, _dev, _value| Ok(())));
2502    }
2503
2504    /// Check the way a closure borrowing from the model is defined: only the raw form
2505    /// accepts it, its result being converted into a [`Value`] before the borrow ends,
2506    /// and the advertised kinds then being explicit.
2507    #[test]
2508    fn closures_borrowing() {
2509
2510        // The dynamic command case: several commands sharing a single handler, each
2511        // closure capturing the index of its own command.
2512        for index in 0..3 {
2513            let def = CommandDef::<Model>::new(format!("Label{index}"))
2514                .out_kind(CommandDataKind::String)
2515                .exec_with(move |model: &Model, dev, _value| {
2516                    Ok(model.label_n(dev, index)?.into())
2517                });
2518            assert_eq!(def.out_kind, Some(CommandDataKind::String));
2519        }
2520
2521        // With an exclusive access to the model, the result borrowing what was written.
2522        let _ = CommandDef::<Model>::new("Push")
2523            .in_kind(CommandDataKind::String)
2524            .out_kind(CommandDataKind::String)
2525            .exec_exclusive_with(|model: &mut Model, _dev, value| {
2526                model.labels.push(String::try_from(value)?);
2527                Ok(model.labels.last().unwrap().as_str().into())
2528            });
2529
2530        // The same for the read function of a dynamic attribute.
2531        for index in 0..3 {
2532            let def = AttrDef::<Model>::new(format!("Label{index}"))
2533                .kind(AttrDataKind::String)
2534                .format(AttrDataFormat::Scalar)
2535                .read_with(move |model: &Model, dev| {
2536                    Ok(AttrRead {
2537                        value: Some(model.label_n(dev, index)?.into()),
2538                        w_value: None,
2539                        quality: AttrQuality::Valid,
2540                        time: SystemTime::now(),
2541                    })
2542                });
2543            assert_eq!(def.kind, Some(AttrDataKind::String));
2544        }
2545
2546    }
2547
2548    /// A model owning what it borrowed from outside of the device, which is what the
2549    /// `'static` bound asks of it: a class lives as long as the server serving it, so a
2550    /// model borrowing from a shorter lifetime has nothing to be borrowed from by then.
2551    struct Owning { values: Vec<f64> }
2552
2553    impl Owning {
2554        fn len(&self, _dev: &Dev<Self>, _arg: ()) -> Result<i32> { Ok(self.values.len() as i32) }
2555        fn first(&self, _dev: &Dev<Self>) -> Result<f64> { Ok(self.values[0]) }
2556    }
2557
2558    /// The definitions of such a model are `'static` themselves, so they can be given to a
2559    /// class without being tied to the scope they were written in.
2560    fn owning_defs(prefix: &str) -> (CommandDef<Owning>, AttrDef<Owning>) {
2561        let command = CommandDef::<Owning>::new("Len")
2562            .exec(Owning::len)
2563            .out_doc(prefix.to_owned());
2564        let attr = AttrDef::<Owning>::new("First")
2565            .read(Owning::first)
2566            // A handler capturing what it needs by value, the definition outliving the
2567            // scope it was built in.
2568            .write_with({
2569                let prefix = prefix.to_owned();
2570                move |_model: &Owning, _dev, _value| {
2571                    let _ = &prefix;
2572                    Ok(())
2573                }
2574            });
2575        (command, attr)
2576    }
2577
2578    #[test]
2579    fn model_owned() {
2580        let values = [1.0, 2.0, 3.0];
2581        let prefix = format!("{} values", values.len());
2582        let (command, attr) = owning_defs(&prefix);
2583        drop(prefix);
2584        assert_eq!(command.out_kind, Some(CommandDataKind::Long));
2585        assert_eq!(attr.kind, Some(AttrDataKind::Double));
2586    }
2587
2588    /// The asynchronous counterparts of the handlers of [`Model`], written as `async fn`
2589    /// methods, which is what the typed async definitions are meant to take.
2590    struct AsyncModel { count: i32, label: String }
2591
2592    impl AsyncModel {
2593
2594        async fn add(&self, _dev: &Dev<Self>, arg: i32) -> Result<i32> {
2595            Ok(self.count + arg)
2596        }
2597
2598        async fn label(&self, _dev: &Dev<Self>, _arg: ()) -> Result<&str> {
2599            Ok(self.label.as_str())
2600        }
2601
2602        async fn incr(&mut self, _dev: &Dev<Self>, arg: i32) -> Result<i32> {
2603            self.count += arg;
2604            Ok(self.count)
2605        }
2606
2607        async fn count(&self, _dev: &Dev<Self>) -> Result<i32> {
2608            Ok(self.count)
2609        }
2610
2611        async fn set_count(&mut self, _dev: &Dev<Self>, arg: i32) -> Result<()> {
2612            self.count = arg;
2613            Ok(())
2614        }
2615
2616        async fn read_label(&self, _dev: &Dev<Self>) -> Result<&str> {
2617            Ok(self.label.as_str())
2618        }
2619
2620        async fn check_label(&self, _dev: &Dev<Self>, arg: String) -> Result<()> {
2621            let _ = arg.len();
2622            Ok(())
2623        }
2624
2625        async fn tick(&mut self, _dev: &Dev<Self>) -> Result<i32> {
2626            self.count += 1;
2627            Ok(self.count)
2628        }
2629
2630        async fn state(&self, _dev: &Dev<Self>) -> Result<DevState> {
2631            Ok(DevState::On)
2632        }
2633
2634        async fn status(&mut self, _dev: &Dev<Self>) -> Result<&str> {
2635            Ok(&self.label)
2636        }
2637
2638        fn status_label(&self, _dev: &Dev<Self>) -> Result<&str> {
2639            Ok(&self.label)
2640        }
2641
2642    }
2643
2644    /// Check that the async execution functions are accepted as `async fn` methods and as
2645    /// boxing closures, that both kinds are derived as for the sync ones, and that each
2646    /// definition is stored with the access it asked for.
2647    #[test]
2648    fn exec_async_signatures() {
2649
2650        let def = CommandDef::<AsyncModel>::new("Add").exec_async(AsyncModel::add);
2651        assert_eq!(def.in_kind, Some(CommandDataKind::Long));
2652        assert_eq!(def.out_kind, Some(CommandDataKind::Long));
2653        assert!(matches!(def.exec, Some(ExecFn::SharedAsync(_))));
2654
2655        // A result borrowed from the model across the future.
2656        let def = CommandDef::<AsyncModel>::new("Label").exec_async(AsyncModel::label);
2657        assert_eq!(def.in_kind, Some(CommandDataKind::Void));
2658        assert_eq!(def.out_kind, Some(CommandDataKind::String));
2659
2660        let def = CommandDef::<AsyncModel>::new("Incr").exec_exclusive_async(AsyncModel::incr);
2661        assert_eq!(def.in_kind, Some(CommandDataKind::Long));
2662        assert!(matches!(def.exec, Some(ExecFn::ExclusiveAsync(_))));
2663
2664        // Raw forms, whose closures box their future themselves and give no kind.
2665        let def = CommandDef::<AsyncModel>::new("Echo")
2666            .in_kind(CommandDataKind::String)
2667            .out_kind(CommandDataKind::String)
2668            .exec_async_with(|model: &AsyncModel, _dev, value| Box::pin(async move {
2669                let arg = <&str>::try_from(&value)?;
2670                Ok(format!("{}{arg}", model.label).into())
2671            }));
2672        assert_eq!(def.in_kind, Some(CommandDataKind::String));
2673        assert!(matches!(def.exec, Some(ExecFn::SharedAsync(_))));
2674
2675        let def = CommandDef::<AsyncModel>::new("SetLabel")
2676            .exec_exclusive_async_with(|model: &mut AsyncModel, _dev, value| Box::pin(async move {
2677                model.label = String::try_from(value)?;
2678                Ok(model.label.as_str().into())
2679            }));
2680        assert_eq!(def.in_kind, None);
2681        assert_eq!(def.out_kind, None);
2682        assert!(matches!(def.exec, Some(ExecFn::ExclusiveAsync(_))));
2683
2684    }
2685
2686    /// Check the same for the async read and write functions of an attribute.
2687    #[test]
2688    fn attr_async_signatures() {
2689
2690        let def = AttrDef::<AsyncModel>::new("Count")
2691            .read_async(AsyncModel::count)
2692            .write_exclusive_async(AsyncModel::set_count);
2693        assert_eq!(def.kind, Some(AttrDataKind::Long));
2694        assert_eq!(def.format, Some(AttrDataFormat::Scalar));
2695        assert!(matches!(def.read, Some(ReadFn::SharedAsync(_))));
2696        assert!(matches!(def.write, Some(WriteFn::ExclusiveAsync(_))));
2697
2698        let def = AttrDef::<AsyncModel>::new("Label")
2699            .read_async(AsyncModel::read_label)
2700            .write_async(AsyncModel::check_label);
2701        assert_eq!(def.kind, Some(AttrDataKind::String));
2702        assert!(matches!(def.write, Some(WriteFn::SharedAsync(_))));
2703
2704        let def = AttrDef::<AsyncModel>::new("Ticks").read_exclusive_async(AsyncModel::tick);
2705        assert!(matches!(def.read, Some(ReadFn::ExclusiveAsync(_))));
2706
2707        let def = AttrDef::<AsyncModel>::new("Raw")
2708            .kind(AttrDataKind::Long)
2709            .scalar()
2710            .read_async_with(|model: &AsyncModel, _dev| Box::pin(async move {
2711                Ok(AttrRead {
2712                    value: Some(model.count.into()),
2713                    w_value: None,
2714                    quality: AttrQuality::Valid,
2715                    time: SystemTime::now(),
2716                })
2717            }))
2718            .write_exclusive_async_with(|model: &mut AsyncModel, _dev, value| Box::pin(async move {
2719                model.count = i32::try_from(value)?;
2720                Ok(())
2721            }));
2722        assert!(matches!(def.read, Some(ReadFn::SharedAsync(_))));
2723        assert!(matches!(def.write, Some(WriteFn::ExclusiveAsync(_))));
2724
2725    }
2726
2727    #[test]
2728    #[should_panic(expected = "the data kind is already defined as String")]
2729    fn incompatible_attr_async_kind() {
2730        let _ = AttrDef::<AsyncModel>::new("Label")
2731            .read_async(AsyncModel::read_label)
2732            .write_exclusive_async(AsyncModel::set_count);
2733    }
2734
2735    /// Check that the async state and status functions are stored with the access they
2736    /// asked for, a later definition replacing an earlier one.
2737    #[test]
2738    fn class_async_handlers() {
2739
2740        let def = ClassDef::<AsyncModel>::new("Motor", |_dev| Ok(AsyncModel { count: 0, label: String::new() }))
2741            .state_async(AsyncModel::state)
2742            .status_exclusive_async(AsyncModel::status);
2743        assert!(matches!(def.state, Some(StateFn::SharedAsync(_))));
2744        assert!(matches!(def.status, Some(StatusFn::ExclusiveAsync(_))));
2745
2746        let def = def
2747            .state_exclusive_async_with(|_model: &mut AsyncModel, _dev| Box::pin(async { Ok(DevState::Moving) }))
2748            .status_async_with(|model: &AsyncModel, _dev| Box::pin(async move { Ok(Scalar::from(model.label.as_str())) }));
2749        assert!(matches!(def.state, Some(StateFn::ExclusiveAsync(_))));
2750        assert!(matches!(def.status, Some(StatusFn::SharedAsync(_))));
2751
2752    }
2753
2754    /// Check that the status functions convert what they give, a method borrowing it from
2755    /// the model as well as a closure giving an owned string.
2756    #[test]
2757    fn class_status_handlers() {
2758
2759        let def = ClassDef::<AsyncModel>::new("Motor", |_dev| Ok(AsyncModel { count: 0, label: String::new() }))
2760            .status(AsyncModel::status_label);
2761        assert!(matches!(def.status, Some(StatusFn::SharedSync(_))));
2762
2763        let def = def.status_exclusive(|model: &mut AsyncModel, _dev: &Dev<AsyncModel>| Ok(format!("Count is {}", model.count)));
2764        assert!(matches!(def.status, Some(StatusFn::ExclusiveSync(_))));
2765
2766    }
2767
2768}