Skip to main content

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, or [`FfiBackend::BoltFfi`] when the generated
142/// app should call BoltFFI-generated Kotlin/Swift bindings.
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    /// Generate and call BoltFFI Kotlin/Swift bindings.
152    #[serde(rename = "boltffi", alias = "bolt-ffi")]
153    BoltFfi,
154}
155
156impl FfiBackend {
157    /// Returns the configuration string for this backend.
158    pub fn as_str(&self) -> &'static str {
159        match self {
160            FfiBackend::Uniffi => "uniffi",
161            FfiBackend::NativeCAbi => "native-c-abi",
162            FfiBackend::BoltFfi => "boltffi",
163        }
164    }
165
166    /// Returns true when this backend needs UniFFI binding generation.
167    pub fn uses_uniffi(&self) -> bool {
168        matches!(self, FfiBackend::Uniffi)
169    }
170
171    /// Returns true when this backend needs BoltFFI binding generation.
172    pub fn uses_boltffi(&self) -> bool {
173        matches!(self, FfiBackend::BoltFfi)
174    }
175
176    /// Returns true when this backend calls the direct mobench C ABI bridge.
177    pub fn uses_native_c_abi(&self) -> bool {
178        matches!(self, FfiBackend::NativeCAbi)
179    }
180}
181
182impl fmt::Display for FfiBackend {
183    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184        f.write_str(self.as_str())
185    }
186}
187
188impl Target {
189    /// Returns the string representation of the target.
190    ///
191    /// # Returns
192    ///
193    /// - `"android"` for [`Target::Android`]
194    /// - `"ios"` for [`Target::Ios`]
195    /// - `"both"` for [`Target::Both`]
196    pub fn as_str(&self) -> &'static str {
197        match self {
198            Target::Android => "android",
199            Target::Ios => "ios",
200            Target::Both => "both",
201        }
202    }
203}
204
205/// Configuration for initializing a new benchmark project.
206///
207/// Used by the `cargo mobench init` command to generate project scaffolding.
208///
209/// # Example
210///
211/// ```
212/// use mobench_sdk::{InitConfig, Target};
213/// use std::path::PathBuf;
214///
215/// let config = InitConfig {
216///     target: Target::Android,
217///     project_name: "my-benchmarks".to_string(),
218///     output_dir: PathBuf::from("./bench-mobile"),
219///     generate_examples: true,
220/// };
221/// ```
222#[derive(Debug, Clone)]
223pub struct InitConfig {
224    /// Target platform(s) to initialize for.
225    pub target: Target,
226    /// Name of the benchmark project/crate.
227    pub project_name: String,
228    /// Output directory for generated files.
229    pub output_dir: PathBuf,
230    /// Whether to generate example benchmark functions.
231    pub generate_examples: bool,
232}
233
234/// Configuration for building mobile apps.
235///
236/// Controls the build process including target platform, optimization level,
237/// and caching behavior.
238///
239/// # Example
240///
241/// ```
242/// use mobench_sdk::{BuildConfig, BuildProfile, Target};
243///
244/// // Release build for Android
245/// let config = BuildConfig {
246///     target: Target::Android,
247///     profile: BuildProfile::Release,
248///     incremental: true,
249///     android_abis: None,
250/// };
251///
252/// // Debug build for iOS
253/// let ios_config = BuildConfig {
254///     target: Target::Ios,
255///     profile: BuildProfile::Debug,
256///     incremental: false,  // Force rebuild
257///     android_abis: None,
258/// };
259/// ```
260#[derive(Debug, Clone)]
261pub struct BuildConfig {
262    /// Target platform to build for.
263    pub target: Target,
264    /// Build profile (debug or release).
265    pub profile: BuildProfile,
266    /// If `true`, skip rebuilding if artifacts already exist.
267    pub incremental: bool,
268    /// Optional Android ABIs to build/package. Defaults to `["arm64-v8a"]`.
269    pub android_abis: Option<Vec<String>>,
270}
271
272/// Build profile controlling optimization and debug info.
273///
274/// Similar to Cargo's `--release` flag, this controls whether the build
275/// is optimized for debugging or performance.
276///
277/// # Example
278///
279/// ```
280/// use mobench_sdk::BuildProfile;
281///
282/// let debug = BuildProfile::Debug;
283/// assert_eq!(debug.as_str(), "debug");
284///
285/// let release = BuildProfile::Release;
286/// assert_eq!(release.as_str(), "release");
287/// ```
288#[derive(Debug, Clone, Copy, PartialEq, Eq)]
289pub enum BuildProfile {
290    /// Debug build with debug symbols and no optimizations.
291    ///
292    /// Faster compilation but slower runtime. Useful for development
293    /// and troubleshooting.
294    Debug,
295    /// Release build with optimizations enabled.
296    ///
297    /// Slower compilation but faster runtime. Use this for actual
298    /// benchmark measurements.
299    Release,
300}
301
302impl BuildProfile {
303    /// Returns the string representation of the profile.
304    ///
305    /// # Returns
306    ///
307    /// - `"debug"` for [`BuildProfile::Debug`]
308    /// - `"release"` for [`BuildProfile::Release`]
309    pub fn as_str(&self) -> &'static str {
310        match self {
311            BuildProfile::Debug => "debug",
312            BuildProfile::Release => "release",
313        }
314    }
315}
316
317/// Result of a successful build operation.
318///
319/// Contains paths to the built artifacts, which can be used for
320/// deployment to BrowserStack or local testing.
321///
322/// # Example
323///
324/// ```ignore
325/// use mobench_sdk::builders::AndroidBuilder;
326///
327/// let result = builder.build(&config)?;
328///
329/// println!("App built at: {:?}", result.app_path);
330/// if let Some(test_suite) = result.test_suite_path {
331///     println!("Test suite at: {:?}", test_suite);
332/// }
333/// ```
334#[derive(Debug, Clone, PartialEq, Eq)]
335pub struct NativeLibraryArtifact {
336    /// ABI name used in Android packaging, for example `arm64-v8a`.
337    pub abi: String,
338    /// Shared library filename, for example `libsample_fns.so`.
339    pub library_name: String,
340    /// Path to the unstripped library produced by Cargo.
341    pub unstripped_path: PathBuf,
342    /// Path to the packaged copy under `jniLibs/`.
343    pub packaged_path: PathBuf,
344}
345
346#[derive(Debug, Clone)]
347pub struct BuildResult {
348    /// Platform that was built.
349    pub platform: Target,
350    /// Path to the main app artifact.
351    ///
352    /// - Android: Path to the APK file
353    /// - iOS: Path to the xcframework directory
354    pub app_path: PathBuf,
355    /// Path to the test suite artifact, if applicable.
356    ///
357    /// - Android: Path to the androidTest APK (for Espresso)
358    /// - iOS: Path to the XCUITest runner zip
359    pub test_suite_path: Option<PathBuf>,
360    /// Native libraries associated with this build, when applicable.
361    pub native_libraries: Vec<NativeLibraryArtifact>,
362}