stinger-ipc 0.0.10__py3-none-any.whl → 0.0.26__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.26.dist-info}/METADATA +3 -2
  2. {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.26.dist-info}/RECORD +35 -29
  3. stingeripc/asyncapi.py +3 -0
  4. stingeripc/components.py +61 -10
  5. stingeripc/schema/schema.yaml +240 -0
  6. stingeripc/templates/cpp/examples/client_main.cpp.jinja2 +18 -0
  7. stingeripc/templates/cpp/include/broker.hpp.jinja2 +18 -10
  8. stingeripc/templates/cpp/include/client.hpp.jinja2 +80 -11
  9. stingeripc/templates/cpp/include/ibrokerconnection.hpp.jinja2 +24 -4
  10. stingeripc/templates/cpp/include/property_structs.hpp.jinja2 +40 -2
  11. stingeripc/templates/cpp/include/server.hpp.jinja2 +5 -1
  12. stingeripc/templates/cpp/include/structs.hpp.jinja2 +2 -0
  13. stingeripc/templates/cpp/partials/args.jinja2 +11 -0
  14. stingeripc/templates/cpp/partials/deserialize.jinja2 +42 -0
  15. stingeripc/templates/cpp/partials/serialize.jinja2 +18 -0
  16. stingeripc/templates/cpp/src/broker.cpp.jinja2 +46 -24
  17. stingeripc/templates/cpp/src/client.cpp.jinja2 +111 -38
  18. stingeripc/templates/cpp/src/property_structs.cpp.jinja2 +25 -0
  19. stingeripc/templates/cpp/src/server.cpp.jinja2 +17 -25
  20. stingeripc/templates/cpp/src/structs.cpp.jinja2 +13 -0
  21. stingeripc/templates/html/app.js.jinja2 +130 -29
  22. stingeripc/templates/html/index.html.jinja2 +96 -8
  23. stingeripc/templates/html/styles.css.jinja2 +135 -0
  24. stingeripc/templates/markdown/index.md.jinja2 +151 -10
  25. stingeripc/templates/python/server.py.jinja2 +138 -18
  26. stingeripc/templates/rust/client/src/lib.rs.jinja2 +24 -5
  27. stingeripc/templates/rust/server/examples/server.rs.jinja2 +59 -23
  28. stingeripc/templates/rust/server/src/lib.rs.jinja2 +72 -51
  29. stingeripc/tools/cli.py +33 -7
  30. stingeripc/tools/cpp_generator.py +90 -0
  31. stingeripc/tools/rust_generator.py +1 -1
  32. stingeripc/templates/rust/payloads/src/handler.rs.jinja2 +0 -0
  33. {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.26.dist-info}/WHEEL +0 -0
  34. {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.26.dist-info}/entry_points.txt +0 -0
  35. {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.26.dist-info}/licenses/LICENSE +0 -0
  36. {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.26.dist-info}/top_level.txt +0 -0
@@ -7,15 +7,31 @@ This is the Server for the {{stinger.name}} interface.
7
7
 
8
8
  import json
9
9
  import logging
10
-
10
+ import threading
11
+ from dataclasses import dataclass, field
11
12
  logging.basicConfig(level=logging.DEBUG)
12
13
 
13
- from typing import Callable, Dict, Any, Optional, List
14
+ from typing import Callable, Dict, Any, Optional, List, Generic, TypeVar
14
15
  from connection import BrokerConnection
15
16
  from method_codes import *
16
17
  import {{stinger.get_enum_module_name()}} as {{stinger.get_enum_module_alias()}}
17
18
  {%macro method_type_annotation(method) %}Callable[[{%if method.arg_list | length > 0%}{%for arg in method.arg_list%}{{arg.python_type}}{%if not loop.last%}, {%endif%}{%endfor%}{%else%}None{%endif%}], {{method.return_value_python_type}}]{%endmacro%}
18
19
 
20
+ T = TypeVar('T')
21
+
22
+ @dataclass
23
+ class PropertyControls(Generic[T]):
24
+ value: T | None = None
25
+ mutex = threading.Lock()
26
+ version: int = -1
27
+ subscription_id: int | None = None
28
+ callbacks: List[Callable[[T], None]] = field(default_factory=list)
29
+
30
+ @dataclass
31
+ class MethodControls:
32
+ subscription_id: int | None = None
33
+ callback: Optional[Callable] = None
34
+
19
35
  class {{stinger.python.server_class_name}}:
20
36
 
21
37
  def __init__(self, connection: BrokerConnection):
@@ -25,18 +41,15 @@ class {{stinger.python.server_class_name}}:
25
41
  self._conn = connection
26
42
  self._conn.set_message_callback(self._receive_message)
27
43
  self._conn.set_last_will(topic="{{stinger.interface_info.0}}", payload=None, qos=1, retain=True)
28
- {%for prop_name, prop_spec in stinger.properties.items()-%}
29
- self._property_{{prop_name}} = None
30
- self._conn.subscribe("{{prop_spec.update_topic}}")
31
- self.changed_value_callback_for_{{pprop_name}} = None
32
- self._publish_interface_info()
44
+ {%for prop_name, prop in stinger.properties.items()%}
45
+ self._property_{{prop_name|snake_case}}: PropertyControls[{%if prop.arg_list | length == 1%}{{prop.arg_list[0].python_class}}{%else%}{{prop.python_class}}{%endif%}, {%for arg in prop.arg_list %}{{arg.python_type}}{%if not loop.last%}, {%endif%}{%endfor%}] = PropertyControls()
46
+ self._property_{{prop_name|snake_case}}.subscription_id = self._conn.subscribe("{{prop.update_topic}}")
33
47
  {%endfor-%}
34
- {%-for method in stinger.methods.values()%}
35
- self._conn.subscribe("{{method.topic}}")
36
- {%endfor-%}
37
- {%for method_name, method in stinger.methods.items()-%}
38
- self._{{method_name|snake_case}}_method_handler: Optional[{{method_type_annotation(method)}}] = None
48
+ {%-for method_name, method in stinger.methods.items()%}
49
+ self._method_{{method_name|snake_case}} = MethodControls()
50
+ self._method_{{method_name|snake_case}}.subscription_id = self._conn.subscribe("{{method.topic}}")
39
51
  {%endfor%}
52
+ self._publish_interface_info()
40
53
 
41
54
  def _receive_message(self, topic: str, payload: str, properties: Dict[str, Any]):
42
55
  """ This is the callback that is called whenever any message is received on a subscribed topic.
@@ -44,7 +57,7 @@ class {{stinger.python.server_class_name}}:
44
57
  self._logger.debug("Received message to %s", topic)
45
58
  {%if stinger.methods | length > 0 -%}
46
59
  {%for method_name, method in stinger.methods.items()-%}
47
- {%if not loop.first%}el{%endif%}if self._conn.is_topic_sub(topic, "{{method.topic}}"):
60
+ {%if not loop.first%}el{%endif%}if (properties.get('SubscriptionId', -1) == self._method_{{method_name|snake_case}}.subscription_id) or self._conn.is_topic_sub(topic, "{{method.topic}}"):
48
61
  try:
49
62
  payload_obj = json.loads(payload)
50
63
  except json.decoder.JSONDecodeError:
@@ -52,7 +65,25 @@ class {{stinger.python.server_class_name}}:
52
65
  else:
53
66
  self._process_{{method_name | snake_case}}_call(topic, payload_obj, properties)
54
67
  {%endfor%}
55
- {%-else%}pass{%-endif%}
68
+ {%-endif%}
69
+ {%for prop_name, prop in stinger.properties.items()%}
70
+ {%if not loop.first%}el{%endif%}if (properties.get('SubscriptionId', -1) == self._property_{{prop_name|snake_case}}.subscription_id) or self._conn.is_topic_sub(topic, "{{prop.update_topic}}"):
71
+ {%if prop.arg_list | length > 1 -%}
72
+ prop_value = {{prop.python_class}}.model_validate_json(payload)
73
+ {%else-%}
74
+ payload_obj = json.loads(payload)
75
+ prop_value = {{prop.arg_list[0].python_class}}(payload_obj["{{prop.arg_list[0].name}}"])
76
+ {%endif-%}
77
+ with self._property_{{prop_name|snake_case}}.mutex:
78
+ self._property_{{prop_name|snake_case}}.value = prop_value
79
+ self._property_{{prop_name|snake_case}}.version += 1
80
+ for callback in self._property_{{prop_name|snake_case}}.callbacks:
81
+ {%-if prop.arg_list | length > 1%}
82
+ callback({%for arg in prop.arg_list %}prop_value.{{arg.name|snake_case}}{%if not loop.last%}, {%endif%}{%endfor%})
83
+ {%-else%}
84
+ callback(prop_value)
85
+ {%-endif%}
86
+ {%endfor%}
56
87
 
57
88
  def _publish_interface_info(self):
58
89
  self._conn.publish("{{stinger.interface_info.0}}", '''{{stinger.interface_info.1 | tojson}}''', qos=1, retain=True)
@@ -78,8 +109,8 @@ class {{stinger.python.server_class_name}}:
78
109
  def handle_{{method_name | snake_case}}(self, handler: {{method_type_annotation(method)}}):
79
110
  """ This is a decorator to decorate a method that will handle the '{{method_name}}' method calls.
80
111
  """
81
- if self._{{method_name|snake_case}}_method_handler is None and handler is not None:
82
- self._{{method_name|snake_case}}_method_handler = handler
112
+ if self._method_{{method_name|snake_case}}.callback is None and handler is not None:
113
+ self._method_{{method_name|snake_case}}.callback = handler
83
114
  else:
84
115
  raise Exception("Method handler already set")
85
116
 
@@ -90,7 +121,7 @@ class {{stinger.python.server_class_name}}:
90
121
  correlation_id = properties.get('CorrelationData') # type: Optional[bytes]
91
122
  response_topic = properties.get('ResponseTopic') # type: Optional[str]
92
123
  self._logger.info("Correlation Data %s", correlation_id)
93
- if self._{{method_name|snake_case}}_method_handler is not None:
124
+ if self._method_{{method_name|snake_case}}.callback is not None:
94
125
  method_args = [] # type: List[Any]
95
126
  {%for arg in method.arg_list -%}
96
127
  if "{{arg.name}}" in payload:
@@ -114,7 +145,7 @@ class {{stinger.python.server_class_name}}:
114
145
  return_json = ""
115
146
  debug_msg = None # type: Optional[str]
116
147
  try:
117
- return_struct = self._{{method_name|snake_case}}_method_handler(*method_args)
148
+ return_struct = self._method_{{method_name|snake_case}}.callback(*method_args)
118
149
  self._logger.debug("Return value is %s", return_struct)
119
150
  {%if method.return_value is false%}
120
151
  return_json = "{}"
@@ -147,7 +178,65 @@ class {{stinger.python.server_class_name}}:
147
178
  self._conn.publish(response_topic, return_json, qos=1, retain=False,
148
179
  correlation_id=correlation_id, return_value=return_code, debug_info=debug_msg)
149
180
  {%endfor%}
181
+ {%for prop_name, prop in stinger.properties.items()%}
182
+ @property
183
+ def {{prop_name | snake_case}}(self) -> {{prop.python_class}}|None:
184
+ """ This property returns the last received value for the '{{prop_name}}' property.
185
+ """
186
+ with self._property_{{prop_name|snake_case}}_mutex:
187
+ return self._property_{{prop_name|snake_case}}
150
188
 
189
+ @{{prop_name | snake_case}}.setter
190
+ def {{prop_name | snake_case}}(self, {%if prop.arg_list | length > 1%}value{%else%}{{prop.arg_list[0].name|snake_case}}{%endif%}: {{prop.python_class}}):
191
+ """ This property sets (publishes) a new value for the '{{prop_name}}' property.
192
+ """
193
+ if not isinstance({%if prop.arg_list | length > 1%}value{%else%}{{prop.arg_list[0].name|snake_case}}{%endif%}, {{prop.python_class}}):
194
+ raise ValueError(f"The value must be {{prop.python_class}}.")
195
+
196
+ {%if prop.arg_list | length > 1%}
197
+ payload = value.model_dump_json()
198
+ {%else%}
199
+ payload = json.dumps({ "{{prop.arg_list[0].name}}": {{prop.arg_list[0].name|snake_case}} })
200
+ {%endif%}
201
+
202
+ if {%if prop.arg_list | length > 1%}value{%else%}{{prop.arg_list[0].name|snake_case}}{%endif%} != self._property_{{prop_name|snake_case}}.value:
203
+ with self._property_{{prop_name|snake_case}}.mutex:
204
+ self._property_{{prop_name|snake_case}}.value = {%if prop.arg_list | length > 1%}value{%else%}{{prop.arg_list[0].name|snake_case}}{%endif%}
205
+ self._property_{{prop_name|snake_case}}.version += 1
206
+ self._conn.publish("{{prop.value_topic}}", payload, qos=1, retain=True)
207
+ for callback in self._property_{{prop_name|snake_case}}.callbacks:
208
+ {%-if prop.arg_list | length > 1%}
209
+ callback({%for arg in prop.arg_list %}value.{{arg.name|snake_case}}{%if not loop.last%}, {%endif%}{%endfor%})
210
+ {%-else%}
211
+ callback({{prop.arg_list[0].name|snake_case}})
212
+ {%-endif%}
213
+
214
+ def set_{{prop_name | snake_case}}(self, {%for arg in prop.arg_list %}{{arg.name|snake_case}}: {{arg.python_type}}{%if not loop.last%}, {%endif%}{%endfor%}):
215
+ """ This method sets (publishes) a new value for the '{{prop_name}}' property.
216
+ """
217
+ {%-for arg in prop.arg_list%}
218
+ if not isinstance({{arg.name|snake_case}}, {{arg.python_class}}){%if arg.optional%} and {{arg.name|snake_case}} is not None{%endif%}:
219
+ raise ValueError(f"The '{{arg.name|snake_case}}' value must be {{arg.python_type}}.")
220
+ {%-endfor%}
221
+
222
+ {%if prop.arg_list | length > 1%}
223
+ obj = stinger_types.{{prop.python_local_type}}({%for arg in prop.arg_list%}
224
+ {{arg.name}}={{arg.name|snake_case}},
225
+ {%endfor%}
226
+ )
227
+ {%else%}
228
+ obj = {{prop.arg_list[0].name | snake_case}}
229
+ {%endif%}
230
+
231
+ # Use the property.setter to do that actual work.
232
+ self.{{prop_name|snake_case}} = obj
233
+
234
+ def on_{{prop_name | snake_case}}_updates(self, handler: Callable[[{%for arg in prop.arg_list %}{{arg.python_type}}{%if not loop.last%}, {%endif%}{%endfor%}], None]):
235
+ """ This method registers a callback to be called whenever a new '{{prop_name}}' property update is received.
236
+ """
237
+ if handler is not None:
238
+ self._property_{{prop_name|snake_case}}.callbacks.append(handler)
239
+ {%endfor%}
151
240
 
152
241
  class {{stinger.python.server_class_name}}Builder:
153
242
  """
@@ -159,6 +248,9 @@ class {{stinger.python.server_class_name}}Builder:
159
248
  {%for method_name, method in stinger.methods.items()%}
160
249
  self._{{method_name|snake_case}}_method_handler: Optional[{{method_type_annotation(method)}}] = None
161
250
  {%-endfor%}
251
+ {%for prop_name, property in stinger.properties.items()%}
252
+ self._{{prop_name|snake_case}}_property_callbacks: List[Callable[[{%for arg in property.arg_list %}{{arg.python_type}}{%if not loop.last%}, {%endif%}{%endfor%}], None]] = []
253
+ {%-endfor%}
162
254
  {%for method_name, method in stinger.methods.items()%}
163
255
  def handle_{{method_name | snake_case}}(self, handler: {{method_type_annotation(method)}}):
164
256
  if self._{{method_name|snake_case}}_method_handler is None and handler is not None:
@@ -166,12 +258,22 @@ class {{stinger.python.server_class_name}}Builder:
166
258
  else:
167
259
  raise Exception("Method handler already set")
168
260
  {%endfor%}
261
+ {%for prop_name, prop in stinger.properties.items()%}
262
+ def on_{{prop_name | snake_case}}_updates(self, handler: Callable[[{%for arg in prop.arg_list %}{{arg.python_type}}{%if not loop.last%}, {%endif%}{%endfor%}], None]):
263
+ """ This method registers a callback to be called whenever a new '{{prop_name}}' property update is received.
264
+ """
265
+ self._{{prop_name|snake_case}}_property_callbacks.append(handler)
266
+ {%endfor%}
169
267
  def build(self) -> {{stinger.python.server_class_name}}:
170
268
  new_server = {{stinger.python.server_class_name}}(self._conn)
171
269
  {%for method_name, method in stinger.methods.items()%}
172
270
  if self._{{method_name|snake_case}}_method_handler is not None:
173
271
  new_server.handle_{{method_name|snake_case}}(self._{{method_name|snake_case}}_method_handler)
174
272
  {%-endfor%}
273
+ {%for prop_name, prop in stinger.properties.items()%}
274
+ for callback in self._{{prop_name|snake_case}}_property_callbacks:
275
+ new_server.on_{{prop_name|snake_case}}_updates(callback)
276
+ {%endfor%}
175
277
  return new_server
176
278
 
177
279
  if __name__ == '__main__':
@@ -187,6 +289,18 @@ if __name__ == '__main__':
187
289
  conn = {{broker.class_name}}({%if broker.hostname is none%}'localhost', 1883{%endif%})
188
290
  server = {{stinger.python.server_class_name}}(conn)
189
291
 
292
+ {%for prop_name, prop in stinger.properties.items()-%}
293
+ {%if prop.arg_list | length > 1%}
294
+ server.{{prop_name | snake_case}} = {{prop.python_class}}(
295
+ {%for arg in prop.arg_list%}
296
+ {{arg.name}}={{arg.get_random_example_value()}},
297
+ {%endfor%}
298
+ )
299
+ {%else%}
300
+ server.{{prop_name | snake_case}} = {{prop.arg_list[0].get_random_example_value()}}
301
+ {%endif%}
302
+ {%endfor%}
303
+
190
304
  {%for method_name, method in stinger.methods.items()%}
191
305
  @server.handle_{{method_name | snake_case}}
192
306
  def {{method_name | snake_case}}({%for arg in method.arg_list%}{{arg.name}}: {{arg.python_type}}{%if not loop.last%}, {%endif%}{%endfor%}) -> {{method.return_value_python_type}}:
@@ -195,6 +309,12 @@ if __name__ == '__main__':
195
309
  return {{method.get_return_value_random_example_value('python')}}
196
310
  {%endfor%}
197
311
 
312
+ {%for prop_name, prop in stinger.properties.items()%}
313
+ @server.on_{{prop_name | snake_case}}_updates
314
+ def on_{{prop_name | snake_case}}_update({%for arg in prop.arg_list %}{{arg.name}}: {{arg.python_type}}{%if not loop.last%}, {%endif%}{%endfor%}):
315
+ print(f"Received update for '{{prop_name}}' property: {%for arg in prop.arg_list %}{ {{arg.name}}= }{%if not loop.last%}, {%endif%}{%endfor%}")
316
+ {%endfor%}
317
+
198
318
  print("Ctrl-C will stop the program.")
199
319
 
200
320
  while True:
@@ -18,7 +18,7 @@ use serde_json;
18
18
  use {{stinger.rust.common_package_name}}::{MethodResultCode, *};
19
19
 
20
20
  use std::sync::{Arc, Mutex};
21
- use tokio::sync::{broadcast, mpsc{%if stinger.methods|length > 0 -%}, oneshot{%endif-%}};
21
+ use tokio::sync::{broadcast, mpsc{%if stinger.methods|length > 0 -%}, oneshot{%endif-%}{%if stinger.properties|length > 0 -%}, watch{%endif-%}};
22
22
  use tokio::task::JoinError;
23
23
 
24
24
  /// This struct is used to store all the MQTTv5 subscription ids
@@ -56,6 +56,7 @@ pub struct {{stinger.name | UpperCamelCase}}Properties {
56
56
  {%-else%}
57
57
  pub {{prop_name | snake_case}}: Arc<Mutex<Option<{{prop.arg_list[0].rust_type}}>>>,
58
58
  {%endif%}
59
+ {{prop_name | snake_case}}_tx_channel: watch::Sender<Option<{%if prop.arg_list | length > 1 %}{{prop.rust_type}}{% else %}{{prop.arg_list[0].rust_type}}{% endif %}>>,
59
60
  {%-endfor%}
60
61
  }
61
62
  {%endif%}
@@ -125,9 +126,10 @@ impl {{stinger.rust.client_struct_name}} {
125
126
  {%-for prop_name, prop in stinger.properties.items()%}
126
127
  {%if prop.arg_list | length > 1 -%}
127
128
  {{prop_name | snake_case}}: Arc::new(Mutex::new(None)),
128
- {%else%}
129
+ {%-else%}
129
130
  {{prop_name | snake_case}}: Arc::new(Mutex::new(None)),
130
- {%-endif%}
131
+ {%-endif-%}
132
+ {{prop_name | snake_case}}_tx_channel: watch::channel(None).0,
131
133
  {%-endfor%}
132
134
  };
133
135
  {%endif%}
@@ -248,6 +250,20 @@ impl {{stinger.rust.client_struct_name}} {
248
250
  }
249
251
  }
250
252
  {%endfor%}
253
+ {%for prop_name, prop in stinger.properties.items()%}
254
+ /// Watch for changes to the `{{prop_name}}` property.
255
+ /// This returns a watch::Receiver that can be awaited on for changes to the property value.
256
+ pub fn watch_{{prop_name | snake_case}}(&self) -> watch::Receiver<Option<{%if prop.arg_list | length > 1 %}{{prop.rust_type}}{% else %}{{prop.arg_list[0].rust_type}}{% endif %}>> {
257
+ self.properties.{{prop_name | snake_case}}_tx_channel.subscribe()
258
+ }
259
+ {%if not prop.read_only %}
260
+ pub fn set_{{prop_name | snake_case}}(&mut self, value: {%if prop.arg_list | length > 1 %}{{prop.rust_type}}{% else %}{{prop.arg_list[0].rust_type}}{% endif %}) -> Result<(), MethodResultCode> {
261
+ let data = {%if prop.arg_list | length > 1 %}value{% else %}value{% endif %};
262
+ let _publish_result = self.mqttier_client.publish_structure("{{prop.update_topic}}".to_string(), &data);
263
+ Ok(())
264
+ }
265
+ {%endif%}
266
+ {%endfor%}
251
267
 
252
268
  /// Starts the tasks that process messages received.
253
269
  pub async fn run_loop(&self) -> Result<(), JoinError> {
@@ -262,8 +278,9 @@ impl {{stinger.rust.client_struct_name}} {
262
278
  let mut guard = self.msg_streamer_rx.lock().expect("Mutex was poisoned");
263
279
  guard.take().expect("msg_streamer_rx should be Some")
264
280
  };
265
-
281
+ {%if stinger.signals | length > 0 %}
266
282
  let sig_chans = self.signal_channels.clone();
283
+ {%endif%}
267
284
  let sub_ids = self.subscription_ids.clone();
268
285
  {%-if stinger.properties|length > 0%}
269
286
  let props = self.properties.clone();
@@ -292,7 +309,9 @@ impl {{stinger.rust.client_struct_name}} {
292
309
  {%if not loop.first%}else {%endif%}if msg.subscription_id == sub_ids.{{prop_name | snake_case}}_property_value {
293
310
  let pl: {{prop.rust_type}} = serde_json::from_slice(&msg.payload).expect("Failed to deserialize");
294
311
  let mut guard = props.{{prop_name | snake_case}}.lock().expect("Mutex was poisoned");
295
- *guard = Some(pl);
312
+ *guard = Some(pl.clone());
313
+ // Notify any watchers of the property that it has changed.
314
+ let _ = props.{{prop_name | snake_case}}_tx_channel.send(Some(pl));
296
315
  }
297
316
  {%endfor%}
298
317
  }
@@ -4,31 +4,64 @@ on the next generation.
4
4
 
5
5
  It contains enumerations used by the {{stinger.name}} interface.
6
6
  */
7
+ use std::any::Any;
7
8
  use futures::{executor::block_on};
8
9
  use mqttier::MqttierClient;
9
- use {{stinger.rust.server_package_name}}::{{stinger.rust.server_struct_name}};
10
+ use {{stinger.rust.server_package_name}}::{ {{stinger.rust.server_struct_name}}{%if stinger.methods|length>0%}, {{stinger.name | UpperCamelCase }}MethodHandlers{%endif%} };
10
11
  use tokio::time::{sleep, Duration};
11
-
12
+ {%if stinger.methods|length > 0%}
13
+ //use {{stinger.rust.server_package_name}}::handler::{{stinger.name | UpperCamelCase }}MethodHandlers;
14
+ //use {{stinger.rust.server_package_name}}::init::Initializable;
15
+ use std::sync::{Arc, Mutex};{%endif%}
12
16
  #[allow(unused_imports)]
13
17
  use {{stinger.rust.common_package_name}}::payloads::{MethodResultCode, *};
14
18
 
15
- {%for method_name, method in stinger.methods.items()%}
16
- fn {{method_name|snake_case}}_handler({%for arg in method.arg_list%}_{{arg.name|snake_case}}: {{arg.rust_type}}{%if not loop.last%}, {%endif%}{%endfor%}) -> Result<{{method.return_value_rust_type}}, MethodResultCode> {
17
- println!("Handling {{method_name}}");
18
- {%-if method.return_value_type == 'struct'%}
19
- let rv = {{method.return_value_rust_type}} {
20
- {%for arg in method.return_value-%}
21
- {{arg.name}}: {{arg.get_random_example_value(lang="rust")}},
22
- {%endfor%}
23
- };
24
- Ok(rv)
25
- {%elif method.return_value_type is not false %}
26
- Ok({{method.return_value.get_random_example_value(lang="rust")}})
27
- {%else%}
28
- Ok(())
29
- {%-endif%}
19
+ {%if stinger.methods|length > 0%}
20
+ struct {{stinger.name | UpperCamelCase }}MethodImpl {
21
+ server: Option<{{stinger.rust.server_struct_name}}>,
30
22
  }
31
- {%endfor%}
23
+
24
+ impl {{stinger.name | UpperCamelCase }}MethodImpl {
25
+ fn new() -> Self {
26
+ Self {
27
+ server: None,
28
+ }
29
+ }
30
+ }
31
+
32
+ impl {{stinger.name | UpperCamelCase }}MethodHandlers for {{stinger.name | UpperCamelCase }}MethodImpl {
33
+
34
+ fn initialize(&mut self, server: {{stinger.rust.server_struct_name}}) -> Result<(), MethodResultCode> {
35
+ self.server = Some(server.clone());
36
+ Ok(())
37
+ }
38
+
39
+ {%for method_name, method in stinger.methods.items()%}
40
+ fn handle_{{method_name|snake_case}}(&self, {%for arg in method.arg_list%}_{{arg.name|snake_case}}: {{arg.rust_type}}{%if not loop.last%}, {%endif%}{%endfor%}) -> Result<{{method.return_value_rust_type}}, MethodResultCode> {
41
+ println!("Handling {{method_name}}");
42
+ {%-if method.return_value_type == 'struct'%}
43
+ let rv = {{method.return_value_rust_type}} {
44
+ {%for arg in method.return_value-%}
45
+ {{arg.name}}: {{arg.get_random_example_value(lang="rust")}},
46
+ {%endfor%}
47
+ };
48
+ Ok(rv)
49
+ {%elif method.return_value_type is not false %}
50
+ Ok({{method.return_value.get_random_example_value(lang="rust")}})
51
+ {%else%}
52
+ Ok(())
53
+ {%-endif%}
54
+ }
55
+ {%endfor%}
56
+
57
+ fn as_any(&self) -> &dyn Any {
58
+ self
59
+ }
60
+ }
61
+
62
+
63
+ {%endif%}
64
+
32
65
 
33
66
 
34
67
  #[tokio::main]
@@ -40,8 +73,13 @@ async fn main() {
40
73
  block_on(async {
41
74
  {%set broker = stinger.get_example_broker()%}
42
75
  let mut connection = MqttierClient::new("localhost", 1883, None).unwrap();
76
+ {%if stinger.methods|length > 0%}
77
+ let handlers: Arc<Mutex<Box<dyn {{stinger.name | UpperCamelCase }}MethodHandlers>>> = Arc::new(Mutex::new(Box::new({{stinger.name | UpperCamelCase }}MethodImpl::new())));
78
+ let mut server = {{stinger.rust.server_struct_name}}::new(&mut connection, handlers.clone()).await;
79
+ {%else%}
43
80
  let mut server = {{stinger.rust.server_struct_name}}::new(&mut connection).await;
44
-
81
+ {%endif%}
82
+
45
83
  {%for prop_name, prop in stinger.properties.items()%}
46
84
  println!("Setting initial value for property '{{prop_name}}'");
47
85
  {%-if prop.arg_list | length == 1 %}
@@ -55,9 +93,7 @@ async fn main() {
55
93
  server.set_{{prop_name | snake_case}}(new_value).await;
56
94
  {%endif%}
57
95
  {%endfor%}{# property initialization -#}
58
- {%-for method_name, method in stinger.methods.items()%}
59
- server.set_method_handler_for_{{method_name|snake_case}}({{method_name|snake_case}}_handler);
60
- {%endfor%}
96
+
61
97
  {%for sig_name, sig in stinger.signals.items()%}
62
98
  sleep(Duration::from_secs(1)).await;
63
99
  println!("Emitting signal '{{sig_name}}'");
@@ -77,7 +113,7 @@ async fn main() {
77
113
  server.set_{{prop_name | snake_case}}(new_value).await;
78
114
  {%endif%}
79
115
  {%endfor%}{# property initialization -#}
80
- let _server_loop_task = server.receive_loop().await;
116
+ let _server_loop_task = server.run_loop().await;
81
117
  });
82
118
  // Ctrl-C to stop
83
119
  }