use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct RepoIdentity {
pub repo_uuid: Uuid,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub namespace: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
}
impl RepoIdentity {
pub fn minted(namespace: &str, name: &str) -> Self {
RepoIdentity {
repo_uuid: Uuid::new_v4(),
namespace: Some(namespace.to_string()),
name: Some(name.to_string()),
}
}
pub fn from_supplied(supplied_repo_uuid: Option<Uuid>, name: &str) -> Option<Self> {
supplied_repo_uuid
.or_else(|| Uuid::parse_str(name).ok())
.map(|repo_uuid| RepoIdentity {
repo_uuid,
namespace: None,
name: None,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
fn server_identity(namespace: &str, name: &str) -> RepoIdentity {
RepoIdentity::minted(namespace, name)
}
#[test]
fn oxen_server_assigns_a_repo_uuid_and_records_both_names() {
let identity = server_identity("ox", "cats");
assert_eq!(identity.namespace.as_deref(), Some("ox"));
assert_eq!(identity.name.as_deref(), Some("cats"));
}
#[test]
fn auth_provider_reads_the_repo_uuid_out_of_the_name_position() {
let repo_uuid = Uuid::new_v4();
let identity = RepoIdentity::from_supplied(None, &repo_uuid.to_string())
.expect("the name position carries the identity");
assert_eq!(identity.repo_uuid, repo_uuid);
assert_eq!(identity.namespace, None);
assert_eq!(identity.name, None);
}
#[test]
fn auth_provider_prefers_a_supplied_uuid_over_the_name_position() {
let supplied = Uuid::new_v4();
let in_name_position = Uuid::new_v4();
let identity = RepoIdentity::from_supplied(Some(supplied), &in_name_position.to_string())
.expect("a supplied UUID is an identity");
assert_eq!(identity.repo_uuid, supplied);
}
#[test]
fn auth_provider_records_nothing_rather_than_assigning() {
assert_eq!(RepoIdentity::from_supplied(None, "cats"), None);
}
#[test]
fn uuids_serialize_canonically_whatever_form_arrived() {
let identity = RepoIdentity::from_supplied(None, "5abd211ee25c494bba0f44ad542443d7")
.expect("an unhyphenated UUID is still a UUID");
let toml = toml::to_string(&identity).expect("serialize");
assert!(
toml.contains("5abd211e-e25c-494b-ba0f-44ad542443d7"),
"expected canonical hyphenated form in:\n{toml}"
);
}
#[test]
fn absent_name_hints_are_not_serialized() {
let repo_uuid = Uuid::new_v4();
let toml = toml::to_string(&RepoIdentity {
repo_uuid,
namespace: None,
name: None,
})
.expect("serialize");
assert_eq!(toml, format!("repo_uuid = \"{repo_uuid}\"\n"));
}
#[test]
fn an_identity_without_a_repo_uuid_is_rejected() {
assert!(toml::from_str::<RepoIdentity>("namespace = \"ox\"\n").is_err());
}
}