defmodule Dragon.Tools.File do @moduledoc """ File handling tools. """ use Dragon.Context import Dragon.Tools.Dict def seek!(fd, offset) do with {:ok, _} <- :file.position(fd, offset), do: fd end def open_file(path) do case File.open(path, [:utf8, :read]) do {:ok, fd} -> {:ok, fd} {:error, :enoent} -> abort("Cannot open file '#{path}', cannot continue") {:error, :eisdir} -> abort("Cannot open file '#{path}', it is a folder. Cannot continue") err -> IO.inspect(err, label: "File open error") abort("Cannot open file, cannot continue") end end def with_open_file(path, func) do with {:ok, fd} <- open_file(path) do try do func.(fd) after :ok = File.close(fd) end end end def write_file(dest, content) do Dragon.Tools.File.makedirs_for_file(dest) case File.write(dest, content) do :ok -> :ok {:error, err} -> abort("Cannot write file '#{dest}': #{err}") end end def drop_root(x, y, z \\ [absolute: false]) def drop_root("", path, _), do: path def drop_root(root, path, opts) do if String.slice(path, 0..(String.length(root) - 1)) == root do start = String.length(root) start = if opts[:absolute], do: start, else: start + 1 String.slice(path, start..-1) else path end end def find_index_file(root, index) do case {index, File.stat(root)} do {index, {:ok, %File.Stat{type: :directory}}} when not is_nil(index) -> find_index_file(Path.join(root, index), nil) {_, {:ok, %File.Stat{type: :regular}}} -> {:ok, root} {_, err} -> err end end def find_file(root, path) do with :error <- find_file_variant(root, Path.split(path)), do: {:error, "File not found (#{Path.join(root, path)})"} end ## TODO: make "directory ok" flag def try_variant(root, part, rest, on_error \\ nil) do case {valid_part(root, part), rest} do {{:directory, _}, []} -> :error {{:directory, path}, more} -> find_file_variant(path, more) {{:file, path}, []} -> {:ok, path} {{:file, _}, _} -> :error _ -> if on_error do on_error.() else :error end end end def find_file_variant(root, [part | rest]) do try_variant(root, part, rest, fn -> case part do "_" <> part -> try_variant(root, part, rest) part -> try_variant(root, "_" <> part, rest) end end) end def find_file_variant(_, []), do: :error def valid_part(root, part) do path = Path.join(root, part) case File.stat(path) do {:ok, %{type: :directory}} -> {:directory, path} {:ok, %{type: :regular}} -> {:file, path} _ -> :error end end def export_fname(fname) do if String.at(fname, 0) == "_" do String.slice(fname, 1..-1) else fname end |> String.downcase() end def makedirs_for_file(dest) do folder = Path.dirname(dest) case File.mkdir_p(folder) do :ok -> :ok err -> abort("Cannot mkdir '#{folder}': #{inspect(err)}") end end ############################################################################## @doc """ Within walk_tree, investigate a file to determine what its type is and store accordingly. If it begins with the dragon header, consider it a dragon template. Otherwise look at the file extension. """ def scan_file(dragon, path, _) do with {:ok, type, rel_path} <- file_type(path) do put_into(dragon, [:files, type, rel_path], []) end end def file_type(path) do type = with_open_file(path, fn fd -> case IO.binread(fd, 10) do "--- dragon" -> :dragon _ -> case Path.extname(path) do ".scss" -> :scss _ -> :file end end end) [_ | rel_path] = Path.split(path) rel_path = Path.join(rel_path) {:ok, type, rel_path} end # need an os-agnostic way to do this def get_true_path(path) do with {p, 0} <- System.cmd("sh", ["-c", "cd #{path} && /bin/pwd -P"]), do: {:ok, String.trim(p)} end end