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 resource_usage: Option<BenchResourceUsageFfi>,
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct BenchResourceUsageFfi {
112 pub cpu_median_ms: Option<u64>,
113 pub peak_memory_kb: Option<u64>,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct SemanticPhaseFfi {
119 pub name: String,
120 pub duration_ns: u64,
121}
122
123impl From<crate::SemanticPhase> for SemanticPhaseFfi {
124 fn from(phase: crate::SemanticPhase) -> Self {
125 Self {
126 name: phase.name,
127 duration_ns: phase.duration_ns,
128 }
129 }
130}
131
132impl From<crate::RunnerReport> for BenchReportFfi {
133 fn from(report: crate::RunnerReport) -> Self {
134 Self {
135 spec: report.spec.into(),
136 samples: report.samples.into_iter().map(Into::into).collect(),
137 phases: report.phases.into_iter().map(Into::into).collect(),
138 resource_usage: report.resource_usage.map(Into::into),
139 }
140 }
141}
142
143impl From<crate::BenchResourceUsage> for BenchResourceUsageFfi {
144 fn from(resource_usage: crate::BenchResourceUsage) -> Self {
145 Self {
146 cpu_median_ms: resource_usage.cpu_median_ms,
147 peak_memory_kb: resource_usage.peak_memory_kb,
148 }
149 }
150}
151
152#[derive(Debug, Clone, Serialize, Deserialize)]
154pub enum BenchErrorFfi {
155 InvalidIterations,
157 UnknownFunction { name: String },
159 ExecutionFailed { reason: String },
161 ConfigError { message: String },
163 IoError { message: String },
165}
166
167impl From<crate::types::BenchError> for BenchErrorFfi {
168 fn from(err: crate::types::BenchError) -> Self {
169 match err {
170 crate::types::BenchError::Runner(runner_err) => match runner_err {
171 crate::timing::TimingError::NoIterations { .. } => BenchErrorFfi::InvalidIterations,
172 crate::timing::TimingError::Execution(msg) => {
173 BenchErrorFfi::ExecutionFailed { reason: msg }
174 }
175 },
176 crate::types::BenchError::UnknownFunction(name, _) => {
177 BenchErrorFfi::UnknownFunction { name }
178 }
179 crate::types::BenchError::Execution(msg) => {
180 BenchErrorFfi::ExecutionFailed { reason: msg }
181 }
182 crate::types::BenchError::Io(e) => BenchErrorFfi::IoError {
183 message: e.to_string(),
184 },
185 crate::types::BenchError::Serialization(e) => BenchErrorFfi::ConfigError {
186 message: e.to_string(),
187 },
188 crate::types::BenchError::Config(msg) => BenchErrorFfi::ConfigError { message: msg },
189 crate::types::BenchError::Build(msg) => BenchErrorFfi::ExecutionFailed {
190 reason: format!("build error: {}", msg),
191 },
192 }
193 }
194}
195
196pub trait IntoFfi<T> {
198 fn into_ffi(self) -> T;
200}
201
202pub trait FromFfi<T> {
204 fn from_ffi(ffi: T) -> Self;
206}
207
208impl<T, U> IntoFfi<U> for T
210where
211 U: From<T>,
212{
213 fn into_ffi(self) -> U {
214 U::from(self)
215 }
216}
217
218impl<T, U> FromFfi<U> for T
219where
220 T: From<U>,
221{
222 fn from_ffi(ffi: U) -> Self {
223 T::from(ffi)
224 }
225}
226
227#[cfg(feature = "full")]
231pub fn run_benchmark_ffi(spec: BenchSpecFfi) -> Result<BenchReportFfi, BenchErrorFfi> {
232 let sdk_spec: crate::BenchSpec = spec.into();
233 crate::run_benchmark(sdk_spec)
234 .map(Into::into)
235 .map_err(Into::into)
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241
242 #[test]
243 fn test_bench_spec_ffi_conversion() {
244 let sdk_spec = crate::BenchSpec {
245 name: "test".to_string(),
246 iterations: 100,
247 warmup: 10,
248 };
249
250 let ffi: BenchSpecFfi = sdk_spec.clone().into();
251 assert_eq!(ffi.name, "test");
252 assert_eq!(ffi.iterations, 100);
253 assert_eq!(ffi.warmup, 10);
254
255 let back: crate::BenchSpec = ffi.into();
256 assert_eq!(back.name, sdk_spec.name);
257 }
258
259 #[test]
260 fn test_bench_sample_ffi_conversion() {
261 let sdk_sample = crate::BenchSample { duration_ns: 12345 };
262 let ffi: BenchSampleFfi = sdk_sample.into();
263 assert_eq!(ffi.duration_ns, 12345);
264 }
265
266 #[test]
267 fn test_bench_report_ffi_conversion() {
268 let report = crate::RunnerReport {
269 spec: crate::BenchSpec {
270 name: "test".to_string(),
271 iterations: 2,
272 warmup: 1,
273 },
274 samples: vec![
275 crate::BenchSample { duration_ns: 100 },
276 crate::BenchSample { duration_ns: 200 },
277 ],
278 phases: vec![crate::SemanticPhase {
279 name: "prove".to_string(),
280 duration_ns: 300,
281 }],
282 resource_usage: None,
283 };
284
285 let ffi: BenchReportFfi = report.into();
286 assert_eq!(ffi.spec.name, "test");
287 assert_eq!(ffi.samples.len(), 2);
288 assert_eq!(ffi.samples[0].duration_ns, 100);
289 assert_eq!(ffi.phases.len(), 1);
290 assert_eq!(ffi.phases[0].name, "prove");
291 }
292
293 #[test]
294 fn test_into_ffi_trait() {
295 let spec = crate::BenchSpec {
296 name: "test".to_string(),
297 iterations: 50,
298 warmup: 5,
299 };
300
301 let ffi: BenchSpecFfi = spec.into_ffi();
302 assert_eq!(ffi.iterations, 50);
303 }
304}