kopium
kopium copied to clipboard
One-of required fields should generate Rust enums, not Option<T>
As only one value can be supplied at a time, it would be better to generate a Rust enum, so that the type system can enforce the one-of constraint.
Example:
podSelector:
description: Selects pods in the same namespace.
oneOf:
- required:
- matchExpressions
- required:
- matchLabels
properties:
matchExpressions:
items:
properties:
key:
type: string
operator:
enum:
- In
- NotIn
- Exists
- DoesNotExist
type: string
values:
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
type: object
x-kubernetes-preserve-unknown-fields: true
type: object
This generates:
/// Selects pods in the same namespace.
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ServerPodSelector {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub matchExpressions: Vec<ServerPodSelectorMatchExpressions>,
pub matchLabels: Option<ServerPodSelectorMatchLabels>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ServerPodSelectorMatchExpressions {
pub key: String,
pub operator: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub values: Vec<String>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ServerPodSelectorMatchLabels {
}
Complete CRD https://gist.github.com/alex-hunt-materialize/2743b1e2e58a49c4df0a11ecb39f46ab
Had a bit of a look at this in #65. I'm expecting we will want to generate something like:
pub enum ServerPodSelector {
MatchExpressions(Vec<ServerPodSelectorMatchExpressions>),
MatchLabels(BTreeMap<String, serde_json::Value>),
}
pub struct ServerPodSelectorMatchExpressions {
pub key: String,
pub operator: ServerPodSelectorMatchExpressionsOperator,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub values: Option<Vec<String>>,
}
pub enum ServerPodSelectorMatchExpressionsOperator {
In,
NotIn,
Exists,
DoesNotExists,
}
depending on choice in #31 for the preserve prop.. need to cross-reference with kube-derive to see what it is generating.
Ok, verified against kube-derive. The following example:
use kube::{CustomResource, CustomResourceExt};
use schemars::JsonSchema;
use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
#[derive(CustomResource, Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[kube(
group = "clux.dev",
version = "v1",
kind = "Server",
namespaced,
)]
#[serde(rename_all = "camelCase")]
pub struct ServerSpec {
pod_selector: ServerPodSelector
}
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub enum ServerPodSelector {
MatchExpressions(Vec<ServerPodSelectorMatchExpressions>),
MatchLabels(BTreeMap<String, String>),
}
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct ServerPodSelectorMatchExpressions {
pub key: String,
pub operator: ServerPodSelectorMatchExpressionsOperator,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub values: Option<Vec<String>>,
}
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
pub enum ServerPodSelectorMatchExpressionsOperator {
In,
NotIn,
Exists,
DoesNotExists,
}
fn main() {
let crd = serde_yaml::to_string(&Server::crd()).unwrap();
println!("CRD: \n{}", crd);
}
prints the crd with the following schema:
description: "Auto-generated derived type for ServerSpec via `CustomResource`"
properties:
spec:
properties:
podSelector:
oneOf:
- required:
- matchExpressions
- required:
- matchLabels
properties:
matchExpressions:
items:
properties:
key:
type: string
operator:
enum:
- In
- NotIn
- Exists
- DoesNotExists
type: string
values:
items:
type: string
nullable: true
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties: true
type: object
type: object
required:
- podSelector
type: object
required:
- spec
title: Server
type: object
which matches almost perfectly.
That output looks fantastic!