Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter
- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
/// {{trait.description | replace("\n", " ")}}
|
||||
pub(crate) trait {{trait.name | title}}ClientSubscriber: Sync + Send {
|
||||
{% for var in trait.vars %}
|
||||
fn on_{{ var.name | snake }}(&self, context: &Context, value: {{ var.data_type.name }});
|
||||
{% endfor %}
|
||||
|
||||
{%- if trait.errors %}
|
||||
fn on_error_raised(&self, context: &Context, error: ::everestrs::ErrorType<errors::{{ trait.name | snake }}::Error>);
|
||||
|
||||
fn on_error_cleared(&self, context: &Context, error: ::everestrs::ErrorType<errors::{{ trait.name | snake }}::Error>);
|
||||
{%- endif %}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "mockall", feature = "trait"))]
|
||||
mockall::mock! {
|
||||
pub(crate) {{trait.name | title}}ClientSubscriber {}
|
||||
impl {{trait.name | title}}ClientSubscriber for {{trait.name | title}}ClientSubscriber {
|
||||
{% for var in trait.vars %}
|
||||
fn on_{{ var.name | snake }}<'a>(&self, context: &Context<'a>, value: {{ var.data_type.name }});
|
||||
{% endfor %}
|
||||
|
||||
{%- if trait.errors %}
|
||||
fn on_error_raised<'a>(&self, context: &Context<'a>, error: ::everestrs::ErrorType<errors::{{ trait.name | snake }}::Error>);
|
||||
|
||||
fn on_error_cleared<'a>(&self, context: &Context<'a>, error: ::everestrs::ErrorType<errors::{{ trait.name | snake }}::Error>);
|
||||
{%- endif %}
|
||||
}
|
||||
}
|
||||
|
||||
fn dispatch_variable_to_{{ trait.name | snake }}(
|
||||
context: &Context,
|
||||
client_subscriber: &dyn {{trait.name | title}}ClientSubscriber,
|
||||
name: &str,
|
||||
value: __serde_json::Value,
|
||||
) -> ::everestrs::Result<()> {
|
||||
match name {
|
||||
{%- for var in trait.vars %}
|
||||
"{{ var.name }}" => {
|
||||
let v: {{ var.data_type.name }} = __serde_json::from_value(value)
|
||||
.map_err(|e| ::everestrs::Error::MessageParsingError(format!("Failed to deserialize variable `{{ var.name }}`: {e:?})")))?;
|
||||
client_subscriber.on_{{ var.name | snake }}(context, v);
|
||||
Ok(())
|
||||
},
|
||||
{%- endfor %}
|
||||
other => Err(::everestrs::Error::MessageParsingError(format!("Unknown variable {other} received.").to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
fn dispatch_error_to_{{ trait.name | snake }} (
|
||||
context: &Context,
|
||||
client_subscriber: &dyn {{ trait.name | title }}ClientSubscriber,
|
||||
error: ::everestrs::FfiErrorType,
|
||||
raised: bool
|
||||
) {
|
||||
{%- if trait.errors %}
|
||||
// The type is errors::{{ trait.name | snake }}::Error
|
||||
let Ok(v) = __serde_yaml::from_str(&error.error_type) else {
|
||||
everestrs::log::error!("Failed to deserialize error `{}`", error.error_type);
|
||||
return;
|
||||
};
|
||||
|
||||
let error_type = ::everestrs::ErrorType {
|
||||
error_type: v,
|
||||
description: error.description,
|
||||
message: error.message,
|
||||
severity: error.severity,
|
||||
};
|
||||
|
||||
if raised {
|
||||
client_subscriber.on_error_raised(context, error_type);
|
||||
} else {
|
||||
client_subscriber.on_error_cleared(context, error_type);
|
||||
}
|
||||
{%- endif %}
|
||||
}
|
||||
|
||||
pub(crate) mod __mockall_{{trait.name | snake }}_client {
|
||||
|
||||
use super::__serde_json;
|
||||
use super::types;
|
||||
use super::errors;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct {{trait.name | title }}ClientPublisher {
|
||||
pub(super) implementation_id: &'static str,
|
||||
pub(super) runtime: ::std::sync::Weak<::everestrs::Runtime>,
|
||||
pub(super) index: usize,
|
||||
}
|
||||
|
||||
impl {{trait.name | title }}ClientPublisher {
|
||||
{%- for cmd in trait.cmds %}
|
||||
/// {{cmd.description | replace("\n", " ")}}
|
||||
///
|
||||
{%- for arg in cmd.arguments %}
|
||||
/// `{{arg.name}}`: {{arg.description | replace("\n", " ")}}
|
||||
{%- endfor %}
|
||||
{% if cmd.result -%}
|
||||
///
|
||||
/// Returns: {{cmd.result.description | replace("\n", " ")}}
|
||||
{% endif -%}
|
||||
pub(crate) fn {{cmd.name | identifier}}(&self,
|
||||
{%- for arg in cmd.arguments %}
|
||||
{{arg.name | identifier }}: {{arg.data_type.name}},
|
||||
{%- endfor %}
|
||||
) -> ::everestrs::Result<{%- if cmd.result -%}
|
||||
{{cmd.result.data_type.name}}
|
||||
{%- else -%}
|
||||
()
|
||||
{%- endif -%}
|
||||
> {
|
||||
let args = __serde_json::json!({
|
||||
{%- for arg in cmd.arguments %}
|
||||
"{{arg.name}}": {{arg.name | identifier}},
|
||||
{%- endfor %}
|
||||
});
|
||||
let rt = self.runtime.upgrade().ok_or_else(|| {
|
||||
::everestrs::Error::HandlerException(
|
||||
"publisher used after Module was dropped".into(),
|
||||
)
|
||||
})?;
|
||||
rt.call_command(self.implementation_id, self.index, "{{ cmd.name }}", &args)
|
||||
}
|
||||
{% endfor %}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "mockall", not(feature = "trait")))]
|
||||
mockall::mock!{
|
||||
pub(crate) {{trait.name | title }}ClientPublisher {
|
||||
{%- for cmd in trait.cmds %}
|
||||
pub(crate) fn {{cmd.name | identifier}}(&self,
|
||||
{%- for arg in cmd.arguments %}
|
||||
{{arg.name | identifier }}: {{arg.data_type.name}},
|
||||
{%- endfor %}
|
||||
) -> ::everestrs::Result<{%- if cmd.result -%}
|
||||
{{cmd.result.data_type.name}}
|
||||
{%- else -%}
|
||||
()
|
||||
{%- endif -%}
|
||||
>;
|
||||
{% endfor %}
|
||||
}
|
||||
|
||||
impl Clone for {{trait.name | title }}ClientPublisher {
|
||||
fn clone(&self) -> Self;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#[cfg_attr(all(feature = "mockall", not(feature = "trait")), mockall_double::double)]
|
||||
pub(crate) use __mockall_{{trait.name | snake }}_client::{{trait.name | title }}ClientPublisher;
|
||||
@@ -0,0 +1,50 @@
|
||||
{% for p_config in provided_config %}
|
||||
/// The configuration for the {{ p_config.name }}.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct {{ p_config.name | title }}Config {
|
||||
{% for config in p_config.config %}
|
||||
/// {{ config.description }}
|
||||
pub(crate) {{ config.name | identifier }}: {{ config.data_type.name }},
|
||||
{% endfor %}
|
||||
}
|
||||
{% endfor %}
|
||||
|
||||
/// The configuration for the module. It also contains the config for all other
|
||||
/// interfaces.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ModuleConfig {
|
||||
{% for config in module_config %}
|
||||
/// {{ config.description }}
|
||||
pub(crate) {{ config.name | identifier }}: {{ config.data_type.name }},
|
||||
{% endfor %}
|
||||
|
||||
{% for p_config in provided_config %}
|
||||
/// The config for the `{{ p_config.name }}` interface.
|
||||
pub(crate) {{ p_config.name }}_config: {{ p_config.name | title }}Config,
|
||||
{% endfor %}
|
||||
}
|
||||
|
||||
/// Returns the config for the whole module.
|
||||
impl Module {
|
||||
pub(crate) fn get_config(&self) -> ModuleConfig {
|
||||
|
||||
let raw_config = self.runtime.get_module_configs();
|
||||
|
||||
{% for p_config in provided_config %}
|
||||
let {{ p_config.name }}_config = {{ p_config.name | title }}Config {
|
||||
{% for config in p_config.config %}
|
||||
{{ config.name | identifier }}: raw_config.get("{{ p_config.name }}").unwrap().get("{{ config.name }}").unwrap().try_into().unwrap(),
|
||||
{% endfor %}
|
||||
};
|
||||
{% endfor %}
|
||||
ModuleConfig {
|
||||
{% for config in module_config %}
|
||||
{{ config.name | identifier }}: raw_config.get("!module").unwrap().get("{{ config.name }}").unwrap().try_into().unwrap(),
|
||||
{% endfor %}
|
||||
|
||||
{% for p_config in provided_config %}
|
||||
{{ p_config.name }}_config,
|
||||
{% endfor %}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{%- for name, errors in involved_errors | items %}
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
pub mod {{ name | snake }} {
|
||||
use everestrs::serde as __serde;
|
||||
{%- for error_group in errors %}
|
||||
/// The error definition of the {{ name }} interface.
|
||||
/// {{ error_group.error_list.description | replace("\n", " ") }}
|
||||
#[derive(Debug, Clone, PartialEq, __serde::Serialize, __serde::Deserialize)]
|
||||
#[serde(crate = "__serde")]
|
||||
pub enum {{ error_group.name | title }}Error {
|
||||
{%- for error_entry in error_group.error_list.errors %}
|
||||
/// {{ error_entry.description | replace("\n", " ")}}
|
||||
#[serde(rename = "{{ error_group.name | snake }}/{{ error_entry.name }}")]
|
||||
{{ error_entry.name | title}},
|
||||
{%- endfor %}
|
||||
}
|
||||
{%- endfor %}
|
||||
/// All possible errors of the {{ name }} interface.
|
||||
#[derive(Debug, Clone, PartialEq, __serde::Serialize, __serde::Deserialize)]
|
||||
#[serde(crate = "__serde")]
|
||||
#[serde(untagged)]
|
||||
pub enum Error {
|
||||
{%- for error_group in errors %}
|
||||
{{ error_group.name | title }}({{ error_group.name | title }}Error),
|
||||
{%- endfor %}
|
||||
}
|
||||
}
|
||||
{%- endfor %}
|
||||
@@ -0,0 +1,302 @@
|
||||
mod generated {
|
||||
|
||||
#![allow(
|
||||
clippy::let_unit_value,
|
||||
clippy::match_single_binding,
|
||||
clippy::upper_case_acronyms,
|
||||
clippy::useless_conversion,
|
||||
clippy::too_many_arguments,
|
||||
dead_code,
|
||||
non_camel_case_types,
|
||||
unused_mut,
|
||||
unused_variables,
|
||||
unused_imports,
|
||||
)]
|
||||
|
||||
use everestrs::serde_json as __serde_json;
|
||||
use everestrs::serde_yaml as __serde_yaml;
|
||||
|
||||
pub mod types {
|
||||
{% include "types" %}
|
||||
}
|
||||
|
||||
pub mod errors {
|
||||
{% include "errors" %}
|
||||
}
|
||||
|
||||
{% include "config" %}
|
||||
|
||||
/// Called when the module receives on_ready from EVerest.
|
||||
pub(crate) trait OnReadySubscriber: Sync + Send {
|
||||
fn on_ready(&self, pub_impl: &ModulePublisher);
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "mockall", feature = "trait"))]
|
||||
mockall::mock! {
|
||||
pub(crate) OnReadySubscriber {}
|
||||
impl OnReadySubscriber for OnReadySubscriber {
|
||||
fn on_ready(&self, pub_impl: &ModulePublisher);
|
||||
}
|
||||
}
|
||||
|
||||
{% for trait in provided_interfaces %}
|
||||
{% include "service" %}
|
||||
{% endfor %}
|
||||
|
||||
{% for trait in required_interfaces %}
|
||||
{% include "client" %}
|
||||
{% endfor %}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[cfg_attr(all(test, feature="mockall", not(feature="trait")), derive(Default))]
|
||||
pub(crate) struct ModulePublisher {
|
||||
{% for provide in provides %}
|
||||
pub(crate) {{ provide.implementation_id | identifier }}: {{provide.interface | title}}ServicePublisher,
|
||||
{% endfor %}
|
||||
{% for require in requires %}
|
||||
{% if require.min_connections == 1 and require.max_connections == 1 %}
|
||||
pub(crate) {{ require.implementation_id | identifier }}: {{require.interface | title}}ClientPublisher,
|
||||
{% elif require.min_connections == require.max_connections %}
|
||||
pub(crate) {{ require.implementation_id | identifier }}_slots: [{{ require.interface | title}}ClientPublisher; {{require.min_connections}}],
|
||||
{% else %}
|
||||
pub(crate) {{ require.implementation_id | identifier }}_slots: Vec<{{require.interface | title}}ClientPublisher>,
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
}
|
||||
|
||||
struct ModuleInner {
|
||||
on_ready: ::std::sync::Arc<dyn OnReadySubscriber>,
|
||||
{% for provide in provides %}
|
||||
{{ provide.implementation_id | identifier }}: ::std::sync::Arc<dyn {{provide.interface | title}}ServiceSubscriber>,
|
||||
{% endfor %}
|
||||
{% for require in requires %}
|
||||
{% if require.min_connections == 1 and require.max_connections == 1 %}
|
||||
{{ require.implementation_id | identifier }}: ::std::sync::Arc<dyn {{require.interface | title}}ClientSubscriber>,
|
||||
{% elif require.min_connections == require.max_connections %}
|
||||
{{ require.implementation_id | identifier }}_slots: [::std::sync::Arc<dyn {{require.interface | title}}ClientSubscriber>; {{require.max_connections}}],
|
||||
{% else %}
|
||||
{{ require.implementation_id | identifier }}_slots: Vec<::std::sync::Arc<dyn {{require.interface | title}}ClientSubscriber>>,
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
publisher: ModulePublisher,
|
||||
ready: ::std::sync::Condvar,
|
||||
ready_flag: ::std::sync::Mutex<bool>,
|
||||
}
|
||||
|
||||
pub(crate) struct Module {
|
||||
runtime: ::std::pin::Pin<::std::sync::Arc<::everestrs::Runtime>>,
|
||||
inner: ::std::sync::OnceLock<::std::sync::Arc<ModuleInner>>,
|
||||
}
|
||||
|
||||
/// The context structure.
|
||||
pub(crate) struct Context<'a> {
|
||||
pub(crate) publisher: &'a ModulePublisher,
|
||||
{# TODO(ddo) Clarify the naming. #}
|
||||
/// The name as in `implementation_id`.
|
||||
pub name: &'a str,
|
||||
/// The index of the slot.
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
impl Module {
|
||||
#[must_use]
|
||||
pub(crate) fn new() -> Self {
|
||||
let runtime = ::everestrs::Runtime::new();
|
||||
Self {
|
||||
runtime,
|
||||
inner: ::std::sync::OnceLock::new(),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn new_with_args(args: ::everestrs::Args) -> Self {
|
||||
let runtime = ::everestrs::Runtime::new_with_args(args);
|
||||
Self {
|
||||
runtime,
|
||||
inner: ::std::sync::OnceLock::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn start
|
||||
{% if requires_with_generics %}
|
||||
<
|
||||
{% for require in requires %}
|
||||
{% if require.min_connections != 1 or require.max_connections != 1 %}
|
||||
{{ require.implementation_id | title }}Callback: FnMut(usize) -> ::std::sync::Arc<dyn {{require.interface | title}}ClientSubscriber>,
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
>
|
||||
{% endif %}
|
||||
(
|
||||
&self,
|
||||
on_ready: ::std::sync::Arc<dyn OnReadySubscriber>,
|
||||
{% for provide in provides %}
|
||||
{{ provide.implementation_id | identifier }}: ::std::sync::Arc<dyn {{provide.interface | title}}ServiceSubscriber>,
|
||||
{% endfor %}
|
||||
{% for require in requires %}
|
||||
{% if require.min_connections == 1 and require.max_connections == 1 %}
|
||||
{{ require.implementation_id | identifier }}: ::std::sync::Arc<dyn {{require.interface | title}}ClientSubscriber>,
|
||||
{% else %}
|
||||
{{ require.implementation_id | identifier }}_cb: {{ require.implementation_id | title }}Callback,
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
) -> &ModulePublisher {
|
||||
let runtime = &self.runtime;
|
||||
let connections = runtime.get_module_connections();
|
||||
// Publishers hold a Weak<Runtime> so ModuleInner -> publishers ->
|
||||
// Runtime -> sub_impl -> ModuleInner is not a cycle. Drop of the
|
||||
// Module deterministically tears down Runtime, ModuleInner, and the
|
||||
// mock subscribers inside it.
|
||||
let runtime_weak = ::std::sync::Arc::downgrade(&::std::pin::Pin::into_inner(runtime.clone()));
|
||||
let inner = self.inner.get_or_init(|| {
|
||||
::std::sync::Arc::new(ModuleInner {
|
||||
on_ready,
|
||||
{% for provide in provides %}
|
||||
{{ provide.implementation_id | identifier }},
|
||||
{% endfor %}
|
||||
{% for require in requires %}
|
||||
{% if require.min_connections == 1 and require.max_connections == 1 %}
|
||||
{{ require.implementation_id | identifier }},
|
||||
{% elif require.min_connections == require.max_connections %}
|
||||
{{ require.implementation_id | identifier }}_slots: ::core::array::from_fn({{ require.implementation_id | identifier }}_cb),
|
||||
{% else %}
|
||||
{{ require.implementation_id | identifier }}_slots: (0..connections.get("{{require.implementation_id}}").cloned().unwrap_or(0)).map({{ require.implementation_id | identifier }}_cb).collect(),
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
#[cfg(any(not(test), not(feature = "mockall"), feature = "trait"))]
|
||||
publisher: ModulePublisher {
|
||||
{% for provide in provides %}
|
||||
{{ provide.implementation_id | identifier }}: {{provide.interface | title}}ServicePublisher {
|
||||
implementation_id: "{{ provide.implementation_id }}",
|
||||
runtime: runtime_weak.clone(),
|
||||
},
|
||||
{% endfor %}
|
||||
{% for require in requires %}
|
||||
{% if require.min_connections == 1 and require.max_connections == 1 %}
|
||||
{{ require.implementation_id | identifier }}: {{require.interface | title}}ClientPublisher {
|
||||
implementation_id: "{{ require.implementation_id }}",
|
||||
runtime: runtime_weak.clone(),
|
||||
index: 0,
|
||||
},
|
||||
{% elif require.min_connections == require.max_connections %}
|
||||
{{ require.implementation_id | identifier }}_slots: ::core::array::from_fn(|i| {{require.interface | title}}ClientPublisher{
|
||||
implementation_id: "{{ require.implementation_id }}",
|
||||
runtime: runtime_weak.clone(),
|
||||
index: i,
|
||||
}),
|
||||
{% else %}
|
||||
{{ require.implementation_id | identifier }}_slots: (0..connections.get("{{require.implementation_id}}").cloned().unwrap_or(0)).map(|i| {{require.interface | title}}ClientPublisher{
|
||||
implementation_id: "{{ require.implementation_id }}",
|
||||
runtime: runtime_weak.clone(),
|
||||
index: i,
|
||||
}).collect(),
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
},
|
||||
#[cfg(all(test, feature = "mockall", not(feature = "trait")))]
|
||||
publisher: ModulePublisher::default(),
|
||||
ready: ::std::sync::Condvar::new(),
|
||||
ready_flag: ::std::sync::Mutex::new(false),
|
||||
})
|
||||
});
|
||||
|
||||
runtime.as_ref().set_subscriber(inner.clone());
|
||||
|
||||
// Block until on_ready has fired.
|
||||
let mut ready = inner.ready_flag.lock().unwrap();
|
||||
while !*ready {
|
||||
ready = inner.ready.wait(ready).unwrap();
|
||||
}
|
||||
|
||||
&inner.publisher
|
||||
}
|
||||
}
|
||||
|
||||
impl ::everestrs::Subscriber for ModuleInner {
|
||||
fn handle_command(
|
||||
&self,
|
||||
implementation_id: &str,
|
||||
name: &str,
|
||||
parameters: ::std::collections::HashMap<String, __serde_json::Value>,
|
||||
) -> ::everestrs::Result<__serde_json::Value> {
|
||||
let context = Context {
|
||||
publisher: &self.publisher,
|
||||
name: implementation_id,
|
||||
index: 0,
|
||||
};
|
||||
match implementation_id {
|
||||
{% for provide in provides %}
|
||||
"{{ provide.implementation_id }}" => {
|
||||
dispatch_command_to_{{ provide.interface | snake }}(&context, self.{{ provide.implementation_id | identifier }}.as_ref(), name, parameters)
|
||||
},
|
||||
{% endfor %}
|
||||
other => Err(::everestrs::Error::MessageParsingError(
|
||||
format!("Unknown implementation_id {other} called."),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_variable(
|
||||
&self,
|
||||
implementation_id: &str,
|
||||
index: usize,
|
||||
name: &str,
|
||||
value: __serde_json::Value,
|
||||
) -> ::everestrs::Result<()> {
|
||||
let context = Context {
|
||||
publisher: &self.publisher,
|
||||
name: implementation_id,
|
||||
index,
|
||||
};
|
||||
match implementation_id {
|
||||
{% for req in requires %}
|
||||
"{{ req.implementation_id }}" => {
|
||||
{% if req.min_connections == 1 and req.max_connections == 1 %}
|
||||
dispatch_variable_to_{{ req.interface | snake }}(&context, self.{{ req.implementation_id | identifier }}.as_ref(), name, value)
|
||||
{% else %}
|
||||
dispatch_variable_to_{{ req.interface | snake }}(&context, self.{{ req.implementation_id | identifier }}_slots[index].as_ref(), name, value)
|
||||
{% endif %}
|
||||
},
|
||||
{% endfor %}
|
||||
other => Err(::everestrs::Error::MessageParsingError(
|
||||
format!("Unknown variable {other} received."),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_on_error(
|
||||
&self,
|
||||
implementation_id: &str,
|
||||
index: usize,
|
||||
error: ::everestrs::FfiErrorType,
|
||||
raised: bool
|
||||
) {
|
||||
let context = Context {
|
||||
publisher: &self.publisher,
|
||||
name: implementation_id,
|
||||
index,
|
||||
};
|
||||
match implementation_id {
|
||||
{% for req in requires %}
|
||||
"{{ req.implementation_id }}" => {
|
||||
{% if req.min_connections == 1 and req.max_connections == 1 %}
|
||||
dispatch_error_to_{{ req.interface | snake }}(&context, self.{{ req.implementation_id | identifier }}.as_ref(), error, raised)
|
||||
{% else %}
|
||||
dispatch_error_to_{{ req.interface | snake }}(&context, self.{{ req.implementation_id | identifier }}_slots[index].as_ref(), error, raised)
|
||||
{% endif %}
|
||||
},
|
||||
{% endfor %}
|
||||
_ => everestrs::log::error!("Received an unknown error from {implementation_id}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn on_ready(&self) {
|
||||
self.on_ready.on_ready(&self.publisher);
|
||||
let mut ready = self.ready_flag.lock().unwrap();
|
||||
*ready = true;
|
||||
self.ready.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/// {{trait.description | replace("\n", " ")}}
|
||||
pub(crate) trait {{trait.name | title}}ServiceSubscriber: Sync + Send {
|
||||
{%- for cmd in trait.cmds %}
|
||||
/// {{cmd.description | replace("\n", " ")}}
|
||||
///
|
||||
{%- for arg in cmd.arguments %}
|
||||
/// `{{arg.name}}`: {{arg.description | replace("\n", " ")}}
|
||||
{%- endfor %}
|
||||
{% if cmd.result -%}
|
||||
///
|
||||
/// Returns: {{cmd.result.description | replace("\n", " ")}}
|
||||
{% endif -%}
|
||||
fn {{cmd.name}}(&self,
|
||||
context: &Context,
|
||||
{%- for arg in cmd.arguments %}
|
||||
{{arg.name | identifier }}: {{arg.data_type.name}},
|
||||
{%- endfor %}
|
||||
) -> ::everestrs::Result<{%- if cmd.result -%}
|
||||
{{cmd.result.data_type.name}}
|
||||
{%- else -%}
|
||||
()
|
||||
{%- endif -%}>;
|
||||
{% endfor %}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "mockall", feature = "trait"))]
|
||||
mockall::mock! {
|
||||
pub(crate) {{trait.name | title}}ServiceSubscriber {}
|
||||
impl {{trait.name | title}}ServiceSubscriber for {{trait.name | title}}ServiceSubscriber {
|
||||
{%- for cmd in trait.cmds %}
|
||||
fn {{cmd.name}}<'a>(&self,
|
||||
context: &Context<'a>,
|
||||
{%- for arg in cmd.arguments %}
|
||||
{{arg.name | identifier }}: {{arg.data_type.name}},
|
||||
{%- endfor %}
|
||||
) -> ::everestrs::Result<{%- if cmd.result -%}
|
||||
{{cmd.result.data_type.name}}
|
||||
{%- else -%}
|
||||
()
|
||||
{%- endif -%}>;
|
||||
{% endfor %}
|
||||
}
|
||||
}
|
||||
|
||||
fn dispatch_command_to_{{ trait.name | snake }}(
|
||||
context: &Context,
|
||||
service: &dyn {{trait.name | title}}ServiceSubscriber,
|
||||
name: &str,
|
||||
mut parameters: ::std::collections::HashMap<String, __serde_json::Value>,
|
||||
) -> ::everestrs::Result<__serde_json::Value> {
|
||||
match name {
|
||||
{%- for cmd in trait.cmds %}
|
||||
"{{ cmd.name }}" => {
|
||||
{%- for arg in cmd.arguments %}
|
||||
let {{ arg.name | identifier }}: {{ arg.data_type.name }} = __serde_json::from_value(
|
||||
parameters.remove("{{ arg.name }}")
|
||||
.ok_or(::everestrs::Error::MessageParsingError("Argument `{{ arg.name }}` not provided".to_string()))?,
|
||||
)
|
||||
.map_err(|e| ::everestrs::Error::MessageParsingError(format!("Failed to deserialize argument `{{ arg.name }}`: {e:?}")))?;
|
||||
{%- endfor %}
|
||||
let retval = service.{{ cmd.name }}(context,
|
||||
{%- for arg in cmd.arguments %}
|
||||
{{ arg.name | identifier }},
|
||||
{%- endfor %}
|
||||
)?;
|
||||
__serde_json::to_value(retval).map_err(|e| ::everestrs::Error::MessageParsingError(format!("Failed to serialize result: {e:?}")))
|
||||
},
|
||||
{%- endfor %}
|
||||
other => Err(::everestrs::Error::MessageParsingError(format!("Unknown command `{other}` called."))),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod __mockall_{{trait.name | snake }}_service {
|
||||
|
||||
use super::types;
|
||||
use super::errors;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct {{trait.name | title }}ServicePublisher {
|
||||
pub(super) implementation_id: &'static str,
|
||||
pub(super) runtime: ::std::sync::Weak<::everestrs::Runtime>,
|
||||
}
|
||||
|
||||
impl {{trait.name | title }}ServicePublisher {
|
||||
{% for var in trait.vars %}
|
||||
pub(crate) fn {{ var.name | identifier }}(&self, value: {{ var.data_type.name }}) -> ::everestrs::Result<()> {
|
||||
if let Some(runtime) = self.runtime.upgrade() {
|
||||
runtime.publish_variable(self.implementation_id, "{{ var.name }}", &value)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
{% endfor %}
|
||||
|
||||
{%- if trait.errors %}
|
||||
pub(crate) fn raise_error(&self, error: ::everestrs::ErrorType<errors::{{ trait.name | snake }}::Error>) {
|
||||
if let Some(runtime) = self.runtime.upgrade() {
|
||||
runtime.raise_error(self.implementation_id, error);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn clear_error(&self, error: errors::{{ trait.name | snake }}::Error) {
|
||||
if let Some(runtime) = self.runtime.upgrade() {
|
||||
runtime.clear_error(self.implementation_id, error, true);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn clear_all_errors(&self) {
|
||||
if let Some(runtime) = self.runtime.upgrade() {
|
||||
runtime.clear_error(self.implementation_id, "", true);
|
||||
}
|
||||
}
|
||||
{%- endif %}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "mockall", not(feature = "trait")))]
|
||||
mockall::mock!{
|
||||
pub(crate) {{trait.name | title }}ServicePublisher {
|
||||
{% for var in trait.vars %}
|
||||
pub(crate) fn {{ var.name | identifier }}(&self, value: {{ var.data_type.name }}) -> ::everestrs::Result<()>;
|
||||
{% endfor %}
|
||||
|
||||
{%- if trait.errors %}
|
||||
pub(crate) fn raise_error(&self, error: ::everestrs::ErrorType<errors::{{ trait.name | snake }}::Error>);
|
||||
|
||||
pub(crate) fn clear_error(&self, error: errors::{{ trait.name | snake }}::Error);
|
||||
|
||||
pub(crate) fn clear_all_errors(&self);
|
||||
{%- endif %}
|
||||
}
|
||||
|
||||
impl Clone for {{trait.name | title }}ServicePublisher {
|
||||
fn clone(&self) -> Self;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#[cfg_attr(all(feature = "mockall", not(feature = "trait")), mockall_double::double)]
|
||||
pub(crate) use __mockall_{{trait.name | snake }}_service::{{trait.name | title }}ServicePublisher;
|
||||
@@ -0,0 +1,31 @@
|
||||
{% for name, types in types.children | items %}
|
||||
pub mod {{ name }} {
|
||||
mod types { pub use super::super::*; }
|
||||
{% include "types" %}
|
||||
}
|
||||
{% endfor %}
|
||||
|
||||
use everestrs::serde as __serde;
|
||||
|
||||
{% for object in types.objects %}
|
||||
#[derive(Debug, Clone, PartialEq, __serde::Serialize, __serde::Deserialize)]
|
||||
#[serde(crate = "__serde")]
|
||||
pub struct {{ object.name }} {
|
||||
{% for p in object.properties %}
|
||||
/// {{ p.description | replace("\n", " ") }}
|
||||
#[serde(rename="{{ p.name }}"{% if p.data_type.extra_serde_annotations %},{{ p.data_type.extra_serde_annotations | join(",") }}{% endif %})]
|
||||
pub {{ p.name | identifier }}: {{ p.data_type.name }},
|
||||
{% endfor %}
|
||||
}
|
||||
{% endfor %}
|
||||
|
||||
{% for enum in types.enums %}
|
||||
#[derive(Debug, Clone, PartialEq, __serde::Serialize, __serde::Deserialize)]
|
||||
#[serde(crate = "__serde")]
|
||||
pub enum {{ enum.name }} {
|
||||
{% for item in enum.items %}
|
||||
{{ item }},
|
||||
{% endfor %}
|
||||
}
|
||||
|
||||
{% endfor %}
|
||||
Reference in New Issue
Block a user