use std::cell::RefCell;
use std::collections::HashMap;
use std::string::String;
use embedder_traits::resources::{self, Resource};
use regex::Regex;
const EXCLUDE_READS: &str = "exclude-reads";
const EXCLUDE_WRITES: &str = "exclude-writes";
const VALID_UUID_REGEX: &str = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
thread_local!(pub static BLUETOOTH_BLOCKLIST: RefCell<BluetoothBlocklist> =
RefCell::new(BluetoothBlocklist(parse_blocklist())));
pub fn uuid_is_blocklisted(uuid: &str, exclude_type: Blocklist) -> bool {
BLUETOOTH_BLOCKLIST.with(|blist| match exclude_type {
Blocklist::All => blist.borrow().is_blocklisted(uuid),
Blocklist::Reads => blist.borrow().is_blocklisted_for_reads(uuid),
Blocklist::Writes => blist.borrow().is_blocklisted_for_writes(uuid),
})
}
pub struct BluetoothBlocklist(Option<HashMap<String, Blocklist>>);
#[derive(Eq, PartialEq)]
pub enum Blocklist {
All, Reads,
Writes,
}
impl BluetoothBlocklist {
pub fn is_blocklisted(&self, uuid: &str) -> bool {
match self.0 {
Some(ref map) => map.get(uuid).is_some_and(|et| et.eq(&Blocklist::All)),
None => false,
}
}
pub fn is_blocklisted_for_reads(&self, uuid: &str) -> bool {
match self.0 {
Some(ref map) => map
.get(uuid)
.is_some_and(|et| et.eq(&Blocklist::All) || et.eq(&Blocklist::Reads)),
None => false,
}
}
pub fn is_blocklisted_for_writes(&self, uuid: &str) -> bool {
match self.0 {
Some(ref map) => map
.get(uuid)
.is_some_and(|et| et.eq(&Blocklist::All) || et.eq(&Blocklist::Writes)),
None => false,
}
}
}
fn parse_blocklist() -> Option<HashMap<String, Blocklist>> {
let valid_uuid_regex = Regex::new(VALID_UUID_REGEX).unwrap();
let content = resources::read_string(Resource::BluetoothBlocklist);
let mut result = HashMap::new();
for line in content.lines() {
if line.is_empty() || line.starts_with('#') {
continue;
}
let mut exclude_type = Blocklist::All;
let mut words = line.split_whitespace();
let uuid = match words.next() {
Some(uuid) => uuid,
None => continue,
};
if !valid_uuid_regex.is_match(uuid) {
return None;
}
match words.next() {
None => {},
Some(EXCLUDE_READS) => {
exclude_type = Blocklist::Reads;
},
Some(EXCLUDE_WRITES) => {
exclude_type = Blocklist::Writes;
},
_ => {
return None;
},
}
if result.contains_key(uuid) {
return None;
}
result.insert(uuid.to_string(), exclude_type);
}
Some(result)
}