stinger-ipc 0.0.5__py3-none-any.whl → 0.0.7__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.5.dist-info → stinger_ipc-0.0.7.dist-info}/METADATA +2 -2
- {stinger_ipc-0.0.5.dist-info → stinger_ipc-0.0.7.dist-info}/RECORD +27 -23
- {stinger_ipc-0.0.5.dist-info → stinger_ipc-0.0.7.dist-info}/entry_points.txt +1 -0
- stingeripc/components.py +5 -5
- stingeripc/lang_symb.py +21 -4
- stingeripc/templates/html/app.js.jinja2 +117 -0
- stingeripc/templates/html/index.html.jinja2 +38 -0
- stingeripc/templates/html/style.css.jinja2 +187 -0
- stingeripc/templates/rust/Cargo.toml.jinja2 +1 -1
- stingeripc/templates/rust/client/Cargo.toml.jinja2 +7 -5
- stingeripc/templates/rust/client/examples/client.rs.jinja2 +9 -10
- stingeripc/templates/rust/client/src/lib.rs.jinja2 +112 -55
- stingeripc/templates/rust/{connection → payloads}/Cargo.toml.jinja2 +8 -4
- stingeripc/templates/rust/{connection → payloads}/examples/pub_and_recv.rs.jinja2 +11 -11
- stingeripc/templates/rust/payloads/src/lib.rs.jinja2 +4 -0
- stingeripc/templates/rust/{connection → payloads}/src/payloads.rs.jinja2 +10 -2
- stingeripc/templates/rust/server/Cargo.toml.jinja2 +4 -4
- stingeripc/templates/rust/server/examples/server.rs.jinja2 +8 -12
- stingeripc/templates/rust/server/src/lib.rs.jinja2 +92 -69
- stingeripc/tools/cli.py +73 -0
- stingeripc/tools/markdown_generator.py +4 -2
- stingeripc/tools/python_generator.py +12 -10
- stingeripc/tools/rust_generator.py +46 -27
- stingeripc/templates/rust/connection/src/lib.rs.jinja2 +0 -262
- {stinger_ipc-0.0.5.dist-info → stinger_ipc-0.0.7.dist-info}/WHEEL +0 -0
- {stinger_ipc-0.0.5.dist-info → stinger_ipc-0.0.7.dist-info}/licenses/LICENSE +0 -0
- {stinger_ipc-0.0.5.dist-info → stinger_ipc-0.0.7.dist-info}/top_level.txt +0 -0
- /stingeripc/templates/rust/{connection → payloads}/src/handler.rs.jinja2 +0 -0
@@ -5,39 +5,38 @@ on the next generation.
|
|
5
5
|
This is the Server for the {{stinger.name}} interface.
|
6
6
|
*/
|
7
7
|
|
8
|
-
|
9
|
-
use connection::{MessagePublisher, Connection, ReceivedMessage};
|
8
|
+
use mqttier::{MqttierClient{%if stinger.methods|length > 0 or stinger.properties|length > 0 %}, ReceivedMessage{%endif%}};
|
10
9
|
|
11
10
|
#[allow(unused_imports)]
|
12
|
-
use
|
13
|
-
|
11
|
+
use {{stinger.rust.common_package_name}}::payloads::{*, MethodResultCode};
|
12
|
+
{%if stinger.methods|length > 0 or stinger.properties|length > 0 %}
|
13
|
+
use serde_json;
|
14
14
|
use std::sync::{Arc, Mutex};
|
15
|
-
use tokio::sync::
|
16
|
-
|
15
|
+
use tokio::sync::mpsc;
|
16
|
+
{%endif%}
|
17
17
|
use tokio::task::JoinError;
|
18
|
-
use serde::{Serialize, Deserialize};
|
19
|
-
use serde_json;
|
20
18
|
|
19
|
+
{%if stinger.methods|length > 0 or stinger.properties|length > 0 %}
|
21
20
|
/// This struct is used to store all the MQTTv5 subscription ids
|
22
21
|
/// for the subscriptions the client will make.
|
23
22
|
#[derive(Clone, Debug)]
|
24
23
|
struct {{stinger.name | UpperCamelCase }}ServerSubscriptionIds {
|
25
24
|
{%for method_name, method in stinger.methods.items()-%}
|
26
|
-
{{method_name | snake_case}}_method_req:
|
25
|
+
{{method_name | snake_case}}_method_req: usize,
|
27
26
|
{%endfor%}
|
28
27
|
{%for prop_name, prop in stinger.properties.items()-%}{%if not prop.read_only %}
|
29
|
-
{{prop_name | snake_case}}_property_update:
|
28
|
+
{{prop_name | snake_case}}_property_update: usize,
|
30
29
|
{%endif%}{%endfor%}
|
31
30
|
}
|
32
|
-
|
31
|
+
{%if stinger.methods|length > 0 %}
|
33
32
|
#[derive(Clone)]
|
34
33
|
struct {{stinger.name | UpperCamelCase }}ServerMethodHandlers {
|
35
34
|
{%for method_name, method in stinger.methods.items()-%}
|
36
35
|
/// Pointer to a function to handle the {{method_name}} method request.
|
37
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>>>,
|
38
37
|
{%endfor%}
|
39
|
-
}
|
40
|
-
|
38
|
+
}{%endif%}{# method handlers struct #}
|
39
|
+
{%if stinger.properties | length > 0%}
|
41
40
|
#[derive(Clone)]
|
42
41
|
struct {{stinger.name | UpperCamelCase}}Properties {
|
43
42
|
{%for prop_name, prop in stinger.properties.items()-%}
|
@@ -49,63 +48,64 @@ struct {{stinger.name | UpperCamelCase}}Properties {
|
|
49
48
|
{%endif%}
|
50
49
|
{%-endfor%}
|
51
50
|
}
|
51
|
+
{%endif%}
|
52
|
+
{%endif%}{# any methods or properties #}
|
52
53
|
|
53
|
-
pub struct {{stinger.
|
54
|
+
pub struct {{stinger.rust.server_struct_name}} {
|
55
|
+
mqttier_client: MqttierClient,
|
56
|
+
{%if stinger.methods|length > 0 or stinger.properties|length > 0 %}
|
54
57
|
/// Temporarily holds the receiver for the MPSC channel. The Receiver will be moved
|
55
58
|
/// to a process loop when it is needed. MQTT messages will be received with this.
|
56
59
|
msg_streamer_rx: Option<mpsc::Receiver<ReceivedMessage>>,
|
57
60
|
|
58
61
|
/// The Sender side of MQTT messages that are received from the broker. This tx
|
59
62
|
/// side is cloned for each subscription made.
|
63
|
+
#[allow(dead_code)]
|
60
64
|
msg_streamer_tx: mpsc::Sender<ReceivedMessage>,
|
61
|
-
|
62
|
-
|
63
|
-
msg_publisher: MessagePublisher,
|
64
|
-
|
65
|
+
{%endif%}
|
66
|
+
{%if stinger.methods|length > 0 %}
|
65
67
|
/// Struct contains all the handlers for the various methods.
|
66
|
-
method_handlers: {{stinger.name | UpperCamelCase }}ServerMethodHandlers,
|
68
|
+
method_handlers: {{stinger.name | UpperCamelCase }}ServerMethodHandlers,{%endif%}
|
67
69
|
{%if stinger.properties|length > 0%}
|
68
70
|
/// Struct contains all the properties.
|
69
71
|
properties: {{stinger.name | UpperCamelCase}}Properties,
|
70
72
|
{%endif%}
|
73
|
+
{%if stinger.methods|length > 0 or stinger.properties|length > 0 %}
|
71
74
|
/// Subscription IDs for all the subscriptions this makes.
|
72
75
|
subscription_ids: {{stinger.name | UpperCamelCase }}ServerSubscriptionIds,
|
76
|
+
{%endif%}
|
73
77
|
|
74
78
|
/// Copy of MQTT Client ID
|
75
|
-
|
79
|
+
#[allow(dead_code)]
|
80
|
+
pub client_id: String,
|
76
81
|
}
|
77
82
|
|
78
|
-
impl {{stinger.
|
79
|
-
pub async fn new(connection: &mut
|
80
|
-
|
81
|
-
|
82
|
-
//let interface_info = String::from(r#"{{stinger.interface_info.1 | tojson}}"#);
|
83
|
-
//connection.publish("{{stinger.interface_info.0}}".to_string(), interface_info, 1).await;
|
84
|
-
|
85
|
-
// Create a channel for messages to get from the Connection object to this {{stinger.name | UpperCamelCase }}Client object.
|
83
|
+
impl {{stinger.rust.server_struct_name}} {
|
84
|
+
pub async fn new(connection: &mut MqttierClient) -> Self {
|
85
|
+
{%if stinger.methods|length > 0 or stinger.properties|length > 0 %}
|
86
|
+
// Create a channel for messages to get from the MqttierClient object to this {{stinger.rust.server_struct_name}} object.
|
86
87
|
// The Connection object uses a clone of the tx side of the channel.
|
87
|
-
let (message_received_tx, message_received_rx) = mpsc::channel(64);
|
88
|
-
|
89
|
-
let publisher = connection.get_publisher();
|
88
|
+
let (message_received_tx, message_received_rx) = mpsc::channel::<ReceivedMessage>(64);
|
90
89
|
|
91
90
|
// Create method handler struct
|
92
91
|
{%for method_name, method in stinger.methods.items()-%}
|
93
|
-
let subscription_id_{{method_name | snake_case}}_method_req = connection.subscribe("{{method.topic}}", message_received_tx.clone()).await;
|
94
|
-
let subscription_id_{{method_name | snake_case}}_method_req = subscription_id_{{method_name | snake_case}}_method_req.unwrap_or_else(|_|
|
92
|
+
let subscription_id_{{method_name | snake_case}}_method_req = connection.subscribe("{{method.topic}}".to_string(), 2, message_received_tx.clone()).await;
|
93
|
+
let subscription_id_{{method_name | snake_case}}_method_req = subscription_id_{{method_name | snake_case}}_method_req.unwrap_or_else(|_| usize::MAX);
|
95
94
|
{%endfor%}
|
96
|
-
|
97
95
|
{%for prop_name, prop in stinger.properties.items()-%}{%if not prop.read_only %}
|
98
|
-
let subscription_id_{{prop_name | snake_case}}_property_update = connection.subscribe("{{prop.update_topic}}", message_received_tx.clone()).await;
|
99
|
-
let subscription_id_{{prop_name | snake_case}}_property_update = subscription_id_{{prop_name | snake_case}}_property_update.unwrap_or_else(|_|
|
96
|
+
let subscription_id_{{prop_name | snake_case}}_property_update = connection.subscribe("{{prop.update_topic}}".to_string(), 2, message_received_tx.clone()).await;
|
97
|
+
let subscription_id_{{prop_name | snake_case}}_property_update = subscription_id_{{prop_name | snake_case}}_property_update.unwrap_or_else(|_| usize::MAX);
|
98
|
+
{%else-%}
|
100
99
|
{%endif%}{%endfor%}
|
101
|
-
|
100
|
+
{%if stinger.methods | length > 0 %}
|
102
101
|
// Create structure for method handlers.
|
103
102
|
let method_handlers = {{stinger.name | UpperCamelCase }}ServerMethodHandlers {
|
104
103
|
{%-for method_name, method in stinger.methods.items()-%}
|
105
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) } ))),
|
106
105
|
{%endfor%}
|
107
106
|
};
|
108
|
-
|
107
|
+
{%endif%}{# method handlers #}
|
108
|
+
|
109
109
|
// Create structure for subscription ids.
|
110
110
|
let sub_ids = {{stinger.name | UpperCamelCase }}ServerSubscriptionIds {
|
111
111
|
{%for method_name, method in stinger.methods.items()-%}
|
@@ -115,6 +115,7 @@ impl {{stinger.name | UpperCamelCase}}Server {
|
|
115
115
|
{{prop_name | snake_case}}_property_update: subscription_id_{{prop_name | snake_case}}_property_update,
|
116
116
|
{%endif%}{%endfor%}
|
117
117
|
};
|
118
|
+
{%endif%}
|
118
119
|
|
119
120
|
{%if stinger.properties|length > 0%}
|
120
121
|
let property_values = {{stinger.name | UpperCamelCase}}Properties {
|
@@ -129,56 +130,62 @@ impl {{stinger.name | UpperCamelCase}}Server {
|
|
129
130
|
};
|
130
131
|
{%endif%}
|
131
132
|
|
132
|
-
{{stinger.
|
133
|
-
|
133
|
+
{{stinger.rust.server_struct_name}} {
|
134
|
+
mqttier_client: connection.clone(),
|
135
|
+
{%if stinger.methods|length > 0 or stinger.properties|length > 0 %}
|
134
136
|
msg_streamer_rx: Some(message_received_rx),
|
135
|
-
msg_streamer_tx: message_received_tx,
|
136
|
-
|
137
|
-
method_handlers: method_handlers,{%if stinger.properties|length > 0%}
|
137
|
+
msg_streamer_tx: message_received_tx,{%if stinger.methods | length > 0 %}
|
138
|
+
method_handlers: method_handlers,{%endif%}{%if stinger.properties | length > 0 %}
|
138
139
|
properties: property_values,{%endif%}
|
139
140
|
subscription_ids: sub_ids,
|
141
|
+
{%endif%}
|
140
142
|
client_id: connection.client_id.to_string(),
|
141
143
|
}
|
142
144
|
}
|
143
145
|
|
144
146
|
{%for sig_name, sig in stinger.signals.items()-%}
|
147
|
+
/// Emits the {{sig_name}} signal with the given arguments.
|
145
148
|
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%}) {
|
146
|
-
let data =
|
149
|
+
let data = {{sig_name|UpperCamelCase}}SignalPayload {
|
147
150
|
{%for arg in sig.arg_list%}
|
148
151
|
{{arg.name}}: {{arg.name|snake_case}},
|
149
152
|
{%endfor%}
|
150
153
|
};
|
151
|
-
self.
|
154
|
+
let _ = self.mqttier_client.publish_state("{{sig.topic}}".to_string(), &data, 1).await;
|
152
155
|
}
|
153
156
|
{%endfor%}
|
154
157
|
|
155
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.
|
156
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) {
|
157
161
|
self.method_handlers.method_handler_for_{{method_name|snake_case}} = Arc::new(Mutex::new(Box::new(cb)));
|
158
162
|
}
|
159
163
|
{%endfor%}
|
160
164
|
|
161
|
-
|
162
165
|
{%for method_name, method in stinger.methods.items()-%}
|
163
|
-
|
164
|
-
|
165
|
-
let
|
166
|
-
let opt_resp_topic =
|
167
|
-
|
168
|
-
let
|
169
|
-
|
166
|
+
/// Handles a request message for the {{method_name}} method.
|
167
|
+
async fn handle_{{method_name|snake_case}}_request(publisher: MqttierClient, handlers: &mut {{stinger.name | UpperCamelCase }}ServerMethodHandlers, msg: ReceivedMessage) {
|
168
|
+
let opt_corr_data = msg.correlation_data;
|
169
|
+
let opt_resp_topic = msg.response_topic;
|
170
|
+
{%if method.return_value_type != false -%}
|
171
|
+
let payload_vec = msg.payload;
|
172
|
+
let payload = serde_json::from_slice::<{{method_name | UpperCamelCase}}RequestObject>(&payload_vec).unwrap();
|
173
|
+
{%endif%}
|
170
174
|
// call the method handler
|
171
175
|
let rv: {{method.return_value_rust_type}} = {
|
172
176
|
let func_guard = handlers.method_handler_for_{{method_name|snake_case}}.lock().unwrap();
|
173
177
|
(*func_guard)({%for arg in method.arg_list%}payload.{{arg.name}}{%if not loop.last%}, {%endif%}{%endfor%}).unwrap()
|
174
178
|
};
|
179
|
+
|
175
180
|
{%-if method.return_value_type == "primitive" -%}
|
176
181
|
let rv = {{method_name | UpperCamelCase}}ReturnValue {
|
177
182
|
{{method.return_value_property_name}}: rv,
|
178
183
|
};
|
179
184
|
{%endif%}
|
185
|
+
|
180
186
|
if let Some(resp_topic) = opt_resp_topic {
|
181
|
-
|
187
|
+
let corr_data = opt_corr_data.unwrap_or_default();
|
188
|
+
publisher.publish_response(resp_topic, &rv, corr_data).await.expect("Failed to publish response structure");
|
182
189
|
} else {
|
183
190
|
eprintln!("No response topic found in message properties.");
|
184
191
|
}
|
@@ -186,21 +193,21 @@ impl {{stinger.name | UpperCamelCase}}Server {
|
|
186
193
|
{%endfor%}
|
187
194
|
|
188
195
|
{%-for prop_name, prop in stinger.properties.items()%}
|
189
|
-
async fn publish_{{prop_name}}_value(
|
196
|
+
async fn publish_{{prop_name}}_value(publisher: MqttierClient, topic: String, data: {{prop.rust_type}})
|
190
197
|
{
|
191
198
|
{%-if prop.arg_list | length == 1%}
|
192
199
|
let new_data = {{prop_name | UpperCamelCase}}Property {
|
193
200
|
{{prop.arg_list[0].name}}: data,
|
194
201
|
};
|
195
|
-
let _pub_result = publisher.
|
202
|
+
let _pub_result = publisher.publish_state(topic, &new_data, 1).await;
|
196
203
|
{%else%}
|
197
|
-
let _pub_result = publisher.
|
204
|
+
let _pub_result = publisher.publish_state(topic, &data, 1).await;
|
198
205
|
{%endif%}
|
199
206
|
}
|
200
207
|
{%if not prop.read_only %}
|
201
|
-
async fn update_{{prop_name}}_value(publisher:
|
208
|
+
async fn update_{{prop_name}}_value(publisher: MqttierClient, topic: Arc<String>, data: Arc<Mutex<Option<{{prop.rust_type}}>>>, msg: ReceivedMessage)
|
202
209
|
{
|
203
|
-
let payload_str = msg.
|
210
|
+
let payload_str = String::from_utf8_lossy(&msg.payload).to_string();
|
204
211
|
let new_data: {{prop_name | UpperCamelCase}}Property = serde_json::from_str(&payload_str).unwrap();
|
205
212
|
let mut locked_data = data.lock().unwrap();
|
206
213
|
{%-if prop.arg_list | length == 1%}
|
@@ -216,7 +223,7 @@ impl {{stinger.name | UpperCamelCase}}Server {
|
|
216
223
|
let data2 = new_data;
|
217
224
|
{%endif%}
|
218
225
|
let _ = tokio::spawn(async move {
|
219
|
-
{{stinger.
|
226
|
+
{{stinger.rust.server_struct_name}}::publish_{{prop_name}}_value(publisher2, topic2, data2).await;
|
220
227
|
});
|
221
228
|
}
|
222
229
|
{%endif%}{# not read only property #}
|
@@ -228,11 +235,11 @@ impl {{stinger.name | UpperCamelCase}}Server {
|
|
228
235
|
*locked_data = Some(data.clone());
|
229
236
|
}
|
230
237
|
|
231
|
-
let publisher2 = self.
|
238
|
+
let publisher2 = self.mqttier_client.clone();
|
232
239
|
let topic2 = self.properties.{{prop_name}}_topic.as_ref().clone();
|
233
240
|
let _ = tokio::spawn(async move {
|
234
241
|
println!("Will publish property {{prop_name}} of type {{prop.rust_type}} to {}", topic2);
|
235
|
-
{{stinger.
|
242
|
+
{{stinger.rust.server_struct_name}}::publish_{{prop_name}}_value(publisher2, topic2, data).await;
|
236
243
|
});
|
237
244
|
}
|
238
245
|
{%endfor%}
|
@@ -242,31 +249,47 @@ impl {{stinger.name | UpperCamelCase}}Server {
|
|
242
249
|
/// Based on the subscription id of the received message, it will call a function to handle the
|
243
250
|
/// received message.
|
244
251
|
pub async fn receive_loop(&mut self) -> Result<(), JoinError> {
|
252
|
+
// Make sure the MqttierClient is connected and running.
|
253
|
+
let _ = self.mqttier_client.run_loop().await;
|
245
254
|
|
255
|
+
{%if stinger.methods|length > 0 or stinger.properties|length > 0 %}
|
246
256
|
// Take ownership of the RX channel that receives MQTT messages. This will be moved into the loop_task.
|
247
257
|
let mut message_receiver = self.msg_streamer_rx.take().expect("msg_streamer_rx should be Some");
|
258
|
+
|
259
|
+
{%if stinger.methods|length > 0 -%}
|
248
260
|
let mut method_handlers = self.method_handlers.clone();
|
261
|
+
{%endif-%}
|
262
|
+
|
249
263
|
let sub_ids = self.subscription_ids.clone();
|
250
|
-
|
264
|
+
|
265
|
+
{%-if stinger.methods|length > 0 %}
|
266
|
+
let publisher = self.mqttier_client.clone();
|
267
|
+
{%endif%}
|
268
|
+
|
251
269
|
{%-if stinger.properties | length > 0 %}
|
252
270
|
let properties = self.properties.clone();
|
253
271
|
{%endif%}
|
254
|
-
|
272
|
+
|
273
|
+
let loop_task = tokio::spawn(async move {
|
255
274
|
while let Some(msg) = message_receiver.recv().await {
|
256
275
|
{%-for method_name, method in stinger.methods.items()%}
|
257
276
|
{%if not loop.first%}else {%endif%}if msg.subscription_id == sub_ids.{{method_name | snake_case}}_method_req {
|
258
|
-
{{stinger.
|
277
|
+
{{stinger.rust.server_struct_name}}::handle_{{method_name|snake_case}}_request(publisher.clone(), &mut method_handlers, msg).await;
|
259
278
|
}
|
260
279
|
{%-endfor%}{# methods #}
|
261
280
|
{%-for prop_name, prop in stinger.properties.items()%}{%if not prop.read_only %}
|
262
281
|
{%if not loop.first or (stinger.methods | length > 0) %}else {%endif%}if msg.subscription_id == sub_ids.{{prop_name | snake_case}}_property_update {
|
263
|
-
{{stinger.
|
282
|
+
{{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;
|
264
283
|
}
|
265
284
|
{%-endif%}{%endfor%}{# properties #}
|
266
|
-
}
|
285
|
+
}
|
286
|
+
println!("No more messages from message_receiver channel");
|
267
287
|
});
|
268
|
-
|
269
|
-
|
288
|
+
let _ = tokio::join!(loop_task);
|
289
|
+
{%endif%} {# any methods or properties #}
|
290
|
+
|
291
|
+
println!("Server receive loop completed [error?]");
|
270
292
|
Ok(())
|
271
293
|
}
|
294
|
+
|
272
295
|
}
|
stingeripc/tools/cli.py
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
|
2
|
+
import os
|
3
|
+
from rich import print
|
4
|
+
from pathlib import Path
|
5
|
+
import typer
|
6
|
+
from typing_extensions import Annotated
|
7
|
+
from jacobsjinjatoo import templator as jj2
|
8
|
+
|
9
|
+
from stingeripc.interface import StingerInterface
|
10
|
+
|
11
|
+
from . import markdown_generator
|
12
|
+
from . import python_generator
|
13
|
+
from . import rust_generator
|
14
|
+
|
15
|
+
|
16
|
+
app = typer.Typer(help="stinger-ipc generator CLI")
|
17
|
+
|
18
|
+
|
19
|
+
@app.command()
|
20
|
+
def generate(
|
21
|
+
language: Annotated[str, typer.Argument(...)],
|
22
|
+
input_file: Annotated[Path, typer.Argument(..., exists=True, file_okay=True, dir_okay=False, readable=True)],
|
23
|
+
output_dir: Annotated[Path, typer.Argument(..., file_okay=False, dir_okay=True, writable=True, readable=True)],
|
24
|
+
):
|
25
|
+
"""Generate code for a Stinger interface.
|
26
|
+
|
27
|
+
LANGUAGE must be one of: rust, python, markdown
|
28
|
+
INPUT_FILE is the .stinger.yaml file
|
29
|
+
OUTPUT_DIR is the directory that will receive generated files
|
30
|
+
"""
|
31
|
+
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")
|
34
|
+
|
35
|
+
if lang == "python":
|
36
|
+
# python_generator.main expects Path arguments via typer
|
37
|
+
python_generator.main(input_file, output_dir)
|
38
|
+
elif lang == "markdown":
|
39
|
+
# markdown_generator.main expects Path arguments via typer
|
40
|
+
markdown_generator.main(input_file, output_dir)
|
41
|
+
elif lang == "web":
|
42
|
+
wt = jj2.WebTemplator(output_dir=output_dir)
|
43
|
+
ct = jj2.CodeTemplator(output_dir=output_dir)
|
44
|
+
wt.add_template_dir(
|
45
|
+
os.path.join(os.path.dirname(__file__), "../templates", "html")
|
46
|
+
)
|
47
|
+
ct.add_template_dir(
|
48
|
+
os.path.join(os.path.dirname(__file__), "../templates", "html")
|
49
|
+
)
|
50
|
+
with open(input_file, "r") as f:
|
51
|
+
stinger = StingerInterface.from_yaml(f)
|
52
|
+
for output_file in [
|
53
|
+
"app.js",
|
54
|
+
"styles.css",
|
55
|
+
]:
|
56
|
+
ct.render_template(f"{output_file}.jinja2", output_file, stinger=stinger)
|
57
|
+
wt.render_template("index.html.jinja2", "index.html", stinger=stinger)
|
58
|
+
else: # rust
|
59
|
+
rust_generator.main(input_file, output_dir)
|
60
|
+
|
61
|
+
|
62
|
+
print(f"Generation for '{lang}' completed.")
|
63
|
+
|
64
|
+
@app.command()
|
65
|
+
def hello():
|
66
|
+
print("Hello world")
|
67
|
+
|
68
|
+
def run():
|
69
|
+
app()
|
70
|
+
|
71
|
+
if __name__ == "__main__":
|
72
|
+
run()
|
73
|
+
|
@@ -15,14 +15,16 @@ def main(inname: Annotated[Path, typer.Argument(exists=True, file_okay=True, dir
|
|
15
15
|
params = {
|
16
16
|
"stinger": stinger,
|
17
17
|
}
|
18
|
-
|
18
|
+
output_dir = Path(outdir).resolve()
|
19
|
+
print(f"[bold green]Output directory:[/bold green] {output_dir}")
|
20
|
+
t = jj2.CodeTemplator(output_dir=output_dir)
|
19
21
|
t.add_template_dir(
|
20
22
|
os.path.join(os.path.dirname(__file__), "../templates", "markdown")
|
21
23
|
)
|
22
24
|
for output_file in [
|
23
25
|
"index.md",
|
24
26
|
]:
|
25
|
-
print(f"[bold red]GENERATING:[/bold red] {output_file}")
|
27
|
+
print(f"[bold red]GENERATING:[/bold red] {os.path.join(outdir, output_file)}")
|
26
28
|
t.render_template(f"{output_file}.jinja2", output_file, **params)
|
27
29
|
|
28
30
|
def run():
|
@@ -1,6 +1,5 @@
|
|
1
1
|
from jacobsjinjatoo import templator as jj2
|
2
2
|
from jacobsjinjatoo import stringmanip
|
3
|
-
import sys
|
4
3
|
import os.path
|
5
4
|
import typer
|
6
5
|
from typing_extensions import Annotated
|
@@ -10,25 +9,27 @@ from pathlib import Path
|
|
10
9
|
|
11
10
|
def main(inname: Annotated[Path, typer.Argument(exists=True, file_okay=True, dir_okay=False, readable=True)], outdir: Annotated[Path, typer.Argument(file_okay=False, dir_okay=True, writable=True, readable=True)]):
|
12
11
|
|
13
|
-
print(f"[bold
|
12
|
+
print(f"[bold]Reading:[/bold] {inname}")
|
14
13
|
with open(inname, "r") as f:
|
15
14
|
stinger = StingerInterface.from_yaml(f)
|
16
15
|
params = {
|
17
16
|
"stinger": stinger,
|
18
17
|
}
|
19
|
-
|
18
|
+
output_dir = Path(outdir).resolve()
|
19
|
+
print(f"[bold]Output directory:[/bold] {output_dir}")
|
20
|
+
t = jj2.CodeTemplator(output_dir=output_dir)
|
20
21
|
t.add_template_dir(
|
21
22
|
os.path.join(os.path.dirname(__file__), "../templates", "python")
|
22
23
|
)
|
23
24
|
for output_file in [
|
24
25
|
"pyproject.toml",
|
25
26
|
]:
|
26
|
-
print(f"[bold
|
27
|
-
t.render_template(f"{output_file}.jinja2",
|
27
|
+
print(f"[bold green]Generating:[/bold green] {output_file}")
|
28
|
+
t.render_template(f"{output_file}.jinja2", output_file, **params)
|
28
29
|
|
29
30
|
package_name = f"{stringmanip.lower_camel_case(stinger.name).lower()}ipc"
|
30
|
-
generated_pkg_dir =
|
31
|
-
print(f"[bold
|
31
|
+
generated_pkg_dir = output_dir / package_name
|
32
|
+
print(f"[bold]Making Directory:[/bold] {generated_pkg_dir}")
|
32
33
|
os.makedirs(generated_pkg_dir, exist_ok=True)
|
33
34
|
for output_file in [
|
34
35
|
"server.py",
|
@@ -37,10 +38,11 @@ def main(inname: Annotated[Path, typer.Argument(exists=True, file_okay=True, dir
|
|
37
38
|
"__init__.py",
|
38
39
|
"method_codes.py",
|
39
40
|
]:
|
40
|
-
|
41
|
-
|
41
|
+
of = generated_pkg_dir / output_file
|
42
|
+
print(f"[bold green]Generating:[/bold green] {of}")
|
43
|
+
output = t.render_template(f"{output_file}.jinja2", of, **params)
|
42
44
|
|
43
|
-
t.render_template("interface_types.py.jinja2",
|
45
|
+
t.render_template("interface_types.py.jinja2", f"{generated_pkg_dir}/{stinger.get_enum_module_name()}.py", **params)
|
44
46
|
|
45
47
|
def run():
|
46
48
|
typer.run(main)
|
@@ -1,50 +1,69 @@
|
|
1
1
|
from jacobsjinjatoo import templator as jj2
|
2
|
-
import sys
|
3
|
-
import yaml
|
4
2
|
import os
|
5
3
|
import shutil
|
4
|
+
import typer
|
5
|
+
from typing_extensions import Annotated
|
6
|
+
from pathlib import Path
|
7
|
+
from rich import print
|
6
8
|
|
7
9
|
from stingeripc import StingerInterface
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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 Rust output for a Stinger interface."""
|
17
|
+
|
18
|
+
with inname.open(mode="r") as f:
|
13
19
|
stinger = StingerInterface.from_yaml(f)
|
14
|
-
params = {
|
15
|
-
"stinger": stinger,
|
16
|
-
}
|
17
20
|
|
18
|
-
|
21
|
+
params = {"stinger": stinger}
|
22
|
+
|
23
|
+
if outdir.is_file():
|
24
|
+
raise RuntimeError("Output directory is a file!")
|
25
|
+
|
26
|
+
if not outdir.is_dir():
|
19
27
|
os.makedirs(outdir)
|
20
28
|
|
21
|
-
|
29
|
+
print(f"OUPUT: {outdir}")
|
22
30
|
|
23
|
-
|
31
|
+
this_file = Path(__file__)
|
32
|
+
this_dir = this_file.parent
|
33
|
+
template_dir = (this_dir / "../templates" / "rust").resolve()
|
34
|
+
|
35
|
+
t = jj2.CodeTemplator(output_dir=outdir)
|
24
36
|
t.add_template_dir(template_dir)
|
25
37
|
|
26
38
|
def recursive_render_templates(local_dir: str):
|
27
|
-
|
39
|
+
local_dir = Path(local_dir)
|
40
|
+
cur_template_dir = template_dir / local_dir
|
28
41
|
for entry in os.listdir(cur_template_dir):
|
29
|
-
if entry ==
|
42
|
+
if entry == "target":
|
30
43
|
# Do not copy 'target' dir
|
31
44
|
continue
|
32
|
-
entry_full_path =
|
33
|
-
entry_local_path =
|
45
|
+
entry_full_path = (cur_template_dir / entry).resolve()
|
46
|
+
entry_local_path = (local_dir / entry)
|
34
47
|
if entry.endswith(".jinja2"):
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
new_dir =
|
40
|
-
|
41
|
-
|
48
|
+
destpath = str(entry_local_path)[:-len(".jinja2")]
|
49
|
+
print(f"GENER: {entry_local_path} -> {destpath}")
|
50
|
+
t.render_template(entry_local_path, destpath, **params)
|
51
|
+
elif entry_full_path.is_dir():
|
52
|
+
new_dir = outdir / entry_local_path
|
53
|
+
print(f"MKDIR: {new_dir.resolve()}")
|
54
|
+
if not new_dir.exists():
|
55
|
+
new_dir.mkdir(parents=True)
|
42
56
|
recursive_render_templates(entry_local_path)
|
43
|
-
elif
|
44
|
-
shutil.copyfile(entry_full_path,
|
45
|
-
print(f"COPY: {
|
57
|
+
elif entry_full_path.is_file():
|
58
|
+
shutil.copyfile(entry_full_path, outdir / entry_local_path)
|
59
|
+
print(f"COPY: {entry_full_path}")
|
46
60
|
|
47
61
|
recursive_render_templates(".")
|
48
62
|
|
63
|
+
|
64
|
+
def run():
|
65
|
+
typer.run(main)
|
66
|
+
|
67
|
+
|
49
68
|
if __name__ == "__main__":
|
50
|
-
|
69
|
+
run()
|