use thiserror::Error;
use uuid::Uuid;
use crate::keyring::{self, KeyringError};
const ID_ACCOUNT: &str = "machine-id";
#[derive(Debug, Error)]
pub enum MachineIdError {
#[error("machineid: application name required")]
EmptyApplication,
#[error("machineid: keyring: {0}")]
Keyring(#[from] KeyringError),
}
pub fn get_or_create(application: &str) -> Result<String, MachineIdError> {
if application.is_empty() {
return Err(MachineIdError::EmptyApplication);
}
if let Ok(existing) = keyring::get(application, ID_ACCOUNT) {
if !existing.is_empty() {
return Ok(existing);
}
}
let id = Uuid::new_v4().simple().to_string();
keyring::set(application, ID_ACCOUNT, &id)?;
Ok(id)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn requires_application() {
assert!(matches!(
get_or_create(""),
Err(MachineIdError::EmptyApplication)
));
}
#[test]
fn stable_across_calls() {
const APP: &str = "onyx-machineid-test";
let Ok(first) = get_or_create(APP) else {
return;
};
if keyring::get(APP, ID_ACCOUNT).is_err() {
let _ = keyring::delete(APP, ID_ACCOUNT);
return;
}
let second = get_or_create(APP).expect("second get_or_create");
let _ = keyring::delete(APP, ID_ACCOUNT);
assert_eq!(first, second, "identity should be stable");
}
}