Skip to main content

diskann_utils/
lazystring.rs

1/*
2 * Copyright (c) Microsoft Corporation.
3 * Licensed under the MIT license.
4 */
5
6use std::fmt::{Debug, Display, Formatter, Result};
7
8/// A macro that behaves like `format!` but constructs a [`LazyString`] to defer string
9/// formatting until the result is actually displayed. If the [`LazyString`] is never
10/// displayed, this construct has minimal overhead.
11///
12/// ```rust
13/// use diskann_utils::lazy_format;
14///
15/// let a: f32 = 10.5;
16/// let b: usize = 20;
17///
18/// let lazy_string = lazy_format!("This is a test. A = {}, B = {}", a, b);
19/// assert_eq!(lazy_string.to_string(), "This is a test. A = 10.5, B = 20");
20///
21/// // Formatting of captured members is deferred until the created `LazyString` is formatted.
22/// #[derive(Default)]
23/// struct Formatted(std::cell::Cell<bool>);
24///
25/// impl Formatted {
26///     fn was_formatted(&self) -> bool {
27///         self.0.get()
28///     }
29///
30///     fn mark_as_formatted(&self) {
31///         self.0.set(true)
32///     }
33/// }
34///
35/// impl std::fmt::Display for Formatted {
36///     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37///         if self.was_formatted() {
38///             f.write_str("yes")
39///         } else {
40///             self.mark_as_formatted();
41///             f.write_str("not yet")
42///         }
43///     }
44/// }
45///
46/// let f = Formatted::default();
47/// let lazy = lazy_format!("Was this formatted: {f}");
48///
49/// assert!(!f.was_formatted(), "string formatting should be deferred");
50/// assert_eq!(lazy.to_string(), "Was this formatted: not yet");
51///
52/// assert!(f.was_formatted());
53/// assert_eq!(lazy.to_string(), "Was this formatted: yes");
54/// ```
55///
56/// # Creating lazily formatted `'static` error messages
57///
58/// The default [`LazyString`] created by this macro borrows from its formatted arguments
59/// and thus has a lifetime constrained to its arguments.
60///
61/// If a lazily formatted `'static` compliant variation is needed, the "move" variant
62/// can be used (assuming all captured arguments are `'static`):
63///
64/// ```rust
65/// use diskann_utils::lazy_format;
66///
67/// fn assert_static<T: 'static>(_: &T) {}
68///
69/// let x = 10;
70///
71/// let lazy = lazy_format!(move, "x = {x}");
72/// assert_static(&lazy);
73/// assert_eq!(lazy.to_string(), "x = 10");
74/// ```
75#[macro_export]
76macro_rules! lazy_format {
77    (move, $($arg:tt)*) => {
78        $crate::LazyString::new(move |f: &mut std::fmt::Formatter<'_>| {
79            ::core::write!(f, $($arg)*)
80        })
81    };
82    ($($arg:tt)*) => {
83        $crate::LazyString::new(|f: &mut std::fmt::Formatter<'_>| {
84            ::core::write!(f, $($arg)*)
85        })
86    };
87}
88
89/// A struct used to lazily defer string formatting until needed. This is used to implement
90/// [`lazy_format!`]: a lazy version of the standard `format!` macro.
91///
92/// See [`lazy_format!`] for usage.
93pub struct LazyString<F>(F)
94where
95    F: Fn(&mut Formatter<'_>) -> Result;
96
97impl<F> LazyString<F>
98where
99    F: Fn(&mut Formatter<'_>) -> Result,
100{
101    /// Construct a new `LazyString` around the provided lambda.
102    #[doc(hidden)]
103    pub fn new(f: F) -> Self {
104        Self(f)
105    }
106}
107
108impl<F> Display for LazyString<F>
109where
110    F: Fn(&mut Formatter<'_>) -> Result,
111{
112    #[inline]
113    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
114        (self.0)(f)
115    }
116}
117
118impl<F> Debug for LazyString<F>
119where
120    F: Fn(&mut Formatter<'_>) -> Result,
121{
122    #[inline]
123    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
124        f.debug_tuple("LazyString")
125            .field(&format_args!("{self}"))
126            .finish()
127    }
128}
129
130///////////
131// Tests //
132///////////
133
134#[cfg(test)]
135mod test {
136    use super::*;
137
138    fn assert_static<T: 'static>(_: &T) {}
139
140    #[test]
141    fn test_lazy_string() {
142        let x: f32 = 10.5;
143        let y: usize = 20;
144
145        let lazy = LazyString::new(|f: &mut std::fmt::Formatter| {
146            write!(f, "Lazy Message: x = {x}, y = {y}")
147        });
148        assert_eq!(lazy.to_string(), "Lazy Message: x = 10.5, y = 20");
149
150        let lazy = lazy_format!("Lazy Message: x = {x}, y = {y}");
151        assert_eq!(lazy.to_string(), "Lazy Message: x = 10.5, y = 20");
152
153        let lazy = lazy_format!(move, "Lazy Message: x = {}, y = {y}", x);
154        assert_static(&lazy);
155        assert_eq!(lazy.to_string(), "Lazy Message: x = 10.5, y = 20");
156
157        // Verify that `Debug` at least runs.
158        assert_eq!(
159            format!("{:?}", lazy),
160            "LazyString(Lazy Message: x = 10.5, y = 20)",
161        );
162    }
163}