clawft_platform/lib.rs
1//! Platform abstraction layer for clawft.
2//!
3//! Provides traits for all platform-dependent operations (HTTP, filesystem,
4//! environment, process spawning) so the core engine can be platform-agnostic
5//! and WASM-compatible.
6//!
7//! # Architecture
8//!
9//! The [`Platform`] trait bundles all platform capabilities via accessor methods.
10//! Each sub-capability has its own trait ([`http::HttpClient`], [`fs::FileSystem`],
11//! [`env::Environment`], [`process::ProcessSpawner`]) with a corresponding native
12//! implementation.
13//!
14//! [`ProcessSpawner`](process::ProcessSpawner) is intentionally excluded from the
15//! [`Platform`] bundle because it is unavailable in WASM environments. It is
16//! accessible via [`Platform::process`] which returns `Option`.
17//!
18//! # Example
19//!
20//! ```rust,no_run
21//! use clawft_platform::{Platform, NativePlatform};
22//! use clawft_platform::http::HttpClient;
23//! use std::collections::HashMap;
24//!
25//! # async fn example() {
26//! let platform = NativePlatform::new();
27//! let response = platform.http()
28//! .get("https://example.com", &HashMap::new())
29//! .await
30//! .unwrap();
31//! assert!(response.is_success());
32//! # }
33//! ```
34
35pub mod config_loader;
36pub mod env;
37pub mod fs;
38pub mod http;
39pub mod process;
40
41#[cfg(feature = "browser")]
42pub mod browser;
43#[cfg(feature = "browser")]
44pub use browser::BrowserPlatform;
45
46use async_trait::async_trait;
47
48/// Bundle of all platform capabilities.
49///
50/// Implementors provide concrete implementations of all platform traits.
51/// The native implementation ([`NativePlatform`]) uses reqwest, tokio::fs,
52/// std::env, and tokio::process. A WASM implementation would use the fetch
53/// API, WASI filesystem, and an in-memory environment.
54///
55/// The `async_trait` bound differs based on target:
56/// - Native: `Send + Sync` required (multi-threaded tokio runtime).
57/// - Browser: `?Send` (single-threaded WASM runtime).
58#[cfg_attr(not(feature = "browser"), async_trait)]
59#[cfg_attr(feature = "browser", async_trait(?Send))]
60pub trait Platform: Send + Sync {
61 /// HTTP client for making API requests.
62 fn http(&self) -> &dyn http::HttpClient;
63
64 /// Filesystem operations.
65 fn fs(&self) -> &dyn fs::FileSystem;
66
67 /// Environment variable access.
68 fn env(&self) -> &dyn env::Environment;
69
70 /// Process spawning capability.
71 ///
72 /// Returns `None` in environments where process spawning is unavailable
73 /// (e.g., WASM).
74 fn process(&self) -> Option<&dyn process::ProcessSpawner>;
75}
76
77/// Native platform implementation using std, tokio, and reqwest.
78///
79/// This is the standard platform for server-side and CLI usage. It provides:
80/// - HTTP via [`reqwest`] with connection pooling and TLS.
81/// - Filesystem via [`tokio::fs`].
82/// - Environment via [`std::env`].
83/// - Process spawning via [`tokio::process`].
84#[cfg(feature = "native")]
85pub struct NativePlatform {
86 http: http::NativeHttpClient,
87 fs: fs::NativeFileSystem,
88 env: env::NativeEnvironment,
89 process: process::NativeProcessSpawner,
90}
91
92#[cfg(feature = "native")]
93impl NativePlatform {
94 /// Create a new native platform with default configuration.
95 pub fn new() -> Self {
96 Self {
97 http: http::NativeHttpClient::new(),
98 fs: fs::NativeFileSystem,
99 env: env::NativeEnvironment,
100 process: process::NativeProcessSpawner,
101 }
102 }
103}
104
105#[cfg(feature = "native")]
106impl Default for NativePlatform {
107 fn default() -> Self {
108 Self::new()
109 }
110}
111
112#[cfg(feature = "native")]
113#[async_trait]
114impl Platform for NativePlatform {
115 fn http(&self) -> &dyn http::HttpClient {
116 &self.http
117 }
118
119 fn fs(&self) -> &dyn fs::FileSystem {
120 &self.fs
121 }
122
123 fn env(&self) -> &dyn env::Environment {
124 &self.env
125 }
126
127 fn process(&self) -> Option<&dyn process::ProcessSpawner> {
128 Some(&self.process)
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 #[test]
137 fn test_native_platform_creation() {
138 let platform = NativePlatform::new();
139 // Verify all accessors return valid references
140 let _http = platform.http();
141 let _fs = platform.fs();
142 let _env = platform.env();
143 assert!(platform.process().is_some());
144 }
145
146 #[test]
147 fn test_native_platform_default() {
148 let platform = NativePlatform::default();
149 assert!(platform.process().is_some());
150 }
151
152 #[test]
153 fn test_platform_is_send_sync() {
154 fn assert_send_sync<T: Send + Sync>() {}
155 assert_send_sync::<NativePlatform>();
156 }
157}