Skip to content

Commit 2616ce0

Browse files
committed
Added a Project.toml and a compat macro.
1 parent da1ffd0 commit 2616ce0

File tree

5 files changed

+240
-3
lines changed

5 files changed

+240
-3
lines changed

Project.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name = "FilePaths"
2+
uuid = "8fc22ac5-c921-52a6-82fd-178b2807b824"
3+
authors = ["Rory Finnegan"]
4+
version = "0.8.0"
5+
6+
[deps]
7+
FilePathsBase = "48062228-2e41-5def-b9a4-89aafe57970f"
8+
MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
9+
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
10+
URIParser = "30578b45-9adc-5946-b283-645ec420af67"
11+
12+
[extras]
13+
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
14+
15+
[targets]
16+
test = ["Test"]

src/FilePaths.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ __precompile__()
22

33
module FilePaths
44

5-
using Reexport, URIParser
5+
using Reexport, URIParser, MacroTools
66
@reexport using FilePathsBase
77

88
include("uri.jl")
9+
include("compat.jl")
910

1011
end # end of module

src/compat.jl

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
macro compat(ex)
2+
new_ex = compat_exp(deepcopy(ex))
3+
4+
return quote
5+
$ex
6+
$new_ex
7+
end |> esc
8+
end
9+
10+
function compat_exp(ex::Expr)
11+
fdef = splitdef(ex)
12+
args = Symbol[]
13+
kwargs = Expr[]
14+
convert_vars = Symbol[]
15+
body = Expr[]
16+
params = Symbol[]
17+
18+
if haskey(fdef, :whereparams)
19+
# Identify any where params that are a subtype of AbstractPath
20+
for (i, p) in enumerate(fdef[:whereparams])
21+
# If the param is a subtype of AbstracPath
22+
# then we store the lookup symbol in the params array
23+
if _ispath(p.args[2])
24+
p.args[2] = :(Union{AbstractString, $(p.args[2])})
25+
push!(params, p.args[1])
26+
end
27+
end
28+
end
29+
30+
if haskey(fdef, :args)
31+
# Identify any args and kwargs that need to modified
32+
for (i, a) in enumerate(fdef[:args])
33+
if isa(a, Expr)
34+
if (a.head) === :kw
35+
T = a.args[1]
36+
if T.args[2] in params
37+
push!(convert_vars, T.args[1])
38+
elseif _ispath(T.args[2])
39+
T.args[2] = :(Union{AbstractString, $(T.args[2])})
40+
push!(convert_vars, T.args[1])
41+
end
42+
43+
push!(args, T.args[1])
44+
else
45+
if a.args[2] in params
46+
push!(convert_vars, a.args[1])
47+
elseif _ispath(a.args[2])
48+
a.args[2] = :(Union{AbstractString, $(a.args[2])})
49+
push!(convert_vars, a.args[1])
50+
end
51+
52+
push!(args, a.args[1])
53+
end
54+
elseif isa(a, Symbol)
55+
push!(args, a)
56+
else
57+
throw(ArgumentError("Uknown argument type $a"))
58+
end
59+
end
60+
end
61+
62+
if haskey(fdef, :kwargs)
63+
for (i, k) in enumerate(fdef[:kwargs])
64+
T = k.args[1]
65+
if T.args[2] in params
66+
push!(convert_vars, T.args[1])
67+
elseif _ispath(T.args[2])
68+
T.args[2] = :(Union{AbstractString, $(T.args[2])})
69+
push!(convert_vars, T.args[1])
70+
end
71+
push!(kwargs, :($(T.args[1])=$(T.args[1])))
72+
end
73+
end
74+
75+
for v in convert_vars
76+
# push!(body, :(println($v)))
77+
push!(body, :($v = Path($v)))
78+
# push!(body, :(println($v)))
79+
end
80+
81+
push!(body, :(result = $(fdef[:name])($(args...); $(kwargs...))))
82+
83+
if haskey(fdef, :rtype) && (fdef[:rtype] in params || _ispath(fdef[:rtype]))
84+
push!(body, :(result = string(result)))
85+
# push!(body, :(println(typeof(result))))
86+
fdef[:rtype] = :String
87+
end
88+
89+
push!(body, :(return result))
90+
91+
fdef[:body].args = body
92+
93+
return MacroTools.combinedef(fdef)
94+
end
95+
96+
function _ispath(t::Symbol)
97+
T = eval(t)
98+
return T <: AbstractPath
99+
end

test/compat.jl

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
module TestPkg
2+
3+
using FilePaths
4+
5+
# Test for basic definitions taking abstract paths
6+
FilePaths.@compat function typical(x::AbstractPath, y::AbstractPath)
7+
return relative(x, y)
8+
end
9+
10+
# Test for definition taking parameterized paths
11+
FilePaths.@compat function parameterized(x::P, y::P) where P <: AbstractPath
12+
return relative(x, y)
13+
end
14+
15+
# Test for definition with a path return type
16+
# (indicating that we should convert it back to a string)
17+
FilePaths.@compat function rtype(x::P, y::P)::P where P <: AbstractPath
18+
return relative(x, y)
19+
end
20+
21+
# Test for a mixed input definition
22+
# z is an unused variable to make sure untyped variables don't break the macro
23+
FilePaths.@compat function mixed(x::P, y::String, z)::P where P <: AbstractPath
24+
return x / y
25+
end
26+
27+
# Test kwargs
28+
FilePaths.@compat function kwargs(x::AbstractPath; y::AbstractPath=cwd())::AbstractPath
29+
return relative(x, y)
30+
end
31+
32+
# Test optional args
33+
FilePaths.@compat function optargs(x::AbstractPath, y::AbstractPath=cwd())::AbstractPath
34+
return relative(x, y)
35+
end
36+
37+
# Test for inline definitions
38+
FilePaths.@compat inline(x::AbstractPath, y::AbstractPath) = relative(x, y)
39+
40+
end
41+
42+
@testset "@compat" begin
43+
cd(abs(parent(Path(@__FILE__)))) do
44+
reg = Sys.iswindows() ? "..\\src\\FilePaths.jl" : "../src/FilePaths.jl"
45+
@test ispath(reg)
46+
p = Path(reg)
47+
48+
@testset "typical" begin
49+
# Passing paths should should work just like calling relative
50+
@test TestPkg.typical(p, home()) == relative(p, home())
51+
52+
# Passing strings should now also work just like calling relative
53+
@test TestPkg.typical(string(p), string(home())) == relative(p, home())
54+
55+
# Mixing strings and paths should also work here
56+
@test TestPkg.typical(string(p), home()) == relative(p, home())
57+
end
58+
59+
@testset "parameterized" begin
60+
# Passing paths should should work just like calling relative
61+
@test TestPkg.parameterized(p, home()) == relative(p, home())
62+
63+
# Passing strings should now also work just like calling relative
64+
@test TestPkg.parameterized(string(p), string(home())) == relative(p, home())
65+
66+
# Mixing strings and paths should error because x and y need to be the same type
67+
@test_throws MethodError TestPkg.parameterized(string(p), home())
68+
end
69+
70+
@testset "rtype" begin
71+
# Passing paths should should work just like calling relative
72+
@test TestPkg.rtype(p, home()) == relative(p, home())
73+
74+
# Passing strings should also convert the output to a string
75+
@test TestPkg.rtype(string(p), string(home())) == string(relative(p, home()))
76+
77+
# Mixing strings and paths should error because x and y need to be the same type
78+
@test_throws MethodError TestPkg.rtype(string(p), home())
79+
end
80+
81+
@testset "mixed" begin
82+
# Passing in a path as the first argument should return the same output as p / home()
83+
prefix = parent(p)
84+
suffix = basename(p)
85+
@test TestPkg.mixed(prefix, suffix, 2) == p
86+
87+
# Passing 2 strings should also convert the output to a string
88+
@test TestPkg.mixed(string(prefix), suffix, 2) == string(p)
89+
90+
# Passing a path where we explicitly want a string will error
91+
@test_throws MethodError TestPkg.mixed(prefix, Path(suffix), 2)
92+
end
93+
94+
@testset "optargs" begin
95+
# Passing paths should should work just like calling relative
96+
@test TestPkg.optargs(p, home()) == relative(p, home())
97+
98+
# Passing strings should also return the relative path as a string
99+
@test TestPkg.optargs(string(p), string(home())) == string(relative(p, home()))
100+
101+
# With optional arguments we should be able to mix strings on either argument
102+
@test TestPkg.optargs(p, string(home())) == string(relative(p, home()))
103+
@test TestPkg.optargs(string(p), home()) == string(relative(p, home()))
104+
105+
# Use default
106+
@test TestPkg.optargs(p) == relative(p, cwd())
107+
@test TestPkg.optargs(string(p)) == string(relative(p, cwd()))
108+
end
109+
110+
@testset "inline" begin
111+
# Passing paths should should work just like calling relative
112+
@test TestPkg.inline(p, home()) == relative(p, home())
113+
114+
# Passing strings should now also work just like calling relative
115+
@test TestPkg.inline(string(p), string(home())) == relative(p, home())
116+
117+
# Mixing strings and paths should also work here
118+
@test TestPkg.inline(string(p), home()) == relative(p, home())
119+
end
120+
end
121+
end

test/runtests.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
using FilePaths
2-
using Compat
3-
using Compat.Test
2+
using Test
43

54
@testset "FilePaths" begin
65

76
include("test_uri.jl")
7+
include("compat.jl")
88

99
end

0 commit comments

Comments
 (0)