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