1-- SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2--
3-- SPDX-License-Identifier: GPL-3.0-or-later
4
5---@class wt.path
6local M = {}
7
8---Split path into components
9---@param path string
10---@return string[]
11function M.split_path(path)
12 local parts = {}
13 for part in path:gmatch("[^/]+") do
14 table.insert(parts, part)
15 end
16 return parts
17end
18
19---Calculate relative path from one absolute path to another
20---@param from string absolute path of starting directory
21---@param to string absolute path of target
22---@return string relative path
23function M.relative_path(from, to)
24 if from == to then
25 return "./"
26 end
27
28 local from_parts = M.split_path(from)
29 local to_parts = M.split_path(to)
30
31 local common = 0
32 for i = 1, math.min(#from_parts, #to_parts) do
33 if from_parts[i] == to_parts[i] then
34 common = i
35 else
36 break
37 end
38 end
39
40 local up_count = #from_parts - common
41 local result = {}
42
43 for _ = 1, up_count do
44 table.insert(result, "..")
45 end
46
47 for i = common + 1, #to_parts do
48 table.insert(result, to_parts[i])
49 end
50
51 if #result == 0 then
52 return "./"
53 end
54
55 return table.concat(result, "/")
56end
57
58---Convert branch name to worktree path based on style
59---@param root string project root path
60---@param branch string branch name
61---@param style string "nested" or "flat"
62---@param separator? string separator for flat style (default "_")
63---@return string worktree path
64function M.branch_to_path(root, branch, style, separator)
65 if style == "flat" then
66 local sep = separator or "_"
67 local escaped_sep = sep:gsub("%%", "%%%%")
68 local flat_name = branch:gsub("/", escaped_sep)
69 return root .. "/" .. flat_name
70 end
71 -- nested style (default): preserve slashes
72 return root .. "/" .. branch
73end
74
75---Check if path_a is inside (or equal to) path_b
76---@param path_a string the path to check
77---@param path_b string the container path
78---@return boolean
79function M.path_inside(path_a, path_b)
80 -- Normalize: ensure no trailing slash for comparison
81 path_b = path_b:gsub("/$", "")
82 path_a = path_a:gsub("/$", "")
83 return path_a == path_b or path_a:sub(1, #path_b + 1) == path_b .. "/"
84end
85
86---Escape special Lua pattern characters in a string
87---@param str string
88---@return string
89function M.escape_pattern(str)
90 return (str:gsub("([%%%-%+%[%]%(%)%.%^%$%*%?])", "%%%1"))
91end
92
93return M