diff --git a/base/path.jl b/base/path.jl index 3b8124f34f174a..218184b339735a 100644 --- a/base/path.jl +++ b/base/path.jl @@ -613,3 +613,30 @@ relpath(path::AbstractString, startpath::AbstractString) = for f in (:isdirpath, :splitdir, :splitdrive, :splitext, :normpath, :abspath) @eval $f(path::AbstractString) = $f(String(path)) end + +""" + uripath(path::AbstractString) + +Encode `path` as a URI as per [RFC1738](https://www.rfc-editor.org/rfc/rfc1738), +[RFC3986](https://www.rfc-editor.org/rfc/rfc3986), and the [Freedesktop File URI +spec](https://www.freedesktop.org/wiki/Specifications/file-uri-spec/). + +## Examples + +```julia-repl +julia> uripath("/home/user/example file.jl") +"file:///home/user/example%20file.jl" +``` +""" +function uripath(path::String) + percent_escape(s) = + '%' * join(map(b -> string(b, base=16), codeunits(s)), '%') + encode_uri_component(s) = + replace(s, r"[^A-Za-z0-9\-_.~]+" => percent_escape) + string("file://", gethostname(), '/', + join(map(encode_uri_component, + split(abspath(path), path_separator_re, keepempty=false)), + '/')) +end + +uripath(path::AbstractString) = uripath(String(path)) diff --git a/test/path.jl b/test/path.jl index 2f4f2d0983a58e..5d9c5ce74c57e3 100644 --- a/test/path.jl +++ b/test/path.jl @@ -311,6 +311,19 @@ test_relpath() end + @testset "uripath" begin + host = gethostname() + drive, absdrive = if Sys.iswindows() "C:$sep", "C:/" else "", "/" end + @test Base.Filesystem.uripath("$(absdrive)some$(sep)file.txt") == "file://$host/$(drive)some/file.txt" + @test Base.Filesystem.uripath("$(absdrive)another$(sep)$(sep)folder$(sep)file.md") == "file://$host/$(drive)another/folder/file.md" + @test Base.Filesystem.uripath("$(absdrive)some file with ^odd% chars") == "file://$host/$(drive)some%20file%20with%20%5eodd%25%20chars" + @test Base.Filesystem.uripath("$(absdrive)weird chars like @#&()[]{}") == "file://$host/$(drive)weird%20chars%20like%20%40%23%26%28%29%5b%5d%7b%7d" + @test Base.Filesystem.uripath("$absdrive") == "file://$host/$drive" + @test Base.Filesystem.uripath(".") == Base.Filesystem.uripath(pwd()) + @test Base.Filesystem.uripath("$(absdrive)unicode$(sep)Δεδομένα") == "file://$host/$(drive)unicode/%ce%94%ce%b5%ce%b4%ce%bf%ce%bc%ce%ad%ce%bd%ce%b1" + @test Base.Filesystem.uripath("$(absdrive)unicode$(sep)🧮🐛🔨") == "file://$host/$(drive)unicode/%f0%9f%a7%ae%f0%9f%90%9b%f0%9f%94%a8" + end + if Sys.iswindows() @testset "issue #23646" begin @test lowercase(relpath("E:\\a\\b", "C:\\c")) == "e:\\a\\b"