use base64::Engine; #[derive(Clone, Copy)] pub enum UriClass { Actor, Object, Activity, } impl AsRef for UriClass { fn as_ref(&self) -> &str { match self { Self::Actor => "actors", Self::Object => "objects", Self::Activity => "activities", } } } /// unpack uri in id if valid, otherwise compose full uri with "{base}/{entity}/{id}" pub fn uri(base: &str, entity: UriClass, id: &str) -> String { if id.starts_with("https://") || id.starts_with("http://") { return id.to_string(); } if id.starts_with('+') { // ready-to-use base64-encoded id if let Some(expanded) = expand(id) { return expanded; } } format!("{}/{}/{}", base, entity.as_ref(), id) } /// decompose local id constructed by uri() fn pub fn decompose(full_id: &str) -> String { full_id // https://example.org/actors/test/followers/page?offset=42 .replace("https://", "") .replace("http://", "") .split('/') // ['example.org', 'actors', 'test', 'followers', 'page?offset=42' ] .nth(2) // 'test' .unwrap_or("") .to_string() } pub fn expand(uri: &str) -> Option { if let Ok(bytes) = base64::prelude::BASE64_URL_SAFE_NO_PAD.decode(uri.replacen('+', "", 1)) { if let Ok(uri) = std::str::from_utf8(&bytes) { return Some(uri.to_string()); } } None } /// encode with base64 remote url and prefix it with + pub fn compact(uri: &str) -> String { let encoded = base64::prelude::BASE64_URL_SAFE_NO_PAD.encode(uri.as_bytes()); format!("+{encoded}") }