an atproto pds written in F# (.NET 9) 馃
pds
fsharp
giraffe
dotnet
atproto
1namespace PDSharp.Core
2
3open System
4open System.Text.RegularExpressions
5
6/// AT-URI parsing and validation
7module AtUri =
8 /// Represents an AT Protocol URI: at://did/collection/rkey
9 type AtUri = { Did : string; Collection : string; Rkey : string }
10
11 let private didPattern = @"^did:[a-z]+:[a-zA-Z0-9._:%-]+$"
12 let private nsidPattern = @"^[a-z][a-z0-9]*(\.[a-z][a-z0-9]*)+$"
13 let private rkeyPattern = @"^[a-zA-Z0-9._~-]+$"
14
15 /// Parse an AT-URI string into components
16 let parse (uri : string) : Result<AtUri, string> =
17 if not (uri.StartsWith("at://")) then
18 Error "AT-URI must start with at://"
19 else
20 let path = uri.Substring(5)
21 let parts = path.Split('/')
22
23 if parts.Length < 3 then
24 Error "AT-URI must have format at://did/collection/rkey"
25 else
26 let did = parts.[0]
27 let collection = parts.[1]
28 let rkey = parts.[2]
29
30 if not (Regex.IsMatch(did, didPattern)) then
31 Error $"Invalid DID format: {did}"
32 elif not (Regex.IsMatch(collection, nsidPattern)) then
33 Error $"Invalid collection NSID: {collection}"
34 elif not (Regex.IsMatch(rkey, rkeyPattern)) then
35 Error $"Invalid rkey format: {rkey}"
36 else
37 Ok { Did = did; Collection = collection; Rkey = rkey }
38
39 /// Convert AtUri back to string
40 let toString (uri : AtUri) : string =
41 $"at://{uri.Did}/{uri.Collection}/{uri.Rkey}"