1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "strum",
derive(strum::EnumString, strum::EnumIter, strum::IntoStaticStr)
)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
/// Abbildung verschiedener in der INVOIC angegebenen Rechnungsarten.
#[non_exhaustive]
pub enum NetznutzungRechnungsart {
#[cfg_attr(feature = "serde", serde(rename = "HANDELSRECHNUNG"))]
#[cfg_attr(feature = "strum", strum(serialize = "HANDELSRECHNUNG"))]
Handelsrechnung,
#[cfg_attr(feature = "serde", serde(rename = "SELBSTAUSGESTELLT"))]
#[cfg_attr(feature = "strum", strum(serialize = "SELBSTAUSGESTELLT"))]
Selbstausgestellt,
/// Unknown or future variant — produced when deserializing a value
/// that is not yet known to this version of the library.
#[cfg_attr(feature = "serde", serde(other, rename = "UNKNOWN"))]
#[cfg_attr(feature = "strum", strum(serialize = "UNKNOWN"))]
Unknown,
}
impl NetznutzungRechnungsart {
/// All variants defined by the BO4E schema, in declaration order.
///
/// Excludes the forward-compatibility [`NetznutzungRechnungsart::Unknown`] catch-all, so this
/// is exactly the set of values that appear on the wire. Available **without**
/// the `strum` feature — use it to drift-guard SQL `CHECK` lists and mappings.
pub const VARIANTS: &'static [Self] = &[Self::Handelsrechnung, Self::Selbstausgestellt];
/// Number of schema-defined variants (equal to `VARIANTS.len()`), excluding the
/// [`NetznutzungRechnungsart::Unknown`] catch-all. Stable for this schema version.
pub const COUNT: usize = Self::VARIANTS.len();
/// Returns an iterator over all **known** variants of `NetznutzungRechnungsart`.
///
/// Yields only variants that correspond to values defined in the BO4E schema
/// (i.e. [`Self::VARIANTS`]), never the [`NetznutzungRechnungsart::Unknown`] catch-all.
/// Available **without** the `strum` feature.
///
/// # Example
/// ```
/// # use rubo4e::current::NetznutzungRechnungsart;
/// // Never yields the `Unknown` catch-all, so the count matches `COUNT`.
/// assert_eq!(NetznutzungRechnungsart::iter_known().count(), NetznutzungRechnungsart::COUNT);
/// assert!(NetznutzungRechnungsart::iter_known().all(|v| v.is_known()));
/// ```
pub fn iter_known() -> impl Iterator<Item = Self> + Clone {
Self::VARIANTS.iter().copied()
}
/// Returns the canonical BO4E wire string (SCREAMING_SNAKE_CASE) for this value.
///
/// [`NetznutzungRechnungsart::Unknown`] renders as `"UNKNOWN"`, matching its serialized form.
pub const fn as_wire(&self) -> &'static str {
match self {
Self::Handelsrechnung => "HANDELSRECHNUNG",
Self::Selbstausgestellt => "SELBSTAUSGESTELLT",
Self::Unknown => "UNKNOWN",
}
}
/// **Strictly** parses a BO4E wire string into a known variant.
///
/// Unlike the lenient `serde` / [`FromStr`](std::str::FromStr) path — which maps
/// any unrecognized value (a typo, a legacy code, or a value from a newer schema)
/// to [`NetznutzungRechnungsart::Unknown`] — this returns
/// [`Err`](crate::error::UnknownVariant) for values not defined in this schema
/// version, including the literal `"UNKNOWN"`. Use it at the ingest boundary to
/// reject bad values instead of silently degrading them.
///
/// # Example
/// ```
/// # use rubo4e::current::NetznutzungRechnungsart;
/// assert_eq!(NetznutzungRechnungsart::from_wire("HANDELSRECHNUNG"), Ok(NetznutzungRechnungsart::Handelsrechnung));
/// // Out-of-schema values are rejected rather than degraded:
/// assert!(NetznutzungRechnungsart::from_wire("NOT_A_REAL_VALUE").is_err());
/// // …including the `Unknown` catch-all's own wire spelling:
/// assert!(NetznutzungRechnungsart::from_wire("UNKNOWN").is_err());
/// ```
pub fn from_wire(s: &str) -> Result<Self, crate::error::UnknownVariant> {
match s {
"HANDELSRECHNUNG" => Ok(Self::Handelsrechnung),
"SELBSTAUSGESTELLT" => Ok(Self::Selbstausgestellt),
other => Err(crate::error::UnknownVariant::new(other)),
}
}
/// Returns `true` if this value is the forward-compatibility
/// [`NetznutzungRechnungsart::Unknown`] catch-all (an out-of-schema value).
pub const fn is_unknown(&self) -> bool {
matches!(self, Self::Unknown)
}
/// Returns `true` if this value is a known, schema-defined variant.
pub const fn is_known(&self) -> bool {
!self.is_unknown()
}
}
impl std::fmt::Display for NetznutzungRechnungsart {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_wire())
}
}
impl AsRef<str> for NetznutzungRechnungsart {
fn as_ref(&self) -> &str {
self.as_wire()
}
}
#[cfg(feature = "versioned")]
impl crate::bo4e_enum_sealed::Sealed for NetznutzungRechnungsart {}
#[cfg(feature = "versioned")]
impl crate::Bo4eEnum for NetznutzungRechnungsart {
const VARIANTS: &'static [Self] = Self::VARIANTS;
const COUNT: usize = Self::COUNT;
fn as_wire(&self) -> &'static str {
Self::as_wire(self)
}
fn from_wire(s: &str) -> Result<Self, crate::error::UnknownVariant> {
Self::from_wire(s)
}
fn is_unknown(&self) -> bool {
Self::is_unknown(self)
}
}
#[cfg(feature = "versioned")]
impl crate::Bo4eStrict for NetznutzungRechnungsart {
fn collect_unknown_enums(&self, path: &str, out: &mut Vec<String>) {
if self.is_unknown() {
out.push(path.to_owned());
}
}
}
#[cfg(feature = "sqlx")]
impl sqlx::Type<sqlx::Postgres> for NetznutzungRechnungsart {
fn type_info() -> sqlx::postgres::PgTypeInfo {
<String as sqlx::Type<sqlx::Postgres>>::type_info()
}
}
/// Encodes as the canonical BO4E wire string, borrowed from `as_wire` — no
/// intermediate `String` or `serde_json::Value` is allocated.
#[cfg(feature = "sqlx")]
impl<'q> sqlx::Encode<'q, sqlx::Postgres> for NetznutzungRechnungsart {
fn encode_by_ref(
&self,
buf: &mut <sqlx::Postgres as sqlx::Database>::ArgumentBuffer<'q>,
) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
let s: &str = self.as_wire();
<&str as sqlx::Encode<'q, sqlx::Postgres>>::encode_by_ref(&s, buf)
}
}
/// Decodes leniently, matching the `serde` path: a value the schema does not
/// define becomes [`NetznutzungRechnungsart::Unknown`] rather than a decode error, so a
/// database row written by a newer schema version still reads back.
///
/// Use [`NetznutzungRechnungsart::from_wire`] on a `String` column, or check
/// [`NetznutzungRechnungsart::is_known`], where out-of-schema values must be rejected.
#[cfg(feature = "sqlx")]
impl<'r> sqlx::Decode<'r, sqlx::Postgres> for NetznutzungRechnungsart {
fn decode(
value: <sqlx::Postgres as sqlx::Database>::ValueRef<'r>,
) -> Result<Self, sqlx::error::BoxDynError> {
let s = <&str as sqlx::Decode<sqlx::Postgres>>::decode(value)?;
Ok(Self::from_wire(s).unwrap_or(Self::Unknown))
}
}
/// Lets `Vec<NetznutzungRechnungsart>` bind to a `TEXT[]` column. Only this crate can
/// provide it: the trait and the enum are both foreign to any consumer, so the
/// orphan rule rules out a downstream impl.
#[cfg(feature = "sqlx")]
impl sqlx::postgres::PgHasArrayType for NetznutzungRechnungsart {
fn array_type_info() -> sqlx::postgres::PgTypeInfo {
<String as sqlx::postgres::PgHasArrayType>::array_type_info()
}
}
#[cfg(test)]
impl proptest::arbitrary::Arbitrary for NetznutzungRechnungsart {
type Parameters = ();
type Strategy = proptest::strategy::BoxedStrategy<Self>;
fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
use proptest::prelude::*;
proptest::sample::select(Self::VARIANTS.to_vec()).boxed()
}
}