···33////
44//// AT URIs are not content-addressed, so the contents of the record they
55//// refer to may also change over time.
66+////
77+//// Documentation: [AT URI Scheme](https://atproto.com/specs/at-uri-scheme)
6879import gleam/result
810import gleam/string
···1214pub type ParseError {
1315 /// Only "at://" is supported
1416 InvalidScheme(value: String)
1717+ /// Scheme is missing from the URI
1818+ MissingScheme
1519 /// Authority not a valid Handle
1620 InvalidHandleAuthority(reason: handle.ParseError)
1721 /// Authority not a valid DID
···4751 )
4852}
49535050-// Return the authority part of the Uri
5454+/// Return the authority part of the Uri
5555+///
5656+/// ## Examples
5757+///
5858+/// ```gleam
5959+/// import possum/at_uri
6060+///
6161+/// let string = "at://did:plc:vwzwgnygau7ed7b7wt5ux7y2/app.bsky.feed.post/3k5nobkf2w72g"
6262+/// let assert Ok(at_uri) = at_uri.parse(string)
6363+///
6464+/// assert at_uri.authority(at_uri) == "did:plc:vwzwgnygau7ed7b7wt5ux7y2"
6565+/// ```
5166pub fn authority(self: AtUri) -> Authority {
5267 self.authority
5368}
54695555-// Return the collecion part of the Uri
7070+/// Return the collecion part of the Uri
7171+///
7272+/// ## Examples
7373+///
7474+/// ```gleam
7575+/// import possum/at_uri
7676+///
7777+/// let string = "at://did:plc:vwzwgnygau7ed7b7wt5ux7y2/app.bsky.feed.post/3k5nobkf2w72g"
7878+/// let assert Ok(at_uri) = at_uri.parse(string)
7979+///
8080+/// assert at_uri.collection(at_uri) == "app.bsky.feed.post"
8181+/// ```
5682pub fn collection(self: AtUri) -> String {
5783 self.collection
5884}
59856060-// Return the rkey part of the Uri
8686+/// Return the rkey part of the Uri
8787+///
8888+/// ## Examples
8989+///
9090+/// ```gleam
9191+/// import possum/at_uri
9292+///
9393+/// let string = "at://did:plc:vwzwgnygau7ed7b7wt5ux7y2/app.bsky.feed.post/3k5nobkf2w72g"
9494+/// let assert Ok(at_uri) = at_uri.parse(string)
9595+///
9696+/// assert at_uri.rkey(at_uri) == "3k5nobkf2w72g"
9797+/// ```
6198pub fn rkey(self: AtUri) -> String {
6299 self.rkey
63100}
···74111///
75112/// let string = "at://did:plc:vwzwgnygau7ed7b7wt5ux7y2/app.bsky.feed.post/3k5nobkf2w72g"
76113/// let assert Ok(at_uri) = at_uri.parse(string)
114114+/// ```
77115pub fn parse(content: String) -> Result(AtUri, ParseError) {
78116 use value <- result.try(case content {
79117 "at://" <> rest -> Ok(rest)
···82120 _ ->
83121 case string.split_once(content, on: "://") {
84122 Ok(scheme) -> Error(InvalidScheme(value: scheme.0))
8585- Error(_) -> Error(InvalidScheme(value: ""))
123123+ Error(_) -> Error(MissingScheme)
86124 }
87125 })
88126···124162 Error(reason) -> Error(InvalidDidAuthority(reason:))
125163 }
126164127127- _handle ->
165165+ string ->
128166 case handle.parse(string) {
129167 Ok(value) -> Ok(Handle(value:))
130168 Error(reason) -> Error(InvalidHandleAuthority(reason:))
···143181/// "at://did:plc:vwzwgnygau7ed7b7wt5ux7y2/app.bsky.feed.post/3k5nobkf2w72g"
144182///
145183/// let assert Ok(at_uri) = at_uri.parse(string)
146146-/// assert at.to_string(at_uri) == string
184184+/// assert at_uri.to_string(at_uri) == string
147185/// ```
148186pub fn to_string(self: AtUri) -> String {
149187 let host = case self.authority {
+23
src/possum/handle.gleam
···22//// but they can be opaque and unfriendly for human use. Handles are mutable
33//// and human-friendly account usernames, in the form of a DNS hostname.
44//// For example, "user.example.com".
55+////
66+//// Documentation: [Handle](https://atproto.com/specs/handle)
5768import gleam/list
79import gleam/regexp
···3941 Handle(value: String)
4042}
41434444+/// Convert a handle into a String
4545+///
4646+/// ## Examples
4747+///
4848+/// ```gleam
4949+/// import possum/handle
5050+///
5151+/// let string = "gleam.run"
5252+/// let assert Ok(handle) = handle.parse(string)
5353+///
5454+/// assert handle.to_string(handle) == "gleam.run"
5555+/// ```
4256pub fn to_string(handle: Handle) -> String {
4357 handle.value
4458}
45594660/// Parse a string into a valid `Handle` type
4761/// If the value is not a valid string then an error is returned.
6262+///
6363+/// ## Examples
6464+///
6565+/// ```gleam
6666+/// import possum/handle
6767+///
6868+/// let string = "gleam.run"
6969+/// let assert Ok(handle) = handle.parse(string)
7070+/// ```
4871pub fn parse(content: String) -> Result(Handle, ParseError) {
4972 use with <- result.try(
5073 regexp.from_string(pattern)
+15
src/possum/nsid.gleam
···44//// The basic structure and semantics of an NSID are a fully-qualified hostname
55//// in reverse domain-name order, followed by an additional name segment.
66//// The hostname part is the **domain authority**, and the final segment is the **name**.
77+////
88+//// Documentation: [Namespaced Identifiers](https://atproto.com/specs/nsid)
79810import gleam/regexp
911import gleam/result
10121313+/// Available on: https://atproto.com/specs/nsid#nsid-syntax
1114const pattern = "^[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(\\.[a-zA-Z]([a-zA-Z0-9]{0,62})?)$"
12151316pub type ParseError {
···2225 Nsid(value: String)
2326}
24272828+/// Convert a NSID into a String
2929+///
3030+/// ## Examples
3131+///
3232+/// ```gleam
3333+/// import possum/nsid
3434+///
3535+/// let string = "com.atproto.sync.getRecord"
3636+///
3737+/// let assert Ok(nsid) = nsid.parse(string)
3838+/// assert nsid.to_string(nsid) == string
3939+/// ```
2540pub fn to_string(nsid: Nsid) -> String {
2641 nsid.value
2742}