touchportal_sdk/
states.rs

1use derive_builder::Builder;
2use indexmap::IndexSet;
3use serde::{Deserialize, Serialize};
4
5/// In Touch Portal the user can use States which can be used by IF statement and with Events for
6/// example but can also be used in button texts or most actions.
7///
8/// With your plugin you can add states to Touch Portal that represent states from the software you
9/// are integrating as a plug-in. You can define a state as part of a category. Events can link to
10/// the id of the states to be able to act on changes of those states for example.
11///
12/// Please note: when a user makes a reference to any of the states from a plug-in in their actions
13/// they are stored in that text locally. When you change the state id for example in your plugin,
14/// all existing references to the old state are not updated and will result in null errors and no
15/// conversion will be done. Only change ID's when you are absolutely sure it will not break
16/// anything for your users.
17#[derive(Debug, Clone, Builder, Deserialize, Serialize)]
18#[serde(rename_all = "camelCase")]
19pub struct State {
20    /// This is the id of the state.
21    ///
22    /// It is used to identify the states within Touch Portal. This id needs to be unique across
23    /// plugins. This means that if you give it the id "1" there is a big chance that it will be a
24    /// duplicate. Touch Portal may reject it or when the other state is updated, yours will be as
25    /// well with wrong data. Best practice is to create a unique prefix for all your states like
26    /// in our case; `tp_sid_fruit`.
27    #[builder(setter(into))]
28    pub(crate) id: String,
29
30    /// This text describes the state and is used in the IF statement to let the user see what
31    /// state it is changing.
32    ///
33    /// We recommend to make this text work in the flow of the inline nature of the IF statement
34    /// within Touch Portal. This is also the title that is used in list where you can use this
35    /// state value for logic execution.
36    #[builder(setter(into))]
37    #[serde(rename = "desc")]
38    pub(crate) description: String,
39
40    /// This is the value the state will have if it is not set already but is looked up.
41    #[builder(setter(into))]
42    #[serde(rename = "default")]
43    initial: String,
44
45    #[serde(flatten)]
46    pub(crate) kind: StateType,
47
48    /// The name of the parent group of this state.
49    ///
50    /// The parent group of this state will be used to group the state in the menus used throughout
51    /// Touch Portal. Every state belonging to the same parent group name will be in the same
52    /// selection menu.
53    #[builder(setter(into, strip_option), default)]
54    #[serde(skip_serializing_if = "Option::is_none")]
55    parent_group: Option<String>,
56}
57
58impl State {
59    pub fn builder() -> StateBuilder {
60        StateBuilder::default()
61    }
62}
63
64#[derive(Debug, Clone, Deserialize, Serialize)]
65#[non_exhaustive]
66#[serde(rename_all = "lowercase")]
67#[serde(tag = "type")]
68pub enum StateType {
69    /// A state where you specify a limited amount of state values the state can be.
70    Choice(ChoiceState),
71
72    /// A state that contains a free text field.
73    ///
74    /// This type can be used for smart conversion as well.
75    ///
76    /// `#FF115599` (`#AARRGGBB`) can be interpreted by the plug-in visuals action as a color. The
77    /// format needs to be this or it will not be seen as a color and will not be converted.
78    ///
79    /// A base64 representation of an image will also be allowed for specific actions such as the
80    /// plug-in visuals action. This will read the base64 string representation and convert it to
81    /// an image and show it on the button. We suggest to keep these as small as possible. Images
82    /// used like this on a button are not stored and only exist temporary. This allows for a
83    /// performant updating process. Allow for multiple updates per second depending on the
84    /// computer used, the device used and the quality of the network.
85    ///
86    /// The base64 string should only hold the base64 data. The meta data should be stripped. The
87    /// format has to be a PNG. It has to be a squared image.
88    Text(TextState),
89}
90
91#[derive(Debug, Clone, Builder, Deserialize, Serialize)]
92#[serde(rename_all = "camelCase")]
93pub struct ChoiceState {
94    /// Specify the collection of values that can be used to choose from.
95    ///
96    /// These can also be dynamically changed if you use the dynamic actions.
97    #[builder(setter(each(name = "choice", into)))]
98    #[serde(rename = "valueChoices")]
99    pub(crate) choices: IndexSet<String>,
100}
101
102impl ChoiceState {
103    pub fn builder() -> ChoiceStateBuilder {
104        ChoiceStateBuilder::default()
105    }
106}
107
108#[derive(Debug, Clone, Builder, Deserialize, Serialize)]
109#[serde(rename_all = "camelCase")]
110pub struct TextState {}
111
112impl TextState {
113    pub fn builder() -> TextStateBuilder {
114        TextStateBuilder::default()
115    }
116}
117
118#[test]
119fn serialize_example_state() {
120    assert_eq!(
121        serde_json::to_value(
122            State::builder()
123                .id("tp_sid_fruit")
124                .description("Fruit Kind description")
125                .initial("Apple")
126                .parent_group("Fruits")
127                .kind(StateType::Choice(
128                    ChoiceState::builder()
129                        .choice("Apple")
130                        .choice("Pears")
131                        .choice("Grapes")
132                        .choice("Bananas")
133                        .build()
134                        .unwrap()
135                ))
136                .build()
137                .unwrap()
138        )
139        .unwrap(),
140        serde_json::json! {{
141          "id":"tp_sid_fruit",
142          "type":"choice",
143          "desc":"Fruit Kind description",
144          "default":"Apple",
145          "parentGroup":"Fruits",
146          "valueChoices": [
147            "Apple",
148            "Pears",
149            "Grapes",
150            "Bananas"
151          ]
152        }}
153    );
154}