1use serde::{Deserialize, Serialize};
33
34pub use crate::uniffi_types::{
36 BenchErrorVariant, BenchReportTemplate, BenchSampleTemplate, BenchSpecTemplate, FromSdkError,
37 FromSdkReport, FromSdkSample, FromSdkSpec,
38};
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct BenchSpecFfi {
45 pub name: String,
47 pub iterations: u32,
49 pub warmup: u32,
51}
52
53impl From<crate::BenchSpec> for BenchSpecFfi {
54 fn from(spec: crate::BenchSpec) -> Self {
55 Self {
56 name: spec.name,
57 iterations: spec.iterations,
58 warmup: spec.warmup,
59 }
60 }
61}
62
63impl From<BenchSpecFfi> for crate::BenchSpec {
64 fn from(spec: BenchSpecFfi) -> Self {
65 Self {
66 name: spec.name,
67 iterations: spec.iterations,
68 warmup: spec.warmup,
69 }
70 }
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct BenchSampleFfi {
76 pub duration_ns: u64,
78}
79
80impl From<crate::BenchSample> for BenchSampleFfi {
81 fn from(sample: crate::BenchSample) -> Self {
82 Self {
83 duration_ns: sample.duration_ns,
84 }
85 }
86}
87
88impl From<BenchSampleFfi> for crate::BenchSample {
89 fn from(sample: BenchSampleFfi) -> Self {
90 Self {
91 duration_ns: sample.duration_ns,
92 }
93 }
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct BenchReportFfi {
99 pub spec: BenchSpecFfi,
101 pub samples: Vec<BenchSampleFfi>,
103 pub phases: Vec<SemanticPhaseFfi>,
105 pub timeline: Vec<HarnessTimelineSpanFfi>,
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct SemanticPhaseFfi {
112 pub name: String,
113 pub duration_ns: u64,
114}
115
116impl From<crate::SemanticPhase> for SemanticPhaseFfi {
117 fn from(phase: crate::SemanticPhase) -> Self {
118 Self {
119 name: phase.name,
120 duration_ns: phase.duration_ns,
121 }
122 }
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct HarnessTimelineSpanFfi {
128 pub phase: String,
129 pub start_offset_ns: u64,
130 pub end_offset_ns: u64,
131 pub iteration: Option<u32>,
132}
133
134impl From<crate::HarnessTimelineSpan> for HarnessTimelineSpanFfi {
135 fn from(span: crate::HarnessTimelineSpan) -> Self {
136 Self {
137 phase: span.phase,
138 start_offset_ns: span.start_offset_ns,
139 end_offset_ns: span.end_offset_ns,
140 iteration: span.iteration,
141 }
142 }
143}
144
145impl From<crate::RunnerReport> for BenchReportFfi {
146 fn from(report: crate::RunnerReport) -> Self {
147 Self {
148 spec: report.spec.into(),
149 samples: report.samples.into_iter().map(Into::into).collect(),
150 phases: report.phases.into_iter().map(Into::into).collect(),
151 timeline: report.timeline.into_iter().map(Into::into).collect(),
152 }
153 }
154}
155
156#[derive(Debug, Clone, Serialize, Deserialize)]
158pub enum BenchErrorFfi {
159 InvalidIterations,
161 UnknownFunction { name: String },
163 ExecutionFailed { reason: String },
165 ConfigError { message: String },
167 IoError { message: String },
169}
170
171impl From<crate::types::BenchError> for BenchErrorFfi {
172 fn from(err: crate::types::BenchError) -> Self {
173 match err {
174 crate::types::BenchError::Runner(runner_err) => match runner_err {
175 crate::timing::TimingError::NoIterations { .. } => BenchErrorFfi::InvalidIterations,
176 crate::timing::TimingError::Execution(msg) => {
177 BenchErrorFfi::ExecutionFailed { reason: msg }
178 }
179 },
180 crate::types::BenchError::UnknownFunction(name, _) => {
181 BenchErrorFfi::UnknownFunction { name }
182 }
183 crate::types::BenchError::Execution(msg) => {
184 BenchErrorFfi::ExecutionFailed { reason: msg }
185 }
186 crate::types::BenchError::Io(e) => BenchErrorFfi::IoError {
187 message: e.to_string(),
188 },
189 crate::types::BenchError::Serialization(e) => BenchErrorFfi::ConfigError {
190 message: e.to_string(),
191 },
192 crate::types::BenchError::Config(msg) => BenchErrorFfi::ConfigError { message: msg },
193 crate::types::BenchError::Build(msg) => BenchErrorFfi::ExecutionFailed {
194 reason: format!("build error: {}", msg),
195 },
196 }
197 }
198}
199
200pub trait IntoFfi<T> {
202 fn into_ffi(self) -> T;
204}
205
206pub trait FromFfi<T> {
208 fn from_ffi(ffi: T) -> Self;
210}
211
212impl<T, U> IntoFfi<U> for T
214where
215 U: From<T>,
216{
217 fn into_ffi(self) -> U {
218 U::from(self)
219 }
220}
221
222impl<T, U> FromFfi<U> for T
223where
224 T: From<U>,
225{
226 fn from_ffi(ffi: U) -> Self {
227 T::from(ffi)
228 }
229}
230
231#[cfg(feature = "full")]
235pub fn run_benchmark_ffi(spec: BenchSpecFfi) -> Result<BenchReportFfi, BenchErrorFfi> {
236 let sdk_spec: crate::BenchSpec = spec.into();
237 crate::run_benchmark(sdk_spec)
238 .map(Into::into)
239 .map_err(Into::into)
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245
246 #[test]
247 fn test_bench_spec_ffi_conversion() {
248 let sdk_spec = crate::BenchSpec {
249 name: "test".to_string(),
250 iterations: 100,
251 warmup: 10,
252 };
253
254 let ffi: BenchSpecFfi = sdk_spec.clone().into();
255 assert_eq!(ffi.name, "test");
256 assert_eq!(ffi.iterations, 100);
257 assert_eq!(ffi.warmup, 10);
258
259 let back: crate::BenchSpec = ffi.into();
260 assert_eq!(back.name, sdk_spec.name);
261 }
262
263 #[test]
264 fn test_bench_sample_ffi_conversion() {
265 let sdk_sample = crate::BenchSample { duration_ns: 12345 };
266 let ffi: BenchSampleFfi = sdk_sample.into();
267 assert_eq!(ffi.duration_ns, 12345);
268 }
269
270 #[test]
271 fn test_bench_report_ffi_conversion() {
272 let report = crate::RunnerReport {
273 spec: crate::BenchSpec {
274 name: "test".to_string(),
275 iterations: 2,
276 warmup: 1,
277 },
278 samples: vec![
279 crate::BenchSample { duration_ns: 100 },
280 crate::BenchSample { duration_ns: 200 },
281 ],
282 phases: vec![crate::SemanticPhase {
283 name: "prove".to_string(),
284 duration_ns: 300,
285 }],
286 timeline: vec![crate::HarnessTimelineSpan {
287 phase: "measured-benchmark".to_string(),
288 start_offset_ns: 0,
289 end_offset_ns: 100,
290 iteration: Some(0),
291 }],
292 };
293
294 let ffi: BenchReportFfi = report.into();
295 assert_eq!(ffi.spec.name, "test");
296 assert_eq!(ffi.samples.len(), 2);
297 assert_eq!(ffi.samples[0].duration_ns, 100);
298 assert_eq!(ffi.phases.len(), 1);
299 assert_eq!(ffi.phases[0].name, "prove");
300 assert_eq!(ffi.timeline.len(), 1);
301 assert_eq!(ffi.timeline[0].phase, "measured-benchmark");
302 }
303
304 #[test]
305 fn test_into_ffi_trait() {
306 let spec = crate::BenchSpec {
307 name: "test".to_string(),
308 iterations: 50,
309 warmup: 5,
310 };
311
312 let ffi: BenchSpecFfi = spec.into_ffi();
313 assert_eq!(ffi.iterations, 50);
314 }
315}