Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 113 additions & 36 deletions diskann-utils/src/lazystring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,73 +3,140 @@
* Licensed under the MIT license.
*/

use std::fmt::{Display, Error, Formatter};
use std::fmt::{Debug, Display, Formatter, Result};

/// A struct used to lazily defer creation of custom async logging messages until we know
/// that the message is actually needed.
/// A macro that behaves like `format!` but constructs a [`LazyString`] to defer string
/// formatting until the result is actually displayed. If the [`LazyString`] is never
/// displayed, this construct has minimal overhead.
///
/// # Context
/// ```rust
/// use diskann_utils::lazy_format;
///
/// Logging in the async context explicitly requires passing of a context pointer to enable
/// CDB to determine the source of error message. To that end, a custom logging function is
/// used.
/// let a: f32 = 10.5;
/// let b: usize = 20;
///
/// let lazy_string = lazy_format!("This is a test. A = {}, B = {}", a, b);
/// assert_eq!(lazy_string.to_string(), "This is a test. A = 10.5, B = 20");
///
/// // Formatting of captured members is deferred until the created `LazyString` is formatted.
/// #[derive(Default)]
/// struct Formatted(std::cell::Cell<bool>);
///
/// impl Formatted {
/// fn was_formatted(&self) -> bool {
/// self.0.get()
/// }
///
/// fn mark_as_formatted(&self) {
/// self.0.set(true)
/// }
/// }
///
/// impl std::fmt::Display for Formatted {
/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
/// if self.was_formatted() {
/// f.write_str("yes")
/// } else {
/// self.mark_as_formatted();
/// f.write_str("not yet")
/// }
/// }
/// }
///
/// let f = Formatted::default();
/// let lazy = lazy_format!("Was this formatted: {f}");
///
/// assert!(!f.was_formatted(), "string formatting should be deferred");
/// assert_eq!(lazy.to_string(), "Was this formatted: not yet");
///
/// assert!(f.was_formatted());
/// assert_eq!(lazy.to_string(), "Was this formatted: yes");
/// ```
///
/// # Creating lazily formatted `'static` error messages
///
/// The `LazyString` captures a lambda that constructs the logging message and implements
/// `std::fmt::Display`, allowing string formatting to only be performed once we know a
/// message needs to be logged.
/// The default [`LazyString`] created by this macro borrows from its formatted arguments
/// and thus has a lifetime constrained to its arguments.
///
/// If a lazily formatted `'static` compliant variation is needed, the "move" variant
/// can be used (assuming all captured arguments are `'static`):
///
Comment on lines +61 to +63
/// ```rust
/// use diskann_utils::lazy_format;
///
/// fn assert_static<T: 'static>(_: &T) {}
///
/// let x = 10;
///
/// let lazy = lazy_format!(move, "x = {x}");
/// assert_static(&lazy);
/// assert_eq!(lazy.to_string(), "x = 10");
/// ```
#[macro_export]
macro_rules! lazy_format {
(move, $($arg:tt)*) => {
$crate::LazyString::new(move |f: &mut std::fmt::Formatter<'_>| {
::core::write!(f, $($arg)*)
})
};
($($arg:tt)*) => {
$crate::LazyString::new(|f: &mut std::fmt::Formatter<'_>| {
::core::write!(f, $($arg)*)
})
};
}

/// A struct used to lazily defer string formatting until needed. This is used to implement
/// [`lazy_format!`]: a lazy version of the standard `format!` macro.
///
/// See [`lazy_format!`] for usage.
pub struct LazyString<F>(F)
where
F: Fn(&mut Formatter<'_>) -> Result<(), Error>;
F: Fn(&mut Formatter<'_>) -> Result;

impl<F> LazyString<F>
where
F: Fn(&mut Formatter<'_>) -> Result<(), Error>,
F: Fn(&mut Formatter<'_>) -> Result,
{
/// Construct a new `LazyString` around the provided lambda.
#[doc(hidden)]
pub fn new(f: F) -> Self {
Self(f)
}
}

impl<F> Display for LazyString<F>
where
F: Fn(&mut Formatter<'_>) -> Result<(), Error>,
F: Fn(&mut Formatter<'_>) -> Result,
{
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
(self.0)(f)
}
}

/// A macro that behaves like `format!` but constructs a `diskann::utils::LazyString`
/// to enable deferred evaluation of the error message.
///
/// Invoking this macro has the following equivalence:
/// ```ignore
/// let a: f32 = 10.5;
/// let b: usize = 20;
/// // Macro form
/// let lazy_from_macro = lazy_format("This is a test. A = {}, B = {}", a, b);
///
/// // Direct form
/// let lazy_direct = crate::utils::LazyString::new(|f: &mut std::fmt::Formatter<'_>| {
/// write!(f, "ihis is a test. A = {}, B = {}", a, b)
/// });
/// ```
#[macro_export]
macro_rules! lazy_format {
($($arg:tt)*) => {
// Must be a full path and only available inside `DiskANN`.
$crate::LazyString::new(|f: &mut std::fmt::Formatter<'_>| {
write!(f, $($arg)*)
})
impl<F> Debug for LazyString<F>
where
F: Fn(&mut Formatter<'_>) -> Result,
{
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
f.debug_tuple("LazyString")
.field(&format_args!("{self}"))
.finish()
}
}

///////////
// Tests //
///////////

#[cfg(test)]
mod test {
use super::*;

fn assert_static<T: 'static>(_: &T) {}

#[test]
fn test_lazy_string() {
let x: f32 = 10.5;
Expand All @@ -82,5 +149,15 @@ mod test {

let lazy = lazy_format!("Lazy Message: x = {x}, y = {y}");
assert_eq!(lazy.to_string(), "Lazy Message: x = 10.5, y = 20");

let lazy = lazy_format!(move, "Lazy Message: x = {}, y = {y}", x);
assert_static(&lazy);
assert_eq!(lazy.to_string(), "Lazy Message: x = 10.5, y = 20");

// Verify that `Debug` at least runs.
assert_eq!(
format!("{:?}", lazy),
"LazyString(Lazy Message: x = 10.5, y = 20)",
);
}
}
Loading