summaryrefslogtreecommitdiff
path: root/script-beta/src/file-uri.lua
blob: 8acd4f648d917fcbd07536102faa76a4bbfaae7b (plain)
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
local platform = require 'bee.platform'

local esc = {
    [':'] = '%3A',
    ['/'] = '%2F',
    ['?'] = '%3F',
    ['#'] = '%23',
    ['['] = '%5B',
    [']'] = '%5D',
    ['@'] = '%40',

    ['!'] = '%21', -- sub-delims
    ['$'] = '%24',
    ['&'] = '%26',
    ["'"] = '%27',
    ['('] = '%28',
    [')'] = '%29',
    ['*'] = '%2A',
    ['+'] = '%2B',
    [','] = '%2C',
    [';'] = '%3B',
    ['='] = '%3D',

    [' '] = '%20',
}

local escPatt = '[^%w%-%.%_%~%/]'

local function normalize(str)
    return str:gsub('%%(%x%x)', function (n)
        return string.char(tonumber(n, 16))
    end)
end

local m = {}

-- c:\my\files               --> file:///c%3A/my/files
-- /usr/home                 --> file:///usr/home
-- \\server\share\some\path  --> file://server/share/some/path

--- path -> uri
---@param path string
---@return string uri
function m.encode(path)
    local authority = ''
    if platform.OS == 'Windows' then
        path = path:gsub('\\', '/')
    end

    if path:sub(1, 2) == '//' then
        local idx = path:find('/', 3)
        if idx then
            authority = path:sub(3, idx)
            path = path:sub(idx + 1)
            if path == '' then
                path = '/'
            end
        else
            authority = path:sub(3)
            path = '/'
        end
    end

    if path:sub(1, 1) ~= '/' then
        path = '/' .. path
    end

    -- lower-case windows drive letters in /C:/fff or C:/fff
    if path:match '/%u:' then
        path = path:lower()
    end

    local uri = 'file://'
        .. authority:gsub(escPatt, esc)
        .. path:gsub(escPatt, esc)
    return uri
end

-- file:///c%3A/my/files          --> c:\my\files
-- file:///usr/home               --> /usr/home
-- file://server/share/some/path  --> \\server\share\some\path

--- uri -> path
---@param uri string
---@return string path
function m.decode(uri)
    local scheme, authority, path = uri:match('([^:]*):?/?/?([^/]*)(.*)')
    if not scheme then
        return ''
    end
    scheme    = normalize(scheme)
    authority = normalize(authority)
    path      = normalize(path)
    local value
    if scheme == 'file' and #authority > 0 and #path > 1 then
        value = '//' .. authority .. path
    elseif path:match '/%a:' then
        value = path:sub(2, 2):lower() .. path:sub(3)
    else
        value = path
    end
    if platform.OS == 'Windows' then
        value = value:gsub('/', '\\')
    end
    return value
end

return m