ocm_types/
common.rs

1use std::fmt::Display;
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Serialize, Deserialize)]
6#[serde(rename_all = "kebab-case")]
7pub enum ShareType {
8    #[default]
9    User,
10    Group,
11    Federation,
12}
13
14/// The OCM Address identifies a user or group "at" an OCM Server.
15/// The OCM Address contains a server specific Party identifier, a host
16/// locating the OCM Server and an optional port. The OCM Address is not a
17/// URI as it does not have scheme and the identifier may contain reserved
18/// characters.
19#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Serialize, Deserialize)]
20#[serde(try_from = "String", into = "String")]
21pub struct OcmAddress {
22    address: String,
23    separator_index: usize
24}
25
26impl OcmAddress {
27    /// Returns the OCM Server specific identifier of a Sending or Receiving Party
28    pub fn get_identifier(&self) -> &str {
29        self.address.split_at(self.separator_index).0
30    }
31
32    /// Returns the address of the OCM Server where the Party identified by the OCM
33    /// Address is located. This can be used to Discover the OCM Server of the Party.
34    pub fn get_server_url(&self) -> &str {
35        self.address.split_at(self.separator_index+1).1
36    }
37}
38
39impl TryFrom<&str> for OcmAddress {
40    type Error = &'static str;
41
42    fn try_from(value: &str) -> Result<Self, Self::Error> {
43        value.to_string().try_into()
44    }
45}
46
47impl TryFrom<String> for OcmAddress {
48    type Error = &'static str;
49
50    fn try_from(value: String) -> Result<Self, Self::Error> {
51        let separator_index = value.rfind('@')
52            .ok_or("Missing '@' separator in OCM Address")?;
53        let host = value.split_at(separator_index+1).1;
54
55        if host.contains('/') {
56            Err("OCM Address may not contain a path or scheme")?
57        };
58        Ok(Self {
59            address: value,
60            separator_index
61        })
62    }
63}
64
65impl Display for OcmAddress {
66    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67        f.write_str(&self.address)?;
68        Ok(())
69    }
70}
71
72impl From<OcmAddress> for String {
73    fn from(val: OcmAddress) -> Self {
74        val.address
75    }
76}
77
78#[cfg(test)]
79mod test {
80    use super::OcmAddress;
81
82
83    #[test]
84    fn invalid_ocm_addresses() {
85        OcmAddress::try_from("user@https://example.org").expect_err("Scheme is not allowed in server address part");
86        OcmAddress::try_from("user@example.org/subfolder").expect_err("Scheme is not allowed in server address part");
87    }
88
89    #[test]
90    fn valid_ocm_addresses() {
91        let address = OcmAddress::try_from("asdf@user@example.org").expect("asdf@user@example.org should be accepted");
92        assert_eq!(("asdf@user", "example.org"), (address.get_identifier(), address.get_server_url()));
93        
94        let address = OcmAddress::try_from("🤡asdf@user@example.org:8080").expect("🤡asdf@user@example.org:8080 should be accepted");
95        assert_eq!(("🤡asdf@user", "example.org:8080"), (address.get_identifier(), address.get_server_url()));
96        
97        let address = OcmAddress::try_from("asdf@user@127.0.0.1").expect("asdf@user@127.0.0.1 should be accepted");
98        assert_eq!(("asdf@user", "127.0.0.1"), (address.get_identifier(), address.get_server_url()));
99        
100        let address = OcmAddress::try_from("asdf@user@[fe80::cc7f:2f7d:80f6:3876%en0]").expect("asdf@user@[fe80::cc7f:2f7d:80f6:3876%en0] should be accepted");
101        assert_eq!(("asdf@user", "[fe80::cc7f:2f7d:80f6:3876%en0]"), (address.get_identifier(), address.get_server_url()));
102    }
103}