Current section

Files

Jump to
possum src possum nsid.gleam
Raw

src/possum/nsid.gleam

//// Namespaced Identifiers (NSIDs) are used to reference Lexicon schemas for
//// records, XRPC endpoints, and more. For example, `com.atproto.sync.getRecord`.
////
//// The basic structure and semantics of an NSID are a fully-qualified hostname
//// in reverse domain-name order, followed by an additional name segment.
//// The hostname part is the **domain authority**, and the final segment is the **name**.
////
//// Documentation: [Namespaced Identifiers](https://atproto.com/specs/nsid)
import gleam/regexp
import gleam/result
/// Available on: https://atproto.com/specs/nsid#nsid-syntax
const 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})?)$"
pub type ParseError {
/// Regex failed to compile
InvalidRegexPattern(pattern: String)
/// String does not have a valid NSID format
InvalidSyntax(value: String)
}
/// A specification for global semantic IDs.
pub opaque type Nsid {
Nsid(value: String)
}
/// Convert a NSID into a String
///
/// ## Examples
///
/// ```gleam
/// import possum/nsid
///
/// let string = "com.atproto.sync.getRecord"
///
/// let assert Ok(nsid) = nsid.parse(string)
/// assert nsid.to_string(nsid) == string
/// ```
pub fn to_string(nsid: Nsid) -> String {
nsid.value
}
/// Parse a string into a valid `Nsid` type
/// If the value is not a valid string then an error is returned.
///
/// ## Examples
///
/// ```gleam
/// import possum/nsid
///
/// let string = "com.atproto.sync.getRecord."
/// let assert Ok(nsid) = nsid.parse(string)
/// ```
pub fn parse(content: String) -> Result(Nsid, ParseError) {
use with <- result.try(
regexp.from_string(pattern)
|> result.replace_error(InvalidRegexPattern(pattern:)),
)
use value <- result.map(case regexp.check(with:, content:) {
True -> Ok(content)
False -> Error(InvalidSyntax(content))
})
Nsid(value:)
}