minarrow/traits/custom_value.rs
1// Copyright 2025 Peter Garfield Bower
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! # **Custom Value Trait Module** - *Makes all your Any+Send+Sync types automatically compatible with Minarrow*
16//!
17//! Includes the [`CustomValue`] trait, enabling storage of arbitrary user-defined
18//! types inside [`enums::Value::Custom`] while maintaining a unified interface
19//! with scalars, arrays, and tables.
20//!
21//! This mechanism supports advanced or intermediate pipeline states-such as
22//! partial aggregates, sketches, or engine-specific outputs-that do not fit into
23//! the standard Arrow type system.
24//!
25//! Dynamic dispatch and `Any` downcasting allow recovery of the concrete type
26//! at runtime for type-specific operations. The library provides a blanket
27//! implementation so that any `Send + Sync + Clone + PartialEq + Debug + 'static`
28//! type can be used without manual implementation.
29//!
30//! ## Key Points
31//! - Enables integration of custom, non-Arrow types in Minarrow pipelines.
32//! - Supports deep cloning, semantic equality, and safe downcasting.
33//! - Borrowed types are not supported; use owned types or `Arc`.
34//! - Intended for specialised use cases; most data should use Arrow-compatible arrays.
35
36use std::{any::Any, sync::Arc};
37
38/// # Custom Value
39///
40/// Trait for any object that can be stored in `enums::Value::Custom`.
41///
42/// `CustomValue` extends *MinArrow's* `Value` universe, allowing engines or
43/// analytics to handle intermediate states and custom types
44/// within the same pipeline abstraction as scalars, arrays, and tables.
45///
46/// You must then manage downcasting on top of the base enum match, so it
47/// it's not the most ergonomic situation, but is available.
48///
49/// Typical use cases include:
50/// - Accumulators, partial aggregates, or sketches.
51/// - Custom algorithm outputs.
52/// - Arbitrary user-defined types requiring unified pipeline integration.
53///
54/// **Dynamic dispatch and downcasting** are used at runtime to recover the inner type
55/// and perform type-specific logic, such as merging, reduction, or finalisation.
56///
57/// ### Implementation Notes:
58/// - **Manual implementation is not required**.
59/// - Any type that implements `Debug`, `Clone`, `PartialEq`, and is `Send + Sync + 'static`
60/// automatically satisfies `CustomValue` via the blanket impl.
61/// - `Any` is automatically implemented by Rust for all `'static` types.
62///
63/// ### Borrowing Constraints:
64/// - **Borrowed types cannot be used in `Value::Custom` directly**, since `Any` requires `'static`.
65/// - To store borrowed data, first promote it to an owned type or wrap it in `Arc`.
66pub trait CustomValue: Any + Send + Sync + std::fmt::Debug {
67 /// Downcasts the type as `Any`
68 fn as_any(&self) -> &dyn Any;
69 /// Returns a deep clone of the object.
70 ///
71 /// Additionally, the `Value` enum automatically derives `Clone`, which is a
72 /// shallow `Arc` clone by default.
73 fn deep_clone(&self) -> Arc<dyn CustomValue>;
74
75 /// Performs semantic equality on the boxed object.
76 ///
77 /// This enables `PartialEq` to be implemented for `Value`,
78 /// since `dyn CustomValue` cannot use `==` directly.
79 fn eq_box(&self, other: &dyn CustomValue) -> bool;
80}
81
82/// Provided extension types implement `Clone`, `PartialEq`, `Debug`
83/// and is `Send` + `Sync + Any`, these methods implement automatically.
84impl<T> CustomValue for T
85where
86 T: Any + Send + Sync + Clone + PartialEq + std::fmt::Debug,
87{
88 fn as_any(&self) -> &dyn Any {
89 self
90 }
91
92 fn deep_clone(&self) -> Arc<dyn CustomValue> {
93 Arc::new(self.clone())
94 }
95
96 fn eq_box(&self, other: &dyn CustomValue) -> bool {
97 other
98 .as_any()
99 .downcast_ref::<T>()
100 .map_or(false, |o| self == o)
101 }
102}