1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
//! Implementation of the classes of a server.
use std::sync::{Arc, Mutex, Weak};
use std::collections::HashMap;
use std::fmt::{self, Debug};
use std::future::Future;
use crate::dev::{All, Named, ToNames, ToNamesNotAll};
use crate::database::{GetPropSpec, SetPropSpec};
use crate::error::{Error, ErrorKind, Result};
use crate::name::{ArcName, Name};
use crate::value::Spectrum;
use super::{reserved_name, Model, Ref, RESERVED_ATTRS};
use super::dev::{AnyDev, Dev};
use super::server::Server;
use super::def::ClassDef;
/// A class of any model, which is how the server holds its classes.
pub trait AnyClass: Debug + Send + Sync {
/// The name of that class.
fn name(&self) -> &str;
/// The rank of that class among the classes of its server, which is the order they
/// were made in and the order their devices are initialized in, see [`Server::sync`].
fn index(&self) -> usize;
/// Add a device of that class to the server, see [`Class::add_dev`].
fn add_dev(&self, name: &str) -> Result<Arc<dyn AnyDev>>;
}
pub struct Class<M: Model> {
/// The name of that class, as its definition gave it, shared with the key it is
/// registered under in the server.
name: Arc<str>,
/// See [`AnyClass::index`].
index: usize,
/// Self reference, for what a class hands its devices of itself.
this: Weak<Self>,
server: Arc<Server>,
def: Arc<ClassDef<M>>,
/// The properties the database holds for the attributes of this class, by attribute
/// name, which every device of the class is configured from, see [`Self::attr_props`].
attr_props: Mutex<HashMap<ArcName, Arc<[Named<Spectrum<str>>]>>>,
}
impl<M: Model> Class<M> {
/// A new class of that server, from its definition, at that rank among the classes of
/// the server, see [`AnyClass::index`].
pub(super) fn new(server: Arc<Server>, def: Arc<ClassDef<M>>, index: usize) -> Arc<Self> {
Arc::new_cyclic(|this| Self {
name: Arc::from(&*def.name),
index,
this: this.clone(),
server,
def,
attr_props: Mutex::new(HashMap::new()),
})
}
#[inline]
pub fn name(&self) -> &str {
&self.name
}
#[inline]
pub fn server(&self) -> &Server {
&self.server
}
// The properties of a class are the database's: everything below asks the database
// of the server for what it holds of this class, see the very same methods on
// [`crate::database::DatabaseProxy`]. The blocking forms are for synchronous handlers,
// which run on the pool, and must never run in an asynchronous one.
/// The names of the properties of this class that match the given filter.
pub async fn list_prop_async(&self, filter: &str) -> Result<Spectrum<str>> {
self.server().database().list_class_prop_async(&self.name, filter).await
}
/// The names of the properties of this class, see [`Self::list_prop_async`].
pub fn list_prop(&self, filter: &str) -> Result<Spectrum<str>> {
self.server().database().list_class_prop(&self.name, filter)
}
/// The properties of this class, parsed into the shape the given names ask for, see
/// [`crate::database::DatabaseProxy::get_class_prop`].
pub fn get_prop_async<Spec, Names: ToNames>(&self, names: Names) -> impl Future<Output = Result<Spec::Output>> + Send
where
Spec: GetPropSpec<Names>,
{
self.server().database().get_class_prop_async::<Spec, Names>(&self.name, names)
}
/// The properties of this class, see [`Self::get_prop_async`].
pub fn get_prop<Spec, Names: ToNames>(&self, names: Names) -> Result<Spec::Output>
where
Spec: GetPropSpec<Names>,
{
self.server().database().get_class_prop::<Spec, Names>(&self.name, names)
}
/// Write the given properties of this class.
pub fn set_prop_async<'a, Spec>(&'a self, spec: Spec) -> impl Future<Output = Result<()>> + Send
where
Spec: SetPropSpec<'a>,
{
self.server().database().set_class_prop_async(&self.name, spec)
}
/// Write the given properties of this class, see [`Self::set_prop_async`].
pub fn set_prop<'a, Spec>(&'a self, spec: Spec) -> Result<()>
where
Spec: SetPropSpec<'a>,
{
self.server().database().set_class_prop(&self.name, spec)
}
/// Delete the given properties of this class.
pub fn del_prop_async<Names: ToNamesNotAll>(&self, names: Names) -> impl Future<Output = Result<()>> + Send {
self.server().database().del_class_prop_async(&self.name, names)
}
/// Delete the given properties of this class, see [`Self::del_prop_async`].
pub fn del_prop<Names: ToNamesNotAll>(&self, names: Names) -> Result<()> {
self.server().database().del_class_prop(&self.name, names)
}
/// The properties the database holds for the attributes of this class of those names,
/// in the same order, an attribute it holds nothing for having none.
///
/// They are the same for every device of the class, so the database is only asked the
/// first time a device has an attribute of that name, and never again: what a device
/// is configured with only changes at the level of the device itself, which is the
/// one a client changes.
///
/// The State and the Status have none, whatever the database holds: they are the
/// server's own, which only the device configures, the way cppTango does.
pub(super) async fn attr_props(&self, names: &[&str]) -> Result<Vec<Arc<[Named<Spectrum<str>>]>>> {
// Only refetch the missing attribute names.
let missing = {
let attr_props = self.attr_props.lock().unwrap();
names.iter()
.copied()
.filter(|name| !attr_props.contains_key(Name::new(name)))
.filter(|name| reserved_name(name, RESERVED_ATTRS).is_none())
.collect::<Vec<_>>()
};
if !missing.is_empty() {
let fetched = self.server()
.database()
.get_class_attr_prop_async::<Spectrum<str>, _, _>(&self.name, missing.as_slice(), All).await?;
let mut attr_props = self.attr_props.lock().unwrap();
for (name, props) in missing.into_iter().zip(fetched) {
attr_props.insert(ArcName::from(Arc::<str>::from(name)), Arc::from(props?));
}
}
let attr_props = self.attr_props.lock().unwrap();
Ok(names
.iter()
.map(|name| attr_props.get(Name::new(name))
.map_or_else(|| Arc::from([]), Arc::clone))
.collect())
}
#[inline]
pub fn def(&self) -> &ClassDef<M> {
&self.def
}
/// Add a device of that class to the server, whose name has to be unique server-wide.
///
/// The device is only registered: it is initialized, served and exported by the next
/// [`Server::sync`].
pub fn add_dev(&self, name: &str) -> Result<Ref<'_, Dev<M>>> {
self.new_dev(name).map(Ref::new)
}
/// See [`Self::add_dev`].
fn new_dev(&self, name: &str) -> Result<Arc<Dev<M>>> {
let mut server_devs = self.server.devs.lock().unwrap();
if server_devs.contains_key(Name::new(name)) {
return Err(Error::new(ErrorKind::DevAlreadyDefined)
.with_append_description(format_args!("Device {name:?}")));
}
let name = Arc::<str>::from(name);
// A class is only ever reached through a reference to it, so it is alive.
let dev = Dev::new(self.this.upgrade().unwrap(), Arc::clone(&name));
server_devs.insert(ArcName::from(name), Arc::clone(&dev) as Arc<dyn AnyDev>);
drop(server_devs);
self.server.pending.lock().unwrap().added.push(Arc::clone(&dev) as Arc<dyn AnyDev>);
Ok(dev)
}
}
impl<M: Model> AnyClass for Class<M> {
#[inline]
fn name(&self) -> &str {
&self.name
}
#[inline]
fn index(&self) -> usize {
self.index
}
fn add_dev(&self, name: &str) -> Result<Arc<dyn AnyDev>> {
self.new_dev(name).map(|dev| dev as Arc<dyn AnyDev>)
}
}
impl<M: Model> Debug for Class<M> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Class").field("name", &self.name).finish_non_exhaustive()
}
}