1use crate::capability::{CapabilityType, OperationCapability};
4use serde::{Deserialize, Serialize};
5
6#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
7pub enum BarcodeFormat {
8 QrCode,
9 Aztec,
10 DataMatrix,
11 Ean13,
12 Ean8,
13 Code128,
14 Code39,
15 Code93,
16 Codabar,
17 Itf,
18 Pdf417,
19 UpcA,
20 UpcE,
21 MaxiCode,
22 Rss14,
23 RssExpanded,
24 Other(String),
25}
26
27#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
28pub struct BarcodePoint {
29 pub x: i32,
30 pub y: i32,
31}
32
33#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
34pub struct BarcodeScanRequest {
35 pub formats: Vec<BarcodeFormat>,
36 pub prompt: Option<String>,
37 pub camera_id: Option<String>,
38 pub timeout_ms: Option<u64>,
39 pub allow_multiple: bool,
40}
41
42#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
43pub struct BarcodeImageDecodeRequest {
44 pub bytes: Vec<u8>,
45 pub content_type: Option<String>,
46 pub formats: Vec<BarcodeFormat>,
47}
48
49#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
50pub struct BarcodeScanResult {
51 pub value: String,
52 pub format: BarcodeFormat,
53 pub raw_bytes: Vec<u8>,
54 pub bounds: Vec<BarcodePoint>,
55 pub symbology_identifier: Option<String>,
56}
57
58#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
59pub struct BarcodeScanResults {
60 pub items: Vec<BarcodeScanResult>,
61}
62
63#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
64pub struct BarcodeScannerError {
65 pub code: String,
66 pub message: String,
67}
68
69impl BarcodeScannerError {
70 pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
77 Self {
78 code: code.into(),
79 message: message.into(),
80 }
81 }
82
83 pub fn unsupported(operation: impl Into<String>) -> Self {
89 Self::new(
90 "unsupported",
91 format!(
92 "barcode scanner operation `{}` is not supported by this host",
93 operation.into()
94 ),
95 )
96 }
97}
98
99pub struct ScanBarcodeCapability;
100impl OperationCapability for ScanBarcodeCapability {
101 type Request = BarcodeScanRequest;
102 type Ok = BarcodeScanResults;
103 type Err = BarcodeScannerError;
104}
105
106pub struct DecodeBarcodeImageCapability;
107impl OperationCapability for DecodeBarcodeImageCapability {
108 type Request = BarcodeImageDecodeRequest;
109 type Ok = BarcodeScanResults;
110 type Err = BarcodeScannerError;
111}
112
113pub struct CancelBarcodeScanCapability;
114impl OperationCapability for CancelBarcodeScanCapability {
115 type Request = ();
116 type Ok = ();
117 type Err = BarcodeScannerError;
118}
119
120pub const SCAN_BARCODE: CapabilityType<ScanBarcodeCapability> =
121 CapabilityType::new("fission.barcode.scan");
122pub const DECODE_BARCODE_IMAGE: CapabilityType<DecodeBarcodeImageCapability> =
123 CapabilityType::new("fission.barcode.decode_image");
124pub const CANCEL_BARCODE_SCAN: CapabilityType<CancelBarcodeScanCapability> =
125 CapabilityType::new("fission.barcode.cancel_scan");
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130
131 #[test]
132 fn barcode_scan_request_round_trips() {
133 let request = BarcodeScanRequest {
134 formats: vec![BarcodeFormat::QrCode, BarcodeFormat::Code128],
135 prompt: Some("Scan the label".into()),
136 camera_id: Some("back".into()),
137 timeout_ms: Some(10_000),
138 allow_multiple: true,
139 };
140
141 let bytes = serde_json::to_vec(&request).unwrap();
142 let decoded: BarcodeScanRequest = serde_json::from_slice(&bytes).unwrap();
143
144 assert_eq!(decoded, request);
145 }
146
147 #[test]
148 fn barcode_results_round_trip() {
149 let results = BarcodeScanResults {
150 items: vec![BarcodeScanResult {
151 value: "https://fission.rs".into(),
152 format: BarcodeFormat::QrCode,
153 raw_bytes: b"https://fission.rs".to_vec(),
154 bounds: vec![BarcodePoint { x: 1, y: 2 }, BarcodePoint { x: 3, y: 4 }],
155 symbology_identifier: None,
156 }],
157 };
158
159 let bytes = serde_json::to_vec(&results).unwrap();
160 let decoded: BarcodeScanResults = serde_json::from_slice(&bytes).unwrap();
161
162 assert_eq!(decoded, results);
163 }
164}