Current section
Files
Jump to
Current section
Files
lib/polyjuice/util/identifiers/user_identifier_v0.ex
# Copyright 2021 Nicolas Jouanin <nico@beerfactory.org>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-FileCopyrightText: 2021 Nicolas Jouanin <nico@beerfactory.org>
# SPDX-License-Identifier: Apache-2.0
defmodule Polyjuice.Util.Identifiers.V0.UserIdentifier do
@type t :: %__MODULE__{
sigil: String.t(),
localpart: String.t(),
domain: String.t()
}
alias Polyjuice.Util.Identifiers.V0.UserIdentifier
@enforce_keys [:sigil, :localpart, :domain]
defstruct [
:sigil,
:localpart,
:domain
]
@sigil "@"
@localpart_regex ~r|^[\x21-\x39\x3b-\x7e]+$|
@max_length 255
@behaviour Polyjuice.Util.Identifiers
@randomize_length 15
@spec generate(domain :: String.t()) :: UserIdentifier.t()
def generate(domain) when is_binary(domain) do
{:ok, user} =
Polyjuice.Util.Randomizer.randomize(@randomize_length, :downcase_numeric)
|> Polyjuice.Util.Identifiers.V0.UserIdentifier.new(domain)
user
end
@impl Polyjuice.Util.Identifiers
def new!(identifier) do
case parse(identifier) do
{:ok, user} -> user
{:error, error} -> raise ArgumentError, to_string(error)
end
end
@impl Polyjuice.Util.Identifiers
def new([localpart, domain]), do: new(localpart, domain)
@impl Polyjuice.Util.Identifiers
def new({localpart, domain}), do: new(localpart, domain)
@impl Polyjuice.Util.Identifiers
def new(%{localpart: localpart, domain: domain}), do: new(localpart, domain)
@impl Polyjuice.Util.Identifiers
def new(identifier), do: parse(identifier)
@spec new(String.t(), String.t()) :: {:ok, UserIdentifier.t()} | {:error, atom}
def new(localpart, domain) when is_binary(localpart) and is_binary(domain) do
user = %UserIdentifier{sigil: @sigil, localpart: localpart, domain: domain}
if valid?(user) do
{:ok, user}
else
{:error, :invalid_user_id}
end
end
@impl Polyjuice.Util.Identifiers
def valid?(%UserIdentifier{} = this) do
Regex.match?(@localpart_regex, this.localpart) && String.length(fqid(this)) < @max_length &&
String.length(this.domain) > 0 && String.length(this.localpart) > 0
end
@impl Polyjuice.Util.Identifiers
def valid?(identifier_string) when is_binary(identifier_string) do
case parse(identifier_string) do
{:ok, _} -> true
_ -> false
end
end
def fqid(%UserIdentifier{} = this) do
"#{@sigil}#{this.localpart}:#{this.domain}"
end
@impl Polyjuice.Util.Identifiers
@spec parse(String.t()) :: {:ok, UserIdentifier.t()} | {:error, atom}
def parse(id) when is_binary(id) do
with @sigil <> id <- id,
[localpart, domain] <- String.split(id, ":", parts: 2, trim: true) do
new(localpart, domain)
else
_ -> {:error, :invalid_user_id}
end
end
end
defimpl String.Chars, for: Polyjuice.Util.Identifiers.V0.UserIdentifier do
def to_string(this), do: Polyjuice.Util.Identifiers.V0.UserIdentifier.fqid(this)
end