use std::collections::HashMap;
use serde::{Deserialize, Serialize};
pub use uuid;
#[doc(hidden)]
pub use uuid::Uuid;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AppId {
plain_id: String,
namespaced_id: String,
uuid: uuid::Uuid,
}
impl AppId {
pub fn new(namespaced_id: &str) -> Result<Self, AppIdError> {
validate_namespaced_id(namespaced_id)?;
let uuid = app_id_to_uuid(namespaced_id);
Ok(Self {
plain_id: namespaced_id.split('.').next_back().unwrap().to_string(),
namespaced_id: namespaced_id.to_string(),
uuid,
})
}
pub fn with_plain_id(mut self, value: &str) -> Self {
self.plain_id = value.to_string();
self
}
pub fn with_uuid(mut self, value: Uuid) -> Self {
self.uuid = value;
self
}
pub fn plain_id(&self) -> &str {
&self.plain_id
}
pub fn namespaced_id(&self) -> &str {
&self.namespaced_id
}
pub fn uuid(&self) -> Uuid {
self.uuid
}
}
pub fn validate_namespaced_id(value: &str) -> Result<(), AppIdError> {
if value.len() > 100 {
return Err(AppIdError::Length);
}
let segments = value.split('.').collect::<Vec<&str>>();
if segments.len() < 2 {
return Err(AppIdError::SegmentCount);
}
for segment in segments {
if segment.len() < 2 {
return Err(AppIdError::SegmentLength);
}
if !segment
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
{
return Err(AppIdError::Character);
}
if !segment.chars().next().unwrap().is_ascii_alphabetic() {
return Err(AppIdError::FirstCharacter);
}
}
Ok(())
}
pub fn normalize_namespaced_id(value: &str) -> String {
value.replace("-", "_").to_ascii_lowercase()
}
const NAMESPACE: Uuid = uuid::uuid!("0192391a-2817-7e1c-988d-5aef70264a82");
pub fn app_id_to_uuid(value: &str) -> Uuid {
Uuid::new_v5(&NAMESPACE, normalize_namespaced_id(value).as_bytes())
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[non_exhaustive]
pub struct AppMetadata {
pub display_name: String,
pub display_version: String,
pub locale_display_name: HashMap<String, String>,
}
impl AppMetadata {
pub fn get_display_name(&self, lang_tag: &str) -> &str {
self.locale_display_name
.get(lang_tag)
.map(|v| v.as_str())
.unwrap_or_else(|| self.display_name.as_str())
}
}
#[derive(Debug, thiserror::Error)]
pub enum AppIdError {
#[error("character")]
Character,
#[error(" first character")]
FirstCharacter,
#[error("segment count")]
SegmentCount,
#[error("segment length")]
SegmentLength,
#[error("length")]
Length,
}