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.
- {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.26.dist-info}/METADATA +3 -2
- {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.26.dist-info}/RECORD +35 -29
- stingeripc/asyncapi.py +3 -0
- stingeripc/components.py +61 -10
- stingeripc/schema/schema.yaml +240 -0
- stingeripc/templates/cpp/examples/client_main.cpp.jinja2 +18 -0
- stingeripc/templates/cpp/include/broker.hpp.jinja2 +18 -10
- stingeripc/templates/cpp/include/client.hpp.jinja2 +80 -11
- stingeripc/templates/cpp/include/ibrokerconnection.hpp.jinja2 +24 -4
- stingeripc/templates/cpp/include/property_structs.hpp.jinja2 +40 -2
- stingeripc/templates/cpp/include/server.hpp.jinja2 +5 -1
- stingeripc/templates/cpp/include/structs.hpp.jinja2 +2 -0
- stingeripc/templates/cpp/partials/args.jinja2 +11 -0
- stingeripc/templates/cpp/partials/deserialize.jinja2 +42 -0
- stingeripc/templates/cpp/partials/serialize.jinja2 +18 -0
- stingeripc/templates/cpp/src/broker.cpp.jinja2 +46 -24
- stingeripc/templates/cpp/src/client.cpp.jinja2 +111 -38
- stingeripc/templates/cpp/src/property_structs.cpp.jinja2 +25 -0
- stingeripc/templates/cpp/src/server.cpp.jinja2 +17 -25
- stingeripc/templates/cpp/src/structs.cpp.jinja2 +13 -0
- stingeripc/templates/html/app.js.jinja2 +130 -29
- stingeripc/templates/html/index.html.jinja2 +96 -8
- stingeripc/templates/html/styles.css.jinja2 +135 -0
- stingeripc/templates/markdown/index.md.jinja2 +151 -10
- stingeripc/templates/python/server.py.jinja2 +138 -18
- stingeripc/templates/rust/client/src/lib.rs.jinja2 +24 -5
- stingeripc/templates/rust/server/examples/server.rs.jinja2 +59 -23
- stingeripc/templates/rust/server/src/lib.rs.jinja2 +72 -51
- stingeripc/tools/cli.py +33 -7
- stingeripc/tools/cpp_generator.py +90 -0
- stingeripc/tools/rust_generator.py +1 -1
- stingeripc/templates/rust/payloads/src/handler.rs.jinja2 +0 -0
- {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.26.dist-info}/WHEEL +0 -0
- {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.26.dist-info}/entry_points.txt +0 -0
- {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.26.dist-info}/licenses/LICENSE +0 -0
- {stinger_ipc-0.0.10.dist-info → stinger_ipc-0.0.26.dist-info}/top_level.txt +0 -0
@@ -5,14 +5,22 @@ on the next generation.
|
|
5
5
|
This is the Server for the {{stinger.name}} interface.
|
6
6
|
*/
|
7
7
|
|
8
|
+
|
9
|
+
|
8
10
|
use mqttier::{MqttierClient{%if stinger.methods|length > 0 or stinger.properties|length > 0 %}, ReceivedMessage{%endif%}};
|
9
11
|
|
10
12
|
#[allow(unused_imports)]
|
11
13
|
use {{stinger.rust.common_package_name}}::payloads::{*, MethodResultCode};
|
14
|
+
use std::any::Any;
|
15
|
+
{%if stinger.methods|length > 0 %}
|
16
|
+
//pub mod handler;
|
17
|
+
//pub mod init;
|
18
|
+
//pub use handler::{{stinger.name | UpperCamelCase }}MethodHandlers;
|
19
|
+
use std::sync::{Arc, Mutex};
|
20
|
+
{%endif%}
|
12
21
|
{%if stinger.methods|length > 0 or stinger.properties|length > 0 %}
|
13
22
|
use serde_json;
|
14
|
-
use
|
15
|
-
use tokio::sync::mpsc;
|
23
|
+
use tokio::sync::{mpsc{%if stinger.properties|length > 0 %}, watch{%endif%}};
|
16
24
|
{%endif%}
|
17
25
|
use tokio::task::JoinError;
|
18
26
|
|
@@ -28,14 +36,7 @@ struct {{stinger.name | UpperCamelCase }}ServerSubscriptionIds {
|
|
28
36
|
{{prop_name | snake_case}}_property_update: usize,
|
29
37
|
{%endif%}{%endfor%}
|
30
38
|
}
|
31
|
-
|
32
|
-
#[derive(Clone)]
|
33
|
-
struct {{stinger.name | UpperCamelCase }}ServerMethodHandlers {
|
34
|
-
{%for method_name, method in stinger.methods.items()-%}
|
35
|
-
/// Pointer to a function to handle the {{method_name}} method request.
|
36
|
-
method_handler_for_{{method_name|snake_case}}: Arc<Mutex<Box<dyn Fn({%for arg in method.arg_list%}{{arg.rust_type}}{%if not loop.last%}, {%endif%}{%endfor%})->Result<{{method.return_value_rust_type}}, MethodResultCode> + Send>>>,
|
37
|
-
{%endfor%}
|
38
|
-
}{%endif%}{# method handlers struct #}
|
39
|
+
|
39
40
|
{%if stinger.properties | length > 0%}
|
40
41
|
#[derive(Clone)]
|
41
42
|
struct {{stinger.name | UpperCamelCase}}Properties {
|
@@ -43,8 +44,10 @@ struct {{stinger.name | UpperCamelCase}}Properties {
|
|
43
44
|
{{prop_name | snake_case}}_topic: Arc<String>,
|
44
45
|
{%-if prop.arg_list | length > 1%}
|
45
46
|
{{prop_name | snake_case}}: Arc<Mutex<Option<{{prop.rust_type}}>>>,
|
47
|
+
{{prop_name | snake_case}}_tx_channel: watch::Sender<Option<{{prop.rust_type}}>>,
|
46
48
|
{%-else%}
|
47
49
|
{{prop_name | snake_case}}: Arc<Mutex<Option<{{prop.arg_list[0].rust_type}}>>>,
|
50
|
+
{{prop_name | snake_case}}_tx_channel: watch::Sender<Option<{{prop.arg_list[0].rust_type}}>>,
|
48
51
|
{%endif%}
|
49
52
|
{%-endfor%}
|
50
53
|
}
|
@@ -64,8 +67,9 @@ pub struct {{stinger.rust.server_struct_name}} {
|
|
64
67
|
msg_streamer_tx: mpsc::Sender<ReceivedMessage>,
|
65
68
|
{%endif%}
|
66
69
|
{%if stinger.methods|length > 0 %}
|
67
|
-
/// Struct contains all the handlers
|
68
|
-
method_handlers: {{stinger.name | UpperCamelCase }}
|
70
|
+
/// Struct contains all the method handlers.
|
71
|
+
method_handlers: Arc<Mutex<Box<dyn {{stinger.name | UpperCamelCase }}MethodHandlers>>>,
|
72
|
+
{%endif%}
|
69
73
|
{%if stinger.properties|length > 0%}
|
70
74
|
/// Struct contains all the properties.
|
71
75
|
properties: {{stinger.name | UpperCamelCase}}Properties,
|
@@ -81,7 +85,7 @@ pub struct {{stinger.rust.server_struct_name}} {
|
|
81
85
|
}
|
82
86
|
|
83
87
|
impl {{stinger.rust.server_struct_name}} {
|
84
|
-
pub async fn new(connection: &mut MqttierClient) -> Self {
|
88
|
+
pub async fn new(connection: &mut MqttierClient{%if stinger.methods|length > 0%}, method_handlers: Arc<Mutex<Box<dyn {{stinger.name | UpperCamelCase }}MethodHandlers>>> {%endif%}) -> Self {
|
85
89
|
{%if stinger.methods|length > 0 or stinger.properties|length > 0 %}
|
86
90
|
// Create a channel for messages to get from the MqttierClient object to this {{stinger.rust.server_struct_name}} object.
|
87
91
|
// The Connection object uses a clone of the tx side of the channel.
|
@@ -97,14 +101,6 @@ impl {{stinger.rust.server_struct_name}} {
|
|
97
101
|
let subscription_id_{{prop_name | snake_case}}_property_update = subscription_id_{{prop_name | snake_case}}_property_update.unwrap_or_else(|_| usize::MAX);
|
98
102
|
{%else-%}
|
99
103
|
{%endif%}{%endfor%}
|
100
|
-
{%if stinger.methods | length > 0 %}
|
101
|
-
// Create structure for method handlers.
|
102
|
-
let method_handlers = {{stinger.name | UpperCamelCase }}ServerMethodHandlers {
|
103
|
-
{%-for method_name, method in stinger.methods.items()-%}
|
104
|
-
method_handler_for_{{method_name|snake_case}}: Arc::new(Mutex::new(Box::new( |{%for arg in method.arg_list-%}_{{loop.index}}{%if not loop.last%}, {%endif%}{%endfor%}| { Err(MethodResultCode::ServerError) } ))),
|
105
|
-
{%endfor%}
|
106
|
-
};
|
107
|
-
{%endif%}{# method handlers #}
|
108
104
|
|
109
105
|
// Create structure for subscription ids.
|
110
106
|
let sub_ids = {{stinger.name | UpperCamelCase }}ServerSubscriptionIds {
|
@@ -121,11 +117,10 @@ impl {{stinger.rust.server_struct_name}} {
|
|
121
117
|
let property_values = {{stinger.name | UpperCamelCase}}Properties {
|
122
118
|
{%-for prop_name, prop in stinger.properties.items()%}
|
123
119
|
{{prop_name | snake_case}}_topic: Arc::new(String::from("{{prop.value_topic}}")),
|
124
|
-
|
125
|
-
{{prop_name | snake_case}}: Arc::new(Mutex::new(None)),
|
126
|
-
{%else%}
|
120
|
+
|
127
121
|
{{prop_name | snake_case}}: Arc::new(Mutex::new(None)),
|
128
|
-
{
|
122
|
+
{{prop_name | snake_case}}_tx_channel: watch::channel(None).0,
|
123
|
+
|
129
124
|
{%-endfor%}
|
130
125
|
};
|
131
126
|
{%endif%}
|
@@ -148,44 +143,45 @@ impl {{stinger.rust.server_struct_name}} {
|
|
148
143
|
pub async fn emit_{{sig_name|snake_case}}(&mut self, {%for arg in sig.arg_list%}{{arg.name|snake_case}}: {{arg.rust_type}}{%if not loop.last%}, {%endif%}{%endfor%}) {
|
149
144
|
let data = {{sig_name|UpperCamelCase}}SignalPayload {
|
150
145
|
{%for arg in sig.arg_list%}
|
151
|
-
|
146
|
+
{{arg.name}}: {{arg.name|snake_case}},
|
152
147
|
{%endfor%}
|
153
148
|
};
|
154
149
|
let _ = self.mqttier_client.publish_structure("{{sig.topic}}".to_string(), &data).await;
|
155
150
|
}
|
156
151
|
{%endfor%}
|
157
152
|
|
158
|
-
{%for method_name, method in stinger.methods.items()-%}
|
159
|
-
/// Sets the function to be called when a request for the {{method_name}} method is received.
|
160
|
-
pub fn set_method_handler_for_{{method_name|snake_case}}(&mut self, cb: impl Fn({%for arg in method.arg_list%}{{arg.rust_type}}{%if not loop.last%}, {%endif%}{%endfor%})->Result<{{method.return_value_rust_type}}, MethodResultCode> + 'static + Send) {
|
161
|
-
self.method_handlers.method_handler_for_{{method_name|snake_case}} = Arc::new(Mutex::new(Box::new(cb)));
|
162
|
-
}
|
163
|
-
{%endfor%}
|
164
|
-
|
165
153
|
{%for method_name, method in stinger.methods.items()-%}
|
166
154
|
/// Handles a request message for the {{method_name}} method.
|
167
|
-
async fn handle_{{method_name|snake_case}}_request(publisher: MqttierClient, handlers:
|
155
|
+
async fn handle_{{method_name|snake_case}}_request(publisher: MqttierClient, handlers: Arc<Mutex<Box<dyn {{stinger.name | UpperCamelCase }}MethodHandlers>>>, msg: ReceivedMessage) {
|
168
156
|
let opt_corr_data = msg.correlation_data;
|
169
157
|
let opt_resp_topic = msg.response_topic;
|
170
|
-
{%if method.
|
158
|
+
{%if method.arg_list | length > 0 -%}
|
171
159
|
let payload_vec = msg.payload;
|
172
160
|
let payload = serde_json::from_slice::<{{method_name | UpperCamelCase}}RequestObject>(&payload_vec).unwrap();
|
173
|
-
{%endif%}
|
161
|
+
{%endif%}{# has arg_list #}
|
174
162
|
// call the method handler
|
175
|
-
let rv: {{method.return_value_rust_type}} = {
|
176
|
-
let
|
177
|
-
(*
|
163
|
+
let rv: Result<{{method.return_value_rust_type}}, MethodResultCode> = {
|
164
|
+
let handler_guard = handlers.lock().unwrap();
|
165
|
+
(*handler_guard).handle_{{method_name|snake_case}}({%for arg in method.arg_list%}payload.{{arg.name}}{%if not loop.last%}, {%endif%}{%endfor%})
|
178
166
|
};
|
179
167
|
|
180
|
-
{%-if method.return_value_type == "primitive" -%}
|
181
|
-
let rv = {{method_name | UpperCamelCase}}ReturnValue {
|
182
|
-
{{method.return_value_property_name}}: rv,
|
183
|
-
};
|
184
|
-
{%endif%}
|
185
|
-
|
186
168
|
if let Some(resp_topic) = opt_resp_topic {
|
187
169
|
let corr_data = opt_corr_data.unwrap_or_default();
|
188
|
-
|
170
|
+
match rv {
|
171
|
+
Ok({%if method.return_value_type is false %}_{%else%}retval{%endif%}) => {
|
172
|
+
{%-if method.return_value_type == "primitive" %}
|
173
|
+
let retval = {{method_name | UpperCamelCase}}ReturnValue {
|
174
|
+
{{method.return_value_property_name}}: retval,
|
175
|
+
};
|
176
|
+
{%elif method.return_value_type is false %}
|
177
|
+
let retval = {{method_name | UpperCamelCase}}ReturnValue {};
|
178
|
+
{%endif%}
|
179
|
+
publisher.publish_response(resp_topic, &retval, corr_data).await.expect("Failed to publish response structure");
|
180
|
+
}
|
181
|
+
Err(err) => {
|
182
|
+
eprintln!("Error occurred while handling {}: {:?}", stringify!({{method_name}}), err);
|
183
|
+
}
|
184
|
+
}
|
189
185
|
} else {
|
190
186
|
eprintln!("No response topic found in message properties.");
|
191
187
|
}
|
@@ -206,7 +202,7 @@ impl {{stinger.rust.server_struct_name}} {
|
|
206
202
|
{%endif%}
|
207
203
|
}
|
208
204
|
{%if not prop.read_only %}
|
209
|
-
async fn update_{{prop_name}}_value(publisher: MqttierClient, topic: Arc<String>, data: Arc<Mutex<Option<{{prop.rust_type}}>>>, msg: ReceivedMessage)
|
205
|
+
async fn update_{{prop_name}}_value(publisher: MqttierClient, topic: Arc<String>, data: Arc<Mutex<Option<{{prop.rust_type}}>>>, watch_sender: watch::Sender<Option<{{prop.rust_type}}>>, msg: ReceivedMessage)
|
210
206
|
{
|
211
207
|
let payload_str = String::from_utf8_lossy(&msg.payload).to_string();
|
212
208
|
let new_data: {{prop_name | UpperCamelCase}}Property = serde_json::from_str(&payload_str).unwrap();
|
@@ -223,10 +219,17 @@ impl {{stinger.rust.server_struct_name}} {
|
|
223
219
|
{%-else%}
|
224
220
|
let data2 = new_data;
|
225
221
|
{%endif%}
|
222
|
+
let data_to_send_to_watchers = data2.clone();
|
223
|
+
let _ = watch_sender.send(Some(data_to_send_to_watchers));
|
226
224
|
let _ = tokio::spawn(async move {
|
227
225
|
{{stinger.rust.server_struct_name}}::publish_{{prop_name}}_value(publisher2, topic2, data2).await;
|
228
226
|
});
|
229
227
|
}
|
228
|
+
|
229
|
+
pub async fn watch_{{prop_name}}(&self) -> watch::Receiver<Option<{{prop.rust_type}}>> {
|
230
|
+
self.properties.{{prop_name}}_tx_channel.subscribe()
|
231
|
+
}
|
232
|
+
|
230
233
|
{%endif%}{# not read only property #}
|
231
234
|
pub async fn set_{{prop_name}}(&mut self, data: {{prop.rust_type}}) {
|
232
235
|
println!("Setting {{prop_name}} of type {{prop.rust_type}}");
|
@@ -236,6 +239,9 @@ impl {{stinger.rust.server_struct_name}} {
|
|
236
239
|
*locked_data = Some(data.clone());
|
237
240
|
}
|
238
241
|
|
242
|
+
let data_to_send_to_watchers = data.clone();
|
243
|
+
let _ = self.properties.{{prop_name}}_tx_channel.send(Some(data_to_send_to_watchers));
|
244
|
+
|
239
245
|
let publisher2 = self.mqttier_client.clone();
|
240
246
|
let topic2 = self.properties.{{prop_name}}_topic.as_ref().clone();
|
241
247
|
let _ = tokio::spawn(async move {
|
@@ -249,7 +255,7 @@ impl {{stinger.rust.server_struct_name}} {
|
|
249
255
|
/// In the task, it loops over messages received from the rx side of the message_receiver channel.
|
250
256
|
/// Based on the subscription id of the received message, it will call a function to handle the
|
251
257
|
/// received message.
|
252
|
-
pub async fn
|
258
|
+
pub async fn run_loop(&mut self) -> Result<(), JoinError> {
|
253
259
|
// Make sure the MqttierClient is connected and running.
|
254
260
|
let _ = self.mqttier_client.run_loop().await;
|
255
261
|
|
@@ -260,7 +266,8 @@ impl {{stinger.rust.server_struct_name}} {
|
|
260
266
|
};
|
261
267
|
|
262
268
|
{%if stinger.methods|length > 0 -%}
|
263
|
-
let
|
269
|
+
let method_handlers = self.method_handlers.clone();
|
270
|
+
self.method_handlers.lock().unwrap().initialize(self.clone()).expect("Failed to initialize method handlers");
|
264
271
|
{%endif-%}
|
265
272
|
|
266
273
|
let sub_ids = self.subscription_ids.clone();
|
@@ -277,12 +284,12 @@ impl {{stinger.rust.server_struct_name}} {
|
|
277
284
|
while let Some(msg) = message_receiver.recv().await {
|
278
285
|
{%-for method_name, method in stinger.methods.items()%}
|
279
286
|
{%if not loop.first%}else {%endif%}if msg.subscription_id == sub_ids.{{method_name | snake_case}}_method_req {
|
280
|
-
{{stinger.rust.server_struct_name}}::handle_{{method_name|snake_case}}_request(publisher.clone(),
|
287
|
+
{{stinger.rust.server_struct_name}}::handle_{{method_name|snake_case}}_request(publisher.clone(), method_handlers.clone(), msg).await;
|
281
288
|
}
|
282
289
|
{%-endfor%}{# methods #}
|
283
290
|
{%-for prop_name, prop in stinger.properties.items()%}{%if not prop.read_only %}
|
284
291
|
{%if not loop.first or (stinger.methods | length > 0) %}else {%endif%}if msg.subscription_id == sub_ids.{{prop_name | snake_case}}_property_update {
|
285
|
-
{{stinger.rust.server_struct_name}}::update_{{prop_name | snake_case}}_value(publisher.clone(), properties.{{prop_name | snake_case}}_topic.clone(), properties.{{prop_name | snake_case}}.clone(), msg).await;
|
292
|
+
{{stinger.rust.server_struct_name}}::update_{{prop_name | snake_case}}_value(publisher.clone(), properties.{{prop_name | snake_case}}_topic.clone(), properties.{{prop_name | snake_case}}.clone(), properties.{{prop_name | snake_case}}_tx_channel.clone(), msg).await;
|
286
293
|
}
|
287
294
|
{%-endif%}{%endfor%}{# properties #}
|
288
295
|
}
|
@@ -296,3 +303,17 @@ impl {{stinger.rust.server_struct_name}} {
|
|
296
303
|
}
|
297
304
|
|
298
305
|
}
|
306
|
+
{%if stinger.methods|length > 0 %}
|
307
|
+
pub trait {{stinger.name | UpperCamelCase }}MethodHandlers: Send + Sync {
|
308
|
+
|
309
|
+
fn initialize(&mut self, server: {{stinger.rust.server_struct_name}}) -> Result<(), MethodResultCode>;
|
310
|
+
|
311
|
+
{%for method_name, method in stinger.methods.items()-%}
|
312
|
+
/// Pointer to a function to handle the {{method_name}} method request.
|
313
|
+
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>;
|
314
|
+
|
315
|
+
{%endfor%}
|
316
|
+
|
317
|
+
fn as_any(&self) -> &dyn Any;
|
318
|
+
}
|
319
|
+
{%endif%}{# methods #}
|
stingeripc/tools/cli.py
CHANGED
@@ -1,17 +1,21 @@
|
|
1
1
|
|
2
2
|
import os
|
3
|
+
import sys
|
4
|
+
from wsgiref.validate import validator
|
3
5
|
from rich import print
|
4
6
|
from pathlib import Path
|
5
7
|
import typer
|
6
8
|
from typing_extensions import Annotated
|
7
9
|
from jacobsjinjatoo import templator as jj2
|
8
|
-
|
10
|
+
from jacobsjsonschema.draft7 import Validator
|
11
|
+
import yaml
|
12
|
+
import yamlloader
|
9
13
|
from stingeripc.interface import StingerInterface
|
10
14
|
|
11
15
|
from . import markdown_generator
|
12
16
|
from . import python_generator
|
13
17
|
from . import rust_generator
|
14
|
-
|
18
|
+
from . import cpp_generator
|
15
19
|
|
16
20
|
app = typer.Typer(help="stinger-ipc generator CLI")
|
17
21
|
|
@@ -24,13 +28,13 @@ def generate(
|
|
24
28
|
):
|
25
29
|
"""Generate code for a Stinger interface.
|
26
30
|
|
27
|
-
LANGUAGE must be one of: rust, python, markdown
|
31
|
+
LANGUAGE must be one of: rust, python, markdown, cpp, web
|
28
32
|
INPUT_FILE is the .stinger.yaml file
|
29
33
|
OUTPUT_DIR is the directory that will receive generated files
|
30
34
|
"""
|
31
35
|
lang = language.lower()
|
32
|
-
if lang not in ("rust", "python", "markdown", "web"):
|
33
|
-
raise typer.BadParameter("language must be one of: rust, python, markdown, web")
|
36
|
+
if lang not in ("rust", "python", "markdown", "cpp", "web"):
|
37
|
+
raise typer.BadParameter("language must be one of: rust, python, markdown, cpp, web")
|
34
38
|
|
35
39
|
if lang == "python":
|
36
40
|
# python_generator.main expects Path arguments via typer
|
@@ -55,12 +59,34 @@ def generate(
|
|
55
59
|
]:
|
56
60
|
ct.render_template(f"{output_file}.jinja2", output_file, stinger=stinger)
|
57
61
|
wt.render_template("index.html.jinja2", "index.html", stinger=stinger)
|
58
|
-
|
62
|
+
elif lang == "cpp":
|
63
|
+
cpp_generator.main(input_file, output_dir)
|
64
|
+
elif lang == "rust":
|
59
65
|
rust_generator.main(input_file, output_dir)
|
60
|
-
|
66
|
+
else:
|
67
|
+
raise RuntimeError("Unreachable code reached")
|
61
68
|
|
62
69
|
print(f"Generation for '{lang}' completed.")
|
63
70
|
|
71
|
+
@app.command()
|
72
|
+
def validate(input_file: Annotated[Path, typer.Argument(..., exists=True, file_okay=True, dir_okay=False, readable=True)]):
|
73
|
+
"""Validate a Stinger interface YAML file.
|
74
|
+
|
75
|
+
INPUT_FILE is the .stinger.yaml file
|
76
|
+
"""
|
77
|
+
schema_file = Path(__file__).parent.parent / "schema" / "schema.yaml"
|
78
|
+
schema_obj = yaml.load(schema_file.open("r"), Loader=yamlloader.ordereddict.Loader)
|
79
|
+
validator = Validator(schema_obj, lazy_error_reporting=False)
|
80
|
+
|
81
|
+
input_obj = yaml.load(input_file.open("r"), Loader=yamlloader.ordereddict.Loader)
|
82
|
+
if result := validator.validate(input_obj):
|
83
|
+
print("Validated: ", result)
|
84
|
+
else:
|
85
|
+
for error in validator.get_errors():
|
86
|
+
print(error)
|
87
|
+
sys.exit(1)
|
88
|
+
sys.exit(0)
|
89
|
+
|
64
90
|
@app.command()
|
65
91
|
def hello():
|
66
92
|
print("Hello world")
|
@@ -0,0 +1,90 @@
|
|
1
|
+
from jacobsjinjatoo import templator as jj2
|
2
|
+
import os
|
3
|
+
import yaml
|
4
|
+
import typer
|
5
|
+
from typing_extensions import Annotated
|
6
|
+
from pathlib import Path
|
7
|
+
from rich import print
|
8
|
+
|
9
|
+
from stingeripc import StingerInterface
|
10
|
+
|
11
|
+
|
12
|
+
def main(
|
13
|
+
inname: Annotated[Path, typer.Argument(exists=True, file_okay=True, dir_okay=False, readable=True)],
|
14
|
+
outdir: Annotated[Path, typer.Argument(file_okay=False, dir_okay=True, writable=True, readable=True)],
|
15
|
+
):
|
16
|
+
"""Generate C++ output for a Stinger interface."""
|
17
|
+
|
18
|
+
with inname.open(mode="r") as f:
|
19
|
+
stinger = StingerInterface.from_yaml(f)
|
20
|
+
|
21
|
+
params = {
|
22
|
+
"stinger": stinger,
|
23
|
+
"source_files": [],
|
24
|
+
"header_files": [],
|
25
|
+
}
|
26
|
+
|
27
|
+
if outdir.is_file():
|
28
|
+
raise RuntimeError("Output directory is a file!")
|
29
|
+
|
30
|
+
if not outdir.is_dir():
|
31
|
+
outdir.mkdir(parents=True)
|
32
|
+
|
33
|
+
source_code_dir = outdir / "src"
|
34
|
+
source_code_dir.mkdir(parents=True, exist_ok=True)
|
35
|
+
|
36
|
+
headers_code_dir = outdir / "include"
|
37
|
+
headers_code_dir.mkdir(parents=True, exist_ok=True)
|
38
|
+
|
39
|
+
examples_code_dir = outdir / "examples"
|
40
|
+
examples_code_dir.mkdir(parents=True, exist_ok=True)
|
41
|
+
|
42
|
+
this_file = Path(__file__)
|
43
|
+
# The templates live under the package `stingeripc/templates/cpp` next to `tools`.
|
44
|
+
# compute that from the tools directory: tools -> parent (stingeripc) -> templates/cpp
|
45
|
+
template_dir = (this_file.parent / ".." / "templates" / "cpp").resolve()
|
46
|
+
|
47
|
+
if not template_dir.is_dir():
|
48
|
+
raise RuntimeError(f"C++ template directory not found: {template_dir}")
|
49
|
+
|
50
|
+
output_dir = outdir.resolve()
|
51
|
+
t = jj2.CodeTemplator(output_dir=output_dir)
|
52
|
+
t.add_template_dir(template_dir)
|
53
|
+
|
54
|
+
headers_template_dir = template_dir / "include"
|
55
|
+
for fname in os.listdir(headers_template_dir):
|
56
|
+
header_code_path = Path("include") / fname
|
57
|
+
if fname.endswith(".jinja2"):
|
58
|
+
print(f"GENERATING HEADER: {fname}")
|
59
|
+
header_dest_path = str(header_code_path)[:-len(".jinja2")]
|
60
|
+
params["header_files"].append(header_dest_path)
|
61
|
+
t.render_template(header_code_path, header_dest_path, **params)
|
62
|
+
|
63
|
+
src_template_dir = template_dir / "src"
|
64
|
+
for fname in os.listdir(src_template_dir):
|
65
|
+
source_code_path = Path("src") / fname
|
66
|
+
if fname.endswith(".jinja2"):
|
67
|
+
print(f"GENERATING SOURCE: {fname}")
|
68
|
+
source_dest_path = str(source_code_path)[:-len(".jinja2")]
|
69
|
+
params["source_files"].append(source_dest_path)
|
70
|
+
t.render_template(source_code_path, source_dest_path, **params)
|
71
|
+
|
72
|
+
example_template_dir = template_dir / "examples"
|
73
|
+
for fname in os.listdir(example_template_dir):
|
74
|
+
example_code_path = Path("examples") / fname
|
75
|
+
if fname.endswith(".jinja2"):
|
76
|
+
print(f"GENERATING EXAMPLE: {fname}")
|
77
|
+
example_dest_path = str(example_code_path)[:-len(".jinja2")]
|
78
|
+
t.render_template(example_code_path, example_dest_path, **params)
|
79
|
+
|
80
|
+
print(f"GENERATING CmakeLists")
|
81
|
+
t.render_template("CMakeLists.txt.jinja2", "CMakeLists.txt", **params)
|
82
|
+
|
83
|
+
|
84
|
+
def run():
|
85
|
+
typer.run(main)
|
86
|
+
|
87
|
+
|
88
|
+
if __name__ == "__main__":
|
89
|
+
run()
|
90
|
+
|
@@ -35,7 +35,7 @@ def main(
|
|
35
35
|
t = jj2.CodeTemplator(output_dir=outdir)
|
36
36
|
t.add_template_dir(template_dir)
|
37
37
|
|
38
|
-
def recursive_render_templates(local_dir: str):
|
38
|
+
def recursive_render_templates(local_dir: str|Path):
|
39
39
|
local_dir = Path(local_dir)
|
40
40
|
cur_template_dir = template_dir / local_dir
|
41
41
|
for entry in os.listdir(cur_template_dir):
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|