Skip to main content

tango/server/
command.rs

1//! Implementation of command instantiations.
2
3use std::sync::Arc;
4
5use crate::error::{Error, Result};
6use crate::value::{CommandDataKind, Value};
7use crate::dev::CommandInfo;
8
9use super::dev::{some_doc, Access, Dev, ModelRef};
10use super::def::{CommandDef, ExecFn};
11use super::Model;
12
13
14/// A command of a single device, which is what the server runs when a client asks for it,
15/// around the definition it was defined with.
16/// 
17/// The definition is shared with the class when the class defines it, and owned by the
18/// device alone when the device added it for itself, see [`Dev::add_command`].
19pub struct Command<M: Model> {
20    /// Comman def, behind a pointer because it's a huge structure..
21    def: Arc<CommandDef<M>>,
22}
23
24impl<M: Model> Command<M> {
25
26    /// A new command of a device, from its definition.
27    pub(super) fn new(def: Arc<CommandDef<M>>) -> Self {
28        Self { def }
29    }
30
31    /// The name of that command, as its definition gave it.
32    #[inline]
33    pub fn name(&self) -> &str {
34        &self.def.name
35    }
36
37    /// The access executing that command needs to the model.
38    pub(super) fn access(&self) -> Access {
39        match &self.def.exec {
40            Some(ExecFn::ExclusiveSync(_) | ExecFn::ExclusiveAsync(_)) => Access::Exclusive,
41            _ => Access::Shared,
42        }
43    }
44
45    /// Whether executing that command may block, its execution function being synchronous,
46    /// see [`crate::pool::run`].
47    pub(super) fn may_block(&self) -> bool {
48        matches!(&self.def.exec, Some(ExecFn::SharedSync(_) | ExecFn::ExclusiveSync(_)))
49    }
50
51    /// What a client is told of that command: what its definition advertises, a side of it
52    /// no execution function derived a kind for being void.
53    pub fn info(&self) -> CommandInfo {
54        CommandInfo {
55            display_level: self.def.display_level,
56            in_kind: self.def.in_kind.unwrap_or(CommandDataKind::Void),
57            out_kind: self.def.out_kind.unwrap_or(CommandDataKind::Void),
58            in_doc: some_doc(&self.def.in_doc),
59            out_doc: some_doc(&self.def.out_doc),
60        }
61    }
62
63    /// Execute that command on the model, borrowed with the access [`Self::access`] asked
64    /// for, and give its result.
65    pub(super) async fn exec(&self, dev: &Dev<M>, model: &mut ModelRef<'_, M>, input: Value) -> Result<Value> {
66        match &self.def.exec {
67            Some(ExecFn::SharedSync(func)) => func(model.shared(), dev, input),
68            Some(ExecFn::ExclusiveSync(func)) => func(model.exclusive()?, dev, input),
69            Some(ExecFn::SharedAsync(func)) => func(model.shared(), dev, input).await,
70            Some(ExecFn::ExclusiveAsync(func)) => func(model.exclusive()?, dev, input).await,
71            None => Err(Error::not_implemented()
72                .with_description(format!("The command {} of the device {} has no execution function", self.name(), dev.name()))),
73        }
74    }
75
76}