zf

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

validate.ex (4628B)


      1 # Zenflows is designed to implement the Valueflows vocabulary,
      2 # written and maintained by srfsh <info@dyne.org>.
      3 # Copyright (C) 2021-2022 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.VF.Validate do
     19 @moduledoc """
     20 Common Valueflows validators for Ecto.Changesets.  All the limitations
     21 here are rough and can be changed in the future.
     22 """
     23 
     24 alias Ecto.Changeset, as: Chset
     25 
     26 require Logger
     27 
     28 @doc "Checks if the given string field is [16, 2048] bytes long."
     29 @spec key(Chset.t(), atom()) :: Chset.t()
     30 def key(cset, field) do
     31 	Chset.validate_change(cset, field, :valflow, fn
     32 		_, str when byte_size(str) < 16 ->
     33 			[{field, "should be at least 16 bytes long"}]
     34 		_, str when byte_size(str) > 2048 ->
     35 			[{field, "should be at most 2048 bytes long"}]
     36 		_, _ ->
     37 			[]
     38 	end)
     39 end
     40 
     41 @doc "Checks if the given string field is [1, 256] bytes long."
     42 @spec name(Chset.t(), atom()) :: Chset.t()
     43 def name(cset, field) do
     44 	Chset.validate_change(cset, field, :valflow, fn
     45 		_, str when byte_size(str) < 1 ->
     46 			[{field, "should be at least 1 byte long"}]
     47 		_, str when byte_size(str) > 256 ->
     48 			[{field, "should be at most 256 bytes long"}]
     49 		_, _ ->
     50 			[]
     51 	end)
     52 end
     53 
     54 @doc "Checks if the given string field is [1, 2048] bytes long."
     55 @spec note(Chset.t(), atom()) :: Chset.t()
     56 def note(cset, field) do
     57 	Chset.validate_change(cset, field, :valflow, fn
     58 		_, str when byte_size(str) < 1 ->
     59 			[{field, "should be at least 1 bytes long"}]
     60 		_, str when byte_size(str) > 2048 ->
     61 			[{field, "should be at most 2048 bytes long"}]
     62 		_, _ ->
     63 			[]
     64 	end)
     65 end
     66 
     67 @doc "Checks if the given string is [1, 512] bytes long."
     68 @spec uri(Chset.t(), atom()) :: Chset.t()
     69 def uri(cset, field) do
     70 	Chset.validate_change(cset, field, :valflow, fn
     71 		_, str when byte_size(str) < 1 ->
     72 			[{field, "should be at least 1 bytes long"}]
     73 		_, str when byte_size(str) > 512 ->
     74 			[{field, "should be at most 512 bytes long"}]
     75 		_, _ ->
     76 			[]
     77 	end)
     78 end
     79 
     80 @mebibyte 1024 * 1024
     81 
     82 @doc """
     83 Check if the given base64-encoded binary data is at least 1B, at most
     84 25MiB in size.  And, display a warning if it is longer than 4MiB.
     85 """
     86 @spec img(Chset.t(), atom()) :: Chset.t()
     87 def img(cset, field) do
     88 	Chset.validate_change(cset, field, :valflow, fn
     89 		_, str when byte_size(str) < 1 ->
     90 			[{field, "should be at least 1B long"}]
     91 		_, str when byte_size(str) > 25 * @mebibyte ->
     92 			[{field, "should be at most 25MiB long"}]
     93 		_, str when byte_size(str) > 4 * @mebibyte ->
     94 			Logger.warning("file exceeds 4MiB")
     95 			[]
     96 		_, _ ->
     97 			[]
     98 	end)
     99 end
    100 
    101 @doc """
    102 Checks if the given classifications (list of strings) for:
    103 
    104 	- Each item in the list is [1, 512] bytes long;
    105 	- The list can contain only [1, 128] items.
    106 """
    107 @spec class(Chset.t(), atom()) :: Chset.t()
    108 def class(cset, field) do
    109 	Chset.validate_change(cset, field, :valflow, fn
    110 		_, [] ->
    111 			[{field, "must contain at least 1 item"}]
    112 		_, list ->
    113 			case do_class(list) do
    114 				{:exceeds, _ind} -> [{field, "must contain at most 128 items"}]
    115 				{:short, ind} -> [{field, "the item at #{ind + 1} cannot be shorter than 1 bytes"}]
    116 				{:long, ind} -> [{field, "the item at #{ind + 1} cannot be longer than 512 bytes"}]
    117 				{:valid, _ind} -> []
    118 			end
    119 	end)
    120 end
    121 
    122 @spec do_class([String.t()]) :: {atom(), integer()}
    123 defp do_class(list) do
    124 	do_class(list, 0, 128)
    125 end
    126 
    127 # The rationale of this function is to loop over the list while decreasing
    128 # `remaining' and increasing `index' until either one of these happen (in
    129 # that order):
    130 #     * remaining hits 0
    131 #     * one of the items in the list is shorter than 3 bytes long
    132 #     * one of the items in the list is longer than 512 bytes long
    133 @spec do_class([String.t()], integer(), integer()) :: {atom(), integer()}
    134 defp do_class([head | tail], index, remaining) do
    135 	cond do
    136 		remaining == 0 -> {:exceeds, index}
    137 		byte_size(head) < 1 -> {:short, index}
    138 		byte_size(head) > 512 -> {:long, index}
    139 		true -> do_class(tail, index + 1, remaining - 1)
    140 	end
    141 end
    142 
    143 defp do_class([], index, _) do
    144 	{:valid, index - 1}
    145 end
    146 end