use std::path::PathBuf;
use crate::{RhoResult, validate_relative_safe_path};
pub mod github;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IdentityLocator {
pub provider: String,
pub handle: String,
}
pub trait IdentityProvider {
fn provider(&self) -> &'static str;
fn validate_handle(&self, handle: &str) -> RhoResult<()>;
fn identity_id(&self, handle: &str) -> RhoResult<String> {
self.validate_handle(handle)?;
Ok(format!("rho://id/{}/{}", self.provider(), handle))
}
fn handle_from_identity_id(&self, identity_id: &str) -> RhoResult<String> {
let locator = parse_identity_id(identity_id)?;
if locator.provider != self.provider() {
return Err(format!(
"unsupported identity provider for {}: {}",
self.provider(),
identity_id
)
.into());
}
self.validate_handle(&locator.handle)?;
Ok(locator.handle)
}
}
pub fn parse_identity_id(identity_id: &str) -> RhoResult<IdentityLocator> {
let Some(rest) = identity_id.strip_prefix("rho://id/") else {
return Err(format!("identity id must start with rho://id/: {identity_id}").into());
};
let Some((provider, handle)) = rest.split_once('/') else {
return Err(format!("identity id must include provider and handle: {identity_id}").into());
};
if provider.is_empty() || handle.is_empty() || handle.contains('/') {
return Err(format!("invalid identity id: {identity_id}").into());
}
validate_relative_safe_path(provider)?;
validate_relative_safe_path(handle)?;
Ok(IdentityLocator {
provider: provider.to_string(),
handle: handle.to_string(),
})
}
pub fn identity_inbox_relative_path(identity_id: &str) -> RhoResult<PathBuf> {
let locator = parse_identity_id(identity_id)?;
Ok(PathBuf::from("id")
.join(locator.provider)
.join(locator.handle))
}