//! Bindings for the OpenShift client API, generated from the OpenAPI spec.
//!
//! Each supported version of OpenShift is represented by a feature name (like `v4_4`). Only one such feature can be enabled at a time.
//! As this crate depends on the other Kubernetes APIs, it references the `k8s_openapi` crate for
//! these types. Selecting a supported version for OpenShift (like `v4_4`) will automatically
//! select the corresponding Kubernetes feature on the `k8s_openapi` crate. So for OpenShift
//! `v4_4` it will automatically enable the feature `k8s_openapi/v1_17`.
//!
//! These docs have been generated with the `
//! ` feature enabled. To see docs for one of the other supported versions, please generate the docs locally with `cargo doc --features 'v4_<>'`
//!
//!
//! # Examples
//!
//! ## Resources
//!
//! This example creates an instance of [`api::core::v1::PodSpec`] with no other properties set, and pretty-prints it.
//!
//! ```rust
//! use k8s_openapi::api::core::v1 as api;
//!
//! fn main() {
//! let pod_spec: api::PodSpec = Default::default();
//! println!("{:#?}", pod_spec);
//! }
//! ```
//!
//! ## Client API
//!
//! (This requires the `api` feature to be enabled. The feature is enabled by default. See ["Crate features"](#crate-features) below for more details.)
//!
//! This example executes the [`api::core::v1::Pod::list_namespaced_pod`] API operation to list all pods inside a namespace.
//! It demonstrates the common patterns implemented by all API operation functions in this crate:
//!
//! 1. The API function has required parameters and optional parameters. All optional parameters are taken as a single struct with optional fields.
//!
//! Specifically for the [`api::core::v1::Pod::list_namespaced_pod`] operation, the `namespace` parameter is required and taken by the function itself,
//! while other optional parameters like `field_selector` are fields of the [`ListOptional`] struct. An instance of
//! this struct is taken as the last parameter of `Pod::list_namespaced_pod`. This struct impls [`Default`] so that you can just pass in `Default::default()`
//! if you don't want to specify values for any of the optional parameters.
//!
//! Some API operations have a single common type for optional parameters:
//!
//! - All create API take optional parameters using the [`CreateOptional`] struct.
//! - All delete API take optional parameters using the [`DeleteOptional`] struct.
//! - All list API take optional parameters using the [`ListOptional`] struct.
//! - All patch API take optional parameters using the [`PatchOptional`] struct.
//! - All replace API take optional parameters using the [`ReplaceOptional`] struct.
//! - All watch API take optional parameters using the [`WatchOptional`] struct.
//! - All delete-collection API take optional parameters using the [`DeleteOptional`] struct for delete options and the [`ListOptional`] struct for list options.
//!
//! Other API functions have their own `Optional` structs with fields corresponding to the specific parameters for those functions,
//! such as [`api::core::v1::ReadNamespacedPodOptional`] for [`api::core::v1::Pod::read_namespaced_pod`]
//!
//! 1. The function returns an [`http::Request`] value with the URL path, query string, and request body filled out according to the parameters
//! given to the function. The function does *not* execute this request. You can execute this `http::Request` using any HTTP client library you want to use.
//! It does not matter whether you use a synchronous client like `reqwest`, or an asynchronous client like `hyper`, or a mock client that returns bytes
//! read from a test file.
//!
//! 1. For each API operation function, there is a corresponding response type. For `Pod::list_namespaced_pod` this is [`ListResponse`]`<`[`api::core::v1::Pod`]`>`.
//! This is an enum with variants for each of the possible HTTP status codes that the operation can return, and contains the data that the API server would
//! return corresponding to that status code. For example, the list-namespaced-pod operation returns a pod list with HTTP 200 OK, so one of the variants of
//! that type is `Ok(`[`List`]`<`[`api::core::v1::Pod`]`>)`
//!
//! 1. The response types impl the [`Response`] trait, which contains a single [`Response::try_from_parts`] function. This function takes an [`http::StatusCode`]
//! and a `&u8` byte buffer, and tries to parse the byte buffer as the response type. For example, if you executed the request and received an HTTP 200 OK response
//! with some bytes, you could call `<ListResponse<Pod> as Response>::try_from_parts(status_code, buf)` and expect to get
//! `Ok(ListResponse::<Pod>::Ok(pod_list))` from it.
//!
//! Once again, this design ensures that the crate is not tied to a specific HTTP client library or interface. It does not matter how you execute the HTTP request,
//! nor whether your library is synchronous or asynchronous, since every HTTP client library gives you a way to get the HTTP response status code and the bytes
//! of the response body.
//!
//! 1. The API operation function also returns another value next to the `http::Request`. This value is a function that takes an [`http::StatusCode`] and returns
//! a [`ResponseBody`]`<ListResponse<Pod>>`. As mentioned above, `Response::try_from_parts` requires you to maintain a byte buffer for the response body.
//! `ResponseBody` is a helper that maintains such a buffer internally. It provides an `append_slice()` function to append slices to this internal buffer,
//! and a `parse()` function to parse the buffer as the expected type (`ListResponse<Pod>` in this case).
//!
//! It is not *necessary* to use the `ResponseBody` returned by the API operation function to parse the response. The `ResponseBody::parse` function is
//! only a wrapper around the underlying `Response::try_from_parts` function, and handles growing and shrinking its inner buffer as necessary. It also
//! helps ensure that the response body is parsed as the *correct* type for the operation, `ListResponse<Pod>` in this case, and not some other type.
//! However, you can instead use your own byte buffer instead of the `ResponseBody` value and call `ListResponse<Pod>::try_from_parts` yourself.
//!
//! 1. The response types are enums with variants corresponding to HTTP status codes. For example, the `ListResponse<Pod>::Ok` variant corresponds to the
//! HTTP 200 response of the list-namespaced-pod API.
//!
//! Each response enum also has an `Other` variant, that is yielded when the response status code does not match any of the other variants.
//! This variant has a `Result<Option<`[`serde_json::Value`]`>, `[`serde_json::Error`]`>` value.
//!
//! If the response body is empty, this value will be `Ok(None)`.
//!
//! If the response body is not empty, this value will be an `Ok(Some(value))` or `Err(err)` from attempting to parse that body as a `serde_json::Value`.
//! If you expect the response body to be a specific JSON type such as [`apimachinery::pkg::apis::meta::v1::Status`], you can use the `serde_json::Value`
//! as a [`serde::Deserializer`] like `let status = <Status as Deserialize>::deserialize(value)?;`. On the other hand, if you expect the response body to not be
//! a JSON value, then ignore the `Err(err)` and parse the raw bytes of the response into the appropriate type.
//!
//! Also see the `get_single_value` and `get_multiple_values` functions in
//! [the `k8s-openapi-tests` directory in the repository](https://github.com/Arnavion/k8s-openapi/tree/master/k8s-openapi-tests/src)
//! for examples of how to use a synchronous client with this style of API.
//!
//! // Re-export of the http crate since it's used in the public API
//! use k8s_openapi::http;
//!
//! use k8s_openapi::api::core::v1 as api;
//!
//! # struct Response;
//! # impl Response {
//! # fn status_code(&self) -> http::StatusCode {
//! # unimplemented!()
//! # }
//! # fn read_into(&self, _buf: &mut [u8]) -> std::io::Result<usize> {
//! # unimplemented!()
//! # }
//! # }
//! #
//! // Assume `execute` is some function that takes an `http::Request` and
//! // executes it synchronously or asynchronously to get a response. This is
//! // provided by your HTTP client library.
//! //
//! // Note that the `http::Request` values returned by API operation functions
//! // only have a URL path, query string and request body filled out. That is,
//! // they do *not* have a URL host. So the real `execute` implementation
//! // would first mutate the URL of the request to an absolute URL with
//! // the API server's authority, add authorization headers, etc before
//! // actually executing it.
//! fn execute(req: http::Request<Vec<u8>>) -> Response { unimplemented!(); }
//!
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
//! // Create a `http::Request` to list all the pods in the
//! // "kube-system" namespace.
//! let (request, response_body) =
//! api::Pod::list_namespaced_pod("kube-system", Default::default())?;
//!
//! // Execute the request and get a response.
//! // If this is an asynchronous operation, you would await
//! // or otherwise yield to the event loop here.
//! let response = execute(request);
//!
//! // Got a status code from executing the request.
//! let status_code: http::StatusCode = response.status_code();
//!
//! // Construct the `ResponseBody<ListResponse<Pod>>` using the
//! // constructor returned by the API function.
//! let mut response_body = response_body(status_code);
//!
//! // Buffer used for each read from the HTTP response.
//! let mut buf = Box::new([0u8; 4096]);
//!
//! let pod_list = loop {
//! // Read some bytes from the HTTP response into the buffer.
//! // If this is an asynchronous operation, you would await or
//! // yield to the event loop here.
//! let read = response.read_into(&mut *buf)?;
//!
//! // `buf` now contains some data read from the response. Append it
//! // to the `ResponseBody` and try to parse it into
//! // the response type.
//! response_body.append_slice(&buf[..read]);
//! let response = response_body.parse();
//! match response {
//! // Successful response (HTTP 200 and parsed successfully)
//! Ok(k8s_openapi::ListResponse::Ok(pod_list)) =>
//! break pod_list,
//!
//! // Some unexpected response
//! // (not HTTP 200, but still parsed successfully)
//! Ok(other) => return Err(format!(
//! "expected Ok but got {} {:?}",
//! status_code, other).into()),
//!
//! // Need more response data.
//! // Read more bytes from the response into the `ResponseBody`
//! Err(k8s_openapi::ResponseError::NeedMoreData) => continue,
//!
//! // Some other error, like the response body being
//! // malformed JSON or invalid UTF-8.
//! Err(err) => return Err(format!(
//! "error: {} {:?}",
//! status_code, err).into()),
//! }
//! };
//!
//! for pod in pod_list.items {
//! println!("{:#?}", pod);
//! }
//!
//! Ok(())
//! }
//! ```
//!
//!
//! # Crate features
//!
//! - This crate contains several `v4_*` features. Enabling one of the `v4_*` features selects which version of the Kubernetes API server this crate should target.
//! For example, enabling the `v4_3` feature means the crate will only contain the API exposed by OpenShift 4.3. It will not expose API
//! that were removed in 4.3 or earlier, nor any API added in 4.4 or later.
//!
//! - The crate also contains a feature named `api`. If this feature is disabled, the library will only contain the resource types like [`api::core::v1::Pod`],
//! and not the associated operation functions like [`api::core::v1::Pod::read_namespaced_pod`]. The `Response` and `Optional` types for the operation functions
//! will also not be accessible.
//!
//! This feature is enabled by default, but can be disabled if your crate does not need the operation functions to save on compile time and resources.
//!
//! One and only one of the `v1_*` features must be enabled at the same time, otherwise the crate will not compile. This ensures that all crates in the crate graph
//! use the same types. If it was possible for one library crate to use `api::core::v1::Pod` corresponding to v1.15 and another to use the type
//! corresponding to v1.16, an application would not be able to use the same `Pod` value with both.
//!
//! Thus, it is recommended that only application crates must enable one of the `v1_*` features, corresponding to the version of Kubernetes
//! that the application wants to support.
//!
//! ```toml
//! # For application crates
//!
//! [dependencies]
//! k8s-openapi = { version = "...", features = ["v1_14"] }
//! ```
//!
//! If you're writing a library crate, your crate *must not* enable any features of `k8s-openapi` directly. The choice of which feature to enable
//! must be left to any application crates that use your library. This ensures that all `k8s-openapi`-using dependencies in that application crate's dependency graph
//! use the same set of `k8s-openapi` types and are interoperable.
//!
//! If your library crate has tests or examples, you should also add a dev-dependency on `k8s-openapi` in addition to the direct dependency,
//! and enable a version feature only for that dev-dependency.
//!
//! ```toml
//! # For library crates
//!
//! [dependencies]
//! k8s-openapi = "..."
//!
//! [dev-dependencies]
//! k8s-openapi = { version = "...", features = ["v1_14"] }
//! ```
//!
//!
//! # Conditional compilation
//!
//! As the previous section explained, library crates must not enable any version features in their `k8s-openapi` dependency. However, your library crate may
//! need to know about which version gets selected eventually.
//!
//! For example:
//!
//! 1. Your crate creates a custom resource definition using the apiextensions v1 API. This API is only available in Kubernetes 1.16+,
//! so your crate would fail to compile if a lower feature was enabled.
//!
//! 1. Your crate creates a custom resource definition. If the `v1_16` or later feature is enabled, your crate wants to use the apiextensions v1 API,
//! otherwise it falls back to the v1beta1 API.
//!
//! There are two ways for your crate to determine which feature of `k8s-openapi` is enabled:
//!
//! 1. The `k8s-openapi` crate exports [`k8s_if_*` macros,](#macros) which either expand to their contents or don't. See the docs of the macros for more details.
//!
//! With these macros, the two cases above would be solved like this:
//!
//! - ```rust,ignore
//! #[macro_use] extern crate k8s_openapi;
//!
//! // The compile_error!() is only emitted if 1.15 or lower is selected.
//! k8s_if_le_1_15! {
//! compile_error!("This crate requires the v1_16 (or higher) feature to be enabled on the k8s-openapi crate.");
//! }
//! ```
//!
//! - ```rust,ignore
//! #[macro_use] extern crate k8s_openapi;
//!
//! k8s_if_le_1_15! {
//! use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1beta1 as apiextensions;
//! }
//! k8s_if_ge_1_16! {
//! use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1 as apiextensions;
//! }
//!
//! // Common fields regardless of the apiextensions version
//! let custom_resource_definition_spec = apiextensions::CustomResourceDefinitionSpec {
//! group: ...,
//! names: ...,
//! scope: ...,
//! ..Default::default()
//! };
//!
//! // Set v1beta1 `version` and `validation` fields on v1.15 and earlier.
//! k8s_if_le_1_15! {
//! let custom_resource_definition_spec = apiextensions::CustomResourceDefinitionSpec {
//! version: <FooBar as k8s_openapi::Resource>::VERSION.to_owned().into(),
//! validation: Some(custom_resource_validation),
//! ..custom_resource_definition_spec
//! };
//! }
//! // Set v1 `versions` field on v1.16 and later.
//! k8s_if_ge_1_16! {
//! let custom_resource_definition_spec = apiextensions::CustomResourceDefinitionSpec {
//! versions: vec![
//! apiextensions::CustomResourceDefinitionVersion {
//! name: <FooBar as k8s_openapi::Resource>::VERSION.to_owned(),
//! schema: Some(custom_resource_validation),
//! served: true,
//! storage: true,
//! ..Default::default()
//! },
//! ].into(),
//! ..custom_resource_definition_spec
//! };
//! }
//! ```
//!
//! 1. The `k8s-openapi` crate emits the selected version number as metadata that your crate can read in a build script
//! from the `DEP_K8S_OPENAPI_*_VERSION` env var.
//!
//! ```rust,no_run
//! // Your crate's build.rs
//!
//! fn main() {
//! let k8s_openapi_version: u32 =
//! std::env::vars_os()
//! .find_map(|(key, value)| {
//! let key = key.into_string().ok()?;
//! if key.starts_with("DEP_K8S_OPENAPI_") && key.ends_with("_VERSION") {
//! let value = value.into_string().ok()?;
//! Some(value)
//! }
//! else {
//! None
//! }
//! }).expect("DEP_K8S_OPENAPI_*_VERSION must have been set by k8s-openapi")
//! .parse().expect("DEP_K8S_OPENAPI_*_VERSION is malformed");
//!
//! // k8s_openapi_version has the format 0x00_MM_NN_00.
//! //
//! // - MM is the major version.
//! // - NN is the minor version.
//! //
//! // Thus, if the v1_16 feature was enabled, k8s_openapi_version would be 0x00_01_10_00
//!
//! // The build script can now do arbitrary things with the information.
//! // For example, it could define custom cfgs:
//! if k8s_openapi_version >= 0x00_01_10_00 {
//! println!(r#"cargo:rustc-cfg=k8s_apiextensions="v1""#);
//! }
//! else {
//! println!(r#"cargo:rustc-cfg=k8s_apiextensions="v1beta1""#);
//! }
//!
//! // or emit new source code files under OUT_DIR, or anything else a build script can do.
//! }
//! ```
//!
//! With these cfgs, the two cases above would be solved like this:
//!
//! - ```rust,ignore
//! // Your crate's src/lib.rs
//!
//! #[cfg(k8s_apiextensions = "v1")]
//! compile_error!("This crate requires the v1_16 (or higher) feature to be enabled on the k8s-openapi crate.");
//! ```
//!
//! - ```rust,ignore
//! #[cfg(k8s_apiextensions = "v1beta1")]
//! use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1beta1 as apiextensions;
//! #[cfg(k8s_apiextensions = "v1")]
//! use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1 as apiextensions;
//!
//! // Common fields regardless of the apiextensions version
//! let custom_resource_definition_spec = apiextensions::CustomResourceDefinitionSpec {
//! group: ...,
//! names: ...,
//! scope: ...,
//! ..Default::default()
//! };
//!
//! // Set v1beta1 `version` and `validation` fields on v1.15 and earlier.
//! #[cfg(k8s_apiextensions = "v1beta1")]
//! let custom_resource_definition_spec = apiextensions::CustomResourceDefinitionSpec {
//! version: <FooBar as k8s_openapi::Resource>::VERSION.to_owned().into(),
//! validation: Some(custom_resource_validation),
//! ..custom_resource_definition_spec
//! };
//! // Set v1 `versions` field on v1.16 and later.
//! #[cfg(k8s_apiextensions = "v1")]
//! let custom_resource_definition_spec = apiextensions::CustomResourceDefinitionSpec {
//! versions: vec![
//! apiextensions::CustomResourceDefinitionVersion {
//! name: <FooBar as k8s_openapi::Resource>::VERSION.to_owned(),
//! schema: Some(custom_resource_validation),
//! served: true,
//! storage: true,
//! ..Default::default()
//! },
//! ].into(),
//! ..custom_resource_definition_spec
//! };
//! ```
//!
//! Note that both approaches require your crate to have a direct dependency on the `k8s-openapi` crate. Neither approach is available if your crate
//! only has a transitive dependency on the `k8s-openapi` crate.
//!
//! The macros approach is easier to use since it doesn't require a build script.
//!
//! The build script method lets you emit arbitrary cfgs, emit arbitrary source code, and generally gives you more options, at the cost of needing a build script.
//! For example, `cfg()`s can be used in places where macros cannot, such as this example for conditionally setting attrs using `cfg_attr`:
//!
//! ```rust,ignore
//! #[derive(
//! Clone, Debug, PartialEq,
//! k8s_openapi_derive::CustomResourceDefinition,
//! serde_derive::Deserialize, serde_derive::Serialize,
//! )]
//! #[custom_resource_definition(
//! group = "k8s-openapi-tests-custom-resource-definition.com",
//! version = "v1",
//! plural = "foobars",
//! namespaced,
//! )]
//! #[cfg_attr(k8s_apiextensions = "v1beta1", custom_resource_definition(has_subresources = "v1beta1"))]
//! #[cfg_attr(k8s_apiextensions = "v1", custom_resource_definition(has_subresources = "v1"))]
//! struct FooBarSpec {
//! prop1: String,
//! prop2: Vec<bool>,
//! #[serde(skip_serializing_if = "Option::is_none")]
//! prop3: Option<i32>,
//! }
//! ```
//!
//! It isn't possible to conditionally set attributes using macros, so the entire `struct FooBarSpec` declaration would have to be duplicated and wrapped inside
//! `k8s_if_le_1_15! { }` and `k8s_if_ge_1_16! { }` respectively.
//!
//!
//! # Custom resource definitions
//!
//! The [`k8s-openapi-derive` crate](https://crates.io/crates/k8s-openapi-derive) provides a custom derive for generating clientsets
//! for custom resources. See that crate's docs for more information.
pub use chrono;
pub use serde_json;
pub use serde_value;
pub use *;
pub use *;
pub use *;
include!;