zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

id.ex (5524B)


      1 # Zenflows is designed to implement the Valueflows vocabulary,
      2 # written and maintained by srfsh <info@dyne.org>.
      3 # Copyright (C) 2021-2023 Dyne.org foundation <foundation@dyne.org>.
      4 #
      5 # This program is free software: you can redistribute it and/or modify
      6 # it under the terms of the GNU Affero General Public License as published by
      7 # the Free Software Foundation, either version 3 of the License, or
      8 # (at your option) any later version.
      9 #
     10 # This program is distributed in the hope that it will be useful,
     11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13 # GNU Affero General Public License for more details.
     14 #
     15 # You should have received a copy of the GNU Affero General Public License
     16 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
     17 
     18 defmodule Zenflows.DB.ID do
     19 @moduledoc """
     20 An Ecto type that implements ULIDs with Crockford's Base32.  It also
     21 provides validation for IDs in GraphQL schemas.
     22 """
     23 
     24 use Ecto.Type
     25 
     26 @typedoc "A Crockford's Base32-encoded string."
     27 @type t() :: <<_::208>> # 26*8
     28 
     29 @typedoc "A raw binary representation of a ULID."
     30 @type raw() :: <<_::128>>
     31 
     32 @impl true
     33 def type(), do: :uuid
     34 
     35 @impl true
     36 def cast(<<_::208>> = data) do
     37 	with {:ok, _} <- decode(data) do
     38 		{:ok, data}
     39 	end
     40 end
     41 def cast(<<_::128>> = raw), do: encode(raw)
     42 def cast(_), do: :error
     43 
     44 @impl true
     45 def dump(id) when byte_size(id) == 26, do: decode(id)
     46 def dump(_), do: :error
     47 
     48 @impl true
     49 def load(<<_::128>> = raw), do: encode(raw)
     50 def load(_), do: :error
     51 
     52 @impl true
     53 def autogenerate(), do: gen()
     54 
     55 @doc "Generates a random ULID."
     56 @spec gen() :: t()
     57 def gen() do
     58 	{:ok, id} = encode(bingen())
     59 	id
     60 end
     61 
     62 @doc "Generates a random ULID in binary format."
     63 @spec bingen() :: raw()
     64 def bingen() do
     65 	import DateTime
     66 
     67 	<<ts::binary-6, _::binary>> =
     68 		utc_now() |> to_unix(:millisecond) |> :binary.encode_unsigned()
     69 	rand = :crypto.strong_rand_bytes(10)
     70 	ts <> rand
     71 end
     72 
     73 @doc "Fetch the timestamp out of an encoded or raw ULID."
     74 @spec ts(t() | raw()) :: {:ok, DateTime.t()} | {:error, atom()}
     75 def ts(<<x0::8, x1::8, x2::8, x3::8, x4::8, x5::8, x6::8, x7::8, x8::8, x9::8, _::binary-16>>) do
     76 	import DateTime
     77 
     78 	<<ts::binary-6, _::bitstring-2>> = <<
     79 		d(x0)::5, d(x1)::5, d(x2)::5, d(x3)::5, d(x4)::5,
     80 		d(x5)::5, d(x6)::5, d(x7)::5, d(x8)::5, d(x9)::5,
     81 	>>
     82 	:binary.decode_unsigned(ts) |> from_unix(:millisecond)
     83 end
     84 def ts(<<ts::binary-6, _::binary-10>>) do
     85 	import DateTime
     86 
     87 	:binary.decode_unsigned(ts) |> from_unix(:millisecond)
     88 end
     89 def ts(_) do
     90 	{:error, :invalid}
     91 end
     92 
     93 defp encode(<<x00::5, x01::5, x02::5, x03::5, x04::5, x05::5, x06::5, x07::5,
     94 		x08::5, x09::5, x10::5, x11::5, x12::5, x13::5, x14::5, x15::5, x16::5,
     95 		x17::5, x18::5, x19::5, x20::5, x21::5, x22::5, x23::5, x24::5, x25::3>>) do
     96 	import Bitwise
     97 
     98 	{:ok, <<
     99 		e(x00), e(x01), e(x02), e(x03), e(x04), e(x05), e(x06), e(x07), e(x08),
    100 		e(x09), e(x10), e(x11), e(x12), e(x13), e(x14), e(x15), e(x16), e(x17),
    101 		e(x18), e(x19), e(x20), e(x21), e(x22), e(x23), e(x24), e(bsl(x25, 2)),
    102 	>>}
    103 end
    104 defp encode(_), do: :error
    105 
    106 defp decode(<<x00::8, x01::8, x02::8, x03::8, x04::8, x05::8, x06::8, x07::8,
    107 		x08::8, x09::8, x10::8, x11::8, x12::8, x13::8, x14::8, x15::8, x16::8,
    108 		x17::8, x18::8, x19::8, x20::8, x21::8, x22::8, x23::8, x24::8, x25::8>>) do
    109 	import Bitwise
    110 
    111 	{:ok, <<
    112 		d(x00)::5, d(x01)::5, d(x02)::5, d(x03)::5, d(x04)::5, d(x05)::5, d(x06)::5, d(x07)::5, d(x08)::5,
    113 		d(x09)::5, d(x10)::5, d(x11)::5, d(x12)::5, d(x13)::5, d(x14)::5, d(x15)::5, d(x16)::5, d(x17)::5,
    114 		d(x18)::5, d(x19)::5, d(x20)::5, d(x21)::5, d(x22)::5, d(x23)::5, d(x24)::5, bsr(d(x25), 2)::3,
    115 	>>}
    116 rescue
    117 	ArgumentError -> :error
    118 end
    119 defp decode(_), do: :error
    120 
    121 @compile {:inline, e: 1}
    122 defp e(0),  do: ?0
    123 defp e(1),  do: ?1
    124 defp e(2),  do: ?2
    125 defp e(3),  do: ?3
    126 defp e(4),  do: ?4
    127 defp e(5),  do: ?5
    128 defp e(6),  do: ?6
    129 defp e(7),  do: ?7
    130 defp e(8),  do: ?8
    131 defp e(9),  do: ?9
    132 defp e(10), do: ?A
    133 defp e(11), do: ?B
    134 defp e(12), do: ?C
    135 defp e(13), do: ?D
    136 defp e(14), do: ?E
    137 defp e(15), do: ?F
    138 defp e(16), do: ?G
    139 defp e(17), do: ?H
    140 defp e(18), do: ?J
    141 defp e(19), do: ?K
    142 defp e(20), do: ?M
    143 defp e(21), do: ?N
    144 defp e(22), do: ?P
    145 defp e(23), do: ?Q
    146 defp e(24), do: ?R
    147 defp e(25), do: ?S
    148 defp e(26), do: ?T
    149 defp e(27), do: ?V
    150 defp e(28), do: ?W
    151 defp e(29), do: ?X
    152 defp e(30), do: ?Y
    153 defp e(31), do: ?Z
    154 
    155 @compile {:inline, d: 1}
    156 defp d(?0), do: 0
    157 defp d(?O), do: 0
    158 defp d(?o), do: 0
    159 defp d(?1), do: 1
    160 defp d(?I), do: 1
    161 defp d(?i), do: 1
    162 defp d(?L), do: 1
    163 defp d(?l), do: 1
    164 defp d(?2), do: 2
    165 defp d(?3), do: 3
    166 defp d(?4), do: 4
    167 defp d(?5), do: 5
    168 defp d(?6), do: 6
    169 defp d(?7), do: 7
    170 defp d(?8), do: 8
    171 defp d(?9), do: 9
    172 defp d(?A), do: 10
    173 defp d(?a), do: 10
    174 defp d(?B), do: 11
    175 defp d(?b), do: 11
    176 defp d(?C), do: 12
    177 defp d(?c), do: 12
    178 defp d(?D), do: 13
    179 defp d(?d), do: 13
    180 defp d(?E), do: 14
    181 defp d(?e), do: 14
    182 defp d(?F), do: 15
    183 defp d(?f), do: 15
    184 defp d(?G), do: 16
    185 defp d(?g), do: 16
    186 defp d(?H), do: 17
    187 defp d(?h), do: 17
    188 defp d(?J), do: 18
    189 defp d(?j), do: 18
    190 defp d(?K), do: 19
    191 defp d(?k), do: 19
    192 defp d(?M), do: 20
    193 defp d(?m), do: 20
    194 defp d(?N), do: 21
    195 defp d(?n), do: 21
    196 defp d(?P), do: 22
    197 defp d(?p), do: 22
    198 defp d(?Q), do: 23
    199 defp d(?q), do: 23
    200 defp d(?R), do: 24
    201 defp d(?r), do: 24
    202 defp d(?S), do: 25
    203 defp d(?s), do: 25
    204 defp d(?T), do: 26
    205 defp d(?t), do: 26
    206 defp d(?V), do: 27
    207 defp d(?v), do: 27
    208 defp d(?W), do: 28
    209 defp d(?w), do: 28
    210 defp d(?X), do: 29
    211 defp d(?x), do: 29
    212 defp d(?Y), do: 30
    213 defp d(?y), do: 30
    214 defp d(?Z), do: 31
    215 defp d(?z), do: 31
    216 defp d(_), do: raise ArgumentError
    217 end