···1414gleam_stdlib = ">= 1.0.0 and < 2.0.0"
1515gleam_http = ">= 4.3.0 and < 5.0.0"
1616gleam_json = ">= 3.1.0 and < 4.0.0"
1717+gleam_regexp = ">= 1.1.1 and < 2.0.0"
17181819[dev_dependencies]
1920gleeunit = ">= 1.0.0 and < 2.0.0"
+2
manifest.toml
···1111 { name = "gleam_http", version = "4.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "82EA6A717C842456188C190AFB372665EA56CE13D8559BF3B1DD9E40F619EE0C" },
1212 { name = "gleam_httpc", version = "5.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_http", "gleam_stdlib"], otp_app = "gleam_httpc", source = "hex", outer_checksum = "C545172618D07811494E97AAA4A0FB34DA6F6D0061FDC8041C2F8E3BE2B2E48F" },
1313 { name = "gleam_json", version = "3.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "44FDAA8847BE8FC48CA7A1C089706BD54BADCC4C45B237A992EDDF9F2CDB2836" },
1414+ { name = "gleam_regexp", version = "1.1.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_regexp", source = "hex", outer_checksum = "9C215C6CA84A5B35BB934A9B61A9A306EC743153BE2B0425A0D032E477B062A9" },
1415 { name = "gleam_stdlib", version = "1.0.3", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1F543AFBA5D33DA493E6087F4E4C4F20D899411343512686C98A8ABB2963CF22" },
1516 { name = "gleeunit", version = "1.11.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "EC31ABA74256AEA531EDF8169931D775BBB384FED0A8A1BDC4DD9354E3E21826" },
1617 { name = "global_value", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "global_value", source = "hex", outer_checksum = "23F74C91A7B819C43ABCCBF49DAD5BB8799D81F2A3736BA9A534BD47F309FF4F" },
···2021gleam_http = { version = ">= 4.3.0 and < 5.0.0" }
2122gleam_httpc = { version = ">= 5.0.0 and < 6.0.0" }
2223gleam_json = { version = ">= 3.1.0 and < 4.0.0" }
2424+gleam_regexp = { version = ">= 1.1.1 and < 2.0.0" }
2325gleam_stdlib = { version = ">= 1.0.0 and < 2.0.0" }
2426gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
2527global_value = { version = ">= 1.0.0 and < 2.0.0" }
+109
src/possum/did.gleam
···11+import gleam/regexp as regex
22+import gleam/result
33+44+const pattern = "/^did:[a-z]+:[a-zA-Z0-9._:%-]*[a-zA-Z0-9._-]$/"
55+66+pub type DidError {
77+ InvalidRegexPattern(pattern: String)
88+ InvalidFormat(value: String)
99+ InvalidDidMethod(value: String)
1010+}
1111+1212+/// AT Protocol uses Decentralized Identifiers (DIDs) as persistent account
1313+/// identifiers. An example DID is did:plc:ewvi7nxzyoun6zhxrhs64oiz.
1414+///
1515+/// Documentation: [DID](https://atproto.com/specs/did)
1616+pub opaque type Did {
1717+ Did(method: Method, identifier: String)
1818+}
1919+2020+/// Return the DID method
2121+///
2222+/// ## Examples
2323+///
2424+/// ```gleam
2525+/// let method =
2626+/// Did(Plc, "k5vecqzf4d5mdvkcu3mx6l5g")
2727+/// |> possum.method
2828+///
2929+/// assert method == did.Plc
3030+/// ```
3131+pub fn method(self: Did) -> Method {
3232+ self.method
3333+}
3434+3535+/// Return the DID identifier.
3636+///
3737+/// This function does convert the value to a valid DID format, for that
3838+/// use `to_string`.
3939+///
4040+/// ## Examples
4141+///
4242+/// ```gleam
4343+/// let identifier =
4444+/// Did(Plc, "k5vecqzf4d5mdvkcu3mx6l5g")
4545+/// |> did.identifier
4646+///
4747+/// assert identifier == "k5vecqzf4d5mdvkcu3mx6l5g"
4848+/// ```
4949+pub fn indentifer(self: Did) -> String {
5050+ self.identifier
5151+}
5252+5353+/// Convert a DID to a string.
5454+///
5555+/// ## Examples
5656+///
5757+/// ```gleam
5858+/// let string =
5959+/// Did(Plc, "k5vecqzf4d5mdvkcu3mx6l5g")
6060+/// |> did.to_string
6161+///
6262+/// assert string == "did:plc:k5vecqzf4d5mdvkcu3mx6l5g"
6363+/// ```
6464+pub fn to_string(self: Did) -> String {
6565+ case self.method {
6666+ Plc -> "did:plc:" <> self.identifier
6767+ Web -> "did:web:" <> self.identifier
6868+ }
6969+}
7070+7171+/// The core DID standard and describes a framework for compliant identifier
7272+/// systems ("DID methods"), of which several exist. To ensure broad interoperation
7373+/// across the ecosystem, atproto only supports a small number of "blessed" DID methods.
7474+///
7575+/// Documentation: [Blessed DID Methods](https://atproto.com/specs/did#blessed-did-methods)
7676+pub type Method {
7777+ /// Self-authenticating DID method developed specifically for use with atproto.
7878+ /// See the DID PLC [website](https://web.plc.directory/) for more details.
7979+ Plc
8080+ /// [W3C community](https://w3c-ccg.github.io/did-method-web/) draft based
8181+ /// on HTTPS (and DNS). The identifier section is a hostname.
8282+ /// This method is supported in atproto to provide an independent
8383+ /// alternative to `did:plc`.
8484+ ///
8585+ /// The special localhost hostname is allowed, but only in testing and development
8686+ /// environments. Port numbers (with separating colon hex-encoded) are only
8787+ /// allowed for localhost, and only in testing and development.
8888+ ///
8989+ /// Documentation: [did:web](https://atproto.com/specs/did#did-web-in-at-protocol)
9090+ Web
9191+}
9292+9393+pub fn new(content: String) -> Result(Did, DidError) {
9494+ use with <- result.try(
9595+ regex.from_string(pattern)
9696+ |> result.replace_error(InvalidRegexPattern(pattern:)),
9797+ )
9898+9999+ use value <- result.try(case regex.check(with:, content:) {
100100+ True -> Ok(content)
101101+ False -> Error(InvalidFormat(content))
102102+ })
103103+104104+ case value {
105105+ "did:plc:" <> identifier -> Ok(Did(method: Plc, identifier:))
106106+ "did:web:" <> identifier -> Ok(Did(method: Web, identifier:))
107107+ value -> Error(InvalidDidMethod(value:))
108108+ }
109109+}