use alloc::string::String;
use core::fmt;
use core::str::FromStr;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use super::naming::{self, NameError};
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize, PartialOrd, Ord)]
pub struct Sri(Uuid);
impl Sri {
pub const NIL: Self = Self(Uuid::nil());
#[must_use]
pub const fn from_uuid(uuid: Uuid) -> Self {
Self(uuid)
}
#[must_use]
pub fn from_parts(hi: i64, lo: i64) -> Self {
Self(Uuid::from_u64_pair(hi as u64, lo as u64))
}
#[must_use]
pub fn to_parts(&self) -> (i64, i64) {
let (hi, lo) = self.0.as_u64_pair();
(hi as i64, lo as i64)
}
#[must_use]
#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
pub fn as_parts(&self) -> (i64, i64) {
let n = self.0.as_u128();
((n >> 64) as i64, n as i64)
}
#[must_use]
pub fn from_bytes(bytes: [u8; 16]) -> Self {
Self(Uuid::from_bytes(bytes))
}
#[must_use]
pub fn to_bytes(&self) -> [u8; 16] {
*self.0.as_bytes()
}
#[must_use]
pub fn as_uuid(&self) -> Uuid {
self.0
}
#[must_use]
pub fn is_nil(&self) -> bool {
self.0.is_nil()
}
pub fn of_path(path: &str) -> Result<Self, NameError> {
naming::sri_of_path(path).map(Self)
}
pub fn of_srn(srn: &Srn) -> Result<Self, NameError> {
Self::of_path(srn.as_str())
}
pub fn child(&self, local_name: &str) -> Result<Self, NameError> {
naming::child_sri(self.0, local_name).map(Self)
}
pub fn from_target(s: &str) -> Result<Self, NameError> {
naming::resolve_target(s).map(Self)
}
}
impl From<Uuid> for Sri {
fn from(uuid: Uuid) -> Self {
Self(uuid)
}
}
impl From<Sri> for Uuid {
fn from(sri: Sri) -> Self {
sri.0
}
}
impl fmt::Display for Sri {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl FromStr for Sri {
type Err = uuid::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Uuid::from_str(s).map(Self)
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
pub struct Srn(String);
impl Srn {
pub fn new(name: impl Into<String>) -> Self {
Self(name.into())
}
pub fn parse(name: impl Into<String>) -> Result<Self, NameError> {
let name = name.into();
naming::sri_of_path(&name)?;
Ok(Self(name))
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
pub fn resolve(&self) -> Result<Sri, NameError> {
Sri::of_path(&self.0)
}
}
impl fmt::Display for Srn {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl AsRef<str> for Srn {
fn as_ref(&self) -> &str {
&self.0
}
}
impl FromStr for Srn {
type Err = NameError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::parse(s)
}
}
#[cfg(test)]
mod tests {
use super::{Sri, Srn};
use crate::cells::naming;
use alloc::string::ToString;
use alloc::vec::Vec;
const MYAPP_SRI: &str = "2dc85832-4752-5fcc-87fb-50df0d636319";
#[test]
fn sri_of_path_matches_naming_derivation() {
let sri = Sri::of_path("myapp").unwrap();
assert_eq!(sri.to_string(), MYAPP_SRI);
assert_eq!(sri.as_uuid(), naming::sri_of_path("myapp").unwrap());
}
#[test]
fn sri_display_parse_roundtrips() {
let sri = Sri::of_path("myapp/gateway").unwrap();
let parsed: Sri = sri.to_string().parse().unwrap();
assert_eq!(sri, parsed);
}
#[test]
fn sri_from_target_accepts_uuid_literal() {
let sri = Sri::of_path("myapp/gateway").unwrap();
assert_eq!(Sri::from_target(&sri.to_string()).unwrap(), sri);
}
#[test]
fn sri_from_target_accepts_srn_path() {
let sri = Sri::of_path("myapp/gateway").unwrap();
assert_eq!(Sri::from_target("myapp/gateway").unwrap(), sri);
}
#[test]
fn sri_child_equals_full_path() {
let gw = Sri::of_path("myapp/gateway").unwrap();
assert_eq!(
gw.child("worker-1").unwrap(),
Sri::of_path("myapp/gateway/worker-1").unwrap(),
);
}
#[test]
fn sri_postcard_roundtrips_as_compact_binary() {
let sri = Sri::of_path("myapp/gateway").unwrap();
let bytes: Vec<u8> = postcard::to_allocvec(&sri).unwrap();
assert!(
bytes.len() <= 17,
"sri postcard encoding = {} bytes",
bytes.len()
);
let back: Sri = postcard::from_bytes(&bytes).unwrap();
assert_eq!(sri, back);
}
#[test]
fn srn_parse_rejects_invalid_paths() {
assert!(Srn::parse("App").is_err());
assert!(Srn::parse("a//b").is_err());
assert!(Srn::parse("").is_err());
}
#[test]
fn srn_resolves_to_same_sri_as_path() {
let srn = Srn::parse("myapp/gateway").unwrap();
assert_eq!(
srn.resolve().unwrap(),
Sri::of_path("myapp/gateway").unwrap()
);
}
}