(*─────────────────────────────────────────────────────────────────────────────┐
│ SPDX-FileCopyrightText: 2025–2026 toastal <https://toast.al/contact/>        │
│ SPDX-License-Identifier: LGPL-2.1-or-later WITH OCaml-LGPL-linking-exception │
└─────────────────────────────────────────────────────────────────────────────*)
(*
	ocaml-uri (RFC 3986) was once used here, but VCS programs & nix-prefetch-*
	scripts accept raw strings for URLs — including SCP-style syntax
	(git@git.example.tld:user/repo) & local paths — which are not valid URIs,
	but are valid for our tools. Following RFC 3986 percent-encoding (meaning
	`@` → `%40`), our tool-valid URI-likes were transformed to valid URIs which
	tooling doesn’t understand. As such, `Locator.t` is just an unboxed wrapper
	over a raw resource-address string (URI, SCP-style, local path, etc.) with
	only structural validation — no RFC conformance.
*)
type t =
	Locator of UTF8.t
[@@unboxed]
[@@deriving eq, ord, show, qcheck]

type error = [
	| `Empty_locator
]
[@@deriving eq, show]

let pp_error ppf = function
	| `Empty_locator ->
		Fmt.pf ppf "Invalid locator: empty string"

let parse = function
	| "" -> Error `Empty_locator
	| str -> Ok (Locator str)

(* WARNING: this is ‘unsafe’, better to use `parse` when dealing with user data *)
let [@inline]make u = Locator u
let [@inline]take (Locator u) = u

let is_valid u = Result.is_ok (parse u)

let jsont : t Jsont.t =
	let dec s =
		match parse s with
		| Ok loc -> loc
		| Error invalid ->
			Jsont.Error.msgf Jsont.Meta.none "%a" pp_error invalid
	in
	Jsont.string
	|> Jsont.map ~kind: "Locator" ~dec ~enc: take

let gen : t QCheck.Gen.t = QCheck.Gen.map make UTF8.gen
