car-integrations 0.52.1

OS-native account-bound integrations (Calendar, Contacts, Mail) for CAR
Documentation
//! Deciding when a device-local store's answer is the final answer.
//!
//! Several backends read a local OS store first and treat a configured remote
//! account (today: Microsoft Graph) as the fallback. The obvious way to write
//! that — `if let Ok(listing) = local_read() { return Ok(listing) }` — conflates
//! two different facts: *the store answered* and *the store has the user's
//! data*. A reachable-but-empty store satisfies the first and not the second.
//!
//! That conflation is car#683. On Windows, someone whose contacts or calendar
//! live only in a configured M365 tenant, and are not synced into the local
//! Windows aggregate store, got `available: true` with an empty list — their
//! real data reported as simply absent, with the account that held it never
//! queried. The failure is silent, which is what makes it worth a named type
//! rather than an inline condition repeated at three call sites.
//!
//! This lives outside the `cfg(target_os = "windows")` blocks on purpose: the
//! decision is the part worth testing, and gating it behind Windows would make
//! it untestable anywhere the tests actually run.

/// A read from a device-local store, before deciding whether a configured
/// remote backend still has to be consulted.
#[derive(Debug)]
pub(crate) enum LocalRead<T> {
    /// At least one item came back — a final answer; no remote call needed.
    Answered(T),
    /// The store was reachable and held nothing. Only a final answer when no
    /// remote backend is configured.
    Empty(T),
    /// The store could not be read at all.
    Unavailable,
}

impl<T> LocalRead<T> {
    /// Classify a local read. `has_items` reports whether the listing carries
    /// any of the payload the caller asked for — the listing type differs per
    /// surface (`contacts`, `calendars`, `events`), so the caller names it.
    pub(crate) fn classify<E>(read: Result<T, E>, has_items: impl FnOnce(&T) -> bool) -> Self {
        match read {
            Ok(listing) if has_items(&listing) => Self::Answered(listing),
            Ok(listing) => Self::Empty(listing),
            Err(_) => Self::Unavailable,
        }
    }

    /// The listing to return without consulting the remote backend, or `None`
    /// when the caller should fall through to it.
    pub(crate) fn final_answer(self, remote_configured: bool) -> Option<T> {
        match self {
            Self::Answered(listing) => Some(listing),
            // Nothing to fall through *to*, so a reachable-but-empty local
            // store is the truthful answer here — and a better one than
            // reporting the backend as pending, which is what the caller's
            // tail would otherwise do.
            Self::Empty(listing) if !remote_configured => Some(listing),
            Self::Empty(_) | Self::Unavailable => None,
        }
    }
}

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

    /// Stands in for `ContactListing` / `CalendarListing` / `EventListing`.
    #[derive(Debug, PartialEq, Eq)]
    struct Listing(Vec<&'static str>);

    fn classify(read: Result<Listing, ()>) -> LocalRead<Listing> {
        LocalRead::classify(read, |listing| !listing.0.is_empty())
    }

    #[test]
    fn a_local_store_with_items_answers_even_when_a_remote_is_configured() {
        let local = classify(Ok(Listing(vec!["ada"])));
        assert_eq!(local.final_answer(true), Some(Listing(vec!["ada"])));
    }

    /// car#683 itself: the empty local store must not be the answer while a
    /// configured account may hold the real data.
    #[test]
    fn an_empty_local_store_falls_through_to_a_configured_remote() {
        let local = classify(Ok(Listing(vec![])));
        assert_eq!(
            local.final_answer(true),
            None,
            "an empty local store must not short-circuit a configured remote — \
             that is how a user's real contacts and events went missing"
        );
    }

    /// ...but with no remote configured there is nothing better to say, and
    /// "reachable and empty" beats reporting the backend as pending.
    #[test]
    fn an_empty_local_store_is_the_answer_when_no_remote_is_configured() {
        let local = classify(Ok(Listing(vec![])));
        assert_eq!(local.final_answer(false), Some(Listing(vec![])));
    }

    #[test]
    fn an_unreadable_local_store_never_answers() {
        assert_eq!(classify(Err(())).final_answer(true), None);
        assert_eq!(
            classify(Err(())).final_answer(false),
            None,
            "with no remote and no local read the caller's pending tail is \
             correct — there is no listing to return"
        );
    }
}