ocpp-types 0.3.0

Strongly typed OCPP 1.6J, 2.0.1, and 2.1 message types for Rust. no_std, no alloc, embedded-friendly.
Documentation
[package]
name = "ocpp-types"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
repository.workspace = true
publish = true
description = "Strongly typed OCPP 1.6J, 2.0.1, and 2.1 message types for Rust. no_std, no alloc, embedded-friendly."
readme = "../../README.md"
keywords = ["ocpp", "ev-charging", "charging-station", "embedded", "no-std"]
categories = ["embedded", "no-std", "encoding", "network-programming"]

# Without this, docs.rs builds default features only -- which would hide the
# `chrono` interop entirely, and document every type in its `alloc` shape with
# no sign that the allocation-free build exists.
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]

[dependencies]
# Pinned to 0.8 on purpose: serde-json-core 0.6 (the latest) still depends on
# heapless 0.8, so moving to 0.9 would link *two* copies of heapless into any
# downstream binary using the `serde` feature -- a real size cost for a crate
# whose point is embedded/no-alloc. Bump this once serde-json-core follows;
# the wrapper types' `TryFrom` impls already handle 0.9's `CapacityError`, so
# it should be a one-line change.
heapless = "0.8"
serde = { version = "1", default-features = false, features = ["derive"], optional = true }
serde-json-core = { version = "0.6", optional = true }
# Interop only, never the representation: `OcppTimestamp` stays the crate's
# own 16-byte type, and this adds conversions for callers already using
# chrono. `default-features = false` keeps it off the `std` path.
chrono = { version = "0.4", default-features = false, optional = true }

[features]
# `alloc` is on by default. Without it every field is stored inline at its
# declared capacity, which is the point of the crate on an MCU but is a poor
# default for the majority of users -- a CSMS, a simulator, a test harness --
# who have an allocator and would otherwise meet multi-kilobyte message types
# for no reason. Turn it off with `default-features = false` for the
# allocation-free build; see the crate docs for sizing guidance.
default = ["alloc"]
serde = ["dep:serde", "dep:serde-json-core", "heapless/serde"]
# Fields with no spec-given bound (no `maxLength`/`maxItems`) use
# `alloc::string::String`/`alloc::vec::Vec<T>` when this is enabled, instead
# of a `heapless` collection sized by a caller-chosen const generic.
# `serde?/alloc` is a weak dependency feature: it only turns on serde's own
# `alloc` support (needed for `Serialize`/`Deserialize` on those types) when
# the `serde` feature is *also* enabled, without pulling serde in on its own.
alloc = ["serde?/alloc"]
# Spec-conformance checks the type system cannot carry: the `maxLength` of a
# string too large to inline as a `heapless::String` (which is a plain
# `alloc::string::String` under `alloc`, and a caller-chosen capacity
# without it), plus every `minItems`/`minimum`/`maximum`/`multipleOf` in the
# schemas, none of which a collection type can express. Off by default: a
# station that only ever *builds* messages from bounded fields pays nothing,
# while a CSMS -- which is where over-long payloads actually originate --
# turns it on. See the `validate` module.
validate = []
# `From`/`Into` between `OcppTimestamp` and `chrono::DateTime`. Additive:
# no message type changes shape, and chrono's own (allocating) serde support
# is not pulled onto the wire path.
chrono = ["dep:chrono"]

[[example]]
name = "basic_usage"

[[example]]
name = "serialization"
required-features = ["serde"]

[[example]]
name = "rpc_error_codes"

[[example]]
name = "unbounded_fields"

[[example]]
name = "envelope"
required-features = ["serde"]

[[example]]
name = "validate"
required-features = ["validate", "alloc"]