mobench_sdk/types.rs
1//! Core types for mobench-sdk.
2//!
3//! This module defines the fundamental types used throughout the SDK:
4//!
5//! - [`BenchError`] - Error types for benchmark and build operations
6//! - [`Target`] - Platform selection (Android, iOS, or both)
7//! - [`BuildConfig`] / [`BuildProfile`] - Build configuration options
8//! - [`BuildResult`] - Output from build operations
9//! - [`InitConfig`] - Project initialization settings
10//!
11//! ## Re-exports from timing module
12//!
13//! For convenience, this module also re-exports types from [`crate::timing`]:
14//!
15//! - [`BenchSpec`] - Benchmark specification (name, iterations, warmup)
16//! - [`BenchSample`] - Single timing measurement
17//! - [`RunnerReport`] - Complete benchmark results
18
19// Re-export timing types for convenience
20pub use crate::timing::{
21 BenchReport as RunnerReport, BenchSample, BenchSpec, BenchSummary, HarnessTimelineSpan,
22 SemanticPhase, TimingError as RunnerError,
23};
24
25use serde::{Deserialize, Serialize};
26use std::fmt;
27use std::path::PathBuf;
28
29/// Error types for mobench-sdk operations.
30///
31/// This enum covers all error conditions that can occur during
32/// benchmark registration, execution, and mobile app building.
33///
34/// # Example
35///
36/// ```ignore
37/// use mobench_sdk::{run_benchmark, BenchSpec, BenchError};
38///
39/// let spec = BenchSpec {
40/// name: "nonexistent".to_string(),
41/// iterations: 10,
42/// warmup: 1,
43/// };
44///
45/// match run_benchmark(spec) {
46/// Ok(report) => println!("Success!"),
47/// Err(BenchError::UnknownFunction(name)) => {
48/// eprintln!("Benchmark '{}' not found", name);
49/// }
50/// Err(e) => eprintln!("Other error: {}", e),
51/// }
52/// ```
53#[derive(Debug, thiserror::Error)]
54pub enum BenchError {
55 /// Error from the underlying benchmark runner.
56 ///
57 /// This wraps errors from [`crate::timing::TimingError`], such as
58 /// zero iterations or execution failures.
59 #[error("benchmark runner error: {0}")]
60 Runner(#[from] crate::timing::TimingError),
61
62 /// The requested benchmark function was not found in the registry.
63 ///
64 /// This occurs when calling [`run_benchmark`](crate::run_benchmark) with
65 /// a function name that hasn't been registered via `#[benchmark]`.
66 ///
67 /// The error includes a list of available benchmarks to help diagnose the issue.
68 #[error(
69 "unknown benchmark function: '{0}'. Available benchmarks: {1:?}\n\nEnsure the function is:\n 1. Annotated with #[benchmark]\n 2. Public (pub fn)\n 3. Takes no parameters and returns ()"
70 )]
71 UnknownFunction(String, Vec<String>),
72
73 /// An error occurred during benchmark execution.
74 ///
75 /// This is a catch-all for execution-time errors that don't fit
76 /// other categories.
77 #[error("benchmark execution failed: {0}")]
78 Execution(String),
79
80 /// An I/O error occurred.
81 ///
82 /// Common causes include missing files, permission issues, or
83 /// disk space problems during build operations.
84 #[error("I/O error: {0}. Check file paths and permissions")]
85 Io(#[from] std::io::Error),
86
87 /// JSON serialization or deserialization failed.
88 ///
89 /// This can occur when reading/writing benchmark specifications
90 /// or configuration files.
91 #[error("serialization error: {0}. Check JSON validity or output serializability")]
92 Serialization(#[from] serde_json::Error),
93
94 /// A configuration error occurred.
95 ///
96 /// This indicates invalid or missing configuration, such as
97 /// malformed TOML files or missing required fields.
98 #[error("configuration error: {0}. Check mobench.toml or CLI flags")]
99 Config(String),
100
101 /// A build error occurred.
102 ///
103 /// This covers failures during mobile app building, including:
104 /// - Missing build tools (cargo-ndk, xcodebuild, etc.)
105 /// - Compilation errors
106 /// - Code signing failures
107 /// - Missing dependencies
108 #[error("build error: {0}")]
109 Build(String),
110}
111
112/// Target platform for benchmarks.
113///
114/// Specifies which mobile platform(s) to build for or run benchmarks on.
115///
116/// # Example
117///
118/// ```
119/// use mobench_sdk::Target;
120///
121/// let target = Target::Android;
122/// assert_eq!(target.as_str(), "android");
123///
124/// let both = Target::Both;
125/// assert_eq!(both.as_str(), "both");
126/// ```
127#[derive(Debug, Clone, Copy, PartialEq, Eq)]
128pub enum Target {
129 /// Android platform (APK with native .so libraries).
130 Android,
131 /// iOS platform (xcframework with static libraries).
132 Ios,
133 /// Both Android and iOS platforms.
134 Both,
135}
136
137/// Mobile FFI backend used by generated benchmark runners.
138///
139/// UniFFI remains the default because it is the historical mobench path. Use
140/// [`FfiBackend::NativeCAbi`] when the generated app should call the generic
141/// mobench JSON C ABI directly, avoiding binding-generator overhead in the
142/// measured path.
143#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
144#[serde(rename_all = "kebab-case")]
145pub enum FfiBackend {
146 /// Generate and call UniFFI Kotlin/Swift bindings.
147 #[default]
148 Uniffi,
149 /// Call `mobench_run_benchmark_json` through a small native C ABI bridge.
150 NativeCAbi,
151}
152
153impl FfiBackend {
154 /// Returns the configuration string for this backend.
155 pub fn as_str(&self) -> &'static str {
156 match self {
157 FfiBackend::Uniffi => "uniffi",
158 FfiBackend::NativeCAbi => "native-c-abi",
159 }
160 }
161
162 /// Returns true when this backend needs UniFFI binding generation.
163 pub fn uses_uniffi(&self) -> bool {
164 matches!(self, FfiBackend::Uniffi)
165 }
166}
167
168impl fmt::Display for FfiBackend {
169 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170 f.write_str(self.as_str())
171 }
172}
173
174impl Target {
175 /// Returns the string representation of the target.
176 ///
177 /// # Returns
178 ///
179 /// - `"android"` for [`Target::Android`]
180 /// - `"ios"` for [`Target::Ios`]
181 /// - `"both"` for [`Target::Both`]
182 pub fn as_str(&self) -> &'static str {
183 match self {
184 Target::Android => "android",
185 Target::Ios => "ios",
186 Target::Both => "both",
187 }
188 }
189}
190
191/// Configuration for initializing a new benchmark project.
192///
193/// Used by the `cargo mobench init` command to generate project scaffolding.
194///
195/// # Example
196///
197/// ```
198/// use mobench_sdk::{InitConfig, Target};
199/// use std::path::PathBuf;
200///
201/// let config = InitConfig {
202/// target: Target::Android,
203/// project_name: "my-benchmarks".to_string(),
204/// output_dir: PathBuf::from("./bench-mobile"),
205/// generate_examples: true,
206/// };
207/// ```
208#[derive(Debug, Clone)]
209pub struct InitConfig {
210 /// Target platform(s) to initialize for.
211 pub target: Target,
212 /// Name of the benchmark project/crate.
213 pub project_name: String,
214 /// Output directory for generated files.
215 pub output_dir: PathBuf,
216 /// Whether to generate example benchmark functions.
217 pub generate_examples: bool,
218}
219
220/// Configuration for building mobile apps.
221///
222/// Controls the build process including target platform, optimization level,
223/// and caching behavior.
224///
225/// # Example
226///
227/// ```
228/// use mobench_sdk::{BuildConfig, BuildProfile, Target};
229///
230/// // Release build for Android
231/// let config = BuildConfig {
232/// target: Target::Android,
233/// profile: BuildProfile::Release,
234/// incremental: true,
235/// android_abis: None,
236/// };
237///
238/// // Debug build for iOS
239/// let ios_config = BuildConfig {
240/// target: Target::Ios,
241/// profile: BuildProfile::Debug,
242/// incremental: false, // Force rebuild
243/// android_abis: None,
244/// };
245/// ```
246#[derive(Debug, Clone)]
247pub struct BuildConfig {
248 /// Target platform to build for.
249 pub target: Target,
250 /// Build profile (debug or release).
251 pub profile: BuildProfile,
252 /// If `true`, skip rebuilding if artifacts already exist.
253 pub incremental: bool,
254 /// Optional Android ABIs to build/package. Defaults to `["arm64-v8a"]`.
255 pub android_abis: Option<Vec<String>>,
256}
257
258/// Build profile controlling optimization and debug info.
259///
260/// Similar to Cargo's `--release` flag, this controls whether the build
261/// is optimized for debugging or performance.
262///
263/// # Example
264///
265/// ```
266/// use mobench_sdk::BuildProfile;
267///
268/// let debug = BuildProfile::Debug;
269/// assert_eq!(debug.as_str(), "debug");
270///
271/// let release = BuildProfile::Release;
272/// assert_eq!(release.as_str(), "release");
273/// ```
274#[derive(Debug, Clone, Copy, PartialEq, Eq)]
275pub enum BuildProfile {
276 /// Debug build with debug symbols and no optimizations.
277 ///
278 /// Faster compilation but slower runtime. Useful for development
279 /// and troubleshooting.
280 Debug,
281 /// Release build with optimizations enabled.
282 ///
283 /// Slower compilation but faster runtime. Use this for actual
284 /// benchmark measurements.
285 Release,
286}
287
288impl BuildProfile {
289 /// Returns the string representation of the profile.
290 ///
291 /// # Returns
292 ///
293 /// - `"debug"` for [`BuildProfile::Debug`]
294 /// - `"release"` for [`BuildProfile::Release`]
295 pub fn as_str(&self) -> &'static str {
296 match self {
297 BuildProfile::Debug => "debug",
298 BuildProfile::Release => "release",
299 }
300 }
301}
302
303/// Result of a successful build operation.
304///
305/// Contains paths to the built artifacts, which can be used for
306/// deployment to BrowserStack or local testing.
307///
308/// # Example
309///
310/// ```ignore
311/// use mobench_sdk::builders::AndroidBuilder;
312///
313/// let result = builder.build(&config)?;
314///
315/// println!("App built at: {:?}", result.app_path);
316/// if let Some(test_suite) = result.test_suite_path {
317/// println!("Test suite at: {:?}", test_suite);
318/// }
319/// ```
320#[derive(Debug, Clone, PartialEq, Eq)]
321pub struct NativeLibraryArtifact {
322 /// ABI name used in Android packaging, for example `arm64-v8a`.
323 pub abi: String,
324 /// Shared library filename, for example `libsample_fns.so`.
325 pub library_name: String,
326 /// Path to the unstripped library produced by Cargo.
327 pub unstripped_path: PathBuf,
328 /// Path to the packaged copy under `jniLibs/`.
329 pub packaged_path: PathBuf,
330}
331
332#[derive(Debug, Clone)]
333pub struct BuildResult {
334 /// Platform that was built.
335 pub platform: Target,
336 /// Path to the main app artifact.
337 ///
338 /// - Android: Path to the APK file
339 /// - iOS: Path to the xcframework directory
340 pub app_path: PathBuf,
341 /// Path to the test suite artifact, if applicable.
342 ///
343 /// - Android: Path to the androidTest APK (for Espresso)
344 /// - iOS: Path to the XCUITest runner zip
345 pub test_suite_path: Option<PathBuf>,
346 /// Native libraries associated with this build, when applicable.
347 pub native_libraries: Vec<NativeLibraryArtifact>,
348}