1use std::sync::{Arc, Mutex};
4use std::time::{Duration, SystemTime};
5
6use crate::error::{Error, ErrorKind, Result};
7use crate::value::{CommandDataKind, Value};
8use crate::dev::{AttrRead, CommandInfo, DevSource, DisplayLevel, RecordedResult};
9
10use super::dev::{some_doc, Access, Dev, ModelRef, ReadCtx};
11use super::def::{CommandDef, ExecFn};
12use super::attr::{Attr, AttrRole};
13use super::history::History;
14use super::{Model, INIT_NAME};
15
16
17pub struct Command<M: Model>(CommandRole<M>);
24
25enum CommandRole<M: Model> {
27 Interface(InterfaceCommand<M>),
29 Attr(Arc<Attr<M>>),
34 Init,
36}
37
38struct InterfaceCommand<M: Model> {
41 def: Arc<CommandDef<M>>,
43 history: Mutex<History<Value>>,
45}
46
47impl<M: Model> Command<M> {
48
49 pub(super) fn new(def: Arc<CommandDef<M>>) -> Self {
51 Self(CommandRole::Interface(InterfaceCommand {
52 def,
53 history: Mutex::new(History::new()),
54 }))
55 }
56
57 pub(super) fn new_with_attr(attr: Arc<Attr<M>>) -> Self {
59 debug_assert_ne!(attr.role(), AttrRole::Interface, "only the State and the Status are commands");
60 Self(CommandRole::Attr(attr))
61 }
62
63 pub(super) fn new_with_init() -> Self {
65 Self(CommandRole::Init)
66 }
67
68 #[inline]
70 pub fn name(&self) -> &str {
71 match &self.0 {
72 CommandRole::Interface(InterfaceCommand { def, .. }) => &def.name,
73 CommandRole::Attr(attr) => attr.name(),
74 CommandRole::Init => INIT_NAME,
75 }
76 }
77
78 pub(super) fn poll_period(&self) -> Option<Duration> {
81 match &self.0 {
82 CommandRole::Interface(InterfaceCommand { def, .. }) => def.poll_period,
83 CommandRole::Attr(_) |
84 CommandRole::Init => None,
85 }
86 }
87
88 pub(super) fn is_pollable(&self) -> bool {
91 match &self.0 {
92 CommandRole::Interface(InterfaceCommand { def, .. }) => matches!(def.in_kind, None | Some(CommandDataKind::Void)),
93 CommandRole::Attr(_) => true,
94 CommandRole::Init => false,
95 }
96 }
97
98 pub(super) fn attr_role(&self) -> AttrRole {
101 match &self.0 {
102 CommandRole::Attr(attr) => attr.role(),
103 CommandRole::Interface(_) |
104 CommandRole::Init => AttrRole::Interface,
105 }
106 }
107
108 pub(super) fn is_init(&self) -> bool {
111 matches!(self.0, CommandRole::Init)
112 }
113
114 pub(super) fn exec_access(&self, dev: &Dev<M>, ctx: &ReadCtx<M>) -> Access {
117 match &self.0 {
118 CommandRole::Interface(InterfaceCommand { def, .. }) => match &def.exec {
119 Some(ExecFn::ExclusiveSync(_) | ExecFn::ExclusiveAsync(_)) => Access::Exclusive,
120 _ => Access::Shared,
121 },
122 CommandRole::Attr(attr) => attr.read_access(dev, ctx),
123 CommandRole::Init => Access::Exclusive,
124 }
125 }
126
127 pub(super) fn involves_state(&self) -> bool {
130 matches!(self.0, CommandRole::Attr(_))
131 }
132
133 pub(super) fn may_block(&self, dev: &Dev<M>, ctx: &ReadCtx<M>) -> bool {
136 match &self.0 {
137 CommandRole::Interface(InterfaceCommand { def, .. }) =>
138 matches!(&def.exec, Some(ExecFn::SharedSync(_) | ExecFn::ExclusiveSync(_))),
139 CommandRole::Attr(attr) =>
140 attr.read_may_block(dev, ctx),
141 CommandRole::Init =>
142 dev.init_may_block(),
143 }
144 }
145
146 pub fn info(&self) -> CommandInfo {
150 match &self.0 {
151 CommandRole::Interface(InterfaceCommand { def, .. }) => CommandInfo {
152 display_level: def.display_level,
153 in_kind: def.in_kind.unwrap_or(CommandDataKind::Void),
154 out_kind: def.out_kind.unwrap_or(CommandDataKind::Void),
155 in_doc: some_doc(&def.in_doc),
156 out_doc: some_doc(&def.out_doc),
157 },
158 CommandRole::Attr(attr) => CommandInfo {
159 display_level: attr.display_level(),
160 in_kind: CommandDataKind::Void,
161 out_kind: match attr.role() {
162 AttrRole::State => CommandDataKind::State,
163 AttrRole::Status => CommandDataKind::String,
164 AttrRole::Interface => unreachable!("only the State and the Status are commands"),
165 },
166 in_doc: None,
167 out_doc: attr.config().doc,
168 },
169 CommandRole::Init => CommandInfo {
170 display_level: DisplayLevel::Operator,
171 in_kind: CommandDataKind::Void,
172 out_kind: CommandDataKind::Void,
173 in_doc: None,
174 out_doc: None,
175 },
176 }
177 }
178
179 pub fn push_history(&self, result: Result<Value>, valid_for: Option<Duration>) -> Result<()> {
193 match &self.0 {
194 CommandRole::Interface(InterfaceCommand { def, history }) => {
195 if !matches!(def.in_kind, None | Some(CommandDataKind::Void)) {
196 return Err(Error::new(ErrorKind::InvalidData)
197 .with_description(format!("Command {:?} cannot be recorded, it takes an argument",
198 self.name())));
199 }
200 history.lock().unwrap().push(SystemTime::now(), result, valid_for);
201 Ok(())
202 }
203 CommandRole::Attr(attr) => attr.push_history(result.map(AttrRead::new), valid_for),
204 CommandRole::Init => Err(Error::new(ErrorKind::InvalidData)
205 .with_description(format!("Command {:?} cannot be recorded", self.name()))),
206 }
207 }
208
209 pub fn history(&self, max: usize) -> Vec<RecordedResult<Value>> {
212 match &self.0 {
213 CommandRole::Interface(InterfaceCommand { history, .. }) => history.lock().unwrap().entries(max),
214 CommandRole::Attr(attr) => attr.history(max).into_iter()
216 .map(|entry| entry.map(|read| read.map(|read| read.value
217 .expect("the State and the Status are always recorded with a value"))))
218 .collect(),
219 CommandRole::Init => Vec::new(),
220 }
221 }
222
223 pub fn clear_history(&self) {
226 match &self.0 {
227 CommandRole::Interface(InterfaceCommand { history, .. }) => history.lock().unwrap().clear(),
228 CommandRole::Attr(attr) => attr.clear_history(),
229 CommandRole::Init => {}
230 }
231 }
232
233 pub fn history_depth(&self) -> usize {
235 match &self.0 {
236 CommandRole::Interface(InterfaceCommand { history, .. }) => history.lock().unwrap().depth(),
237 CommandRole::Attr(attr) => attr.history_depth(),
238 CommandRole::Init => 0,
239 }
240 }
241
242 pub fn set_history_depth(&self, depth: usize) {
245 match &self.0 {
246 CommandRole::Interface(InterfaceCommand { history, .. }) => history.lock().unwrap().set_depth(depth),
247 CommandRole::Attr(attr) => attr.set_history_depth(depth),
248 CommandRole::Init => {}
249 }
250 }
251
252 pub(super) fn not_polled_err(&self, dev: &Dev<M>) -> Error {
255 Error::new(ErrorKind::CommandNotPolled)
256 .with_description(format!("Command {:?} of device {:?} has no result recorded to be \
257 answered from the cache", self.name(), dev.name()))
258 }
259
260 pub(super) async fn exec(&self, dev: &Dev<M>, model: &mut ModelRef<'_, M>, input: Value, ctx: &mut ReadCtx<M>, source: DevSource) -> Result<Value> {
268
269 let (def, history) = match &self.0 {
270 CommandRole::Interface(InterfaceCommand { def, history }) => (def, history),
271 CommandRole::Attr(attr) => {
272 return attr.read(dev, model, ctx, source).await?.value
273 .ok_or_else(|| Error::new(ErrorKind::Internal)
274 .with_description(format!("Attribute {:?} of device {:?} was read with no value",
275 attr.name(), dev.name())));
276 }
277 CommandRole::Init => {
278 return Err(Error::new(ErrorKind::Internal)
279 .with_description(format!("Command {:?} of device {:?} cannot be executed on a borrowed model",
280 self.name(), dev.name())));
281 }
282 };
283
284 if let DevSource::CacheDev | DevSource::Cache = source {
287
288 let history = history.lock().unwrap();
289 if let Some(result) = history.latest() {
290 return result.clone();
291 }
292
293 if source == DevSource::Cache {
294 return Err(if history.is_empty() {
295 self.not_polled_err(dev)
296 } else {
297 Error::new(ErrorKind::ValueOutdated)
298 .with_description(format!("The latest result recorded of command {:?} of device {:?} is outdated",
299 self.name(), dev.name()))
300 });
301 }
302
303 }
304
305 match &def.exec {
306 Some(ExecFn::SharedSync(func)) => func(model.shared(), dev, input),
307 Some(ExecFn::ExclusiveSync(func)) => func(model.exclusive()?, dev, input),
308 Some(ExecFn::SharedAsync(func)) => func(model.shared(), dev, input).await,
309 Some(ExecFn::ExclusiveAsync(func)) => func(model.exclusive()?, dev, input).await,
310 None => Err(Error::not_implemented()
311 .with_description(format!("Command {:?} of device {:?} has no execution function", self.name(), dev.name()))),
312 }
313
314 }
315
316}