Skip to main content

fission_shell_winit/
barcode.rs

1use fission_core::{
2    collect_data_stream, BarcodeFormat, BarcodeImageDecodeRequest, BarcodePoint,
3    BarcodeScanRequest, BarcodeScanResult, BarcodeScanResults, BarcodeScannerError, Bytes,
4    CANCEL_BARCODE_SCAN, DECODE_BARCODE_IMAGE, SCAN_BARCODE,
5};
6use fission_shell::async_host::AsyncRegistry;
7use std::sync::Arc;
8
9/// Host-side barcode scanner provider.
10pub trait BarcodeScannerHost: Send + Sync + 'static {
11    /// Runs a live barcode scanning session and returns decoded results.
12    fn scan(&self, request: BarcodeScanRequest) -> Result<BarcodeScanResults, BarcodeScannerError>;
13    /// Decodes barcode results from an image stream supplied by the app.
14    fn decode_image(
15        &self,
16        request: BarcodeImageDecodeRequest,
17        image: Bytes,
18    ) -> Result<BarcodeScanResults, BarcodeScannerError>;
19    /// Cancels the active live barcode scanning session.
20    fn cancel_scan(&self) -> Result<(), BarcodeScannerError>;
21}
22
23#[derive(Debug, Default)]
24pub struct UnsupportedBarcodeScannerHost;
25
26impl BarcodeScannerHost for UnsupportedBarcodeScannerHost {
27    fn scan(
28        &self,
29        _request: BarcodeScanRequest,
30    ) -> Result<BarcodeScanResults, BarcodeScannerError> {
31        Err(BarcodeScannerError::unsupported("scan"))
32    }
33
34    fn decode_image(
35        &self,
36        _request: BarcodeImageDecodeRequest,
37        _image: Bytes,
38    ) -> Result<BarcodeScanResults, BarcodeScannerError> {
39        Err(BarcodeScannerError::unsupported("decode_image"))
40    }
41
42    fn cancel_scan(&self) -> Result<(), BarcodeScannerError> {
43        Err(BarcodeScannerError::unsupported("cancel_scan"))
44    }
45}
46
47#[derive(Debug, Clone)]
48pub struct MemoryBarcodeScannerHost {
49    results: BarcodeScanResults,
50}
51
52impl MemoryBarcodeScannerHost {
53    pub fn new(results: BarcodeScanResults) -> Self {
54        Self { results }
55    }
56}
57
58impl Default for MemoryBarcodeScannerHost {
59    fn default() -> Self {
60        Self {
61            results: BarcodeScanResults {
62                items: vec![BarcodeScanResult {
63                    value: "fission://barcode/memory".into(),
64                    format: BarcodeFormat::QrCode,
65                    raw_bytes: b"fission://barcode/memory".to_vec(),
66                    bounds: vec![
67                        BarcodePoint { x: 0, y: 0 },
68                        BarcodePoint { x: 64, y: 0 },
69                        BarcodePoint { x: 64, y: 64 },
70                        BarcodePoint { x: 0, y: 64 },
71                    ],
72                    symbology_identifier: None,
73                }],
74            },
75        }
76    }
77}
78
79impl BarcodeScannerHost for MemoryBarcodeScannerHost {
80    fn scan(
81        &self,
82        _request: BarcodeScanRequest,
83    ) -> Result<BarcodeScanResults, BarcodeScannerError> {
84        Ok(self.results.clone())
85    }
86
87    fn decode_image(
88        &self,
89        _request: BarcodeImageDecodeRequest,
90        _image: Bytes,
91    ) -> Result<BarcodeScanResults, BarcodeScannerError> {
92        Ok(self.results.clone())
93    }
94
95    fn cancel_scan(&self) -> Result<(), BarcodeScannerError> {
96        Ok(())
97    }
98}
99
100pub(crate) fn register_barcode_scanner_capabilities(
101    async_registry: &mut AsyncRegistry,
102    host: Arc<dyn BarcodeScannerHost>,
103) {
104    let scan_host = host.clone();
105    async_registry.register_operation_capability(SCAN_BARCODE, move |request, _| {
106        let host = scan_host.clone();
107        async move { host.scan(request) }
108    });
109
110    let decode_host = host.clone();
111    async_registry.register_operation_capability(DECODE_BARCODE_IMAGE, move |request, ctx| {
112        let host = decode_host.clone();
113        async move {
114            let stream = ctx.open_data_stream(request.stream).map_err(|error| {
115                BarcodeScannerError::new("stream_open_failed", error.to_string())
116            })?;
117            let image = collect_data_stream(stream).await.map_err(|error| {
118                BarcodeScannerError::new("stream_read_failed", error.to_string())
119            })?;
120            host.decode_image(request, image)
121        }
122    });
123
124    async_registry.register_operation_capability(CANCEL_BARCODE_SCAN, move |(), _| {
125        let host = host.clone();
126        async move { host.cancel_scan() }
127    });
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133
134    #[test]
135    fn unsupported_host_reports_errors() {
136        let host = UnsupportedBarcodeScannerHost;
137        assert!(host.scan(BarcodeScanRequest::default()).is_err());
138        assert!(host
139            .decode_image(BarcodeImageDecodeRequest::default(), Bytes::new())
140            .is_err());
141    }
142
143    #[test]
144    fn memory_host_scans_and_decodes() {
145        let host = MemoryBarcodeScannerHost::default();
146        let scan = host.scan(BarcodeScanRequest::default()).unwrap();
147        let decode = host
148            .decode_image(BarcodeImageDecodeRequest::default(), Bytes::new())
149            .unwrap();
150        assert_eq!(scan, decode);
151        assert_eq!(scan.items[0].format, BarcodeFormat::QrCode);
152    }
153}