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:
Eric F
2026-06-08 00:38:27 -04:00
parent 468cfeaa50
commit d398a6ced2
7326 changed files with 1177561 additions and 7 deletions

View File

@@ -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;

View File

@@ -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 %}
}
}
}

View File

@@ -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 %}

View File

@@ -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();
}
}
}

View File

@@ -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;

View File

@@ -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 %}