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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
//! This crate is on its own not really usable. It is re-exported together with the required
//! traits in the [optionable](https://docs.rs/optionable/) crate. The relevant docs can be also found there.
//!
//! This is only a separate crate as derive macros have to be a separate crate.
use TokenStream;
/// Derive macro to derive the `Optionable` trait for structs/enums recursively by generating
/// a type with all fields recursively replaced with `Option` versions.
/// All non-required fields have to implement the `Optionable` trait. This trait is already implemented by this library
/// for many primitive types, wrapper and container types.
///
/// ### Type-level attributes (on the struct/enum level)
/// - **`optionable_attr`**: Helper for the `derive` type-level attribute, for details see the `derive` attribute.
/// - **`derive`**: Allows to specify derive attributes that should be attached to the generate optioned struct/enum.
/// If you need to forward additional helper attributes to the generated type use `attr_copy` or `optionable_attr`
/// with the attribute to forward as content (works for type and field attributes).
/// ```rust,ignore
/// #[derive(optionable)]
/// #[optionable(derive(Deserialize, Serialize))]
/// #[optionable_attr(serde(rename_all = "camelCase"))]
/// struct MyStruct{
/// #[optionable_attr(serde(rename = "firstName"))]
/// name: String,
/// surname: String,
/// }
/// ```
/// - **`attr_copy`**: Attributes that should be copied over the optioned type.
/// If no `key` filter is set all attributes with the given path are copied over.
/// ```rust,ignore
/// #[derive(optionable)]
/// #[optionable(derive(Deserialize, Serialize))]
/// #[optionable(attr_copy(path=serde,key=rename))]
/// struct MyStruct{
/// name: String,
/// surname: String,
/// }
/// - **`no_convert`**: Does not derive the `OptionableConvert` implementation.
/// Might be required if the target type involves smart pointers or unsized fields.
/// ```rust,ignore
/// #[derive(optionable)]
/// #[optionable(no_convert)]
/// struct MyStruct{}
/// ```
/// - **`option_wrap`**: Whether to wrap a field that is already an `Option<T>` inside an outer `Option<Option<T>>` for the optioned type.
/// Useful for struct patching but not helpful once serializing to e.g. JSON is involved.
/// ```rust,ignore
/// #[derive(optionable)]
/// #[optionable(option_wrap)]
/// struct MyStruct{}
/// ```
/// - **`suffix`**: The name of the generated optioned struct/enum will be `<original><suffix>` with suffix
/// defaulting to `"Opt"`. The suffix value can be adjusted via e.g. `#[optionable(suffix="Ac")]`.
/// ```rust,ignore
/// #[derive(optionable)]
/// #[optionable(suffix="Ac")]
/// struct MyStruct{}
/// ```
///
/// ### Field-level attributes (for structs and struct-typed enum variants)
/// - **`optionable_attr`**: Helper for the `derive` type-level attribute, for details see the `derive` attribute.
/// - **`required`**: The annotated field will be kept as is and won't be transformed into some optional variant
/// for the derived optioned Struct.
/// ```rust,ignore
/// #[derive(optionable)]
/// struct MyStruct{
/// street: String; // will be an `Option<String>` in the derived `MyStructOpt`.
/// #[optionable(required)]
/// number: u32; // will also be a u32 in the derived `MyStructOpt`.
/// }
/// ```
/// - **`optioned_type`**: For circumvention of the orphan rule. Uses the provided type for the optioned variant of the field.
/// If `no_convert` is not set the provided type has to implement `OptionedConvert<O>` where `O` is the type of this field in the original type.
/// ```rust,ignore
/// #[derive(optionable)]
/// struct MyStruct{
/// street: String; // will be an `Option<String>` in the derived `MyStructOpt`.
/// #[optionable(optioned_type=MyInt)]
/// number: i32; // will be a `MyInt` in the derived `MyStructOpt`.
/// }
/// ````
/// - **`merge`**: Customize the merge behaviour for the derived `OptionableConvert::merge` implementation. The following values are supported (e.g. as `#[merge(atomic)]`):
/// - `optionable_convert` Default behavior, just call `OptionableConvert::merge` for the given field entry.
/// - `atomic` Always override the field completly when present in the optioned type.
/// - `append_not_present` Kubernetes `set` merge logic. Appends all entries that are not already present, requires for the field type to `impl Extend<T> + IntoIterator<Item=T> where T: PartialEq`.
/// - `iter_map` Kubernetes `map` merge logic. Identifies if entries belong together via the `optionable::merge::OptionableMapKeysEq` trait. Merges entries that belong together (short-circuit, first match is merged) and appends all entries that are not already present, requires for the field type to `impl Extend<T> + IntoIterator<Item=T> where T: OptionableMapKeysEq + OptionableConvert`.
/// - `ignore` Entry is not merged but kept as is.
/// - **`merge_map_key`**: Implies `required` (see above). Furthermore, if at least one field has this attribute the trait `OptionableMapKeysEq` is implemented
/// which uses the corresponding fields to determine if the "mapping keys" of two elements are equal. If the given struct is `T` this becomes utilized if another struct has a field
/// with the `merge(map)` attribute set and has a type that `impl IntoIterator<Item=T>`.
/// ```rust,ignore
/// #[derive(optionable)]
/// struct EnvVar{
/// #[merge_map_key]
/// street: String; // will be a `String` in the derived `EnvVarOpt` and used as a merge key for the merge logic of `Container` below.
/// value: String; // will be an `Option<String>` in the derived `EnvVarOpt`.
/// }
///
/// #[derive(optionable)]
/// struct Container{
/// #[merge(iter_map)]
/// env: Vec<EnvVar>; // will be a `Option<Vec<EnvVarOpt>>` in the derived `ContainerOpt`. The `OptionableConvert::merge` logic will merge entries that share the same keys (here the `name`) short-circuiting together and append those which did not find a merge target.
/// }
/// ````
///
/// ### Kubernetes type-level attributes (on the struct/enum level)
/// Specialized Kubernetes specific type attributes to help deriving optioned types (in go called `ApplyConfiguration`)
/// for Kubernetes resources/subfields.
///
/// - **`kube`**: Intended to be placed on types implementing subfields of a kube CRD spec.
/// Copies over any `#[(serde(rename="...")]`,`#[(serde(rename_all="...")]` and `#[(serde(rename_all_fields="...")]` attributes over to the optioned types.
/// ```rust,ignore
/// #[optionable(kube())]
/// #[derive(Optionable, Clone, Debug, Deserialize, Serialize, JsonSchema)]
/// pub struct MyCrdSpecTemplate {
/// pub replicas: u32,
/// }
/// ```
///
/// If you need to configure optionable explicitly setting this is e.g. for a Struct equivalent to:
/// ```rust,ignore
/// #[optionable(
/// attr_copy(path=serde,key=rename)
/// attr_copy(path=serde,key=rename_all)
/// attr_copy(path=serde,key=rename_all_fields)
/// )]
/// ```
///
/// - **`kube(resource)`**: Very specialized version of the `kube()` attribute intended to be placed on the root element of a `kube::CustomResource`.
/// Besides forwarding the serde rename attribute like `kube()` it also adds the following behaviour:
/// - Marks the `.metadata` field as required for the optioned type.
/// - Derives `kube::core::Resource` for the optioned type by referencing the original root `kube::CustomResource`.
/// - Adds specialized serde Serializing/Deserializing behaviour to the optioned type to handle the Group-Version-Kind envelope used by Kubernetes
/// (utilizing a [`PhantomData`-Marker](https://docs.rs/optionable/latest/optionable/kube/fn.serialize_api_envelope.html) on the optioned root type).
/// ```rust,ignore
/// #[derive(CustomResource)]
/// #[kube(
/// group = "example.localhost",
/// version = "v1",
/// kind = "CustomCrd",
/// namespaced,
/// derive = "Optionable",
/// attr = "optionable(kube(resource),derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize))",
/// )]
/// ```
/// Internal method for `derive_optionable` to simplify error handling.
/// Derive macro to derive the `DeepMerge` trait from `k8s_openapi` for structs/enums.
/// All fields have to implement the `DeepMerge` trait.
/// This trait is already implemented `k8s_openapi` for many primitive types, wrapper and container types.
///
/// ### Type-level attributes (on the struct/enum level)
/// - **`crate_k8s_openpi`**: Crate name for `k8s_openapi` where `DeepMerge` can be found. Defaults to `k8s_openapi`.
/// - **`crate_optionable`**: Crate name for `optionable` where helper functions used by the derived implentations are located.
/// Defaults to `optionable`.
///
/// ### Field-level attributes (for structs and struct-typed enum variants)
/// - **`method`**: Customize the merge behaviour. The following values are supported (e.g. as `#[deepmerge(atomic)]`):
/// - `deepmerge` Default behavior, just call `Deepmerge::merge_from` for the given field entry.
/// - `granular` merges the map entries using `Deepmerge::merge_from` if both exist.
/// - `atomic` Always override the field completly when present in the optioned type.
/// - `append_not_present` Kubernetes `set` merge logic. Appends all entries that are not already present, requires for the field type to `impl Extend<T> + IntoIterator<Item=T> where T: PartialEq`.
/// - `iter_map` Kubernetes `map` merge logic. Identifies if entries belong together via the `optionable::merge::OptionableMapKeysEq` trait. Merges entries that belong together (short-circuit, first match is merged) and appends all entries that are not already present, requires for the field type to `impl Extend<T> + IntoIterator<Item=T> where T: OptionableMapKeysEq + OptionableConvert`.
/// - `ignore` Entry is not merged but kept as is.
///
/// Internal method for `derive_optionable` to simplify error handling.
/// Derive macro to derive the `MapKeysEq` trait from `optionabe` for structs.
///
/// ### Type-level attributes (on the struct level)
/// - **`crate_optionable`**: Crate name for `optionable`. Defaults to `optionable`.
///
/// ### Field-level attributes
/// - **`map_key`**: Marks a field as being a `map_key` that has to be equal on two structs for `MapKeysEq::map_keys_eq` to be true. The corresponding field has to `impl PartialEq`.
/// Internal method for `derive_optionable` to simplify error handling.