diff --git a/Cargo.toml b/Cargo.toml index 58e71146d..695f0650a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,7 @@ base64 = "0.22.0" bytes = "1.1.0" chrono = { version = "0.4.34", default-features = false } darling = "0.20.3" -derivative = "2.1.1" +educe = { version = "0.6.0", default-features = false } either = "1.6.1" form_urlencoded = "1.2.0" futures = { version = "0.3.17", default-features = false } diff --git a/deny.toml b/deny.toml index b9852a057..a7a99e607 100644 --- a/deny.toml +++ b/deny.toml @@ -61,11 +61,6 @@ multiple-versions = "deny" [[bans.skip]] name = "rustls-native-certs" -[[bans.skip]] -# We need to replace derivative to get rid of syn 1 -# https://github.com/kube-rs/kube/issues/1583 -name = "syn" - [[bans.skip]] # base64 did some annoying breaking changes name = "base64" diff --git a/kube-runtime/Cargo.toml b/kube-runtime/Cargo.toml index 0be4682c2..4e343341f 100644 --- a/kube-runtime/Cargo.toml +++ b/kube-runtime/Cargo.toml @@ -32,7 +32,7 @@ rust.unsafe_code = "forbid" [dependencies] futures = { workspace = true, features = ["async-await"] } kube-client = { path = "../kube-client", version = "=0.95.0", default-features = false, features = ["jsonpatch", "client"] } -derivative.workspace = true +educe = { workspace = true, features = ["Clone", "Debug", "Hash", "PartialEq"] } serde.workspace = true ahash.workspace = true parking_lot.workspace = true diff --git a/kube-runtime/src/controller/mod.rs b/kube-runtime/src/controller/mod.rs index 3880d83fe..4a32dac36 100644 --- a/kube-runtime/src/controller/mod.rs +++ b/kube-runtime/src/controller/mod.rs @@ -12,7 +12,7 @@ use crate::{ watcher::{self, metadata_watcher, watcher, DefaultBackoff}, }; use backoff::backoff::Backoff; -use derivative::Derivative; +use educe::Educe; use futures::{ channel, future::{self, BoxFuture}, @@ -267,20 +267,21 @@ where /// NOTE: The reason is ignored for comparison purposes. This means that, for example, /// an object can only occupy one scheduler slot, even if it has been scheduled for multiple reasons. /// In this case, only *the first* reason is stored. -#[derive(Derivative)] -#[derivative( - Debug(bound = "K::DynamicType: Debug"), - Clone(bound = "K::DynamicType: Clone"), - PartialEq(bound = "K::DynamicType: PartialEq"), - Eq(bound = "K::DynamicType: Eq"), - Hash(bound = "K::DynamicType: Hash") +#[derive(Educe)] +#[educe( + Debug(bound("K::DynamicType: Debug")), + Clone(bound("K::DynamicType: Clone")), + PartialEq(bound("K::DynamicType: PartialEq")), + Hash(bound("K::DynamicType: Hash")) )] pub struct ReconcileRequest { pub obj_ref: ObjectRef, - #[derivative(PartialEq = "ignore", Hash = "ignore")] + #[educe(PartialEq(ignore), Hash(ignore))] pub reason: ReconcileReason, } +impl Eq for ReconcileRequest where K::DynamicType: Eq {} + impl From> for ReconcileRequest { fn from(obj_ref: ObjectRef) -> Self { ReconcileRequest { diff --git a/kube-runtime/src/lib.rs b/kube-runtime/src/lib.rs index 553fca629..50a0b961d 100644 --- a/kube-runtime/src/lib.rs +++ b/kube-runtime/src/lib.rs @@ -10,9 +10,11 @@ #![deny(clippy::all)] #![deny(clippy::pedantic)] -// Triggered by many derive macros (kube-derive, derivative) +// Triggered by many derive macros (kube-derive, educe) #![allow(clippy::default_trait_access)] #![allow(clippy::type_repetition_in_bounds)] +// Triggered by educe derives on enums +#![allow(clippy::used_underscore_binding)] // Triggered by Tokio macros #![allow(clippy::semicolon_if_nothing_returned)] // Triggered by nightly clippy on idiomatic code diff --git a/kube-runtime/src/reflector/dispatcher.rs b/kube-runtime/src/reflector/dispatcher.rs index 2db706a71..1060dab2b 100644 --- a/kube-runtime/src/reflector/dispatcher.rs +++ b/kube-runtime/src/reflector/dispatcher.rs @@ -4,7 +4,7 @@ use core::{ }; use std::{fmt::Debug, sync::Arc}; -use derivative::Derivative; +use educe::Educe; use futures::Stream; use pin_project::pin_project; use std::task::ready; @@ -14,8 +14,8 @@ use async_broadcast::{InactiveReceiver, Receiver, Sender}; use super::Lookup; -#[derive(Derivative)] -#[derivative(Debug(bound = "K: Debug, K::DynamicType: Debug"), Clone)] +#[derive(Educe)] +#[educe(Debug(bound("K: Debug, K::DynamicType: Debug")), Clone)] // A helper type that holds a broadcast transmitter and a broadcast receiver, // used to fan-out events from a root stream to multiple listeners. pub(crate) struct Dispatcher diff --git a/kube-runtime/src/reflector/object_ref.rs b/kube-runtime/src/reflector/object_ref.rs index 47e8b2d2f..a4db8473d 100644 --- a/kube-runtime/src/reflector/object_ref.rs +++ b/kube-runtime/src/reflector/object_ref.rs @@ -1,4 +1,4 @@ -use derivative::Derivative; +use educe::Educe; use k8s_openapi::{api::core::v1::ObjectReference, apimachinery::pkg::apis::meta::v1::OwnerReference}; #[cfg(doc)] use kube_client::core::ObjectMeta; use kube_client::{ @@ -98,13 +98,12 @@ impl Lookup for K { } } -#[derive(Derivative)] -#[derivative( - Debug(bound = "K::DynamicType: Debug"), - PartialEq(bound = "K::DynamicType: PartialEq"), - Eq(bound = "K::DynamicType: Eq"), - Hash(bound = "K::DynamicType: Hash"), - Clone(bound = "K::DynamicType: Clone") +#[derive(Educe)] +#[educe( + Debug(bound("K::DynamicType: Debug")), + PartialEq(bound("K::DynamicType: PartialEq")), + Hash(bound("K::DynamicType: Hash")), + Clone(bound("K::DynamicType: Clone")) )] /// A typed and namedspaced (if relevant) reference to a Kubernetes object /// @@ -141,10 +140,12 @@ pub struct ObjectRef { /// /// This is *not* considered when comparing objects, but may be used when converting to and from other representations, /// such as [`OwnerReference`] or [`ObjectReference`]. - #[derivative(Hash = "ignore", PartialEq = "ignore")] + #[educe(Hash(ignore), PartialEq(ignore))] pub extra: Extra, } +impl Eq for ObjectRef where K::DynamicType: Eq {} + /// Non-vital information about an object being referred to /// /// See [`ObjectRef::extra`]. diff --git a/kube-runtime/src/reflector/store.rs b/kube-runtime/src/reflector/store.rs index 343fd9602..f96ae6ec6 100644 --- a/kube-runtime/src/reflector/store.rs +++ b/kube-runtime/src/reflector/store.rs @@ -4,7 +4,7 @@ use crate::{ watcher, }; use ahash::AHashMap; -use derivative::Derivative; +use educe::Educe; use parking_lot::RwLock; use std::{fmt::Debug, hash::Hash, sync::Arc}; use thiserror::Error; @@ -179,8 +179,8 @@ where /// /// Cannot be constructed directly since one writer handle is required, /// use `Writer::as_reader()` instead. -#[derive(Derivative)] -#[derivative(Debug(bound = "K: Debug, K::DynamicType: Debug"), Clone)] +#[derive(Educe)] +#[educe(Debug(bound("K: Debug, K::DynamicType: Debug")), Clone)] pub struct Store where K::DynamicType: Hash + Eq, diff --git a/kube-runtime/src/scheduler.rs b/kube-runtime/src/scheduler.rs index 195b7a4ec..967a5e126 100644 --- a/kube-runtime/src/scheduler.rs +++ b/kube-runtime/src/scheduler.rs @@ -295,7 +295,7 @@ mod tests { use crate::utils::KubeRuntimeStreamExt; use super::{debounced_scheduler, scheduler, ScheduleRequest}; - use derivative::Derivative; + use educe::Educe; use futures::{channel::mpsc, future, poll, stream, FutureExt, SinkExt, StreamExt}; use std::{pin::pin, task::Poll}; use tokio::time::{advance, pause, sleep, Duration, Instant}; @@ -309,9 +309,9 @@ mod tests { } /// Message type that is always considered equal to itself - #[derive(Derivative, Eq, Clone, Debug)] - #[derivative(PartialEq, Hash)] - struct SingletonMessage(#[derivative(PartialEq = "ignore", Hash = "ignore")] u8); + #[derive(Educe, Eq, Clone, Debug)] + #[educe(PartialEq, Hash)] + struct SingletonMessage(#[educe(PartialEq(ignore), Hash(ignore))] u8); #[tokio::test] async fn scheduler_should_hold_and_release_items() { diff --git a/kube-runtime/src/utils/delayed_init.rs b/kube-runtime/src/utils/delayed_init.rs index 493947a23..7e273ae55 100644 --- a/kube-runtime/src/utils/delayed_init.rs +++ b/kube-runtime/src/utils/delayed_init.rs @@ -1,6 +1,5 @@ use std::{fmt::Debug, sync::Mutex, task::Poll}; -use derivative::Derivative; use futures::{channel, Future, FutureExt}; use thiserror::Error; use tracing::trace; @@ -26,8 +25,7 @@ impl Debug for Initializer { /// /// Can be considered equivalent to a [`channel::oneshot`] channel, except for that /// the value produced is retained for subsequent calls to [`Self::get`]. -#[derive(Derivative)] -#[derivative(Debug)] +#[derive(Debug)] pub struct DelayedInit { state: Mutex>, } diff --git a/kube-runtime/src/watcher.rs b/kube-runtime/src/watcher.rs index 63b1503ac..33157cbde 100644 --- a/kube-runtime/src/watcher.rs +++ b/kube-runtime/src/watcher.rs @@ -5,7 +5,7 @@ use crate::utils::ResetTimerBackoff; use async_trait::async_trait; use backoff::{backoff::Backoff, ExponentialBackoff}; -use derivative::Derivative; +use educe::Educe; use futures::{stream::BoxStream, Stream, StreamExt}; use kube_client::{ api::{ListParams, Resource, ResourceExt, VersionMatch, WatchEvent, WatchParams}, @@ -121,8 +121,8 @@ impl Event { } } -#[derive(Derivative, Default)] -#[derivative(Debug)] +#[derive(Educe, Default)] +#[educe(Debug)] /// The internal finite state machine driving the [`watcher`] enum State { /// The Watcher is empty, and the next [`poll`](Stream::poll_next) will start the initial LIST to get all existing objects @@ -137,7 +137,7 @@ enum State { /// Kubernetes 1.27 Streaming Lists /// The initial watch is in progress InitialWatch { - #[derivative(Debug = "ignore")] + #[educe(Debug(ignore))] stream: BoxStream<'static, kube_client::Result>>, }, /// The initial LIST was successful, so we should move on to starting the actual watch. @@ -150,7 +150,7 @@ enum State { /// with `Empty`. Watching { resource_version: String, - #[derivative(Debug = "ignore")] + #[educe(Debug(ignore))] stream: BoxStream<'static, kube_client::Result>>, }, }