use fission_core::{
NfcAvailability, NfcEmulationRequest, NfcError, NfcRecord, NfcScanRequest, NfcSessionReceipt,
NfcTag, NfcTechnology, NfcWriteRequest, CANCEL_NFC_SESSION, EMULATE_NFC_TAG,
GET_NFC_AVAILABILITY, SCAN_NFC_TAG, WRITE_NFC_TAG,
};
use fission_shell::async_host::AsyncRegistry;
use std::sync::Arc;
pub trait NfcHost: Send + Sync + 'static {
fn availability(&self) -> Result<NfcAvailability, NfcError>;
fn scan_tag(&self, request: NfcScanRequest) -> Result<NfcTag, NfcError>;
fn write_tag(&self, request: NfcWriteRequest) -> Result<NfcSessionReceipt, NfcError>;
fn emulate_tag(&self, request: NfcEmulationRequest) -> Result<NfcSessionReceipt, NfcError>;
fn cancel_session(&self) -> Result<(), NfcError>;
}
#[derive(Debug, Default)]
pub struct UnsupportedNfcHost;
impl NfcHost for UnsupportedNfcHost {
fn availability(&self) -> Result<NfcAvailability, NfcError> {
Ok(NfcAvailability::default())
}
fn scan_tag(&self, _request: NfcScanRequest) -> Result<NfcTag, NfcError> {
Err(NfcError::unsupported("scan_tag"))
}
fn write_tag(&self, _request: NfcWriteRequest) -> Result<NfcSessionReceipt, NfcError> {
Err(NfcError::unsupported("write_tag"))
}
fn emulate_tag(&self, _request: NfcEmulationRequest) -> Result<NfcSessionReceipt, NfcError> {
Err(NfcError::unsupported("emulate_tag"))
}
fn cancel_session(&self) -> Result<(), NfcError> {
Err(NfcError::unsupported("cancel_session"))
}
}
#[derive(Debug, Clone)]
pub struct MemoryNfcHost {
tag: NfcTag,
}
impl MemoryNfcHost {
pub fn new(tag: NfcTag) -> Self {
Self { tag }
}
}
impl Default for MemoryNfcHost {
fn default() -> Self {
Self {
tag: NfcTag {
id: Some(vec![0xF1, 0x55, 0x10, 0x01]),
technologies: vec![NfcTechnology::Ndef],
records: vec![NfcRecord::uri("fission://memory-nfc")],
raw_payload: None,
},
}
}
}
impl NfcHost for MemoryNfcHost {
fn availability(&self) -> Result<NfcAvailability, NfcError> {
Ok(NfcAvailability {
supported: true,
enabled: true,
read: true,
write: true,
card_emulation: true,
})
}
fn scan_tag(&self, _request: NfcScanRequest) -> Result<NfcTag, NfcError> {
Ok(self.tag.clone())
}
fn write_tag(&self, request: NfcWriteRequest) -> Result<NfcSessionReceipt, NfcError> {
Ok(NfcSessionReceipt {
session_id: Some(format!("memory-nfc-write-{}", request.records.len())),
completed: true,
})
}
fn emulate_tag(&self, request: NfcEmulationRequest) -> Result<NfcSessionReceipt, NfcError> {
Ok(NfcSessionReceipt {
session_id: Some(format!("memory-nfc-emulate-{}", request.records.len())),
completed: true,
})
}
fn cancel_session(&self) -> Result<(), NfcError> {
Ok(())
}
}
pub(crate) fn register_nfc_capabilities(
async_registry: &mut AsyncRegistry,
host: Arc<dyn NfcHost>,
) {
let availability_host = host.clone();
async_registry.register_operation_capability(GET_NFC_AVAILABILITY, move |(), _| {
let host = availability_host.clone();
async move { host.availability() }
});
let scan_host = host.clone();
async_registry.register_operation_capability(SCAN_NFC_TAG, move |request, _| {
let host = scan_host.clone();
async move { host.scan_tag(request) }
});
let write_host = host.clone();
async_registry.register_operation_capability(WRITE_NFC_TAG, move |request, _| {
let host = write_host.clone();
async move { host.write_tag(request) }
});
let emulate_host = host.clone();
async_registry.register_operation_capability(EMULATE_NFC_TAG, move |request, _| {
let host = emulate_host.clone();
async move { host.emulate_tag(request) }
});
async_registry.register_operation_capability(CANCEL_NFC_SESSION, move |(), _| {
let host = host.clone();
async move { host.cancel_session() }
});
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unsupported_host_reports_unavailable() {
let host = UnsupportedNfcHost;
let availability = host.availability().unwrap();
assert!(!availability.supported);
assert!(host.scan_tag(NfcScanRequest::default()).is_err());
}
#[test]
fn memory_host_scans_and_writes() {
let host = MemoryNfcHost::default();
let tag = host.scan_tag(NfcScanRequest::default()).unwrap();
assert_eq!(tag.technologies, vec![NfcTechnology::Ndef]);
let receipt = host
.write_tag(NfcWriteRequest {
records: vec![NfcRecord::text("en", "ok")],
..Default::default()
})
.unwrap();
assert!(receipt.completed);
}
}