use core::fmt;
use lpc55::bootloader::UuidSelectable;
use pcsc::{Protocols, Scope, ShareMode};
use crate::{apps::admin::App as Admin, Result, Select as _, Uuid};
pub struct Session {
session: pcsc::Context,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Info {
pub name: String,
pub serial: String,
pub vendor: String,
pub version: String,
pub atr: String,
}
pub struct Device {
pub(crate) device: pcsc::Card,
pub name: String,
}
pub fn list() -> Vec<Device> {
Session::new()
.map(|session| session.devices())
.unwrap_or_else(|_| vec![])
}
impl Session {
pub fn is_available() -> bool {
Self::new().is_ok()
}
pub fn new() -> Result<Self> {
Ok(Self {
session: pcsc::Context::establish(Scope::User)?,
})
}
pub fn connect(&self, info: &str) -> Result<Device> {
let cstring = std::ffi::CString::new(info.as_bytes()).unwrap();
Ok(Device {
device: self
.session
.connect(&cstring, ShareMode::Shared, Protocols::ANY)?,
name: info.to_string(),
})
}
pub fn infos(&self) -> Result<Vec<Info>> {
let mut card_names_buffer = vec![0; self.session.list_readers_len()?];
let infos = self
.session
.list_readers(&mut card_names_buffer)?
.map(|name_cstr| name_cstr.to_string_lossy().to_string())
.filter_map(|name| self.connect(&name).ok().map(|device| (name, device)))
.map(|(name, device)| {
Info {
name,
vendor: device.attribute(pcsc::Attribute::VendorName),
serial: device.attribute(pcsc::Attribute::VendorIfdSerialNo),
version: device.attribute(pcsc::Attribute::VendorIfdVersion),
atr: device.attribute(pcsc::Attribute::AtrString),
}
})
.collect();
Ok(infos)
}
pub fn devices(&self) -> Vec<Device> {
self.infos()
.unwrap_or_else(|_| vec![])
.iter()
.filter_map(|info| self.connect(&info.name).ok())
.collect()
}
}
impl Device {
fn attribute(&self, attribute: pcsc::Attribute) -> String {
let attribute = self.device.get_attribute_owned(attribute).ok();
attribute
.map(|attribute| String::from_utf8_lossy(&attribute).to_string())
.map(|mut attribute| {
while let Some('\0') = attribute.chars().last() {
attribute.truncate(attribute.len() - 1);
}
attribute
})
.unwrap_or_else(|| "".to_string())
}
}
impl fmt::Debug for Device {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> core::result::Result<(), fmt::Error> {
write!(f, "{}", &self.name)
}
}
impl fmt::Display for Device {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> core::result::Result<(), fmt::Error> {
fmt::Debug::fmt(self, f)
}
}
impl UuidSelectable for Device {
fn try_uuid(&mut self) -> Result<Uuid> {
let mut admin = Admin::select(self)?;
admin.uuid()
}
fn list() -> Vec<Self> {
let session = match Session::new() {
Ok(session) => session,
_ => return Vec::default(),
};
session.devices()
}
}