this repo has no description
1use trust_dns_resolver::Resolver;
2use trust_dns_resolver::config::{ResolverConfig, ResolverOpts};
3
4#[derive(Debug)]
5pub struct DidDoc {
6 pub did: String,
7}
8
9fn get_txt_did(handle: &String) -> Result<String, ()> {
10 // create a txt resolver
11 let resolver = match Resolver::new(ResolverConfig::default(), ResolverOpts::default()) {
12 Ok(val) => val,
13 Err(_) => return Err(()),
14 };
15
16 // resolve _atproto.handle to a TXT record
17 let txt_res = match resolver.txt_lookup("_atproto.".to_owned() + &handle) {
18 Ok(val) => val,
19 Err(_) => return Err(()), // collect all entries and convert to strings
20 }
21 .into_iter()
22 .map(|x| x.to_string())
23 .collect::<Vec<_>>();
24
25 // filter entries which do not start with `did=`
26 let did_res = txt_res
27 .clone()
28 .extract_if(.., |x| x.starts_with("did="))
29 .collect::<Vec<_>>();
30 // only 1 did= can exist
31 // https://atproto.com/specs/handle#:~:text=If%20multiple%20valid%20records%20with%20different%20DIDs%20are%20present,%20resolution%20should%20fail.
32 if did_res.len() != 1 {
33 return Err(());
34 }
35 let did = did_res[0].clone();
36
37 return Ok(did.clone());
38}
39
40fn get_http_did(handle: &String) -> Result<String, ()> {
41 let res =
42 match reqwest::blocking::get("https://".to_owned() + handle + "/.well-known/atproto-did") {
43 Ok(val) => val,
44 Err(_) => return Err(()),
45 };
46
47 // as per spec, non 2xx code means failure
48 if !res.status().is_success() {
49 return Err(());
50 }
51
52 let did_unparsed = match res.text() {
53 Ok(val) => val,
54 Err(_) => return Err(()),
55 };
56
57 let did = did_unparsed.trim();
58
59 if !did.starts_with("did:") {
60 return Err(());
61 };
62 return Ok(String::from(did));
63}
64
65fn parse_doc(did: String, text: String) -> Result<DidDoc, ()> {
66 return Ok(DidDoc { did: String::new() });
67}
68
69fn get_plc_doc(plc: &str) -> Result<DidDoc, ()> {
70 let res = match reqwest::blocking::get("https://plc.directory/did:plc".to_owned() + plc) {
71 Ok(val) => val,
72 Err(_) => return Err(()),
73 };
74
75 if !res.status().is_success() {
76 return Err(());
77 }
78
79 return parse_doc(
80 "did:plc:".to_owned() + plc,
81 match res.text() {
82 Ok(val) => val,
83 Err(_) => return Err(()),
84 },
85 );
86}
87
88fn get_web_doc(web: &str) -> Result<DidDoc, ()> {
89 let res = match reqwest::blocking::get("https://".to_owned() + web + "/.well-known/did.json") {
90 Ok(val) => val,
91 Err(_) => return Err(()),
92 };
93
94 if !res.status().is_success() {
95 return Err(());
96 }
97
98 return parse_doc(
99 "did:web:".to_owned() + web,
100 match res.text() {
101 Ok(val) => val,
102 Err(_) => return Err(()),
103 },
104 );
105}
106
107pub fn get_did(handle: String) -> Result<DidDoc, ()> {
108 let did = if let Ok(did) = get_txt_did(&handle) {
109 did
110 } else {
111 if let Ok(did) = get_http_did(&handle) {
112 did
113 } else {
114 return Err(());
115 }
116 };
117
118 let did_doc = if did.starts_with("did:plc:") {
119 get_plc_doc(&did[8..])
120 } else if did.starts_with("did:web:") {
121 get_web_doc(&did[8..])
122 } else {
123 Err(())
124 };
125
126 return did_doc;
127}