|
| 1 | +module URIs2 |
| 2 | + |
| 3 | +import URIs |
| 4 | + |
| 5 | +export URI, uri2filepath, filepath2uri, @uri_str |
| 6 | + |
| 7 | +struct URI |
| 8 | + scheme::Union{String,Nothing} |
| 9 | + authority::Union{String,Nothing} |
| 10 | + path::String |
| 11 | + query::Union{String,Nothing} |
| 12 | + fragment::Union{String,Nothing} |
| 13 | +end |
| 14 | + |
| 15 | +function percent_decode(str::AbstractString) |
| 16 | + return URIs.unescapeuri(str) |
| 17 | +end |
| 18 | + |
| 19 | +function URI(value::AbstractString) |
| 20 | + m = match(r"^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?", value) |
| 21 | + |
| 22 | + m===nothing && error("Invalid argument.") |
| 23 | + |
| 24 | + return URI( |
| 25 | + m.captures[2], |
| 26 | + m.captures[4]===nothing ? nothing : percent_decode(m.captures[4]), |
| 27 | + m.captures[5]===nothing ? nothing : percent_decode(m.captures[5]), |
| 28 | + m.captures[7]===nothing ? nothing : percent_decode(m.captures[7]), |
| 29 | + m.captures[9]===nothing ? nothing : percent_decode(m.captures[9]) |
| 30 | + ) |
| 31 | +end |
| 32 | + |
| 33 | +function URI(; |
| 34 | + scheme::Union{AbstractString,Nothing}=nothing, |
| 35 | + authority::Union{AbstractString,Nothing}=nothing, |
| 36 | + path::AbstractString="", |
| 37 | + query::Union{AbstractString,Nothing}=nothing, |
| 38 | + fragment::Union{AbstractString,Nothing}=nothing |
| 39 | + ) |
| 40 | + return URI(scheme, authority, path, query, fragment) |
| 41 | +end |
| 42 | + |
| 43 | +@inline function is_rfc3986_unreserved(c::Char) |
| 44 | + return 'A' <= c <= 'Z' || |
| 45 | + 'a' <= c <= 'z' || |
| 46 | + '0' <= c <= '9' || |
| 47 | + c == '-' || |
| 48 | + c == '.' || |
| 49 | + c == '_' || |
| 50 | + c == '~' |
| 51 | +end |
| 52 | + |
| 53 | +@inline function is_rfc3986_sub_delim(c::Char) |
| 54 | + return c == '!' || |
| 55 | + c == '$' || |
| 56 | + c == '&' || |
| 57 | + c == '\'' || |
| 58 | + c == '(' || |
| 59 | + c == ')' || |
| 60 | + c == '*' || |
| 61 | + c == '+' || |
| 62 | + c == ',' || |
| 63 | + c == ';' || |
| 64 | + c == '=' |
| 65 | +end |
| 66 | + |
| 67 | +@inline function is_rfc3986_pchar(c::Char) |
| 68 | + return is_rfc3986_unreserved(c) || |
| 69 | + is_rfc3986_sub_delim(c) || |
| 70 | + c == ':' || |
| 71 | + c == '@' |
| 72 | +end |
| 73 | + |
| 74 | +@inline function is_rfc3986_query(c::Char) |
| 75 | + return is_rfc3986_pchar(c) || c=='/' || c=='?' |
| 76 | +end |
| 77 | + |
| 78 | +@inline function is_rfc3986_fragment(c::Char) |
| 79 | + return is_rfc3986_pchar(c) || c=='/' || c=='?' |
| 80 | +end |
| 81 | + |
| 82 | +@inline function is_rfc3986_userinfo(c::Char) |
| 83 | + return is_rfc3986_unreserved(c) || |
| 84 | + is_rfc3986_sub_delim(c) || |
| 85 | + c == ':' |
| 86 | +end |
| 87 | + |
| 88 | +@inline function is_rfc3986_reg_name(c::Char) |
| 89 | + return is_rfc3986_unreserved(c) || |
| 90 | + is_rfc3986_sub_delim(c) |
| 91 | +end |
| 92 | + |
| 93 | +function encode(io::IO, s::AbstractString, issafe::Function) |
| 94 | + for c in s |
| 95 | + if issafe(c) |
| 96 | + print(io, c) |
| 97 | + else |
| 98 | + print(io, '%') |
| 99 | + print(io, uppercase(string(Int(c), base=16, pad=2))) |
| 100 | + end |
| 101 | + end |
| 102 | +end |
| 103 | + |
| 104 | +@inline function is_ipv4address(s::AbstractString) |
| 105 | + if length(s)==1 |
| 106 | + return '0' <= s[1] <= '9' |
| 107 | + elseif length(s)==2 |
| 108 | + return '1' <= s[1] <= '9' && '0' <= s[2] <= '9' |
| 109 | + elseif length(s)==3 |
| 110 | + return (s[1]=='1' && '0' <= s[2] <= '9' && '0' <= s[3] <= '9') || |
| 111 | + (s[1]=='2' && '0' <= s[2] <= '4' && '0' <= s[3] <= '9') || |
| 112 | + (s[1]=='2' && s[2] == '5' && '0' <= s[3] <= '5') |
| 113 | + else |
| 114 | + return false |
| 115 | + end |
| 116 | +end |
| 117 | + |
| 118 | +@inline function is_ipliteral(s::AbstractString) |
| 119 | + # TODO Implement this |
| 120 | + return false |
| 121 | +end |
| 122 | + |
| 123 | +function encode_host(io::IO, s::AbstractString) |
| 124 | + if is_ipv4address(s) || is_ipliteral(s) |
| 125 | + print(io, s) |
| 126 | + else |
| 127 | + # The host must be a reg-name |
| 128 | + encode(io, s, is_rfc3986_reg_name) |
| 129 | + end |
| 130 | +end |
| 131 | + |
| 132 | +function encode_path(io::IO, s::AbstractString) |
| 133 | + # TODO Write our own version |
| 134 | + print(io, URIs.escapepath(s)) |
| 135 | +end |
| 136 | + |
| 137 | +function Base.print(io::IO, uri::URI) |
| 138 | + scheme = uri.scheme |
| 139 | + authority = uri.authority |
| 140 | + path = uri.path |
| 141 | + query = uri.query |
| 142 | + fragment = uri.fragment |
| 143 | + |
| 144 | + if scheme!==nothing |
| 145 | + print(io, scheme) |
| 146 | + print(io, ':') |
| 147 | + end |
| 148 | + |
| 149 | + if authority!==nothing |
| 150 | + print(io, "//") |
| 151 | + |
| 152 | + idx = findfirst("@", authority) |
| 153 | + if idx !== nothing |
| 154 | + # <user>@<auth> |
| 155 | + userinfo = SubString(authority, 1:idx.start-1) |
| 156 | + host_and_port = SubString(authority, idx.start + 1) |
| 157 | + encode(io, userinfo, is_rfc3986_userinfo) |
| 158 | + print(io, '@') |
| 159 | + else |
| 160 | + host_and_port = SubString(authority, 1) |
| 161 | + end |
| 162 | + |
| 163 | + idx3 = findfirst(":", host_and_port) |
| 164 | + if idx3 === nothing |
| 165 | + encode_host(io, host_and_port) |
| 166 | + else |
| 167 | + # <auth>:<port> |
| 168 | + encode_host(io, SubString(host_and_port, 1:idx3.start-1)) |
| 169 | + print(io, SubString(host_and_port, idx3.start)) |
| 170 | + end |
| 171 | + end |
| 172 | + |
| 173 | + # Append path |
| 174 | + encode_path(io, path) |
| 175 | + |
| 176 | + if query!==nothing |
| 177 | + print(io, '?') |
| 178 | + encode(io, query, is_rfc3986_query) |
| 179 | + end |
| 180 | + |
| 181 | + if fragment!==nothing |
| 182 | + print(io, '#') |
| 183 | + encode(io, fragment, is_rfc3986_fragment) |
| 184 | + end |
| 185 | + |
| 186 | + return nothing |
| 187 | +end |
| 188 | + |
| 189 | +function Base.string(uri::URI) |
| 190 | + io = IOBuffer() |
| 191 | + |
| 192 | + print(io, uri) |
| 193 | + |
| 194 | + return String(take!(io)) |
| 195 | +end |
| 196 | + |
| 197 | +macro uri_str(ex) |
| 198 | + return URI(ex) |
| 199 | +end |
| 200 | + |
| 201 | +include("uri_helpers.jl") |
| 202 | + |
| 203 | +end |
0 commit comments