Skip to content

Commit 1dd3047

Browse files
committed
refactor: Modularize token generation
This separates token generation code to be more modular. Depending on the context, individual parts of the code need to be placed in different locations. Two major refactors are the change of macro input as well as moving module generation out of the struct definition generation. This commit doesn't include changes for enum generation. This will be done in a separate commit.
1 parent 70726a2 commit 1dd3047

File tree

18 files changed

+568
-290
lines changed

18 files changed

+568
-290
lines changed

crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots.snap

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 51 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,57 @@
1+
//! This module contains attributes which can be used on containers (structs and enums).
2+
//!
3+
//! Generally there are two different containers, called "standalone" and "nested" based on which
4+
//! context they are used in. Standalone containers define versioning directly on the struct or
5+
//! enum. This is useful for single versioned containers. This type of versioning is fine as long
6+
//! as there is no other versioned container in the same file using the same versions. If that is
7+
//! the case, the generated modules will collide. There are two possible solutions for this: move
8+
//! each versioned container into its own file or use the nested declarations. It should be noted
9+
//! that there might be cases where it is fine to separate each container into its own file. One
10+
//! such case is when each container serves distinctively different use-cases and provide numerous
11+
//! associated items like functions.
12+
//!
13+
//! In cases where separate files are not desired, the nested mode can be used. The nested mode
14+
//! allows to declare versions on a module which contains containers which shall be versioned
15+
//! according to the defined versions. This approach allows defining multiple versioned containers
16+
//! in the same file without module collisions.
17+
//!
18+
//! The attributes used must be tailored to both of these two modes, because not all arguments are
19+
//! valid in all modes. As such different attributes allow different validation mechanisms. One
20+
//! such an example is that nested containers must not define versions as the definition is done
21+
//! on the module. This is in direct contrast to containers used in standalone mode.
22+
123
use std::{cmp::Ordering, ops::Deref};
224

325
use darling::{
426
util::{Flag, SpannedValue},
5-
Error, FromMeta, Result,
27+
Error, FromAttributes, FromMeta, Result,
628
};
729
use itertools::Itertools;
8-
use k8s_version::Version;
930

10-
/// This struct contains supported container attributes.
31+
use crate::attrs::common::{KubernetesArguments, SkipArguments, VersionArguments};
32+
33+
/// This struct contains supported container attributes which can be applied to structs and enums.
1134
///
1235
/// Currently supported attributes are:
1336
///
1437
/// - `version`, which can occur one or more times. See [`VersionAttributes`].
38+
/// - `k8s`, which enables Kubernetes specific features and allows customization if these features.
1539
/// - `options`, which allow further customization of the generated code.
16-
/// See [`ContainerAttributes`].
40+
/// See [`StandaloneOptionArguments`].
1741
#[derive(Debug, FromMeta)]
18-
#[darling(and_then = ContainerAttributes::validate)]
19-
pub(crate) struct ContainerAttributes {
42+
#[darling(and_then = StandaloneContainerAttributes::validate)]
43+
pub(crate) struct StandaloneContainerAttributes {
2044
#[darling(multiple, rename = "version")]
21-
pub(crate) versions: SpannedValue<Vec<VersionAttributes>>,
45+
pub(crate) versions: SpannedValue<Vec<VersionArguments>>,
2246

2347
#[darling(rename = "k8s")]
24-
pub(crate) kubernetes_attrs: Option<KubernetesAttributes>,
48+
pub(crate) kubernetes_args: Option<KubernetesArguments>,
2549

2650
#[darling(default, rename = "options")]
27-
pub(crate) common_option_attrs: OptionAttributes,
51+
pub(crate) common_option_args: StandaloneOptionArguments,
2852
}
2953

30-
impl ContainerAttributes {
54+
impl StandaloneContainerAttributes {
3155
fn validate(mut self) -> Result<Self> {
3256
// Most of the validation for individual version strings is done by the
3357
// k8s-version crate. That's why the code below only checks that at
@@ -47,7 +71,7 @@ impl ContainerAttributes {
4771

4872
// Ensure that versions are defined in sorted (ascending) order to keep
4973
// code consistent.
50-
if !self.common_option_attrs.allow_unsorted.is_present() {
74+
if !self.common_option_args.allow_unsorted.is_present() {
5175
let original = self.versions.deref().clone();
5276
self.versions
5377
.sort_by(|lhs, rhs| lhs.name.partial_cmp(&rhs.name).unwrap_or(Ordering::Equal));
@@ -91,7 +115,7 @@ impl ContainerAttributes {
91115

92116
// Ensure that the 'k8s' feature is enabled when the 'k8s()'
93117
// attribute is used.
94-
if self.kubernetes_attrs.is_some() && cfg!(not(feature = "k8s")) {
118+
if self.kubernetes_args.is_some() && cfg!(not(feature = "k8s")) {
95119
return Err(Error::custom(
96120
"the `#[versioned(k8s())]` attribute can only be used when the `k8s` feature is enabled",
97121
));
@@ -101,74 +125,30 @@ impl ContainerAttributes {
101125
}
102126
}
103127

104-
/// This struct contains supported version options.
105-
///
106-
/// Supported options are:
107-
///
108-
/// - `name` of the version, like `v1alpha1`.
109-
/// - `deprecated` flag to mark that version as deprecated.
110-
/// - `skip` option to skip generating various pieces of code.
111-
/// - `doc` option to add version-specific documentation.
112-
#[derive(Clone, Debug, FromMeta)]
113-
pub(crate) struct VersionAttributes {
114-
pub(crate) deprecated: Flag,
115-
pub(crate) name: Version,
116-
pub(crate) skip: Option<CommonSkipAttributes>,
117-
pub(crate) doc: Option<String>,
118-
}
119-
120-
/// This struct contains supported option attributes.
128+
/// This struct contains supported option arguments for containers used in standalone mode.
121129
///
122-
/// Supported attributes are:
130+
/// Supported arguments are:
123131
///
124-
/// - `allow_unsorted`, which allows declaring versions in unsorted order,
125-
/// instead of enforcing ascending order.
132+
/// - `allow_unsorted`, which allows declaring versions in unsorted order, instead of enforcing
133+
/// ascending order.
126134
/// - `skip` option to skip generating various pieces of code.
127135
#[derive(Clone, Debug, Default, FromMeta)]
128-
pub(crate) struct OptionAttributes {
136+
pub(crate) struct StandaloneOptionArguments {
129137
pub(crate) allow_unsorted: Flag,
130-
pub(crate) skip: Option<CommonSkipAttributes>,
138+
pub(crate) skip: Option<SkipArguments>,
131139
}
132140

133-
/// This struct contains supported Kubernetes attributes.
134-
///
135-
/// Supported attributes are:
136-
///
137-
/// - `skip`, which controls skipping parts of the generation.
138-
/// - `kind`, which allows overwriting the kind field of the CRD. This defaults
139-
/// to the struct name (without the 'Spec' suffix).
140-
/// - `group`, which sets the CRD group, usually the domain of the company.
141-
#[derive(Clone, Debug, FromMeta)]
142-
pub(crate) struct KubernetesAttributes {
143-
pub(crate) skip: Option<KubernetesSkipAttributes>,
144-
pub(crate) singular: Option<String>,
145-
pub(crate) plural: Option<String>,
146-
pub(crate) kind: Option<String>,
147-
pub(crate) namespaced: Flag,
148-
pub(crate) group: String,
149-
}
141+
#[derive(Debug, FromAttributes)]
142+
#[darling(attributes(versioned))]
143+
pub(crate) struct NestedContainerAttributes {
144+
#[darling(rename = "k8s")]
145+
pub(crate) kubernetes_args: Option<KubernetesArguments>,
150146

151-
/// This struct contains supported kubernetes skip attributes.
152-
///
153-
/// Supported attributes are:
154-
///
155-
/// - `merged_crd` flag, which skips generating the `crd()` and `merged_crd()`
156-
/// functions are generated.
157-
#[derive(Clone, Debug, FromMeta)]
158-
pub(crate) struct KubernetesSkipAttributes {
159-
/// Whether the `crd()` and `merged_crd()` generation should be skipped for
160-
/// this container.
161-
pub(crate) merged_crd: Flag,
147+
#[darling(default, rename = "options")]
148+
pub(crate) common_option_args: NestedOptionArguments,
162149
}
163150

164-
/// This struct contains supported common skip attributes.
165-
///
166-
/// Supported attributes are:
167-
///
168-
/// - `from` flag, which skips generating [`From`] implementations when provided.
169151
#[derive(Clone, Debug, Default, FromMeta)]
170-
pub(crate) struct CommonSkipAttributes {
171-
/// Whether the [`From`] implementation generation should be skipped for all
172-
/// versions of this container.
173-
pub(crate) from: Flag,
152+
pub(crate) struct NestedOptionArguments {
153+
pub(crate) skip: Option<SkipArguments>,
174154
}

crates/stackable-versioned-macros/src/attrs/common/item.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use proc_macro2::Span;
44
use syn::{spanned::Spanned, Attribute, Ident, Path, Type};
55

66
use crate::{
7-
attrs::common::ContainerAttributes,
7+
attrs::common::StandaloneContainerAttributes,
88
codegen::common::Attributes,
99
consts::{DEPRECATED_FIELD_PREFIX, DEPRECATED_VARIANT_PREFIX},
1010
};
@@ -23,7 +23,7 @@ where
2323
/// declared container versions.
2424
fn validate_versions(
2525
&self,
26-
container_attrs: &ContainerAttributes,
26+
container_attrs: &StandaloneContainerAttributes,
2727
item: &I,
2828
) -> Result<(), darling::Error>;
2929
}
@@ -35,7 +35,7 @@ where
3535
{
3636
fn validate_versions(
3737
&self,
38-
container_attrs: &ContainerAttributes,
38+
container_attrs: &StandaloneContainerAttributes,
3939
item: &I,
4040
) -> Result<(), darling::Error> {
4141
// NOTE (@Techassi): Can we maybe optimize this a little?
@@ -360,7 +360,6 @@ fn default_default_fn() -> SpannedValue<Path> {
360360
pub(crate) struct ChangedAttributes {
361361
pub(crate) since: SpannedValue<Version>,
362362
pub(crate) from_name: Option<SpannedValue<String>>,
363-
364363
pub(crate) from_type: Option<SpannedValue<Type>>,
365364
}
366365

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use darling::{util::Flag, FromMeta};
2+
3+
/// This struct contains supported Kubernetes arguments.
4+
///
5+
/// Supported arguments are:
6+
///
7+
/// - `skip`, which controls skipping parts of the generation.
8+
/// - `kind`, which allows overwriting the kind field of the CRD. This defaults to the struct name
9+
/// (without the 'Spec' suffix).
10+
/// - `group`, which sets the CRD group, usually the domain of the company.
11+
#[derive(Clone, Debug, FromMeta)]
12+
pub(crate) struct KubernetesArguments {
13+
pub(crate) skip: Option<KubernetesSkipArguments>,
14+
pub(crate) singular: Option<String>,
15+
pub(crate) plural: Option<String>,
16+
pub(crate) kind: Option<String>,
17+
pub(crate) namespaced: Flag,
18+
pub(crate) group: String,
19+
}
20+
21+
/// This struct contains supported kubernetes skip arguments.
22+
///
23+
/// Supported arguments are:
24+
///
25+
/// - `merged_crd` flag, which skips generating the `crd()` and `merged_crd()` functions are
26+
/// generated.
27+
#[derive(Clone, Debug, FromMeta)]
28+
pub(crate) struct KubernetesSkipArguments {
29+
/// Whether the `crd()` and `merged_crd()` generation should be skipped for
30+
/// this container.
31+
pub(crate) merged_crd: Flag,
32+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,40 @@
1+
use darling::{util::Flag, FromMeta};
2+
use k8s_version::Version;
3+
14
mod container;
25
mod item;
6+
mod k8s;
7+
mod module;
38

49
pub(crate) use container::*;
510
pub(crate) use item::*;
11+
pub(crate) use k8s::*;
12+
pub(crate) use module::*;
13+
14+
/// This struct contains supported version arguments.
15+
///
16+
/// Supported arguments are:
17+
///
18+
/// - `name` of the version, like `v1alpha1`.
19+
/// - `deprecated` flag to mark that version as deprecated.
20+
/// - `skip` option to skip generating various pieces of code.
21+
/// - `doc` option to add version-specific documentation.
22+
#[derive(Clone, Debug, FromMeta)]
23+
pub(crate) struct VersionArguments {
24+
pub(crate) deprecated: Flag,
25+
pub(crate) name: Version,
26+
pub(crate) skip: Option<SkipArguments>,
27+
pub(crate) doc: Option<String>,
28+
}
29+
30+
/// This struct contains supported common skip arguments.
31+
///
32+
/// Supported arguments are:
33+
///
34+
/// - `from` flag, which skips generating [`From`] implementations when provided.
35+
#[derive(Clone, Debug, Default, FromMeta)]
36+
pub(crate) struct SkipArguments {
37+
/// Whether the [`From`] implementation generation should be skipped for all versions of this
38+
/// container.
39+
pub(crate) from: Flag,
40+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use darling::{
2+
util::{Flag, SpannedValue},
3+
FromMeta, Result,
4+
};
5+
6+
use crate::attrs::common::{SkipArguments, VersionArguments};
7+
8+
#[derive(Debug, FromMeta)]
9+
#[darling(and_then = ModuleAttributes::validate)]
10+
pub(crate) struct ModuleAttributes {
11+
#[darling(multiple, rename = "version")]
12+
pub(crate) versions: SpannedValue<Vec<VersionArguments>>,
13+
14+
#[darling(default, rename = "options")]
15+
pub(crate) common_option_args: ModuleOptionArguments,
16+
}
17+
18+
impl ModuleAttributes {
19+
fn validate(self) -> Result<Self> {
20+
// TODO (@Techassi): Make this actually validate
21+
Ok(self)
22+
}
23+
}
24+
25+
#[derive(Debug, Default, FromMeta)]
26+
pub(crate) struct ModuleOptionArguments {
27+
pub(crate) skip: Option<SkipArguments>,
28+
pub(crate) preserve_module: Flag,
29+
pub(crate) allow_unsorted: Flag,
30+
}

0 commit comments

Comments
 (0)