From 16e15e9c67109f6fa778b1553f0b347b7788bb14 Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 18 Jun 2024 13:18:12 -0400 Subject: [PATCH 1/2] Add extra type and callback documentation --- lib/philomena/comments/query.ex | 2 +- lib/philomena/filters/query.ex | 2 +- lib/philomena/galleries/query.ex | 2 +- lib/philomena/images/query.ex | 2 +- lib/philomena/posts/query.ex | 2 +- lib/philomena/reports/query.ex | 2 +- lib/philomena/tags/query.ex | 2 +- lib/philomena_media/analyzers/analyzer.ex | 3 ++ lib/philomena_media/intensities.ex | 2 +- lib/philomena_media/processors.ex | 18 ++++++------ lib/philomena_media/processors/processor.ex | 20 +++++++++---- lib/philomena_proxy/scrapers.ex | 6 ++-- lib/philomena_query/batch.ex | 18 ++++++++++++ lib/philomena_query/parse/parser.ex | 28 +++++++++++++++++-- lib/philomena_query/search.ex | 26 +++++++++++++++++ lib/philomena_query/search_index.ex | 31 ++++++++++++++++++--- 16 files changed, 134 insertions(+), 32 deletions(-) diff --git a/lib/philomena/comments/query.ex b/lib/philomena/comments/query.ex index 6b2bea42..9e9c8986 100644 --- a/lib/philomena/comments/query.ex +++ b/lib/philomena/comments/query.ex @@ -88,7 +88,7 @@ defmodule Philomena.Comments.Query do defp parse(fields, context, query_string) do fields - |> Parser.parser() + |> Parser.new() |> Parser.parse(query_string, context) end diff --git a/lib/philomena/filters/query.ex b/lib/philomena/filters/query.ex index adf53b09..3b6bb3ef 100644 --- a/lib/philomena/filters/query.ex +++ b/lib/philomena/filters/query.ex @@ -29,7 +29,7 @@ defmodule Philomena.Filters.Query do defp parse(fields, context, query_string) do fields - |> Parser.parser() + |> Parser.new() |> Parser.parse(query_string, context) end diff --git a/lib/philomena/galleries/query.ex b/lib/philomena/galleries/query.ex index 9baad469..e04ceecc 100644 --- a/lib/philomena/galleries/query.ex +++ b/lib/philomena/galleries/query.ex @@ -18,7 +18,7 @@ defmodule Philomena.Galleries.Query do query_string = query_string || "" fields() - |> Parser.parser() + |> Parser.new() |> Parser.parse(query_string) end end diff --git a/lib/philomena/images/query.ex b/lib/philomena/images/query.ex index 575c6e71..9eedcd74 100644 --- a/lib/philomena/images/query.ex +++ b/lib/philomena/images/query.ex @@ -140,7 +140,7 @@ defmodule Philomena.Images.Query do defp parse(fields, context, query_string) do fields - |> Parser.parser() + |> Parser.new() |> Parser.parse(query_string, context) end diff --git a/lib/philomena/posts/query.ex b/lib/philomena/posts/query.ex index 27773776..331655c7 100644 --- a/lib/philomena/posts/query.ex +++ b/lib/philomena/posts/query.ex @@ -86,7 +86,7 @@ defmodule Philomena.Posts.Query do defp parse(fields, context, query_string) do fields - |> Parser.parser() + |> Parser.new() |> Parser.parse(query_string, context) end diff --git a/lib/philomena/reports/query.ex b/lib/philomena/reports/query.ex index d5adc2cc..c9d9be44 100644 --- a/lib/philomena/reports/query.ex +++ b/lib/philomena/reports/query.ex @@ -16,7 +16,7 @@ defmodule Philomena.Reports.Query do def compile(query_string) do fields() - |> Parser.parser() + |> Parser.new() |> Parser.parse(query_string || "", %{}) end end diff --git a/lib/philomena/tags/query.ex b/lib/philomena/tags/query.ex index 5bfd2126..da148da4 100644 --- a/lib/philomena/tags/query.ex +++ b/lib/philomena/tags/query.ex @@ -19,7 +19,7 @@ defmodule Philomena.Tags.Query do def compile(query_string) do fields() - |> Parser.parser() + |> Parser.new() |> Parser.parse(query_string || "") end end diff --git a/lib/philomena_media/analyzers/analyzer.ex b/lib/philomena_media/analyzers/analyzer.ex index cf3b28ec..c96f0005 100644 --- a/lib/philomena_media/analyzers/analyzer.ex +++ b/lib/philomena_media/analyzers/analyzer.ex @@ -1,5 +1,8 @@ defmodule PhilomenaMedia.Analyzers.Analyzer do @moduledoc false + @doc """ + Generate a `m:PhilomenaMedia.Analyzers.Result` for file at the given path. + """ @callback analyze(Path.t()) :: PhilomenaMedia.Analyzers.Result.t() end diff --git a/lib/philomena_media/intensities.ex b/lib/philomena_media/intensities.ex index 5abd71e0..ea095295 100644 --- a/lib/philomena_media/intensities.ex +++ b/lib/philomena_media/intensities.ex @@ -36,7 +36,7 @@ defmodule PhilomenaMedia.Intensities do > #### Info {: .info} > - > Clients should prefer to use `m:PhilomenaMedia.Processors.intensities/2`, as it handles + > Clients should prefer to use `PhilomenaMedia.Processors.intensities/2`, as it handles > media files of any type supported by this library, not just PNG or JPEG. ## Examples diff --git a/lib/philomena_media/processors.ex b/lib/philomena_media/processors.ex index 23c49dcf..22ce613b 100644 --- a/lib/philomena_media/processors.ex +++ b/lib/philomena_media/processors.ex @@ -62,29 +62,31 @@ defmodule PhilomenaMedia.Processors do alias PhilomenaMedia.Processors.{Gif, Jpeg, Png, Svg, Webm} alias PhilomenaMedia.Mime - # The name of a version, like :large + @typedoc "The name of a version, like `:large`." @type version_name :: atom() @type dimensions :: {integer(), integer()} @type version_list :: [{version_name(), dimensions()}] - # The file name of a processed version, like "large.png" + @typedoc "The file name of a processed version, like `large.png`." @type version_filename :: String.t() - # A single file to be copied to satisfy a request for a version name + @typedoc "A single file to be copied to satisfy a request for a version name." @type copy_request :: {:copy, Path.t(), version_filename()} - # A list of thumbnail versions to copy into place + @typedoc "A list of thumbnail versions to copy into place." @type thumbnails :: {:thumbnails, [copy_request()]} - # Replace the original file to strip metadata or losslessly optimize + @typedoc "Replace the original file to strip metadata or losslessly optimize." @type replace_original :: {:replace_original, Path.t()} - # Apply the computed corner intensities + @typedoc "Apply the computed corner intensities." @type intensities :: {:intensities, Intensities.t()} - # An edit script, representing the changes to apply to the storage backend - # after successful processing + @typedoc """ + An edit script, representing the changes to apply to the storage backend + after successful processing. + """ @type edit_script :: [thumbnails() | replace_original() | intensities()] @doc """ diff --git a/lib/philomena_media/processors/processor.ex b/lib/philomena_media/processors/processor.ex index 2c3acc0b..8b9f568f 100644 --- a/lib/philomena_media/processors/processor.ex +++ b/lib/philomena_media/processors/processor.ex @@ -5,17 +5,25 @@ defmodule PhilomenaMedia.Processors.Processor do alias PhilomenaMedia.Processors alias PhilomenaMedia.Intensities - # Generate a list of version filenames for the given version list. + @doc """ + Generate a list of version filenames for the given version list. + """ @callback versions(Processors.version_list()) :: [Processors.version_filename()] - # Process the media at the given path against the given version list, and return an - # edit script with the resulting files + @doc """ + Process the media at the given path against the given version list, and return an + edit script with the resulting files. + """ @callback process(Result.t(), Path.t(), Processors.version_list()) :: Processors.edit_script() - # Perform post-processing optimization tasks on the file, to reduce its size - # and strip non-essential metadata + @doc """ + Perform post-processing optimization tasks on the file, to reduce its size + and strip non-essential metadata. + """ @callback post_process(Result.t(), Path.t()) :: Processors.edit_script() - # Generate corner intensities for the given path + @doc """ + Generate corner intensities for the given path. + """ @callback intensities(Result.t(), Path.t()) :: Intensities.t() end diff --git a/lib/philomena_proxy/scrapers.ex b/lib/philomena_proxy/scrapers.ex index 67711045..a96f0817 100644 --- a/lib/philomena_proxy/scrapers.ex +++ b/lib/philomena_proxy/scrapers.ex @@ -3,16 +3,16 @@ defmodule PhilomenaProxy.Scrapers do Scrape utilities to facilitate uploading media from other websites. """ - # The URL to fetch, as a string. + @typedoc "The URL to fetch, as a string." @type url :: String.t() - # An individual image in a list associated with a scrape result. + @typedoc "An individual image in a list associated with a scrape result." @type image_result :: %{ url: url(), camo_url: url() } - # Result of a successful scrape. + @typedoc "Result of a successful scrape." @type scrape_result :: %{ source_url: url(), description: String.t() | nil, diff --git a/lib/philomena_query/batch.ex b/lib/philomena_query/batch.ex index ce78cb6a..676b436a 100644 --- a/lib/philomena_query/batch.ex +++ b/lib/philomena_query/batch.ex @@ -13,13 +13,31 @@ defmodule PhilomenaQuery.Batch do alias Philomena.Repo import Ecto.Query + @typedoc """ + Represents an object which may be operated on via `m:Ecto.Query`. + + This could be a schema object (e.g. `m:Philomena.Images.Image`) or a fully formed query + `from i in Image, where: i.hidden_from_users == false`. + """ @type queryable :: any() @type batch_size :: {:batch_size, integer()} @type id_field :: {:id_field, atom()} @type batch_options :: [batch_size() | id_field()] + @typedoc """ + The callback for `record_batches/3`. + + Takes a list of schema structs which were returned in the batch. Return value is ignored. + """ @type record_batch_callback :: ([struct()] -> any()) + + @typedoc """ + The callback for `query_batches/3`. + + Takes an `m:Ecto.Query` that can be processed with `m:Philomena.Repo` query commmands, such + as `Philomena.Repo.update_all/3` or `Philomena.Repo.delete_all/2`. Return value is ignored. + """ @type query_batch_callback :: ([Ecto.Query.t()] -> any()) @doc """ diff --git a/lib/philomena_query/parse/parser.ex b/lib/philomena_query/parse/parser.ex index ba4b0597..a9d40222 100644 --- a/lib/philomena_query/parse/parser.ex +++ b/lib/philomena_query/parse/parser.ex @@ -41,12 +41,34 @@ defmodule PhilomenaQuery.Parse.Parser do TermRangeParser } + @typedoc """ + User-supplied context argument. + + Provided to `parse/3` and passed to the transform callback. + """ @type context :: any() + + @typedoc "Query in the search engine JSON query language." @type query :: map() + @typedoc "Whether the default field is `:term` (not analyzed) or `:ngram` (analyzed)." @type default_field_type :: :term | :ngram + @typedoc """ + Return value of the transform callback. + + On `{:ok, query}`, the query is incorporated into the parse tree at the current location. + On `{:error, error}`, parsing immediately stops and the error is returned from the parser. + """ @type transform_result :: {:ok, query()} | {:error, String.t()} + + @typedoc """ + Type of the transform callback. + + The transform callback receives the context argument passed to `parse/3` and the remainder of + the term. For instance `my:example` would match a transform rule with the key `"my"`, and + the remainder passed to the callback would be `"example"`. + """ @type transform :: (context, String.t() -> transform_result()) @type t :: %__MODULE__{ @@ -112,11 +134,11 @@ defmodule PhilomenaQuery.Parse.Parser do aliases: %{"hidden" => "hidden_from_users"} ] - Parser.parser(options) + Parser.new(options) """ - @spec parser(keyword()) :: t() - def parser(options) do + @spec new(keyword()) :: t() + def new(options) do parser = struct(Parser, options) fields = diff --git a/lib/philomena_query/search.ex b/lib/philomena_query/search.ex index b4960657..140adf67 100644 --- a/lib/philomena_query/search.ex +++ b/lib/philomena_query/search.ex @@ -18,10 +18,36 @@ defmodule PhilomenaQuery.Search do # todo: fetch through compile_env? @policy Philomena.SearchPolicy + @typedoc """ + Any schema module which has an associated search index. See the policy module + for more information. + """ @type schema_module :: @policy.schema_module() + + @typedoc """ + Represents an object which may be operated on via `m:Ecto.Query`. + + This could be a schema object (e.g. `m:Philomena.Images.Image`) or a fully formed query + `from i in Image, where: i.hidden_from_users == false`. + """ @type queryable :: any() + + @typedoc """ + A query body, as deliverable to any index's `_search` endpoint. + + See the query DSL documentation for additional information: + https://opensearch.org/docs/latest/query-dsl/ + """ @type query_body :: map() + @typedoc """ + Given a term at the given path, replace the old term with the new term. + + `path` is a list of names to be followed to find the old term. For example, + a document containing `{"condiments": "dijon"}` would permit `["condiments"]` + as the path, and a document containing `{"namespaced_tags": {"name": ["old"]}}` + would permit `["namespaced_tags", "name"]` as the path. + """ @type replacement :: %{ path: [String.t()], old: term(), diff --git a/lib/philomena_query/search_index.ex b/lib/philomena_query/search_index.ex index 3a4fe9da..119d2613 100644 --- a/lib/philomena_query/search_index.ex +++ b/lib/philomena_query/search_index.ex @@ -1,11 +1,34 @@ defmodule PhilomenaQuery.SearchIndex do - # Returns the index name for the index. - # This is usually a collection name like "images". + @moduledoc """ + Behaviour module for schemas with search indexing. + """ + + @doc """ + Returns the index name for the index. + + This is usually a collection name like "images". + + See https://opensearch.org/docs/latest/api-reference/index-apis/create-index/ for + reference on index naming restrictions. + """ @callback index_name() :: String.t() - # Returns the mapping and settings for the index. + @doc """ + Returns the mapping and settings for the index. + + See https://opensearch.org/docs/latest/api-reference/index-apis/put-mapping/ for + reference on the mapping syntax, and the following pages for which types may be + used in mappings: + - https://opensearch.org/docs/latest/field-types/ + - https://opensearch.org/docs/latest/analyzers/index-analyzers/ + """ @callback mapping() :: map() - # Returns the JSON representation of the given struct for indexing in OpenSearch. + @doc """ + Returns the JSON representation of the given struct for indexing in OpenSearch. + + See https://opensearch.org/docs/latest/api-reference/document-apis/index-document/ for + reference on how this value is used. + """ @callback as_json(struct()) :: map() end From 7df6ef3ab592202b594df02704a3487e29b43fb2 Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 18 Jun 2024 13:21:06 -0400 Subject: [PATCH 2/2] Add ex_doc for documentation generation --- mix.exs | 4 +++- mix.lock | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 41e15a6f..f9cded72 100644 --- a/mix.exs +++ b/mix.exs @@ -11,7 +11,8 @@ defmodule Philomena.MixProject do start_permanent: Mix.env() == :prod, aliases: aliases(), deps: deps(), - dialyzer: [plt_add_apps: [:mix]] + dialyzer: [plt_add_apps: [:mix]], + docs: [formatters: ["html"]] ] end @@ -85,6 +86,7 @@ defmodule Philomena.MixProject do {:credo, "~> 1.6", only: [:dev, :test], runtime: false}, {:credo_envvar, "~> 0.1", only: [:dev, :test], runtime: false}, {:credo_naming, "~> 2.0", only: [:dev, :test], runtime: false}, + {:ex_doc, "~> 0.30", only: [:dev], runtime: false}, # Security checks {:sobelow, "~> 0.11", only: [:dev, :test], runtime: true}, diff --git a/mix.lock b/mix.lock index f8e68868..46729250 100644 --- a/mix.lock +++ b/mix.lock @@ -17,6 +17,7 @@ "db_connection": {:hex, :db_connection, "2.6.0", "77d835c472b5b67fc4f29556dee74bf511bbafecdcaf98c27d27fa5918152086", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c2f992d15725e721ec7fbc1189d4ecdb8afef76648c746a8e1cad35e3b8a35f3"}, "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, "ecto": {:hex, :ecto, "3.11.2", "e1d26be989db350a633667c5cda9c3d115ae779b66da567c68c80cfb26a8c9ee", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c38bca2c6f8d8023f2145326cc8a80100c3ffe4dcbd9842ff867f7fc6156c65"}, "ecto_network": {:hex, :ecto_network, "1.5.0", "a930c910975e7a91237b858ebf0f4ad7b2aae32fa846275aa203cb858459ec73", [:mix], [{:ecto_sql, ">= 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:phoenix_html, ">= 0.0.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.14.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "4d614434ae3e6d373a2f693d56aafaa3f3349714668ffd6d24e760caf578aa2f"}, "ecto_sql": {:hex, :ecto_sql, "3.11.2", "c7cc7f812af571e50b80294dc2e535821b3b795ce8008d07aa5f336591a185a8", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.11.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "73c07f995ac17dbf89d3cfaaf688fcefabcd18b7b004ac63b0dc4ef39499ed6b"}, @@ -26,6 +27,7 @@ "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "ex_aws": {:git, "https://github.com/liamwhite/ex_aws.git", "a340859dd8ac4d63bd7a3948f0994e493e49bda4", [ref: "a340859dd8ac4d63bd7a3948f0994e493e49bda4"]}, "ex_aws_s3": {:hex, :ex_aws_s3, "2.5.3", "422468e5c3e1a4da5298e66c3468b465cfd354b842e512cb1f6fbbe4e2f5bdaf", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: false]}, {:sweet_xml, ">= 0.0.0", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm", "4f09dd372cc386550e484808c5ac5027766c8d0cd8271ccc578b82ee6ef4f3b8"}, + "ex_doc": {:hex, :ex_doc, "0.34.0", "ab95e0775db3df71d30cf8d78728dd9261c355c81382bcd4cefdc74610bef13e", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "60734fb4c1353f270c3286df4a0d51e65a2c1d9fba66af3940847cc65a8066d7"}, "expo": {:hex, :expo, "0.5.2", "beba786aab8e3c5431813d7a44b828e7b922bfa431d6bfbada0904535342efe2", [:mix], [], "hexpm", "8c9bfa06ca017c9cb4020fabe980bc7fdb1aaec059fd004c2ab3bff03b1c599c"}, "exq": {:hex, :exq, "0.19.0", "06eb92944dad39f0954dc8f63190d3e24d11734eef88cf5800883e57ebf74f3c", [:mix], [{:elixir_uuid, ">= 1.2.0", [hex: :elixir_uuid, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:poison, ">= 1.2.0 and < 6.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:redix, ">= 0.9.0", [hex: :redix, repo: "hexpm", optional: false]}], "hexpm", "24fc0ebdd87cc7406e1034fb46c2419f9c8a362f0ec634d23b6b819514d36390"}, "file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"}, @@ -37,6 +39,9 @@ "inet_cidr": {:hex, :inet_cidr, "1.0.8", "d26bb7bdbdf21ae401ead2092bf2bb4bf57fe44a62f5eaa5025280720ace8a40", [:mix], [], "hexpm", "d5b26da66603bb56c933c65214c72152f0de9a6ea53618b56d63302a68f6a90e"}, "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, "mail": {:hex, :mail, "0.3.1", "cb0a14e4ed8904e4e5a08214e686ccf6f9099346885db17d8c309381f865cc5c", [:mix], [], "hexpm", "1db701e89865c1d5fa296b2b57b1cd587587cca8d8a1a22892b35ef5a8e352a6"}, + "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, "mimerl": {:hex, :mimerl, "1.3.0", "d0cd9fc04b9061f82490f6581e0128379830e78535e017f7780f37fea7545726", [:rebar3], [], "hexpm", "a1e15a50d1887217de95f0b9b0793e32853f7c258a5cd227650889b38839fe9d"},