Skip to main content

trait_kit/
lib.rs

1// Copyright (c) 2026 Kirky.X
2// SPDX-License-Identifier: MIT
3//! trait-kit — 模块标准接口与能力管理中心
4//!
5//! 提供模块定义标准接口和 Kit 能力管理中心的轻量实现。
6
7#![deny(unsafe_code)]
8#![warn(clippy::all, clippy::pedantic)]
9#![allow(clippy::module_name_repetitions)]
10#![doc = include_str!("../README.md")]
11
12pub mod core;
13mod error;
14pub mod kit;
15
16pub mod i18n;
17
18pub mod prelude;
19
20pub use error::TraitKitError;
21pub use error::TraitKitResult;
22
23#[cfg(feature = "async")]
24pub use core::AsyncAutoBuilder;
25#[cfg(feature = "async")]
26pub use kit::{AsyncKit, AsyncReady, AsyncUnbuilt};
27
28#[cfg(all(feature = "lifecycle", feature = "async"))]
29pub use core::AsyncLifecycle;
30#[cfg(feature = "lifecycle")]
31pub use core::Lifecycle;
32
33#[cfg(all(feature = "health", feature = "async"))]
34pub use core::AsyncHealthCheck;
35#[cfg(feature = "health")]
36pub use core::{HealthCheck, HealthStatus};
37
38#[cfg(feature = "observer")]
39pub use core::BuildObserver;
40
41#[cfg(all(feature = "scope", feature = "async"))]
42pub use kit::AsyncScope;
43#[cfg(feature = "scope")]
44pub use kit::Scope;
45
46#[cfg(all(feature = "shutdown", feature = "async"))]
47pub use kit::AsyncShutdownCoordinator;
48#[cfg(feature = "shutdown")]
49pub use kit::{ShutdownCoordinator, ShutdownPhase, ShutdownPhaseResult, ShutdownResult};
50
51/// Shared test helpers for async test modules (`block_on` executor + `MockError`).
52///
53/// Extracted to deduplicate between `core::meta::async_tests` and
54/// `kit::async_kit::tests` (audit LOW-003). Gated on `async` feature because
55/// both consumer test mods are `#[cfg(all(test, feature = "async"))]`.
56#[cfg(all(test, feature = "async"))]
57pub(crate) mod test_helpers {
58    use std::future::Future;
59    use std::task::{self, Poll};
60
61    /// Minimal single-threaded `Future` executor for tests (no extra deps).
62    ///
63    /// Uses `Waker::noop()` (stable since 1.85) because the `async` feature
64    /// deliberately stays dep-free (no `tokio` / `futures` test runtime).
65    ///
66    /// # Panics
67    ///
68    /// Panics if the future does not complete within `MAX_POLLS` iterations,
69    /// preventing infinite loops from hanging the test suite.
70    pub(crate) fn block_on<F: Future>(future: F) -> F::Output {
71        /// Maximum number of poll iterations before panicking.
72        /// Generous enough for any reasonable test future.
73        const MAX_POLLS: u32 = 1_000_000;
74
75        let waker = task::Waker::noop();
76        // `Context::from_waker` takes `&Waker`; the borrow is required by the
77        // API signature (not a clippy false positive).
78        #[allow(clippy::needless_borrow)]
79        let mut cx = task::Context::from_waker(&waker);
80        let mut future = std::pin::pin!(future);
81        let mut polls = 0u32;
82        loop {
83            match future.as_mut().poll(&mut cx) {
84                Poll::Ready(v) => return v,
85                Poll::Pending => {
86                    polls += 1;
87                    assert!(
88                        polls < MAX_POLLS,
89                        "block_on: future did not complete within \
90                         {MAX_POLLS} poll iterations (possible infinite loop)"
91                    );
92                    std::hint::spin_loop();
93                }
94            }
95        }
96    }
97
98    /// Mock error type for tests verifying `AsyncAutoBuilder` trait signatures.
99    #[derive(Debug, thiserror::Error)]
100    #[allow(dead_code, reason = "mock error type verifies trait signature only")]
101    pub(crate) enum MockError {
102        #[error("mock build failed: {0}")]
103        Failed(String),
104    }
105
106    #[cfg(test)]
107    mod block_on_tests {
108        use super::*;
109        use std::sync::atomic::{AtomicU32, Ordering};
110        use std::task::Poll;
111
112        #[test]
113        fn block_on_handles_pending() {
114            static POLL_COUNT: AtomicU32 = AtomicU32::new(0);
115            POLL_COUNT.store(0, Ordering::SeqCst);
116
117            let result = block_on(std::future::poll_fn(|_cx| {
118                let n = POLL_COUNT.fetch_add(1, Ordering::SeqCst);
119                if n < 1 {
120                    Poll::Pending
121                } else {
122                    Poll::Ready(42)
123                }
124            }));
125            assert_eq!(result, 42);
126            assert!(POLL_COUNT.load(Ordering::SeqCst) >= 2);
127        }
128    }
129}