Constellation, Spacedust, Slingshot, UFOs: atproto crates and services for microcosm
0

Configure Feed

Select the types of activity you want to include in your feed.

at main 4.0 kB View raw
1use fluent_uri::Uri; 2 3pub mod at_uri; 4pub mod did; 5#[cfg(feature = "json")] 6pub mod record; 7 8#[cfg(feature = "json")] 9pub use record::collect_links; 10 11#[derive(Debug, Clone, Ord, Eq, PartialOrd, PartialEq)] 12pub enum Link { 13 AtUri(String), 14 Uri(String), 15 Did(String), 16} 17 18impl Link { 19 pub fn into_string(self) -> String { 20 match self { 21 Link::AtUri(s) => s, 22 Link::Uri(s) => s, 23 Link::Did(s) => s, 24 } 25 } 26 pub fn as_str(&self) -> &str { 27 match self { 28 Link::AtUri(s) => s, 29 Link::Uri(s) => s, 30 Link::Did(s) => s, 31 } 32 } 33 pub fn name(&self) -> &'static str { 34 match self { 35 Link::AtUri(_) => "at-uri", 36 Link::Uri(_) => "uri", 37 Link::Did(_) => "did", 38 } 39 } 40 pub fn at_uri_collection(&self) -> Option<String> { 41 if let Link::AtUri(at_uri) = self { 42 at_uri::at_uri_collection(at_uri) 43 } else { 44 None 45 } 46 } 47 pub fn did(&self) -> Option<String> { 48 let did = match self { 49 Link::AtUri(s) => { 50 let rest = s.strip_prefix("at://")?; // todo: this might be safe to unwrap? 51 if let Some((did, _)) = rest.split_once("/") { 52 did 53 } else { 54 rest 55 } 56 } 57 Link::Uri(_) => return None, 58 Link::Did(did) => did, 59 }; 60 Some(did.to_string()) 61 } 62} 63 64#[derive(Debug, PartialEq)] 65pub struct CollectedLink { 66 pub path: String, 67 pub target: Link, 68} 69 70// normalizing is a bit opinionated but eh 71pub fn parse_uri(s: &str) -> Option<String> { 72 Uri::parse(s).map(|u| u.normalize().into_string()).ok() 73} 74 75pub fn parse_any_link(s: &str) -> Option<Link> { 76 at_uri::parse_at_uri(s).map(Link::AtUri).or_else(|| { 77 did::parse_did(s) 78 .map(Link::Did) 79 .or_else(|| parse_uri(s).map(Link::Uri)) 80 }) 81} 82 83#[cfg(test)] 84mod tests { 85 use super::*; 86 87 #[test] 88 fn test_uri_parse() { 89 let s = "https://example.com"; 90 let uri = parse_uri(s).unwrap(); 91 assert_eq!(uri.as_str(), s); 92 } 93 94 #[test] 95 fn test_uri_normalizes() { 96 let s = "HTTPS://example.com/../"; 97 let uri = parse_uri(s).unwrap(); 98 assert_eq!(uri.as_str(), "https://example.com/"); 99 } 100 101 #[test] 102 fn test_uri_invalid() { 103 assert!(parse_uri("https:\\bad-example.com").is_none()); 104 } 105 106 #[test] 107 fn test_any_parse() { 108 assert_eq!( 109 parse_any_link("https://example.com"), 110 Some(Link::Uri("https://example.com".into())) 111 ); 112 113 assert_eq!( 114 parse_any_link( 115 "at://did:plc:44ybard66vv44zksje25o7dz/app.bsky.feed.post/3jwdwj2ctlk26" 116 ), 117 Some(Link::AtUri( 118 "at://did:plc:44ybard66vv44zksje25o7dz/app.bsky.feed.post/3jwdwj2ctlk26".into() 119 )), 120 ); 121 122 assert_eq!( 123 parse_any_link("did:plc:44ybard66vv44zksje25o7dz"), 124 Some(Link::Did("did:plc:44ybard66vv44zksje25o7dz".into())) 125 ); 126 127 assert_eq!( 128 parse_any_link("tel:5551234567"), 129 Some(Link::Uri("tel:5551234567".into())), 130 ); 131 132 assert_eq!(parse_any_link("3jwdwj2ctlk26"), None); 133 assert_eq!(parse_any_link("self"), None); 134 assert_eq!(parse_any_link(""), None); 135 } 136 137 #[test] 138 fn test_at_uri_collection() { 139 assert_eq!( 140 parse_any_link("https://example.com") 141 .unwrap() 142 .at_uri_collection(), 143 None 144 ); 145 assert_eq!( 146 parse_any_link("did:web:bad-example.com") 147 .unwrap() 148 .at_uri_collection(), 149 None 150 ); 151 assert_eq!( 152 parse_any_link("at://did:web:bad-example.com/my.collection/3jwdwj2ctlk26") 153 .unwrap() 154 .at_uri_collection(), 155 Some("my.collection".into()) 156 ); 157 } 158}