1/*
  2 * SPDX-FileCopyrightText: 2026 Copyright (c) Contributors to the Eclipse Foundation
  3 *
  4 * See the NOTICE file(s) distributed with this work for additional
  5 * information regarding copyright ownership.
  6 *
  7 * This program and the accompanying materials are made available under the
  8 * terms of the Apache License Version 2.0 which is available at
  9 * https://www.apache.org/licenses/LICENSE-2.0
 10 *
 11 * SPDX-License-Identifier: Apache-2.0
 12 */
 13
 14//! The narrow, diagnostic-use communication contract.
 15//!
[docs] 16//! [[ dimpl~communication-control-access, Capability-limited diagnostic communication access, dimpl ]]
 17//!
 18//! Defined here rather than in the default communication plugin's crate, so that
 19//! transport crates and replacement plugins can depend on the contract without
 20//! coupling to that implementation.
 21use std::time::Duration;
 22
 23use super::{
 24    DEFAULT_DEFERRED_RETRY_AFTER, VariantDetectionMode,
 25    operation::{ActivationCause, CommunicationOperation, CommunicationOperationFailure},
 26};
 27
 28/// Authoritative communication lifecycle state.
 29///
 30/// [`CommunicationState::Enabling`] identifies the lifecycle operation in
 31/// progress, while [`CommunicationState::Error`] retains the structured failure
 32/// from the latest lifecycle operation. Use this type when callers need the
 33/// complete current state or failure details.
 34#[derive(Debug, Clone, PartialEq, Eq)]
 35pub enum CommunicationState {
 36    /// Communication has not been enabled, the state is not owned and can be enabled by anyone.
 37    Disabled,
 38    /// An enabling lifecycle operation is in progress.
 39    Enabling(CommunicationOperation),
 40    /// Transport is enabled and every stage the claiming operation called for
 41    /// has completed.
 42    ///
 43    /// Which stages those were depends on the operation. An
 44    /// [`Enable`](CommunicationOperation::Enable) reaches `Enabled` with no
 45    /// variant detected. `Enabled` does not mean detection concluded. Detection
 46    /// settles per ECU and asynchronously, so per-ECU `VariantState` is the
 47    /// readiness signal.
 48    Enabled,
 49    /// Transport disablement is in progress.
 50    Disabling,
 51    /// Transport is physically disabled and exclusively owned by a `DisableLease`.
 52    DisabledExclusive,
 53    /// Reconfiguration compensation is incomplete. Transport remains disabled
 54    /// and activation is blocked until an exclusive recovery lease succeeds.
 55    RecoveryRequired(CommunicationOperationFailure),
 56    /// The latest lifecycle operation failed.
 57    Error(CommunicationOperationFailure),
 58}
 59
 60/// Error returned by communication guard acquisition.
 61///
 62/// This is a minimal enum containing only the states that can prevent
 63/// guard acquisition. Lifecycle operation failures should be reported via
 64/// the `Result` returned by a communication plugin's `enable()` and
 65/// `DisableLease::release()`.
 66#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
 67pub enum CommunicationError {
 68    /// Communication is currently disabled. No new guards can be acquired.
 69    #[error("Communication is disabled")]
 70    Disabled,
 71    /// Communication is currently being enabled.
 72    #[error("Communication is being enabled")]
 73    Enabling,
 74    /// Communication is currently being disabled.
 75    #[error("Communication is being disabled")]
 76    Disabling,
 77    /// Communication is disabled and exclusively owned by a disable lease.
 78    #[error("Communication is exclusively disabled")]
 79    DisabledExclusive,
 80    /// Communication has failed and cannot be used.
 81    #[error("Communication has failed: {0}")]
 82    Failed(CommunicationOperationFailure),
 83}
 84
 85/// Opaque RAII guard marking communication as in use, so exclusive disable
 86/// cannot begin while diagnostic activity is ongoing.
 87///
 88/// The implementation is owned by whichever [`CommunicationAccess`] minted it
 89/// via [`CommunicationGuard::new`]. This type only stores it type-erased and
 90/// drops it. Communication is free again once the last guard is dropped.
 91#[must_use = "The guard must be held for the complete diagnostic-activity lifetime. Communication \
 92              is marked as 'free' once the last guard is dropped. As long as any guard lives, \
 93              communication cannot be disabled."]
 94pub struct CommunicationGuard {
 95    _inner: Box<dyn Send + Sync>,
 96}
 97
 98impl CommunicationGuard {
 99    /// Wraps an implementation-defined guard so that its concrete type stays
100    /// private to the [`CommunicationAccess`] implementation that minted it.
101    pub fn new(inner: impl Send + Sync + 'static) -> Self {
102        Self {
103            _inner: Box::new(inner),
104        }
105    }
106}
107
108/// Narrow capability that does not expose full lifecycle control.
109///
110/// Consumers can inspect state, hold communication active for an operation and
111/// request activation, but cannot disable it, run explicit whole-vehicle
112/// (re-)detection, or otherwise administer the underlying plugin.
113pub trait CommunicationAccess: Send + Sync + 'static {
114    /// Returns the authoritative lifecycle state.
115    fn state(&self) -> CommunicationState;
116
117    /// Acquires diagnostic communication if it is enabled.
118    /// The returned guard prevents the communication from being shut down while it is alive.
119    ///
120    /// # Errors
121    ///
122    /// Returns an error when communication is not enabled.
123    fn acquire(&self) -> Result<CommunicationGuard, CommunicationError>;
124
125    /// Requests activation without blocking.
126    ///
127    /// Callers must check [`state`](Self::state) before using communication.
128    fn request_activate(&self, cause: ActivationCause) -> CommunicationState;
129
130    /// Returns the retry hint for requests deferred while communication starts.
131    ///
132    /// The default preserves compatibility for custom access implementations
133    /// that do not expose configuration-owned retry timing.
134    fn retry_after(&self) -> Duration {
135        DEFAULT_DEFERRED_RETRY_AFTER
136    }
137
138    /// Returns the effective detection policy for the currently enabled (or
139    /// currently being enabled) communication runtime.
140    ///
141    /// Lets a consumer waiting on its own readiness signal distinguish
142    /// "detection is still settling" from "nothing will ever settle this".
143    fn variant_detection(&self) -> VariantDetectionMode;
144}