diskann_utils/lazystring.rs
1/*
2 * Copyright (c) Microsoft Corporation.
3 * Licensed under the MIT license.
4 */
5
6use std::fmt::{Display, Error, Formatter};
7
8/// A struct used to lazily defer creation of custom async logging messages until we know
9/// that the message is actually needed.
10///
11/// # Context
12///
13/// Logging in the async context explicitly requires passing of a context pointer to enable
14/// CDB to determine the source of error message. To that end, a custom logging function is
15/// used.
16///
17/// The `LazyString` captures a lambda that constructs the logging message and implements
18/// `std::fmt::Display`, allowing string formatting to only be performed once we know a
19/// message needs to be logged.
20pub struct LazyString<F>(F)
21where
22 F: Fn(&mut Formatter<'_>) -> Result<(), Error>;
23
24impl<F> LazyString<F>
25where
26 F: Fn(&mut Formatter<'_>) -> Result<(), Error>,
27{
28 /// Construct a new `LazyString` around the provided lambda.
29 pub fn new(f: F) -> Self {
30 Self(f)
31 }
32}
33
34impl<F> Display for LazyString<F>
35where
36 F: Fn(&mut Formatter<'_>) -> Result<(), Error>,
37{
38 #[inline]
39 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
40 (self.0)(f)
41 }
42}
43
44/// A macro that behaves like `format!` but constructs a `diskann::utils::LazyString`
45/// to enable deferred evaluation of the error message.
46///
47/// Invoking this macro has the following equivalence:
48/// ```ignore
49/// let a: f32 = 10.5;
50/// let b: usize = 20;
51/// // Macro form
52/// let lazy_from_macro = lazy_format("This is a test. A = {}, B = {}", a, b);
53///
54/// // Direct form
55/// let lazy_direct = crate::utils::LazyString::new(|f: &mut std::fmt::Formatter<'_>| {
56/// write!(f, "ihis is a test. A = {}, B = {}", a, b)
57/// });
58/// ```
59#[macro_export]
60macro_rules! lazy_format {
61 ($($arg:tt)*) => {
62 // Must be a full path and only available inside `DiskANN`.
63 $crate::LazyString::new(|f: &mut std::fmt::Formatter<'_>| {
64 write!(f, $($arg)*)
65 })
66 }
67}
68
69#[cfg(test)]
70mod test {
71 use super::*;
72
73 #[test]
74 fn test_lazy_string() {
75 let x: f32 = 10.5;
76 let y: usize = 20;
77
78 let lazy = LazyString::new(|f: &mut std::fmt::Formatter| {
79 write!(f, "Lazy Message: x = {x}, y = {y}")
80 });
81 assert_eq!(lazy.to_string(), "Lazy Message: x = 10.5, y = 20");
82
83 let lazy = lazy_format!("Lazy Message: x = {x}, y = {y}");
84 assert_eq!(lazy.to_string(), "Lazy Message: x = 10.5, y = 20");
85 }
86}