Skip to content

Commit

Permalink
More typespecs (#482)
Browse files Browse the repository at this point in the history
* lib/earmark.ex: added type specs

* lib/earmark/ast_tools.ex: added typespecs

* lib/earmark/error.ex: added Earmark.Error.t

* lib/earmark/cli/implementation.ex: added typespecs for Earmark.Cli.Implementation.run/1
  • Loading branch information
bradhanks authored Mar 3, 2024
1 parent 39a2045 commit 3fa1708
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 14 deletions.
1 change: 1 addition & 0 deletions lib/earmark.ex
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ defmodule Earmark do
Accesses current hex version of the `Earmark` application. Convenience for
`iex` usage.
"""
@spec version :: String.t()
def version() do
with {:ok, version} <- :application.get_key(:earmark, :vsn),
do: to_string(version)
Expand Down
49 changes: 36 additions & 13 deletions lib/earmark/ast_tools.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ defmodule Earmark.AstTools do
...(3)> [{"target", "_blank"}, title: "where?"])
[{"alt", "nowhere"}, {"href", "url"}, {"target", "_blank nonsense"}, {"title", "where?"}]
"""
@spec merge_atts(Earmark.ast_attributes(), map() | Keyword.t()) :: Earmark.ast_attributes()
def merge_atts(attrs, new)

def merge_atts(attrs, new) do
attrs
|> Enum.into(%{})
Expand All @@ -27,28 +29,40 @@ defmodule Earmark.AstTools do
end

@doc """
Convenience function to access an attribute
Convenience function to access an attribute from an AST node or a list of attributes
iex(4)> find_att_in_node({"a", [{"class", "link"}], [], %{}}, "class")
"link"
iex(5)> find_att_in_node({"a", [{"class", "link"}], [], %{}}, "target")
nil
iex(6)> find_att_in_node({"a", [{"class", "link"}], [], %{}}, "target", :default)
:default
iex(7)> find_att_in_node([{"class", "link"}], "class")
iex(6)> find_att_in_node([{"class", "link"}], "class")
"link"
iex(8)> find_att_in_node([{"class", "link"}], "target")
iex(7)> find_att_in_node([{"class", "link"}], "target")
nil
"""
@spec find_att_in_node(Earmark.ast_node() | Earmark.ast_attributes(), binary()) :: any()
def find_att_in_node(node_or_atts, att), do: _find_att(node_or_atts, att)

@doc """
Convenience function to access an attribute from an AST node or a list of attributes with a default value.
iex(8)> find_att_in_node({"a", [{"class", "link"}], [], %{}}, "target", :default)
:default
iex(9)> find_att_in_node([{"class", "link"}], "target", :default)
:default
"""
def find_att_in_node(node_or_atts, att), do: _find_att(node_or_atts, att)
def find_att_in_node(node_or_atts, att, default), do: _find_att_with_default(node_or_atts, att, default)

@spec find_att_in_node(Earmark.ast_node() | Earmark.ast_attributes(), binary(), any()) :: any()
def find_att_in_node(node_or_atts, att, default),
do: _find_att_with_default(node_or_atts, att, default)

@doc """
A convenience function that extracts the original attributes to be merged with new attributes
Expand All @@ -58,6 +72,7 @@ defmodule Earmark.AstTools do
{"img", [{"alt", "here there"}, {"src", "there"}], [], %{some: "meta"}}
"""
@spec merge_atts_in_node(Earmark.ast_node(), map() | Keyword.t()) :: Earmark.ast_node()
def merge_atts_in_node({tag, atts, content, meta}, new_atts) do
{tag, merge_atts(atts, new_atts), content, meta}
end
Expand All @@ -71,34 +86,42 @@ defmodule Earmark.AstTools do
{"p", "text"}
"""
def node_only_fn(fun), do:
fn {_, _, _, _} = quad -> fun.(quad)
text -> text
@spec node_only_fn((Earmark.ast_node() -> any())) :: (Earmark.ast_node() | binary() -> any())
def node_only_fn(fun),
do: fn
{_, _, _, _} = quad -> fun.(quad)
text -> text
end

defp _combine_atts(_key, original, new), do: "#{new} #{original}"

defp _find_att(node_or_atts, att)
defp _find_att({_, atts, _, _}, att), do: _find_att(atts, att)

defp _find_att(atts, att) do
atts
|> Enum.find_value(fn {key, value} -> if att == key, do: value end)
end

defp _find_att_with_default(node_or_atts, att, default)
defp _find_att_with_default({_, atts, _, _}, att, default), do: _find_att_with_default(atts, att, default)

defp _find_att_with_default({_, atts, _, _}, att, default),
do: _find_att_with_default(atts, att, default)

defp _find_att_with_default(atts, att, default) do
atts
|> Enum.find_value(default, fn {key, value} -> if att == key, do: value end)
end

defp _stringyfy(mappy)

defp _stringyfy(map) when is_map(map) do
map |> Enum.into([]) |> _stringyfy()
end

defp _stringyfy(list) when is_list(list) do
list |> Enum.map(fn {k, v} -> {to_string(k), v} end) |> Enum.into(%{})
end

end

# SPDX-License-Identifier: Apache-2.0
2 changes: 1 addition & 1 deletion lib/earmark/cli/implementation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ defmodule Earmark.Cli.Implementation do
{:stdio, "<html>\n <h1>\nShort2</h1>\n<p>\n<em>Short3</em></p>\n<!-- SPDX-License-Identifier: Apache-2.0 -->\n\n</html>\n"}
"""

@spec run(String.t() | [String.t()]) :: {:stdio, String.t()} | {:stderr, String.t()}
def run(argv) do
argv
|> parse_args()
Expand Down
2 changes: 2 additions & 0 deletions lib/earmark/error.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ defmodule Earmark.Error do

defexception [:message]

@type t :: %__MODULE__{message: binary()}

@doc false
def exception(msg), do: %__MODULE__{message: msg}

Expand Down

0 comments on commit 3fa1708

Please sign in to comment.