0
votes

I am trying to improve the code for my Elixir library which looks like this:

def dirs(path, regex_dir \\ ".+") when (is_bitstring(path) or is_list(path)) do
  file_path = case String.valid? path do
    true -> [path]
    false -> path
  end
  do_dirs(file_path, [], regex_dir)
end

defp do_dirs([], result, regex_dir) do
  result
end

defp do_dirs(paths ,result, regex_dir) do
  [h | t] = paths
  do_dirs(t, result ++ dirs_list(h, regex_dir), regex_dir)
end

defp dirs_list(path, regex_dir) when is_bitstring(path) do
  Finder.new()
  |> Finder.with_directory_regex(Regex.compile!(regex_dir))
  |> Finder.only_directories()
  |> Finder.find(Path.expand(path))
  |> Enum.to_list
  |> Enum.sort
end

I especially hate the part where I check if file_path is a valid string but I can't figure out a way to beautify it or make it more idiomatic Elixir. I want dirs to be able to accept both bitstring and list arguments.

1

1 Answers

2
votes

I would extract the part that normalizes the path into a separate method. It basically needs to handle two cases:

  • bitstring
  • list of bitstings

However, due to the fact that single quoted strings are implemented as character lists, I see a problem when someone accidentally passes such a value to your library. It will be detected as a list and probably result in an error deep inside the library instead of giving a helpful error message. This means you need to handle the following two cases as well:

  • character list
  • list of character lists

I suggest you try to convert potential character lists to string where appropriate.

def dirs(path, regex_dir \\ ".+") do
  path
  |> normalize_path
  |> do_dirs([], regex_dir)
end

# list of bitstrings
defp normalize_path([path | rest]) when is_bitstring(path) do
  [path | normalize_path(rest)]
end

# list of character lists
defp normalize_path([path | rest]) when is_list(path) do
  [to_string(path) | normalize_path(rest)]
end

defp normalize_path([]) do
  []
end

# bitstring
defp normalize_path(path) when is_bitstring(path) do
  [path]
end

# character list
defp normalize_path(path) when is_list(path) do
  [to_string(path)]
end

It will do the following conversions:

normalize_path 'foo'             #=> ["foo"]
normalize_path "foo"             #=> ["foo"]
normalize_path ['foo', 'bar']    #=> ["foo", "bar"]
normalize_path ["foo", "bar"]    #=> ["foo", "bar"]
normalize_path ["foo", 'bar']    #=> ["foo", "bar"]
normalize_path ['foo', "bar"]    #=> ["foo", "bar"]