Current section
Files
Jump to
Current section
Files
lib/prompt_runner/paths.ex
defmodule PromptRunner.Paths do
@moduledoc false
@spec resolve(String.t() | nil, String.t() | nil) :: String.t() | nil
def resolve(path, base_dir \\ nil)
def resolve(nil, _base_dir), do: nil
def resolve(path, base_dir) when is_binary(path) do
path
|> expand(base_dir)
|> canonicalize()
end
defp expand(path, base_dir) do
cond do
Path.type(path) == :absolute -> Path.expand(path)
is_binary(base_dir) -> Path.expand(path, base_dir)
true -> Path.expand(path)
end
end
defp canonicalize(path) do
case Path.split(path) do
[root | rest] -> walk(root, rest)
[] -> path
end
end
defp walk(current, []), do: current
defp walk(current, [segment | rest]) do
candidate = Path.join(current, segment)
case File.lstat(candidate) do
{:ok, %File.Stat{type: :symlink}} ->
candidate
|> resolve_link_target()
|> walk(rest)
{:ok, _stat} ->
walk(candidate, rest)
{:error, _reason} ->
Path.join(current, Path.join([segment | rest]))
end
end
defp resolve_link_target(path) do
case File.read_link(path) do
{:ok, target} ->
target =
if Path.type(target) == :absolute do
Path.expand(target)
else
Path.expand(target, Path.dirname(path))
end
canonicalize(target)
{:error, _reason} ->
path
end
end
end