uuid.ex (5806B)
1 defmodule Ecto.UUID do 2 @moduledoc """ 3 An Ecto type for UUID strings. 4 """ 5 6 use Ecto.Type 7 8 @typedoc """ 9 A hex-encoded UUID string. 10 """ 11 @type t :: <<_::288>> 12 13 @typedoc """ 14 A raw binary representation of a UUID. 15 """ 16 @type raw :: <<_::128>> 17 18 @doc false 19 def type, do: :uuid 20 21 @doc """ 22 Casts to a UUID. 23 """ 24 @spec cast(t | raw | any) :: {:ok, t} | :error 25 def cast(<< a1, a2, a3, a4, a5, a6, a7, a8, ?-, 26 b1, b2, b3, b4, ?-, 27 c1, c2, c3, c4, ?-, 28 d1, d2, d3, d4, ?-, 29 e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12 >>) do 30 << c(a1), c(a2), c(a3), c(a4), 31 c(a5), c(a6), c(a7), c(a8), ?-, 32 c(b1), c(b2), c(b3), c(b4), ?-, 33 c(c1), c(c2), c(c3), c(c4), ?-, 34 c(d1), c(d2), c(d3), c(d4), ?-, 35 c(e1), c(e2), c(e3), c(e4), 36 c(e5), c(e6), c(e7), c(e8), 37 c(e9), c(e10), c(e11), c(e12) >> 38 catch 39 :error -> :error 40 else 41 hex_uuid -> {:ok, hex_uuid} 42 end 43 44 def cast(<< _::128 >> = raw_uuid), do: {:ok, encode(raw_uuid)} 45 def cast(_), do: :error 46 47 @doc """ 48 Same as `cast/1` but raises `Ecto.CastError` on invalid arguments. 49 """ 50 @spec cast!(t | raw | any) :: t 51 def cast!(value) do 52 case cast(value) do 53 {:ok, hex_uuid} -> hex_uuid 54 :error -> raise Ecto.CastError, type: __MODULE__, value: value 55 end 56 end 57 58 @compile {:inline, c: 1} 59 60 defp c(?0), do: ?0 61 defp c(?1), do: ?1 62 defp c(?2), do: ?2 63 defp c(?3), do: ?3 64 defp c(?4), do: ?4 65 defp c(?5), do: ?5 66 defp c(?6), do: ?6 67 defp c(?7), do: ?7 68 defp c(?8), do: ?8 69 defp c(?9), do: ?9 70 defp c(?A), do: ?a 71 defp c(?B), do: ?b 72 defp c(?C), do: ?c 73 defp c(?D), do: ?d 74 defp c(?E), do: ?e 75 defp c(?F), do: ?f 76 defp c(?a), do: ?a 77 defp c(?b), do: ?b 78 defp c(?c), do: ?c 79 defp c(?d), do: ?d 80 defp c(?e), do: ?e 81 defp c(?f), do: ?f 82 defp c(_), do: throw(:error) 83 84 @doc """ 85 Converts a string representing a UUID into a raw binary. 86 """ 87 @spec dump(t | any) :: {:ok, raw} | :error 88 def dump(<< a1, a2, a3, a4, a5, a6, a7, a8, ?-, 89 b1, b2, b3, b4, ?-, 90 c1, c2, c3, c4, ?-, 91 d1, d2, d3, d4, ?-, 92 e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12 >>) do 93 << d(a1)::4, d(a2)::4, d(a3)::4, d(a4)::4, 94 d(a5)::4, d(a6)::4, d(a7)::4, d(a8)::4, 95 d(b1)::4, d(b2)::4, d(b3)::4, d(b4)::4, 96 d(c1)::4, d(c2)::4, d(c3)::4, d(c4)::4, 97 d(d1)::4, d(d2)::4, d(d3)::4, d(d4)::4, 98 d(e1)::4, d(e2)::4, d(e3)::4, d(e4)::4, 99 d(e5)::4, d(e6)::4, d(e7)::4, d(e8)::4, 100 d(e9)::4, d(e10)::4, d(e11)::4, d(e12)::4 >> 101 catch 102 :error -> :error 103 else 104 raw_uuid -> {:ok, raw_uuid} 105 end 106 107 def dump(_), do: :error 108 109 @compile {:inline, d: 1} 110 111 defp d(?0), do: 0 112 defp d(?1), do: 1 113 defp d(?2), do: 2 114 defp d(?3), do: 3 115 defp d(?4), do: 4 116 defp d(?5), do: 5 117 defp d(?6), do: 6 118 defp d(?7), do: 7 119 defp d(?8), do: 8 120 defp d(?9), do: 9 121 defp d(?A), do: 10 122 defp d(?B), do: 11 123 defp d(?C), do: 12 124 defp d(?D), do: 13 125 defp d(?E), do: 14 126 defp d(?F), do: 15 127 defp d(?a), do: 10 128 defp d(?b), do: 11 129 defp d(?c), do: 12 130 defp d(?d), do: 13 131 defp d(?e), do: 14 132 defp d(?f), do: 15 133 defp d(_), do: throw(:error) 134 135 @doc """ 136 Same as `dump/1` but raises `Ecto.ArgumentError` on invalid arguments. 137 """ 138 @spec dump!(t | any) :: raw 139 def dump!(value) do 140 case dump(value) do 141 {:ok, raw_uuid} -> raw_uuid 142 :error -> raise ArgumentError, "cannot dump given UUID to binary: #{inspect(value)}" 143 end 144 end 145 146 @doc """ 147 Converts a binary UUID into a string. 148 """ 149 @spec load(raw | any) :: {:ok, t} | :error 150 def load(<<_::128>> = raw_uuid), do: {:ok, encode(raw_uuid)} 151 152 def load(<<_::64, ?-, _::32, ?-, _::32, ?-, _::32, ?-, _::96>> = string) do 153 raise ArgumentError, "trying to load string UUID as Ecto.UUID: #{inspect string}. " <> 154 "Maybe you wanted to declare :uuid as your database field?" 155 end 156 157 def load(_), do: :error 158 159 @doc """ 160 Same as `load/1` but raises `Ecto.ArgumentError` on invalid arguments. 161 """ 162 @spec load!(raw | any) :: t 163 def load!(value) do 164 case load(value) do 165 {:ok, hex_uuid} -> hex_uuid 166 :error -> raise ArgumentError, "cannot load given binary as UUID: #{inspect(value)}" 167 end 168 end 169 170 @doc """ 171 Generates a random, version 4 UUID. 172 """ 173 @spec generate() :: t 174 def generate(), do: encode(bingenerate()) 175 176 @doc """ 177 Generates a random, version 4 UUID in the binary format. 178 """ 179 @spec bingenerate() :: raw 180 def bingenerate() do 181 <<u0::48, _::4, u1::12, _::2, u2::62>> = :crypto.strong_rand_bytes(16) 182 <<u0::48, 4::4, u1::12, 2::2, u2::62>> 183 end 184 185 # Callback invoked by autogenerate fields. 186 @doc false 187 def autogenerate, do: generate() 188 189 @spec encode(raw) :: t 190 defp encode(<< a1::4, a2::4, a3::4, a4::4, 191 a5::4, a6::4, a7::4, a8::4, 192 b1::4, b2::4, b3::4, b4::4, 193 c1::4, c2::4, c3::4, c4::4, 194 d1::4, d2::4, d3::4, d4::4, 195 e1::4, e2::4, e3::4, e4::4, 196 e5::4, e6::4, e7::4, e8::4, 197 e9::4, e10::4, e11::4, e12::4 >>) do 198 << e(a1), e(a2), e(a3), e(a4), e(a5), e(a6), e(a7), e(a8), ?-, 199 e(b1), e(b2), e(b3), e(b4), ?-, 200 e(c1), e(c2), e(c3), e(c4), ?-, 201 e(d1), e(d2), e(d3), e(d4), ?-, 202 e(e1), e(e2), e(e3), e(e4), e(e5), e(e6), e(e7), e(e8), e(e9), e(e10), e(e11), e(e12) >> 203 end 204 205 @compile {:inline, e: 1} 206 207 defp e(0), do: ?0 208 defp e(1), do: ?1 209 defp e(2), do: ?2 210 defp e(3), do: ?3 211 defp e(4), do: ?4 212 defp e(5), do: ?5 213 defp e(6), do: ?6 214 defp e(7), do: ?7 215 defp e(8), do: ?8 216 defp e(9), do: ?9 217 defp e(10), do: ?a 218 defp e(11), do: ?b 219 defp e(12), do: ?c 220 defp e(13), do: ?d 221 defp e(14), do: ?e 222 defp e(15), do: ?f 223 end