(*---------------------------------------------------------------------------
   Copyright (c) 2008 The uuidm programmers. All rights reserved.
   SPDX-License-Identifier: ISC
  ---------------------------------------------------------------------------*)

(** Universally unique identifiers (UUIDs).

    [Uuidm] implements 128 bits universally unique identifiers version
    3, 5 (name based with MD5, SHA-1 hashing), 4 (random based), 7
    (random and timestamp based) and 8 (custom) according to
    {{:https://www.rfc-editor.org/rfc/rfc9562}RFC 9562}.

    See the {{!page-index.quick}quick start}. *)

(** {1:bits Bits} *)

type bits4 = int
(** The type for 4 bits stored in the 4 lower bits of an [int] value.
    The higher bits are either set to zero or ignored on use. *)

type bits12 = int
(** The type for 12 bits stored in the 12 lower bits of an [int] value.
    The higher bits are either set to zero or ignored on use. *)

type bits62 = int64
(** The type for 62 bits stored in the 62 lower bits of an [int64] value.
    The higher bits are either set to zero or ignored on use. *)

(** {1:uuids UUIDs} *)

type t
(** The type for UUIDs. *)

val v3 : t -> string -> t
(** [v3 ns n] is a
    {{:https://www.rfc-editor.org/rfc/rfc9562#name-uuid-version-3}V3 UUID}
    (name based with MD5 hashing) named by [n] and namespaced by [ns]. *)

val v4 : bytes -> t
(** [v4 b] is a {{:https://www.rfc-editor.org/rfc/rfc9562#name-uuid-version-4}
    V4 UUID} (random based) that uses the first 16 bytes of
    [b] for randomness. See also {!v4_gen}.

    {b Warning.} The randomness is seen literally in the result. *)

val v5 : t -> string -> t
(** [v5 ns n] is a
    {{:https://www.rfc-editor.org/rfc/rfc9562#name-uuid-version-5}V5 UUID}
    (name based with SHA-1 hashing) named by [n] and
    namespaced by [ns]. See {{!page-index.name_based}this example}. *)

val v7 : time_ms:int64 -> rand_a:bits12 -> rand_b:bits62 -> t
(** [v7 ~time_ms ~rand_a ~rand_b] is a
    {{:https://www.rfc-editor.org/rfc/rfc9562#name-uuid-version-7}V7 UUID}
    (time and random based) using the 64-bit millisecond POSIX timestamp
    [t_ms] and random bits [rand_a] and [rand_b]. See also {!v7_ns},
    {!v7_non_monotonic_gen} and {!v7_monotonic_gen}.

    {b Warning.} The timestamp and the randomness are seen literally
    in the result. *)

val v7_ns : time_ns:int64 -> rand_b:bits62 -> t
(** [v7_ns ~time_ns ~rand_b] is a
    {{:https://www.rfc-editor.org/rfc/rfc9562#name-uuid-version-7}V7
    UUID} (time and random based) using the {e unsigned} 64-bit
    nanosecond POSIX timestamp [time_ns] and random bits [rand_b]. The
    [rand_a] field is used with the timestamp's submillisecond precision
    with about 244 nanoseconds resolution. See also {!v7}.

    {b Warning.} The timestamp and the randomness are seen literally in
    the result. *)

val v8 : string -> t
(** [v8 s] is a {{:https://www.rfc-editor.org/rfc/rfc9562#name-uuid-version-8}
    V8 UUID} (custom) that uses the 16 bytes of [s] but overwrites the
    {!version} and {!variant} bytes to make it a propert V8 UUID. Raises
    [Invalid_argument] if the length of [s] is not [16]. *)

(** {1:gen Generators}

    {b Warning.} If you use the generators take into account the following
    points:

    {ul
    {- Sequences of UUIDs are generated with {!Random}. This is
       suitably random but {e predictable} by an observer. Use the
       base constuctors with random bytes generated by a
       cryptographically secure pseudorandom number generator (CSPRNG) if that
       is an issue.}
    {- Sequences of UUIDs generated from a given {!Random.State.t}
       value are not guaranteed to be stable across OCaml or Uuidm versions.
       Use the base constructors with your own
       pseudorandom number generator if that is an issue.}
    {- Sequences of UUIDs generated using a {!posix_ms_clock} assume
       the clock is monotonic in order to generate monotonic UUIDs.
       If you derive it from {!Unix.gettimeofday} this may not be the case.}} *)

type posix_ms_clock = unit -> int64
(** The type for millisecond precision POSIX time clocks.  *)

val v4_gen : Random.State.t -> (unit -> t)
(** [v4_gen state] is a function generating {!v4} UUIDs using
    random [state]. See {{!page-index.random_based}this example}. *)

val v7_non_monotonic_gen :
  now_ms:posix_ms_clock -> Random.State.t -> (unit -> t)
(** [v7_non_monotonic_gen ~now_ms state] is a function generating
    {!v7} UUIDs using [now_ms] for the timestamp [time_ms] and random [state]
    for [rand_a] and [rand_b]. UUIDs generated in the same millisecond
    may not be be monotonic. Use {!v7_monotonic_gen} for that. *)

val v7_monotonic_gen :
  now_ms:posix_ms_clock -> Random.State.t -> (unit -> t option)
(** [v7_monotonic_gen ~posix_now_ms state] is a function that
    generates monotonic {!v7} UUIDs using [now_ms] for the timestamp
    [time_ms], [rand_a] as a counter if the clock did not move between
    two UUID generations and [random] state for [rand_b]. This allows
    to generate up to 4096 monotonic UUIDs per millisecond. [None] is
    returned if the counter rolls over before the millisecond
    increments. See {{!page-index.time_based}this example}.*)

(** {1:constants Constants} *)

val nil : t
(** [nil] is the
    {{:https://www.rfc-editor.org/rfc/rfc9562#name-nil-uuid}nil} UUID. *)

val max : t
(** [max] is the {{:https://www.rfc-editor.org/rfc/rfc9562#name-max-uuid}max}
    UUID. *)

val ns_dns : t
(** [ns_dns] is the DNS namespace UUID. *)

val ns_url : t
(** [ns_url] is the URL namespace UUID. *)

val ns_oid : t
(** [ns_oid] is the ISO OID namespace UUID. *)

val ns_X500 : t
(** [ns_dn] is the X.500 DN namespace UUID. *)

(** {1:properties Properties} *)

val variant : t -> bits4
(** [variant u] is the
    {{:https://www.rfc-editor.org/rfc/rfc9562#name-variant-field}variant field}
    of [u], including the "don't-care" values. *)

val version : t -> bits4
(** [version u] is the
    {{:https://www.rfc-editor.org/rfc/rfc9562#name-version-field}version field}
    of [u]. *)

val time_ms : t -> int64 option
(** [time_ms u] is the
    {{:https://www.rfc-editor.org/rfc/rfc9562#name-uuid-version-7}
    [unit_ts_ms]} millisecond POSIX timestamp of [u] as a 64-bit
    integer. This is [None] if [u] is not a V7 UUID. *)

(** {1:preds Predicates and comparisons} *)

val equal : t -> t -> bool
(** [equal u u'] is [true] iff [u] and [u'] are equal. *)

val compare : t -> t -> int
(** [compare] is the binary order on UUIDs. *)

(** {1:fmt_binary Standard binary format}

    This is the binary format mandated by
    {{:https://www.rfc-editor.org/rfc/rfc9562#name-uuid-format}RFC 9562}. *)

val of_binary_string : ?pos:int -> string -> t option
(** [of_binary_string pos s] is the UUID represented by the 16 bytes starting
    at [pos] (defaults to [0]) in [s]. No particular checks are
    performed on the bytes. The result is [None] if the string is not
    long enough. *)

val to_binary_string : t -> string
(** [to_binary_string u] is [u] as a 16 bytes long string. *)

(** {1:fmt_binary_mixed Mixed-endian binary format}

    This is the binary format in which the three first fields of UUIDs
    (which are oblivious to this module) are read and written in
    little-endian. This corresponds to how UEFI or Microsoft formats
    UUIDs. *)

val of_mixed_endian_binary_string : ?pos:int -> string -> t option
(** [of_mixed_endian_binary_string] is like {!of_bytes} but decodes
    the mixed endian serialization. *)

val to_mixed_endian_binary_string : t -> string
(** [to_mixed_endian_binary_string] is like {!to_bytes} but encodes
    the mixed endian serialization. *)

(**/**)
val unsafe_of_binary_string : string -> t
val unsafe_to_binary_string : t -> string
(**/**)

(** {1:fmt_ascii US-ASCII format} *)

val of_string : ?pos:int -> string -> t option
(** [of_string pos s] converts the substring of [s] starting at [pos]
    (defaults to [0]) of the form ["XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"]
    where X is a lower or upper case hexadecimal number to an
    UUID. The result is [None] if a parse error occurs. Any extra
    characters after are ignored. *)

val to_string : ?upper:bool -> t -> string
(** [to_string u] is [u] as a string of the form
    ["XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"] where X is a lower
    (or upper if [upper] is [true]) case hexadecimal number. *)

val pp : Format.formatter -> t -> unit
(** [pp ppf u] formats [u] with {!to_string} on [ppf]. *)

val pp' : upper:bool -> Format.formatter -> t -> unit
(** [pp' ~upper ppf u] formats [u] with {!to_string}[ ~upper] on [ppf]. *)

(** {1:deprecated Deprecated} *)

type version =
[ `V3 of t * string (** Name based with MD5 hashing *)
| `V4 (** Random based *)
| `V5 of t * string (** Name based with SHA-1 hasing *) ]
[@ocaml.deprecated "Use the version specific Uuidm.v* functions."]
(** The type for UUID versions and generation parameters.
    {ul
    {- [`V3] and [`V5] specify a namespace and a name for the generation.}
    {- [`V4] is random based with a private state seeded with
       {!Stdlib.Random.State.make_self_init}. Use {!v4_gen} to specify
       your own seed. Use {!v4} to specify your own randomness.

       {b Warning.} The sequence resulting from repeatedly calling
       [v `V4] is random but predictable see {!v4_gen}.}} *)

[@@ocaml.warning "-3"]

val v : version -> t
[@@ocaml.deprecated "Use the version specific Uuidm.v* functions."]

val pp_string : ?upper:bool -> Format.formatter -> t -> unit
[@@ocaml.deprecated "Use Uuidm.pp' instead"]

val of_bytes : ?pos:int -> string -> t option
[@@ocaml.deprecated "Use Uuidm.of_binary_string instead"]

val to_bytes : t -> string
[@@ocaml.deprecated "Use Uuidm.to_binary_string instead"]

val of_mixed_endian_bytes : ?pos:int -> string -> t option
[@@ocaml.deprecated "Use Uuidm.of_mixed_endian_binary_string instead"]

val to_mixed_endian_bytes : t -> string
[@@ocaml.deprecated "Use Uuidm.to_mixed_endian_binary_string instead"]

(**/**)
val print : ?upper:bool -> Format.formatter -> t -> unit (* deprecated *)
[@@ocaml.deprecated "Use Uuidm.pp_string instead"]

val create : version -> t (* deprecated *)
[@@ocaml.deprecated "Use Uuidm.v instead"]

val unsafe_of_bytes : string -> t
[@@ocaml.deprecated "Use Uuidm.unsafe_of_binary_string instead"]

val unsafe_to_bytes : t -> string
[@@ocaml.deprecated "Use Uuidm.unsafe_to_binary_string instead"]
(**/**)

[@@ocaml.warning "-3"]
