identifier.clj

Bowmap · bowmap.git · Tickets

Branches > main > 0d6765e > files / src / net / bowmap / server / protocol / identifier.clj

(ns net.bowmap.server.protocol.identifier
  (:require
   [clojure.string :as str]
   [net.bowmap.server.protocol.schema.minecraft :as mc]))

(def namespace-regex #"^[a-z0-9.\-_]+$")
(def name-regex #"^#?[a-z0-9.\-_:]+$")

(defn identifier?
  "Returns whether 'x' is a valid protocol identifier - a namspace qualified keyword.
   Colons in the name of an identifier may used to separate directories.
   If the identifier's name starts with a number, it must be prefixed with a '#'.
   Identifiers in the 'minecraft' namespace should use the fully qualified minecraft
   schema namespace, rather than :minecraft/name."
  [x]
  (and (qualified-keyword? x)
       (re-matches namespace-regex (namespace x))
       (re-matches name-regex (name x))))

(defn encode-namespace
  "Encodes an identifier namespace into a valid protocol namespace."
  [identifier]
  (if (= (namespace identifier) mc/minecraft-namespace)
    "minecraft"
    (namespace identifier)))

(defn encode-name
  "Encodes an identifier name into a valid protocol name."
  [identifier]
  (-> (name identifier)
      (str/replace-first #"^#" "")
      (str/replace ":" "/")))

(defn encode-identifier
  "Encodes an identifier as a protocol identifier string."
  [identifier]
  (str (encode-namespace identifier) ":" (encode-name identifier)))

(defn decode-namespace
  "Decodes an encoded identifier namespace into a valid identifier object namespace."
  [encoded-namespace]
  (if (= encoded-namespace "minecraft")
    mc/minecraft-namespace
    encoded-namespace))

(defn decode-name
  "Decodes an encoded identifier name into a valid identifier object name."
  [encoded-name]
  (-> encoded-name
      (str/replace #"/" ":")
      (str/replace #"^([0-9])" "#$1")))

(defn decode-identifier
  "Returns an identifier from an encoded string namespace and name.
   Throws if the encoded values are not valid identifiers."
  [namespace name]
  (let [identifier (keyword (decode-namespace namespace) (decode-name name))]
    (if (identifier? identifier)
      identifier
      (throw (ex-info "Cannot decode that value into an identifier."
                      {:namespace namespace :name name})))))

(defn tag
  "Returns the protocol tag of an identifier, which is the encoded identifier
   preceded by a '#'."
  [identifier]
  (apply str "#" (encode-identifier identifier)))