Skip to main content

diskann_benchmark_runner/
input.rs

1/*
2 * Copyright (c) Microsoft Corporation.
3 * Licensed under the MIT license.
4 */
5
6use crate::Checker;
7
8/// Inputs to [`Benchmarks`](crate::Benchmark).
9///
10/// These begin as [`raw`](Self::Raw) data transfer objects before final construction via
11/// [`from_raw`](Self::from_raw).
12pub trait Input: Sized + std::fmt::Debug + 'static {
13    /// The raw form of this input that is deserialized from input files and serialized as
14    /// [`examples`](Self::example). The raw nature of this type reflects that no input
15    /// validation has been performed beyond the checks performed by its
16    /// [`Deserialize`](serde::Deserialize) implementation.
17    ///
18    /// Final object validation is performed via [`from_raw`](Self::from_raw).
19    type Raw: serde::de::DeserializeOwned + serde::Serialize;
20
21    /// Return the discriminant associated with this type.
22    ///
23    /// This is used to map inputs types to their respective parsers.
24    ///
25    /// Well formed implementations should always return the same result.
26    fn tag() -> &'static str;
27
28    /// Construct `Self` from the raw deserialized representation, performing any necessary
29    /// validation checks (e.g., resolving file paths via the [`Checker`]).
30    fn from_raw(raw: Self::Raw, checker: &mut Checker) -> anyhow::Result<Self>;
31
32    /// Serialize `self` to a [`serde_json::Value`].
33    fn serialize(&self) -> anyhow::Result<serde_json::Value>;
34
35    /// Return an example of a raw input for this [`Input`].
36    ///
37    /// This is used to supply sample JSON layouts in the benchmark CLI.
38    fn example() -> Self::Raw;
39}
40
41pub(crate) mod internal {
42    use crate::{Checker, Features, internal::visibility::Visibility};
43
44    /// Runtime representation of a deserialized [`Input`].
45    #[derive(Debug)]
46    pub(crate) struct Any {
47        any: Box<dyn DynAny>,
48    }
49
50    impl Any {
51        pub(crate) fn new<T>(input: T) -> Self
52        where
53            T: super::Input,
54        {
55            Self {
56                any: Box::new(input),
57            }
58        }
59
60        #[must_use = "this function has no side effects"]
61        pub(crate) fn tag(&self) -> &'static str {
62            self.any.tag()
63        }
64
65        #[must_use = "this function has no side effects"]
66        pub(crate) fn downcast_ref<T>(&self) -> Option<&T>
67        where
68            T: std::any::Any,
69        {
70            self.any.as_any().downcast_ref::<T>()
71        }
72
73        #[must_use = "this function has no side effects"]
74        pub(crate) fn is<T>(&self) -> bool
75        where
76            T: std::any::Any,
77        {
78            self.any.as_any().is::<T>()
79        }
80
81        #[must_use = "this function has no side effects"]
82        pub(crate) fn serialize(&self) -> anyhow::Result<serde_json::Value> {
83            self.any.serialize()
84        }
85    }
86
87    trait DynAny: std::fmt::Debug {
88        fn tag(&self) -> &'static str;
89        fn as_any(&self) -> &dyn std::any::Any;
90        fn serialize(&self) -> anyhow::Result<serde_json::Value>;
91    }
92
93    impl<T> DynAny for T
94    where
95        T: super::Input,
96    {
97        fn tag(&self) -> &'static str {
98            <Self as super::Input>::tag()
99        }
100
101        fn as_any(&self) -> &dyn std::any::Any {
102            self
103        }
104
105        fn serialize(&self) -> anyhow::Result<serde_json::Value> {
106            <Self as super::Input>::serialize(self)
107        }
108    }
109
110    /// Crate interface for working with inputs.
111    #[derive(Debug)]
112    pub(crate) struct Input(Box<dyn DynInput>);
113
114    impl Input {
115        /// Construct a new, non-gated `Input` for `T`.
116        pub(crate) fn new<T>() -> Self
117        where
118            T: super::Input,
119        {
120            Self(Box::new(Wrapper::<T>::new()))
121        }
122
123        /// Create a gated input.
124        pub(crate) fn gated(tag: &'static str, features: Features) -> Self {
125            Self(Box::new(Gated { tag, features }))
126        }
127
128        pub(crate) fn tag(&self) -> &'static str {
129            self.0.tag()
130        }
131
132        pub(crate) fn try_deserialize(
133            &self,
134            serialized: &serde_json::Value,
135            checker: &mut Checker,
136        ) -> anyhow::Result<Any> {
137            self.0.try_deserialize(serialized, checker)
138        }
139
140        pub(crate) fn example(&self) -> anyhow::Result<serde_json::Value> {
141            self.0.example()
142        }
143
144        pub(crate) fn visibility(&self) -> Visibility<'_> {
145            self.0.visibility()
146        }
147
148        pub(crate) fn as_any(&self) -> &dyn std::any::Any {
149            self.0.as_any()
150        }
151
152        pub(crate) fn type_name(&self) -> &'static str {
153            self.0.type_name()
154        }
155
156        /// Return a `std::fmt::Display` implementation that pretty-prints the input tag as
157        /// well as any visibility modifiers.
158        pub(crate) fn display(&self) -> Display<'_> {
159            Display(self)
160        }
161
162        // Test
163        #[cfg(test)]
164        pub(crate) fn is_concrete<T>(&self) -> bool
165        where
166            T: 'static,
167        {
168            self.as_any().is::<Wrapper<T>>()
169        }
170
171        #[cfg(test)]
172        pub(crate) fn is_gated(&self) -> bool {
173            self.as_any().is::<Gated>()
174        }
175    }
176
177    /// Orders inputs in the following order:
178    ///
179    /// 1. Available items in alphabetical order.
180    /// 2. Unavailable (gated) items in alphabetical order.
181    pub(crate) fn order(a: &Input, b: &Input) -> std::cmp::Ordering {
182        a.visibility()
183            .cmp(&b.visibility())
184            .then_with(|| a.tag().cmp(b.tag()))
185    }
186
187    /// Return item from [`Input::display`].
188    pub(crate) struct Display<'a>(&'a Input);
189
190    impl std::fmt::Debug for Display<'_> {
191        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
192            f.debug_struct("input::Display")
193                .field("tag", &self.0.tag())
194                .finish()
195        }
196    }
197
198    impl std::fmt::Display for Display<'_> {
199        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
200            write!(f, "{}", self.0.tag())?;
201            match self.0.visibility() {
202                Visibility::Available => {}
203                Visibility::Gated { features } => {
204                    write!(f, " (requires the {})", features)?;
205                }
206            }
207            Ok(())
208        }
209    }
210
211    trait DynInput: std::fmt::Debug {
212        fn tag(&self) -> &'static str;
213        fn try_deserialize(
214            &self,
215            serialized: &serde_json::Value,
216            checker: &mut Checker,
217        ) -> anyhow::Result<Any>;
218        fn example(&self) -> anyhow::Result<serde_json::Value>;
219
220        // visibility
221        fn visibility(&self) -> Visibility<'_>;
222
223        // reflection
224        fn as_any(&self) -> &dyn std::any::Any;
225        fn type_name(&self) -> &'static str;
226    }
227
228    /// A wrapper around [`super::Input`] types.
229    struct Wrapper<T>(std::marker::PhantomData<T>);
230
231    impl<T> Wrapper<T> {
232        const fn new() -> Self {
233            Self(std::marker::PhantomData)
234        }
235    }
236
237    impl<T> std::fmt::Debug for Wrapper<T> {
238        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
239            write!(f, "Wrapper<{}>", std::any::type_name::<T>())
240        }
241    }
242
243    impl<T> DynInput for Wrapper<T>
244    where
245        T: super::Input,
246    {
247        fn tag(&self) -> &'static str {
248            T::tag()
249        }
250        fn try_deserialize(
251            &self,
252            serialized: &serde_json::Value,
253            checker: &mut Checker,
254        ) -> anyhow::Result<Any> {
255            let raw = <T::Raw as serde::Deserialize<'_>>::deserialize(serialized)?;
256            Ok(Any::new(T::from_raw(raw, checker)?))
257        }
258        fn example(&self) -> anyhow::Result<serde_json::Value> {
259            Ok(serde_json::to_value(T::example())?)
260        }
261        fn visibility(&self) -> Visibility<'_> {
262            Visibility::Available
263        }
264        fn as_any(&self) -> &dyn std::any::Any {
265            self
266        }
267        fn type_name(&self) -> &'static str {
268            std::any::type_name::<T>()
269        }
270    }
271
272    /// An input that isn't compiled into a version of a binary, but exists behind a feature
273    /// flag. Including it internally allows us to provide better error messages if we
274    /// discover this `tag` in the wild so we can point users towards its associated `features`.
275    #[derive(Debug)]
276    struct Gated {
277        tag: &'static str,
278        features: Features,
279    }
280
281    impl DynInput for Gated {
282        fn tag(&self) -> &'static str {
283            self.tag
284        }
285        fn try_deserialize(
286            &self,
287            _serialized: &serde_json::Value,
288            _checker: &mut Checker,
289        ) -> anyhow::Result<Any> {
290            anyhow::bail!(
291                "use of the \"{}\" input is gated behind the {}",
292                self.tag,
293                self.features
294            );
295        }
296        fn example(&self) -> anyhow::Result<serde_json::Value> {
297            anyhow::bail!(
298                "use of the \"{}\" input is gated behind the {}",
299                self.tag,
300                self.features,
301            );
302        }
303        fn visibility(&self) -> Visibility<'_> {
304            Visibility::Gated {
305                features: &self.features,
306            }
307        }
308        fn as_any(&self) -> &dyn std::any::Any {
309            self
310        }
311        fn type_name(&self) -> &'static str {
312            std::any::type_name::<Self>()
313        }
314    }
315}