use std::sync::{Arc, OnceLock};
use serde::Serialize;
use crate::error::Result;
use crate::fingerprint_spec::DeviceIdSource;
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DeviceIdDescription {
pub device_id: String,
pub version: u32,
pub platform: String,
pub source: DeviceIdSource,
pub param_names: Vec<String>,
}
pub trait DeviceIdResolver: Send + Sync {
fn device_name(&self) -> String;
fn device_id(&self) -> Result<String>;
fn accepts_device_id(&self, _device_id: &str) -> bool {
false
}
fn describe_device(&self) -> Result<Option<DeviceIdDescription>> {
Ok(None)
}
}
impl<T: DeviceIdResolver + ?Sized> DeviceIdResolver for std::sync::Arc<T> {
fn device_name(&self) -> String {
(**self).device_name()
}
fn device_id(&self) -> Result<String> {
(**self).device_id()
}
fn accepts_device_id(&self, device_id: &str) -> bool {
(**self).accepts_device_id(device_id)
}
fn describe_device(&self) -> Result<Option<DeviceIdDescription>> {
(**self).describe_device()
}
}
#[derive(Debug, Clone)]
pub struct StaticDeviceIdResolver {
name: String,
id: String,
}
impl StaticDeviceIdResolver {
pub fn new(name: impl Into<String>, id: impl Into<String>) -> Self {
Self { name: name.into(), id: id.into() }
}
}
impl DeviceIdResolver for StaticDeviceIdResolver {
fn device_name(&self) -> String {
self.name.clone()
}
fn device_id(&self) -> Result<String> {
Ok(self.id.clone())
}
}
pub struct MigratingDeviceIdResolver {
current: Arc<dyn DeviceIdResolver>,
previous: Vec<Arc<dyn DeviceIdResolver>>,
previous_ids: OnceLock<Vec<String>>,
}
impl MigratingDeviceIdResolver {
pub fn new(current: Arc<dyn DeviceIdResolver>, previous: Vec<Arc<dyn DeviceIdResolver>>) -> Self {
Self { current, previous, previous_ids: OnceLock::new() }
}
}
impl DeviceIdResolver for MigratingDeviceIdResolver {
fn device_name(&self) -> String {
self.current.device_name()
}
fn device_id(&self) -> Result<String> {
self.current.device_id()
}
fn accepts_device_id(&self, device_id: &str) -> bool {
if device_id.is_empty() {
return false;
}
self.previous_ids
.get_or_init(|| {
self.previous
.iter()
.filter_map(|resolver| resolver.device_id().ok())
.filter(|id| !id.is_empty())
.collect()
})
.iter()
.any(|previous| previous == device_id)
}
fn describe_device(&self) -> Result<Option<DeviceIdDescription>> {
self.current.describe_device()
}
}
impl std::fmt::Debug for MigratingDeviceIdResolver {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MigratingDeviceIdResolver").field("previous", &self.previous.len()).finish_non_exhaustive()
}
}