fedimovies/src/activitypub/identifiers.rs
2023-01-21 22:56:46 +00:00

173 lines
5 KiB
Rust

use regex::Regex;
use uuid::Uuid;
use crate::errors::ValidationError;
pub enum LocalActorCollection {
Inbox,
Outbox,
Followers,
Following,
Subscribers,
}
impl LocalActorCollection {
pub fn of(&self, actor_id: &str) -> String {
let name = match self {
Self::Inbox => "inbox",
Self::Outbox => "outbox",
Self::Followers => "followers",
Self::Following => "following",
Self::Subscribers => "subscribers",
};
format!("{}/{}", actor_id, name)
}
}
pub fn local_actor_id(instance_url: &str, username: &str) -> String {
format!("{}/users/{}", instance_url, username)
}
pub fn local_actor_inbox(instance_url: &str, username: &str) -> String {
let actor_id = local_actor_id(instance_url, username);
LocalActorCollection::Inbox.of(&actor_id)
}
pub fn local_actor_outbox(instance_url: &str, username: &str) -> String {
let actor_id = local_actor_id(instance_url, username);
LocalActorCollection::Outbox.of(&actor_id)
}
pub fn local_actor_followers(instance_url: &str, username: &str) -> String {
let actor_id = local_actor_id(instance_url, username);
LocalActorCollection::Followers.of(&actor_id)
}
pub fn local_actor_following(instance_url: &str, username: &str) -> String {
let actor_id = local_actor_id(instance_url, username);
LocalActorCollection::Following.of(&actor_id)
}
pub fn local_actor_subscribers(instance_url: &str, username: &str) -> String {
let actor_id = local_actor_id(instance_url, username);
LocalActorCollection::Subscribers.of(&actor_id)
}
pub fn local_instance_actor_id(instance_url: &str) -> String {
format!("{}/actor", instance_url)
}
pub fn local_object_id(instance_url: &str, internal_object_id: &Uuid) -> String {
format!("{}/objects/{}", instance_url, internal_object_id)
}
pub fn local_emoji_id(instance_url: &str, emoji_name: &str) -> String {
format!("{}/objects/emojis/{}", instance_url, emoji_name)
}
pub fn parse_local_actor_id(
instance_url: &str,
actor_id: &str,
) -> Result<String, ValidationError> {
let url_regexp_str = format!(
"^{}/users/(?P<username>[0-9a-z_]+)$",
instance_url.replace('.', r"\."),
);
let url_regexp = Regex::new(&url_regexp_str)
.map_err(|_| ValidationError("error"))?;
let url_caps = url_regexp.captures(actor_id)
.ok_or(ValidationError("invalid actor ID"))?;
let username = url_caps.name("username")
.ok_or(ValidationError("invalid actor ID"))?
.as_str()
.to_owned();
Ok(username)
}
pub fn parse_local_object_id(
instance_url: &str,
object_id: &str,
) -> Result<Uuid, ValidationError> {
let url_regexp_str = format!(
"^{}/objects/(?P<uuid>[0-9a-f-]+)$",
instance_url.replace('.', r"\."),
);
let url_regexp = Regex::new(&url_regexp_str)
.map_err(|_| ValidationError("error"))?;
let url_caps = url_regexp.captures(object_id)
.ok_or(ValidationError("invalid object ID"))?;
let internal_object_id: Uuid = url_caps.name("uuid")
.ok_or(ValidationError("invalid object ID"))?
.as_str().parse()
.map_err(|_| ValidationError("invalid object ID"))?;
Ok(internal_object_id)
}
#[cfg(test)]
mod tests {
use crate::utils::id::new_uuid;
use super::*;
const INSTANCE_URL: &str = "https://example.org";
#[test]
fn test_parse_local_actor_id() {
let username = parse_local_actor_id(
INSTANCE_URL,
"https://example.org/users/test",
).unwrap();
assert_eq!(username, "test".to_string());
}
#[test]
fn test_parse_local_actor_id_wrong_path() {
let error = parse_local_actor_id(
INSTANCE_URL,
"https://example.org/user/test",
).unwrap_err();
assert_eq!(error.to_string(), "invalid actor ID");
}
#[test]
fn test_parse_local_actor_id_invalid_username() {
let error = parse_local_actor_id(
INSTANCE_URL,
"https://example.org/users/tes-t",
).unwrap_err();
assert_eq!(error.to_string(), "invalid actor ID");
}
#[test]
fn test_parse_local_actor_id_invalid_instance_url() {
let error = parse_local_actor_id(
INSTANCE_URL,
"https://example.gov/users/test",
).unwrap_err();
assert_eq!(error.to_string(), "invalid actor ID");
}
#[test]
fn test_parse_local_object_id() {
let expected_uuid = new_uuid();
let object_id = format!(
"https://example.org/objects/{}",
expected_uuid,
);
let internal_object_id = parse_local_object_id(
INSTANCE_URL,
&object_id,
).unwrap();
assert_eq!(internal_object_id, expected_uuid);
}
#[test]
fn test_parse_local_object_id_invalid_uuid() {
let object_id = "https://example.org/objects/1234";
let error = parse_local_object_id(
INSTANCE_URL,
object_id,
).unwrap_err();
assert_eq!(error.to_string(), "invalid object ID");
}
}