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