aws-sdk-s3 1.135.0

AWS SDK for Amazon Simple Storage Service
Documentation
// Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT.
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

//! Coalesce function that returns the first non-None value.

/*
 * Implementation Note: Autoderef Specialization
 *
 * This implementation uses autoderef specialization to dispatch to different
 * coalesce behaviors based on the types of the two arguments at compile time.
 *
 * The technique exploits Rust's method resolution, which automatically dereferences
 * types to find matching method implementations. Method resolution prefers implementations
 * that require fewer dereferences, creating a priority ordering.
 *
 * Three specialization levels (highest to lowest priority):
 *
 * 1. `&&&(&Option<T>, &Option<T>)` - Both args are Option
 *    Returns: Option<T> via a.or(b)
 *
 * 2. `&&(&Option<T>, &T)` - First arg is Option, second is concrete
 *    Returns: T via a.unwrap_or(b)
 *
 * 3. `&(&T, &U)` - Both args are concrete (or any other combination)
 *    Returns: T (always returns first arg)
 *
 * The macro call `(&&&(&a, &b)).coalesce()` starts with three references. Method resolution
 * tries each impl in order by dereferencing:
 * - First tries `&&&(&Option<T>, &Option<T>)` (no deref needed) - matches if both are Option
 * - Then tries `&&(&Option<T>, &T)` (one deref) - matches if first is Option, second isn't
 * - Finally tries `&(&T, &U)` (two derefs) - matches everything else
 *
 * This allows the macro to handle Option/non-Option combinations without runtime checks,
 * selecting the appropriate coalesce logic at compile time based on argument types.
 *
 * This approach was heavily inspired by this blog post: https://lukaskalbertodt.github.io/2019/12/05/generalized-autoref-based-specialization.html
 */

/// Helper trait to implement the coalesce! macro
pub(crate) trait Coalesce {
    /// The first arg
    type Arg1;
    /// The second arg
    type Arg2;
    /// The result of comparing Arg1 and Arg1
    type Result;

    /// Evaluates arguments in order and returns the first non-empty result, otherwise returns the result of the last argument.
    fn coalesce(&self) -> fn(Self::Arg1, Self::Arg2) -> Self::Result;
}

impl<T> Coalesce for &&&(&Option<T>, &Option<T>) {
    type Arg1 = Option<T>;
    type Arg2 = Option<T>;
    type Result = Option<T>;

    fn coalesce(&self) -> fn(Self::Arg1, Self::Arg2) -> Self::Result {
        |a: Option<T>, b: Option<T>| a.or(b)
    }
}

impl<T> Coalesce for &&(&Option<T>, &T) {
    type Arg1 = Option<T>;
    type Arg2 = T;
    type Result = T;

    fn coalesce(&self) -> fn(Self::Arg1, Self::Arg2) -> Self::Result {
        |a: Option<T>, b: T| a.unwrap_or(b)
    }
}

impl<T, U> Coalesce for &(&T, &U) {
    type Arg1 = T;
    type Arg2 = U;
    type Result = T;

    fn coalesce(&self) -> fn(Self::Arg1, Self::Arg2) -> Self::Result {
        |a: T, _b| a
    }
}

/// Evaluates arguments in order and returns the first non-empty result, otherwise returns the result of the last argument.
macro_rules! coalesce {
    ($a:expr) => {$a};
    ($a:expr, $b:expr) => {{
        use crate::endpoint_lib::coalesce::Coalesce;
        let a = $a;
        let b = $b;
        (&&&(&a, &b)).coalesce()(a, b)
    }};
    ($a:expr, $b:expr $(, $c:expr)* $(,)?) => {
        $crate::endpoint_lib::coalesce::coalesce!($crate::endpoint_lib::coalesce::coalesce!($a, $b) $(, $c)*)
    }
}

pub(crate) use coalesce;

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

    #[test]
    fn base_cases() {
        // All types optional
        let a = Some("a");
        let b = Some("b");

        assert_eq!(coalesce!(a, b), Some("a"));

        let a = None;
        let b = Some("b");

        assert_eq!(coalesce!(a, b), Some("b"));

        let a = Some("a");
        let b = None;

        assert_eq!(coalesce!(a, b), Some("a"));

        let a: Option<&str> = None;
        let b: Option<&str> = None;

        assert_eq!(coalesce!(a, b), None);

        // Some non-optional types
        let a = "a";
        let b = Some("b");

        assert_eq!(coalesce!(a, b), "a");

        let a = Some("a");
        let b = "b";

        assert_eq!(coalesce!(a, b), "a");

        let a = None;
        let b = "b";

        assert_eq!(coalesce!(a, b), "b");

        let a = "a";
        let b: Option<&str> = None;

        assert_eq!(coalesce!(a, b), "a");

        // All types non-optional
        let a = "a";
        let b = "b";

        assert_eq!(coalesce!(a, b), "a");

        // Works with trailing comma (makes codegen easier)
        let a = "a";
        let b = "b";

        assert_eq!(coalesce!(a, b,), "a");
    }

    #[test]
    fn longer_cases() {
        assert_eq!(coalesce!(None, None, None, None, None, None, None, None, None, None, "a"), "a");

        // In the generated code all of the inputs are typed variables, so the turbofish isn't needed in practice
        assert_eq!(
            coalesce!(
                None,
                None,
                None,
                None,
                "a",
                None::<&str>,
                None::<&str>,
                None::<&str>,
                None::<&str>,
                None::<&str>,
                None::<&str>
            ),
            "a"
        );

        assert_eq!(
            coalesce!(
                None,
                None,
                None,
                None,
                "a",
                None::<&str>,
                Some("b"),
                None::<&str>,
                None::<&str>,
                None::<&str>,
                "c"
            ),
            "a"
        );

        assert_eq!(coalesce!(None, None, None, Some("a"), None, None, None, None, None, None, "b"), "a");
        assert_eq!(
            coalesce!(Some("a"), None, None, Some("b"), None, None, None, None, None, None,),
            Some("a")
        );

        assert_eq!(coalesce!("a", "b", "c", "d", "e", "f", "g",), "a");

        assert_eq!(coalesce!(None, None, None, None, None, None, None, None, None, None::<&str>,), None);
    }
}