summaryrefslogtreecommitdiffstats
path: root/aconf/path/base.lua
blob: 4e2bf8f60eb7ca8a40c89b4c41af50f9c00e081a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
--[[
Copyright (c) 2012-2015 Kaarle Ritvanen
See LICENSE file for license details
--]]

local M = {}


local object = require('aconf.object')
local class = object.class

local map = require('aconf.util').map


M.Special = class()
function M.Special:tostring() return self.symbol end

local Up = class(M.Special)
Up.symbol = '..'

local Wildcard = class(M.Special)
Wildcard.symbol = '*'



M.Syntax = class()

M.Syntax.up = Up()
M.Syntax.wildcard = Wildcard()


function M.Syntax:get_special(comp)
   return ({['..']=M.Syntax.up, ['*']=M.Syntax.wildcard})[comp]
end


function M.Syntax:is_absolute(path) return path:sub(1, 1) == '/' end


function M.Syntax:escape(comp)
   if type(comp) == 'table' then
      if object.isinstance(object.toinstance(comp), M.Special) then
	 return comp:tostring()
      end
   end
   if type(comp) == 'number' then return tostring(comp) end
   local res = comp:gsub('([\\/])', '\\%1')
   return (self:get_special(res) or tonumber(res)) and '\\'..res or res
end


function M.Syntax:rawjoin(p1, p2, ...)
   if not p2 then return p1 end
   if not self:is_absolute(p2) then p2 = '/'..p2 end
   return self:rawjoin((p1 == '/' and '' or p1)..p2, ...)
end

function M.Syntax:join(parent, ...)
   local args = map(function(c) return self:escape(c) end, {...})
   if parent > '' then table.insert(args, 1, parent) end
   return self:rawjoin(table.unpack(args))
end


function M.Syntax:split(path)
   local res = {}
   local comp = ''
   local escaped

   local function merge(s)
      if s > '' then
	 table.insert(
	    res, not escaped and (self:get_special(s) or tonumber(s)) or s
	 )
      end
   end

   while true do
      local prefix, sep, suffix = path:match('([^\\/]*)([\\/])(.*)')
      if not prefix then
	 merge(comp..path)
	 return res
      end

      comp = comp..prefix
      if sep == '\\' then
	 comp = comp..suffix:sub(1, 1)
	 escaped = true
	 path = suffix:sub(2, -1)
      else
	 merge(comp)
	 comp = ''
	 escaped = false
	 path = suffix
      end
   end
end


function M.Syntax:is_unique(path)
   for _, comp in ipairs(self:split(path)) do
      if comp == self.wildcard then return false end
   end
   return true
end

function M.Syntax:is_subordinate(p1, p2)
   p1 = self:split(p1)
   for i, comp in ipairs(self:split(p2)) do
      if p1[i] ~= comp then return false end
   end
   return true
end


function M.Syntax:to_absolute(path, base)
   if not self:is_absolute(path) then
      path = base..(base ~= '/' and '/' or '')..path
   end
   local comps = self:split(path)
   local i = 1
   while i <= #comps do
      if comps[i] == self.up then
	 if i == 1 then error('Invalid path: '..path) end
	 table.remove(comps, i - 1)
	 table.remove(comps, i - 1)
	 i = i - 1
      else i = i + 1 end
   end
   return self:join('/', table.unpack(comps))
end


function M.Syntax:parent(path)
   local comps = self:split(path)
   table.remove(comps)
   return self:join('/', table.unpack(comps))
end

function M.Syntax:name(path)
   local comps = self:split(path)
   return comps[#comps]
end


function M.Syntax:export(mod)
   if not mod then mod = {} end
   for k, v in pairs(M.Syntax) do
      if k ~= 'export' then
	 mod[k] = type(v) == 'function' and function(...)
	    return v(self, ...)
         end or v
      end
   end
   return mod
end


return M