diff --git a/.JuliaFormatter.toml b/.JuliaFormatter.toml new file mode 100644 index 0000000..323237b --- /dev/null +++ b/.JuliaFormatter.toml @@ -0,0 +1 @@ +style = "blue" diff --git a/.github/workflows/Format.yml b/.github/workflows/Format.yml new file mode 100644 index 0000000..99d7ff8 --- /dev/null +++ b/.github/workflows/Format.yml @@ -0,0 +1,13 @@ +name: Format suggestions +on: + pull_request: + # this argument is not required if you don't use the `suggestion-label` input + types: [ opened, reopened, synchronize, labeled, unlabeled ] +jobs: + code-style: + runs-on: ubuntu-latest + steps: + - uses: julia-actions/julia-format@v3 + with: + version: '1' # Set `version` to '1.0.54' if you need to use JuliaFormatter.jl v1.0.54 (default: '1') + suggestion-label: 'format-suggest' # leave this unset or empty to show suggestions for all PRs \ No newline at end of file diff --git a/README.md b/README.md index b9f8da8..8924aac 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![codecov.io](https://codecov.io/github/rofinn/FilePathsBase.jl/coverage.svg?branch=master)](https://codecov.io/rofinn/FilePathsBase.jl?branch=master) [![](https://img.shields.io/badge/docs-stable-blue.svg)](https://rofinn.github.io/FilePathsBase.jl/stable) [![](https://img.shields.io/badge/docs-dev-blue.svg)](https://rofinn.github.io/FilePathsBase.jl/dev) +[![Code Style: Blue](https://img.shields.io/badge/code%20style-blue-4495d1.svg)](https://github.com/JuliaDiff/BlueStyle) FilePathsBase.jl provides a type based approach to working with filesystem paths in julia. diff --git a/docs/make.jl b/docs/make.jl index 7395620..8fba385 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,6 +1,5 @@ using Documenter, FilePathsBase, FilePathsBase.TestPaths - const SETUP_CODE = quote using FilePathsBase using FilePathsBase: /, join @@ -8,16 +7,11 @@ end DocMeta.setdocmeta!(FilePathsBase, :DocTestSetup, SETUP_CODE; recursive=true) -makedocs( +makedocs(; modules=[FilePathsBase], - format=Documenter.HTML( - prettyurls = get(ENV, "CI", nothing) == "true", - ), + format=Documenter.HTML(; prettyurls=get(ENV, "CI", nothing) == "true"), pages=[ - "Home" => "index.md", - "Design" => "design.md", - "FAQ" => "faq.md", - "API" => "api.md", + "Home" => "index.md", "Design" => "design.md", "FAQ" => "faq.md", "API" => "api.md" ], repo="https://github.com/rofinn/FilePathsBase.jl/blob/{commit}{path}#L{line}", sitename="FilePathsBase.jl", @@ -26,9 +20,9 @@ makedocs( # strict = true, ) -deploydocs( - repo = "github.com/rofinn/FilePathsBase.jl.git", - target = "build", - deps = nothing, - make = nothing, +deploydocs(; + repo="github.com/rofinn/FilePathsBase.jl.git", + target="build", + deps=nothing, + make=nothing, ) diff --git a/ext/FilePathsBaseMmapExt.jl b/ext/FilePathsBaseMmapExt.jl index b3ba481..70a2061 100644 --- a/ext/FilePathsBaseMmapExt.jl +++ b/ext/FilePathsBaseMmapExt.jl @@ -2,6 +2,8 @@ module FilePathsBaseMmapExt using Mmap using FilePathsBase -Mmap.mmap(fp::FilePathsBase.SystemPath, args...; kwargs...) = Mmap.mmap(string(fp), args...; kwargs...) +function Mmap.mmap(fp::FilePathsBase.SystemPath, args...; kwargs...) + return Mmap.mmap(string(fp), args...; kwargs...) +end -end #module \ No newline at end of file +end #module diff --git a/ext/FilePathsBaseTestExt.jl b/ext/FilePathsBaseTestExt.jl index f52a771..06d097f 100644 --- a/ext/FilePathsBaseTestExt.jl +++ b/ext/FilePathsBaseTestExt.jl @@ -1,751 +1,757 @@ module FilePathsBaseTestExt - using Dates: Dates - using FilePathsBase - using FilePathsBase: /, join - using Test - using FilePathsBase.TestPaths - - # NOTE: Most paths should test their own constructors as necessary. - - function TestPaths.test_registration(ps::PathSet{P}) where P <: AbstractPath - @testset "Path constructor" begin - str = string(ps.root) - @test tryparse(P, str) !== nothing - @test Path(str) == ps.root - @test p"foo/bar" == Path("foo/bar") +using Dates: Dates +using FilePathsBase +using FilePathsBase: /, join +using Test +using FilePathsBase.TestPaths + +# NOTE: Most paths should test their own constructors as necessary. + +function TestPaths.test_registration(ps::PathSet{P}) where {P<:AbstractPath} + @testset "Path constructor" begin + str = string(ps.root) + @test tryparse(P, str) !== nothing + @test Path(str) == ps.root + @test p"foo/bar" == Path("foo/bar") + end +end + +function TestPaths.test_show(ps::PathSet) + @testset "show" begin + str = string(ps.root) + # For windows paths + str = replace(str, "\\" => "/") + @test sprint(show, ps.root; context=:compat => true) == "p\"$str\"" + # TODO: Figure out why this is broken. + @test_broken sprint(show, ps.root; context=:compat => false) == str + end +end + +function TestPaths.test_cmd(ps::PathSet) + @testset "cmd" begin + str = string(ps.root) + @test `echo $str` == `echo $(ps.root)` + end +end + +function TestPaths.test_parse(ps::PathSet{P}) where {P<:AbstractPath} + @testset "parsing" begin + str = string(ps.root) + @test parse(P, str) == ps.root + @test tryparse(P, str) == ps.root + end +end + +function TestPaths.test_convert(ps::PathSet{P}) where {P<:AbstractPath} + @testset "convert" begin + str = string(ps.root) + @test convert(P, str) == ps.root + @test convert(String, ps.root) == str + end +end + +function TestPaths.test_components(ps::PathSet) + @testset "components" begin + str = string(ps.root) + @test ps.root.anchor == ps.root.drive * ps.root.root + @test ps.quux.segments[(end - 2):end] == ("bar", "qux", "quux.tar.gz") + + # Check that isless on the path segments works + @test ps.bar < ps.foo + @test sort([ps.foo, ps.bar, ps.fred]) == [ps.bar, ps.foo, ps.fred] + end +end + +function TestPaths.test_indexing(ps::PathSet) + @test firstindex(ps.root) == 1 + @test lastindex(ps.root) == length(ps.root.segments) + # `begin` indexing was only added in 1.4, so we'll leave this test commented + # out for now as there isn't a compat for that syntax. + # @test ps.root[begin] == ps.root.segments[1] + @test ps.baz[end] == "baz.txt" + @test ps.quux[(end - 2):end] == ("bar", "qux", "quux.tar.gz") + @test ps.quux[:] == ps.quux.segments[:] +end + +function TestPaths.test_iteration(ps::PathSet) + @test eltype(ps.root) == String + @test tuple(ps.quux...) == ps.quux.segments + @test all(in(ps.quux), ("bar", "qux", "quux.tar.gz")) +end + +function TestPaths.test_parents(ps::PathSet) + @testset "parents" begin + @test parent(ps.foo) == ps.root + @test parent(ps.qux) == ps.bar + @test dirname(ps.foo) == ps.root + + @test hasparent(ps.qux) + _parents = parents(ps.qux) + @test _parents[end] == ps.bar + @test _parents[end - 1] == ps.root + @test _parents[1] == Path(ps.root; segments=()) + + # More abstract path tests for edge cases when no parent exists + @test hasparent(p"/foo") + @test hasparent(p"./foo") + @test hasparent(p"~/foo") + @test !hasparent(p"foo") + @test !hasparent(p"~") + @test !hasparent(p".") + @test !hasparent(p"/") + + # Test that relative paths with no parents return p"." + @test parent(Path(basename(ps.foo))) == p"." + + # Test that parent on p"." should be === + path = p"." + @test parent(path) === path + + # Test that parent on p"/" should be === + path = p"/" + @test parent(path) === path + + # Test inclusion of root in parents + relparents = parents(PosixPath(ps.root.segments, "")) + absparents = parents(PosixPath(ps.root.segments, "/")) + @test relparents !== absparents + @test length(absparents) == length(relparents) + 1 + @test !in(absparents[1], relparents) + @test absparents[1] == PosixPath((), "/") + end +end + +function TestPaths.test_descendants_and_ascendants(ps::PathSet) + @testset "descendants and ascendants" begin + # Test base cases + @test isdescendant(p"/a/b", p"/") + @test isdescendant(p"/a/b", p"/a") + @test isdescendant(p"/a/b/c", p"/a") + @test isdescendant(p"/a/b", p"/a/b") + @test !isdescendant(p"/a/b", p"/a/b/c") + @test !isdescendant(p"/a/b", p"/c") + @test isascendant(p"/a", p"/a/b") + @test !isascendant(p"/a/b", p"/a") + + # Test without our path types + @test isdescendant(ps.foo, ps.root) + @test isdescendant(ps.qux, ps.root) + @test isdescendant(ps.foo, ps.foo) + @test !isdescendant(ps.root, ps.foo) + @test !isdescendant(ps.root, ps.qux) + @test isascendant(ps.root, ps.foo) + @test isascendant(ps.root, ps.quux) + @test !isascendant(ps.qux, ps.root) + end +end + +function TestPaths.test_join(ps::PathSet) + @testset "join" begin + @test join(ps.root, "bar") == ps.bar + @test ps.root / "foo" / "baz.txt" == ps.baz + @test ps.root / "foobaz.txt" == ps.root / "foo" * "baz.txt" + @test ps.root ./ ["foo", "bar"] == [ps.foo, ps.bar] + @test ps.root / "foo/baz.txt" == ps.baz + @test ps.root / p"foo/baz.txt" == ps.baz + @test ps.root / p"foo" / p"baz.txt" == ps.baz + @test ps.root / p"foo" / "baz.txt" == ps.baz + + # TODO: Maybe normalize this case for the user? {ps.root}/foo/./baz.txt + @test normalize(ps.root / p"foo" / "" / "baz.txt") == ps.baz + + # Check that joining absolute paths matches base + @test ps.root / p"/foo/baz.txt" == p"/foo/baz.txt" + end +end + +function TestPaths.test_splitext(ps::PathSet) + @testset "splitext" begin + @test splitext(ps.foo) == (ps.foo, "") + @test splitext(ps.baz) == (ps.foo / "baz", ".txt") + @test splitext(ps.quux) == (ps.qux / "quux.tar", ".gz") + end +end +function TestPaths.test_basename(ps::PathSet) + @testset "basename" begin + @test basename(ps.foo) == "foo" + @test basename(ps.baz) == "baz.txt" + @test basename(ps.quux) == "quux.tar.gz" + end +end +function TestPaths.test_splitdir(ps::PathSet) + @testset "splitdir" begin + @test splitdir(ps.foo) == (ps.root, "foo") + @test splitdir(ps.baz) == (ps.root / "foo", "baz.txt") + @test splitdir(ps.quux) == (ps.root / "bar" / "qux", "quux.tar.gz") + end +end + +function TestPaths.test_filename(ps::PathSet) + @testset "filename" begin + @test filename(ps.foo) == "foo" + @test filename(ps.baz) == "baz" + @test filename(ps.quux) == "quux.tar" + end +end + +function TestPaths.test_extensions(ps::PathSet) + @testset "extensions" begin + @test extension(ps.foo) == "" + @test extension(ps.baz) == "txt" + @test extension(ps.quux) == "gz" + @test extensions(ps.foo) == [] + @test extensions(ps.baz) == ["txt"] + @test extensions(ps.quux) == ["tar", "gz"] + end +end + +function TestPaths.test_isempty(ps::PathSet{P}) where {P<:AbstractPath} + @testset "isempty" begin + @test !isempty(ps.foo) + @test isempty(P()) + end +end + +function TestPaths.test_normalize(ps::PathSet) + @testset "normalize" begin + @test normalize(ps.bar / ".." / "foo") == ps.foo + @test normalize(ps.bar / ".") == ps.bar + @test normpath(ps.bar / ".") == ps.bar + end +end + +function TestPaths.test_canonicalize(ps::PathSet) + @testset "canonicalize" begin + # NOTE: We call `canonicalize` on ps.bar in the `normalize` case because on + # macOS the temp directory may include a symlink. + @test canonicalize(ps.bar / ".." / "foo") == + normalize(canonicalize(ps.bar) / ".." / "foo") + @test canonicalize(ps.bar / ".") == normalize(canonicalize(ps.bar) / ".") + @test realpath(ps.bar / ".") == canonicalize(ps.bar / ".") + + if ps.plugh !== nothing + if isa(ps.plugh, WindowsPath) && VERSION < v"1.2" + @test_broken canonicalize(ps.plugh) == canonicalize(ps.foo) + else + @test canonicalize(ps.plugh) == canonicalize(ps.foo) + end end end +end - function TestPaths.test_show(ps::PathSet) - @testset "show" begin - str = string(ps.root) - # For windows paths - str = replace(str, "\\" => "/") - @test sprint(show, ps.root; context=:compat => true) == "p\"$str\"" - # TODO: Figure out why this is broken. - @test_broken sprint(show, ps.root; context=:compat => false) == str - end +function TestPaths.test_relative(ps::PathSet) + @testset "relative" begin + @test relative(ps.foo, ps.qux).segments == ("..", "..", "foo") end +end - function TestPaths.test_cmd(ps::PathSet) - @testset "cmd" begin - str = string(ps.root) - @test `echo $str` == `echo $(ps.root)` - end +function TestPaths.test_absolute(ps::PathSet) + @testset "absolute" begin + @test isabsolute(ps.root) || isabsolute(absolute(ps.root)) + @test absolute(ps.root) == abspath(ps.root) end +end - function TestPaths.test_parse(ps::PathSet{P}) where P <: AbstractPath - @testset "parsing" begin - str = string(ps.root) - @test parse(P, str) == ps.root - @test tryparse(P, str) == ps.root - end +function TestPaths.test_isdir(ps::PathSet) + @testset "isdir" begin + @test isdir(ps.foo) + @test !isdir(ps.baz) end +end - function TestPaths.test_convert(ps::PathSet{P}) where P <: AbstractPath - @testset "convert" begin - str = string(ps.root) - @test convert(P, str) == ps.root - @test convert(String, ps.root) == str - end +function TestPaths.test_isfile(ps::PathSet) + @testset "isfile" begin + @test isfile(ps.baz) + @test !isfile(ps.foo) end +end - function TestPaths.test_components(ps::PathSet) - @testset "components" begin - str = string(ps.root) - @test ps.root.anchor == ps.root.drive * ps.root.root - @test ps.quux.segments[end-2:end] == ("bar", "qux", "quux.tar.gz") +function TestPaths.test_stat(ps::PathSet) + @testset "stat" begin + s = stat(ps.root) + fields = fieldnames(typeof(s)) - # Check that isless on the path segments works - @test ps.bar < ps.foo - @test sort([ps.foo, ps.bar, ps.fred]) == [ps.bar, ps.foo, ps.fred] - end - end + @test :size in fields + @test :ctime in fields + @test :mtime in fields + @test :user in fields + @test :mode in fields - function TestPaths.test_indexing(ps::PathSet) - @test firstindex(ps.root) == 1 - @test lastindex(ps.root) == length(ps.root.segments) - # `begin` indexing was only added in 1.4, so we'll leave this test commented - # out for now as there isn't a compat for that syntax. - # @test ps.root[begin] == ps.root.segments[1] - @test ps.baz[end] == "baz.txt" - @test ps.quux[end-2:end] == ("bar", "qux", "quux.tar.gz") - @test ps.quux[:] == ps.quux.segments[:] - end - - function TestPaths.test_iteration(ps::PathSet) - @test eltype(ps.root) == String - @test tuple(ps.quux...) == ps.quux.segments - @test all(in(ps.quux), ("bar", "qux", "quux.tar.gz")) - end - - function TestPaths.test_parents(ps::PathSet) - @testset "parents" begin - @test parent(ps.foo) == ps.root - @test parent(ps.qux) == ps.bar - @test dirname(ps.foo) == ps.root - - @test hasparent(ps.qux) - _parents = parents(ps.qux) - @test _parents[end] == ps.bar - @test _parents[end - 1] == ps.root - @test _parents[1] == Path(ps.root; segments=()) - - # More abstract path tests for edge cases when no parent exists - @test hasparent(p"/foo") - @test hasparent(p"./foo") - @test hasparent(p"~/foo") - @test !hasparent(p"foo") - @test !hasparent(p"~") - @test !hasparent(p".") - @test !hasparent(p"/") - - # Test that relative paths with no parents return p"." - @test parent(Path(basename(ps.foo))) == p"." - - # Test that parent on p"." should be === - path = p"." - @test parent(path) === path - - # Test that parent on p"/" should be === - path = p"/" - @test parent(path) === path - - # Test inclusion of root in parents - relparents = parents(PosixPath(ps.root.segments, "")) - absparents = parents(PosixPath(ps.root.segments, "/")) - @test relparents !== absparents - @test length(absparents) == length(relparents) + 1 - @test !in(absparents[1], relparents) - @test absparents[1] == PosixPath((), "/") + if ps.link + @test lstat(ps.plugh) != stat(ps.plugh) end - end - function TestPaths.test_descendants_and_ascendants(ps::PathSet) - @testset "descendants and ascendants" begin - # Test base cases - @test isdescendant(p"/a/b", p"/") - @test isdescendant(p"/a/b", p"/a") - @test isdescendant(p"/a/b/c", p"/a") - @test isdescendant(p"/a/b", p"/a/b") - @test !isdescendant(p"/a/b", p"/a/b/c") - @test !isdescendant(p"/a/b", p"/c") - @test isascendant(p"/a", p"/a/b") - @test !isascendant(p"/a/b", p"/a") - - # Test without our path types - @test isdescendant(ps.foo, ps.root) - @test isdescendant(ps.qux, ps.root) - @test isdescendant(ps.foo, ps.foo) - @test !isdescendant(ps.root, ps.foo) - @test !isdescendant(ps.root, ps.qux) - @test isascendant(ps.root, ps.foo) - @test isascendant(ps.root, ps.quux) - @test !isascendant(ps.qux, ps.root) - end + str = sprint(show, s) end +end - function TestPaths.test_join(ps::PathSet) - @testset "join" begin - @test join(ps.root, "bar") == ps.bar - @test ps.root / "foo" / "baz.txt" == ps.baz - @test ps.root / "foobaz.txt" == ps.root / "foo" * "baz.txt" - @test ps.root ./ ["foo", "bar"] == [ps.foo, ps.bar] - @test ps.root / "foo/baz.txt" == ps.baz - @test ps.root / p"foo/baz.txt" == ps.baz - @test ps.root / p"foo" / p"baz.txt" == ps.baz - @test ps.root / p"foo" / "baz.txt" == ps.baz +# Minimal testing of issocket, isfifo, ischardev, isblockdev and ismount which +# people won't typically include. +function TestPaths.test_issocket(ps::PathSet) + @test !issocket(ps.root) +end - # TODO: Maybe normalize this case for the user? {ps.root}/foo/./baz.txt - @test normalize(ps.root / p"foo" / "" / "baz.txt") == ps.baz +function TestPaths.test_isfifo(ps::PathSet) + @test !isfifo(ps.root) +end - # Check that joining absolute paths matches base - @test ps.root / p"/foo/baz.txt" == p"/foo/baz.txt" - end - end +function TestPaths.test_ischardev(ps::PathSet) + @test !ischardev(ps.root) +end - function TestPaths.test_splitext(ps::PathSet) - @testset "splitext" begin - @test splitext(ps.foo) == (ps.foo, "") - @test splitext(ps.baz) == (ps.foo / "baz", ".txt") - @test splitext(ps.quux) == (ps.qux / "quux.tar", ".gz") - end - end - function TestPaths.test_basename(ps::PathSet) - @testset "basename" begin - @test basename(ps.foo) == "foo" - @test basename(ps.baz) == "baz.txt" - @test basename(ps.quux) == "quux.tar.gz" - end - end - function TestPaths.test_splitdir(ps::PathSet) - @testset "splitdir" begin - @test splitdir(ps.foo) == (ps.root, "foo") - @test splitdir(ps.baz) == (ps.root / "foo", "baz.txt") - @test splitdir(ps.quux) == (ps.root / "bar" / "qux", "quux.tar.gz") - end - end +function TestPaths.test_isblockdev(ps::PathSet) + @test !isblockdev(ps.root) +end - function TestPaths.test_filename(ps::PathSet) - @testset "filename" begin - @test filename(ps.foo) == "foo" - @test filename(ps.baz) == "baz" - @test filename(ps.quux) == "quux.tar" - end - end +function TestPaths.test_ismount(ps::PathSet) + @test !ismount(ps.root) +end - function TestPaths.test_extensions(ps::PathSet) - @testset "extensions" begin - @test extension(ps.foo) == "" - @test extension(ps.baz) == "txt" - @test extension(ps.quux) == "gz" - @test extensions(ps.foo) == [] - @test extensions(ps.baz) == ["txt"] - @test extensions(ps.quux) == ["tar", "gz"] - end +function TestPaths.test_filesize(ps::PathSet) + @testset "filesize" begin + @test filesize(ps.baz) > 0 end +end - function TestPaths.test_isempty(ps::PathSet{P}) where P <: AbstractPath - @testset "isempty" begin - @test !isempty(ps.foo) - @test isempty(P()) - end +function TestPaths.test_modified(ps::PathSet) + @testset "modified" begin + @test isa(modified(ps.baz), Dates.AbstractDateTime) + @test modified(ps.baz) >= modified(ps.root) end +end - function TestPaths.test_normalize(ps::PathSet) - @testset "normalize" begin - @test normalize(ps.bar / ".." / "foo") == ps.foo - @test normalize(ps.bar / ".") == ps.bar - @test normpath(ps.bar / ".") == ps.bar - - end +function TestPaths.test_created(ps::PathSet) + @testset "created" begin + @test isa(created(ps.baz), Dates.AbstractDateTime) + @test created(ps.baz) >= created(ps.root) end +end - function TestPaths.test_canonicalize(ps::PathSet) - @testset "canonicalize" begin - # NOTE: We call `canonicalize` on ps.bar in the `normalize` case because on - # macOS the temp directory may include a symlink. - @test canonicalize(ps.bar / ".." / "foo") == normalize(canonicalize(ps.bar) / ".." / "foo") - @test canonicalize(ps.bar / ".") == normalize(canonicalize(ps.bar) / ".") - @test realpath(ps.bar / ".") == canonicalize(ps.bar / ".") +function TestPaths.test_isexecutable(ps::PathSet{P}) where {P<:AbstractPath} + @testset "isexecutable" begin + # I'm not entirely sure how to test this generally + @test !isexecutable(ps.baz) - if ps.plugh !== nothing - if isa(ps.plugh, WindowsPath) && VERSION < v"1.2" - @test_broken canonicalize(ps.plugh) == canonicalize(ps.foo) - else - @test canonicalize(ps.plugh) == canonicalize(ps.foo) - end - end + # Directories should be executable for system paths + if P <: SystemPath + @test isexecutable(ps.foo) end end +end - function TestPaths.test_relative(ps::PathSet) - @testset "relative" begin - @test relative(ps.foo, ps.qux).segments == ("..", "..", "foo") - end - end +function TestPaths.test_isreadable(ps::PathSet) + @testset "isreadable" begin + # Our test files should be readable by default + @test isreadable(ps.baz) + @test isreadable(ps.quux) - function TestPaths.test_absolute(ps::PathSet) - @testset "absolute" begin - @test isabsolute(ps.root) || isabsolute(absolute(ps.root)) - @test absolute(ps.root) == abspath(ps.root) - end + # Our test directories should also be readable by default + @test isreadable(ps.foo) + @test isreadable(ps.qux) end +end - function TestPaths.test_isdir(ps::PathSet) - @testset "isdir" begin - @test isdir(ps.foo) - @test !isdir(ps.baz) - end - end +function TestPaths.test_iswritable(ps::PathSet) + @testset "iswritable" begin + # Our test files should be writable by default + @test iswritable(ps.baz) + @test iswritable(ps.quux) - function TestPaths.test_isfile(ps::PathSet) - @testset "isfile" begin - @test isfile(ps.baz) - @test !isfile(ps.foo) - end + # Our test directories should also be writable by default + @test iswritable(ps.foo) + @test iswritable(ps.qux) end +end - function TestPaths.test_stat(ps::PathSet) - @testset "stat" begin - s = stat(ps.root) - fields = fieldnames(typeof(s)) +function TestPaths.test_cd(ps::PathSet{P}) where {P<:AbstractPath} + if P <: SystemPath + @testset "cd" begin + init_path = canonicalize(cwd()) - @test :size in fields - @test :ctime in fields - @test :mtime in fields - @test :user in fields - @test :mode in fields - - if ps.link - @test lstat(ps.plugh) != stat(ps.plugh) + cd(ps.foo) do + cd_path = canonicalize(cwd()) + @test cd_path != init_path + @test cd_path == canonicalize(ps.foo) end - str = sprint(show, s) - end - end - - # Minimal testing of issocket, isfifo, ischardev, isblockdev and ismount which - # people won't typically include. - function TestPaths.test_issocket(ps::PathSet) - @test !issocket(ps.root) - end - - function TestPaths.test_isfifo(ps::PathSet) - @test !isfifo(ps.root) - end - - function TestPaths.test_ischardev(ps::PathSet) - @test !ischardev(ps.root) - end - - function TestPaths.test_isblockdev(ps::PathSet) - @test !isblockdev(ps.root) - end - - function TestPaths.test_ismount(ps::PathSet) - @test !ismount(ps.root) - end - - function TestPaths.test_filesize(ps::PathSet) - @testset "filesize" begin - @test filesize(ps.baz) > 0 - end - end + @test canonicalize(cwd()) == init_path - function TestPaths.test_modified(ps::PathSet) - @testset "modified" begin - @test isa(modified(ps.baz), Dates.AbstractDateTime) - @test modified(ps.baz) >= modified(ps.root) + cd(ps.qux) + cd_path = canonicalize(cwd()) + @test cd_path != init_path + @test cd_path == canonicalize(ps.qux) + cd(init_path) + @test canonicalize(cwd()) == init_path end end +end - function TestPaths.test_created(ps::PathSet) - @testset "created" begin - @test isa(created(ps.baz), Dates.AbstractDateTime) - @test created(ps.baz) >= created(ps.root) - end +function TestPaths.test_readpath(ps::PathSet) + @testset "readpath" begin + @test readdir(ps.root) == ["bar", "foo", "fred"] + @test readdir(ps.qux) == ["quux.tar.gz"] + @test readpath(ps.root) == [ps.bar, ps.foo, ps.fred] + @test readpath(ps.qux) == [ps.quux] end +end - function TestPaths.test_isexecutable(ps::PathSet{P}) where P <: AbstractPath - @testset "isexecutable" begin - # I'm not entirely sure how to test this generally - @test !isexecutable(ps.baz) +function TestPaths.test_walkpath(ps::PathSet{P}) where {P} + @testset "walkpath" begin + topdown = [ps.bar, ps.qux, ps.quux, ps.foo, ps.baz, ps.fred, ps.plugh] + bottomup = [ps.quux, ps.qux, ps.bar, ps.baz, ps.foo, ps.plugh, ps.fred] - # Directories should be executable for system paths - if P <: SystemPath - @test isexecutable(ps.foo) - end - end - end + @test collect(walkpath(ps.root; topdown=true)) == topdown + @test collect(walkpath(ps.root; topdown=false)) == bottomup - function TestPaths.test_isreadable(ps::PathSet) - @testset "isreadable" begin - # Our test files should be readable by default - @test isreadable(ps.baz) - @test isreadable(ps.quux) + @test eltype(walkpath(ps.root)) == P # should return a typed collection - # Our test directories should also be readable by default - @test isreadable(ps.foo) - @test isreadable(ps.qux) - end + # tests consistency of definition, assume network usage acceptable + @test diskusage(ps.root) == mapreduce(filesize, +, walkpath(ps.root)) + @test diskusage(ps.baz) == ncodeunits("Hello World!") end +end - function TestPaths.test_iswritable(ps::PathSet) - @testset "iswritable" begin - # Our test files should be writable by default - @test iswritable(ps.baz) - @test iswritable(ps.quux) - - # Our test directories should also be writable by default - @test iswritable(ps.foo) - @test iswritable(ps.qux) +function TestPaths.test_read(ps::PathSet) + @testset "read" begin + @test read(ps.baz, String) == "Hello World!" + open(ps.quux, "r") do io + @test read(io, String) == "Hello Again!" end end +end - function TestPaths.test_cd(ps::PathSet{P}) where P <: AbstractPath - if P <: SystemPath - @testset "cd" begin - init_path = canonicalize(cwd()) +function TestPaths.test_write(ps::PathSet) + @testset "write" begin + write(ps.baz, "Goodbye World!") + @test read(ps.baz, String) == "Goodbye World!" - cd(ps.foo) do - cd_path = canonicalize(cwd()) - @test cd_path != init_path - @test cd_path == canonicalize(ps.foo) - end - - @test canonicalize(cwd()) == init_path - - cd(ps.qux) - cd_path = canonicalize(cwd()) - @test cd_path != init_path - @test cd_path == canonicalize(ps.qux) - cd(init_path) - @test canonicalize(cwd()) == init_path + @testset "truncate" begin + open(ps.quux, "w") do io + write(io, "Hello?") end + @test read(ps.quux, String) == "Hello?" end - end - function TestPaths.test_readpath(ps::PathSet) - @testset "readpath" begin - @test readdir(ps.root) == ["bar", "foo", "fred"] - @test readdir(ps.qux) == ["quux.tar.gz"] - @test readpath(ps.root) == [ps.bar, ps.foo, ps.fred] - @test readpath(ps.qux) == [ps.quux] + @testset "append" begin + open(ps.quux, "a") do io + write(io, " Did you need something?") + end + @test read(ps.quux, String) == "Hello? Did you need something?" end - end - - function TestPaths.test_walkpath(ps::PathSet{P}) where P - @testset "walkpath" begin - topdown = [ps.bar, ps.qux, ps.quux, ps.foo, ps.baz, ps.fred, ps.plugh] - bottomup = [ps.quux, ps.qux, ps.bar, ps.baz, ps.foo, ps.plugh, ps.fred] - @test collect(walkpath(ps.root; topdown=true)) == topdown - @test collect(walkpath(ps.root; topdown=false)) == bottomup - - @test eltype(walkpath(ps.root)) == P # should return a typed collection - - # tests consistency of definition, assume network usage acceptable - @test diskusage(ps.root) == mapreduce(filesize, +, walkpath(ps.root)) - @test diskusage(ps.baz) == ncodeunits("Hello World!") - end - end + @testset "read/write" begin + open(ps.quux, "w+") do io + write(io, "Goodnight!") + seekstart(io) + @test read(io, String) == "Goodnight!" + end - function TestPaths.test_read(ps::PathSet) - @testset "read" begin - @test read(ps.baz, String) == "Hello World!" - open(ps.quux, "r") do io - @test read(io, String) == "Hello Again!" + open(ps.quux, "a+") do io + write(io, " Zzzz") + seekstart(io) + @test read(io, String) == "Goodnight! Zzzz" end end end - - function TestPaths.test_write(ps::PathSet) - @testset "write" begin - write(ps.baz, "Goodbye World!") - @test read(ps.baz, String) == "Goodbye World!" - - @testset "truncate" begin - open(ps.quux, "w") do io - write(io, "Hello?") +end + +function TestPaths.test_mkdir(ps::PathSet) + @testset "mkdir" begin + garply = ps.root / "corge" / "grault" / "garply" + @test_throws ErrorException mkdir(garply) + @test mkdir(garply; recursive=true) == garply + @test exists(garply) + @test_throws ErrorException mkdir(garply; recursive=true) + @test mkdir(garply; recursive=true, exist_ok=true) == garply + rm(ps.root / "corge"; recursive=true) + @test !exists(garply) + end +end + +function TestPaths.test_cp(ps::PathSet) + @testset "cp" begin + cp(ps.foo, ps.qux / "foo"; force=true) + @test exists(ps.qux / "foo" / "baz.txt") + @test_throws ArgumentError cp(ps.foo, ps.qux / "foo") + cp(ps.foo, ps.qux / "foo"; force=true) + rm(ps.qux / "foo"; recursive=true) + + @testset "non-existent destination parent" begin + # Test consistency with `cp` (e.g., destination parent must exist) + dest = ps.root / "non-existent" / "destination" + + # The base behaviour can only be tested on system paths. + if isa(dest, SystemPath) + if VERSION >= v"1.2" # Error type changed in Julia 1.2 + @test_throws ProcessFailedException run(`cp -r $(ps.foo) $dest`) + else + @test_throws ErrorException run(`cp -r $(ps.foo) $dest`) end - @test read(ps.quux, String) == "Hello?" - end - @testset "append" begin - open(ps.quux, "a") do io - write(io, " Did you need something?") + if VERSION >= v"1.4" + @test_throws Base.IOError cp(string(ps.foo), string(dest)) + else + @test_throws SystemError cp(string(ps.foo), string(dest)) end - @test read(ps.quux, String) == "Hello? Did you need something?" end - @testset "read/write" begin - open(ps.quux, "w+") do io - write(io, "Goodnight!") - seekstart(io) - @test read(io, String) == "Goodnight!" - end - - open(ps.quux, "a+") do io - write(io, " Zzzz") - seekstart(io) - @test read(io, String) == "Goodnight! Zzzz" - end - end + # TODO: Use a more specific error type + @test_throws ErrorException cp(ps.foo, dest) end end +end - function TestPaths.test_mkdir(ps::PathSet) - @testset "mkdir" begin - garply = ps.root / "corge" / "grault" / "garply" - @test_throws ErrorException mkdir(garply) - @test mkdir(garply; recursive=true) == garply - @test exists(garply) - @test_throws ErrorException mkdir(garply; recursive=true) - @test mkdir(garply; recursive=true, exist_ok=true) == garply - rm(ps.root / "corge"; recursive=true) - @test !exists(garply) - end +function TestPaths.test_mv(ps::PathSet) + @testset "mv" begin + garply = ps.root / "corge" / "grault" / "garply" + mkdir(garply; recursive=true, exist_ok=true) + @test exists(garply) + mv(ps.root / "corge", ps.foo / "corge"; force=true) + @test exists(ps.foo / "corge" / "grault" / "garply") + rm(ps.foo / "corge"; recursive=true) end +end - function TestPaths.test_cp(ps::PathSet) - @testset "cp" begin - cp(ps.foo, ps.qux / "foo"; force=true) +function TestPaths.test_sync(ps::PathSet) + @testset "sync" begin + @testset "empty destination" begin + sync(ps.foo, ps.qux / "foo") @test exists(ps.qux / "foo" / "baz.txt") - @test_throws ArgumentError cp(ps.foo, ps.qux / "foo") - cp(ps.foo, ps.qux / "foo"; force=true) - rm(ps.qux / "foo"; recursive=true) - - @testset "non-existent destination parent" begin - # Test consistency with `cp` (e.g., destination parent must exist) - dest = ps.root / "non-existent" / "destination" - - # The base behaviour can only be tested on system paths. - if isa(dest, SystemPath) - if VERSION >= v"1.2" # Error type changed in Julia 1.2 - @test_throws ProcessFailedException run(`cp -r $(ps.foo) $dest`) - else - @test_throws ErrorException run(`cp -r $(ps.foo) $dest`) - end - - if VERSION >= v"1.4" - @test_throws Base.IOError cp(string(ps.foo), string(dest)) - else - @test_throws SystemError cp(string(ps.foo), string(dest)) - end - end - # TODO: Use a more specific error type - @test_throws ErrorException cp(ps.foo, dest) - end + # Test that the copied baz file has a newer modified time + baz_t = modified(ps.qux / "foo" / "baz.txt") + @test modified(ps.baz) < baz_t end - end - function TestPaths.test_mv(ps::PathSet) - @testset "mv" begin - garply = ps.root / "corge" / "grault" / "garply" - mkdir(garply; recursive=true, exist_ok=true) - @test exists(garply) - mv(ps.root / "corge", ps.foo / "corge"; force=true) - @test exists(ps.foo / "corge" / "grault" / "garply") - rm(ps.foo / "corge"; recursive=true) + @testset "empty source" begin + @test_throws ArgumentError sync(ps.root / "quux", ps.foo) end - end - - function TestPaths.test_sync(ps::PathSet) - @testset "sync" begin - @testset "empty destination" begin - sync(ps.foo, ps.qux / "foo") - @test exists(ps.qux / "foo" / "baz.txt") - - # Test that the copied baz file has a newer modified time - baz_t = modified(ps.qux / "foo" / "baz.txt") - @test modified(ps.baz) < baz_t - end - @testset "empty source" begin - @test_throws ArgumentError sync(ps.root / "quux", ps.foo) - end - - @testset "new source" begin - # Don't cp unchanged files when a new file is added - # NOTE: sleep before we make a new file, so it's clear that the - # modified time has changed. - baz_t = modified(ps.qux / "foo" / "baz.txt") - sleep(1) - write(ps.foo / "test.txt", "New src") - sync(ps.foo, ps.qux / "foo") - @test exists(ps.qux / "foo" / "test.txt") - @test read(ps.qux / "foo" / "test.txt", String) == "New src" - @test modified(ps.qux / "foo" / "baz.txt") == baz_t - @test modified(ps.qux / "foo" / "test.txt") > baz_t - end - - @testset "new destination" begin - # Newer file of the same size is likely the result of an upload which - # will always have a newer last modified time. - test_t = modified(ps.foo / "test.txt") - sleep(1) - write(ps.qux / "foo" / "test.txt", "New dst") - @test modified(ps.qux / "foo" / "test.txt") > test_t - sync(ps.foo, ps.qux / "foo") - @test read(ps.qux / "foo" / "test.txt", String) == "New dst" - @test modified(ps.qux / "foo" / "test.txt") > test_t - end + @testset "new source" begin + # Don't cp unchanged files when a new file is added + # NOTE: sleep before we make a new file, so it's clear that the + # modified time has changed. + baz_t = modified(ps.qux / "foo" / "baz.txt") + sleep(1) + write(ps.foo / "test.txt", "New src") + sync(ps.foo, ps.qux / "foo") + @test exists(ps.qux / "foo" / "test.txt") + @test read(ps.qux / "foo" / "test.txt", String) == "New src" + @test modified(ps.qux / "foo" / "baz.txt") == baz_t + @test modified(ps.qux / "foo" / "test.txt") > baz_t + end - @testset "no delete" begin - # Test not deleting a file on sync - rm(ps.foo / "test.txt") - sync(ps.foo, ps.qux / "foo") - @test exists(ps.qux / "foo" / "test.txt") - end + @testset "new destination" begin + # Newer file of the same size is likely the result of an upload which + # will always have a newer last modified time. + test_t = modified(ps.foo / "test.txt") + sleep(1) + write(ps.qux / "foo" / "test.txt", "New dst") + @test modified(ps.qux / "foo" / "test.txt") > test_t + sync(ps.foo, ps.qux / "foo") + @test read(ps.qux / "foo" / "test.txt", String) == "New dst" + @test modified(ps.qux / "foo" / "test.txt") > test_t + end - @testset "delete" begin - # Test passing delete flag - sync(ps.foo, ps.qux / "foo"; delete=true) - @test !exists(ps.qux / "foo" / "test.txt") - rm(ps.qux / "foo"; recursive=true) - end + @testset "no delete" begin + # Test not deleting a file on sync + rm(ps.foo / "test.txt") + sync(ps.foo, ps.qux / "foo") + @test exists(ps.qux / "foo" / "test.txt") + end - @testset "mixed types" begin - @testset "directory -> file" begin - @test_throws ArgumentError sync(ps.foo, ps.quux) - end + @testset "delete" begin + # Test passing delete flag + sync(ps.foo, ps.qux / "foo"; delete=true) + @test !exists(ps.qux / "foo" / "test.txt") + rm(ps.qux / "foo"; recursive=true) + end - @testset "file -> directory" begin - @test_throws ArgumentError sync(ps.quux, ps.foo) - end + @testset "mixed types" begin + @testset "directory -> file" begin + @test_throws ArgumentError sync(ps.foo, ps.quux) end - @testset "walkpath order" begin - # Test a condtion where the index could reorder the walkpath order. - tmp_src = ps.root / "tmp-src" - mkdir(tmp_src) - src_file = tmp_src / "file1" - write(src_file, "Hello World!") - - src_folder = tmp_src / "folder1" - mkdir(src_folder) - src_folder_file = src_folder / "file2" - write(src_folder_file, "") # empty file - - src_folder2 = src_folder / "folder2" # nested folders - mkdir(src_folder2) - src_folder2_file = src_folder2 / "file3" - write(src_folder2_file, "Test") - - tmp_dst = ps.root / "tmp_dst" - mkdir(tmp_dst) - sync(tmp_src, tmp_dst) - @test exists(tmp_dst / "folder1" / "folder2" / "file3") - rm(tmp_src; recursive=true) - rm(tmp_dst; recursive=true) + @testset "file -> directory" begin + @test_throws ArgumentError sync(ps.quux, ps.foo) end + end - @testset "non-existent destination parent" begin - # Test consistency with `cp` (e.g., destination parent must exist) - # See full `cp` comparison tests above - dest = ps.root / "non-existent" / "destination" + @testset "walkpath order" begin + # Test a condtion where the index could reorder the walkpath order. + tmp_src = ps.root / "tmp-src" + mkdir(tmp_src) + src_file = tmp_src / "file1" + write(src_file, "Hello World!") - # TODO: Use a more specific error type - @test_throws ErrorException sync(ps.foo, dest) - end - end - end + src_folder = tmp_src / "folder1" + mkdir(src_folder) + src_folder_file = src_folder / "file2" + write(src_folder_file, "") # empty file - function TestPaths.test_symlink(ps::PathSet) - if ps.link - @testset "symlink" begin - @test_throws ErrorException symlink(ps.foo, ps.plugh) - symlink(ps.foo, ps.plugh; exist_ok=true, overwrite=true) - symlink(ps.foo, ps.plugh; exist_ok=true) - @test_throws ErrorException symlink(ps.foo / "thud", ps.plugh; exist_ok=true, overwrite=true) - end - end - end + src_folder2 = src_folder / "folder2" # nested folders + mkdir(src_folder2) + src_folder2_file = src_folder2 / "file3" + write(src_folder2_file, "Test") - function TestPaths.test_touch(ps::PathSet) - @testset "touch" begin - newfile = ps.root / "newfile" - touch(newfile) - @test exists(newfile) - @test ispath(newfile) - rm(newfile) + tmp_dst = ps.root / "tmp_dst" + mkdir(tmp_dst) + sync(tmp_src, tmp_dst) + @test exists(tmp_dst / "folder1" / "folder2" / "file3") + rm(tmp_src; recursive=true) + rm(tmp_dst; recursive=true) end - end - function TestPaths.test_tmpname(ps::PathSet) - @testset "tmpname" begin - @test isa(tmpname(), AbstractPath) - @test hasparent(tmpname()) - @test exists(parent(tmpname())) - end - end + @testset "non-existent destination parent" begin + # Test consistency with `cp` (e.g., destination parent must exist) + # See full `cp` comparison tests above + dest = ps.root / "non-existent" / "destination" - function TestPaths.test_tmpdir(ps::PathSet) - @testset "tmpname" begin - @test isa(tmpdir(), AbstractPath) - @test exists(tmpdir()) - @test isdir(tmpdir()) + # TODO: Use a more specific error type + @test_throws ErrorException sync(ps.foo, dest) end end +end - function TestPaths.test_mktmp(ps::PathSet) - @testset "mktmp" begin - mktmp(ps.root) do path, io - @test exists(path) - @test iswritable(io) - write(io, "Foobar") - seekstart(io) - @test read(io, String) == "Foobar" - end +function TestPaths.test_symlink(ps::PathSet) + if ps.link + @testset "symlink" begin + @test_throws ErrorException symlink(ps.foo, ps.plugh) + symlink(ps.foo, ps.plugh; exist_ok=true, overwrite=true) + symlink(ps.foo, ps.plugh; exist_ok=true) + @test_throws ErrorException symlink( + ps.foo / "thud", ps.plugh; exist_ok=true, overwrite=true + ) end end - - function TestPaths.test_mktmpdir(ps::PathSet) - @testset "mktmpdir" begin - mktmpdir(ps.root) do path - @test exists(path) - write(path / "test.txt", "Foobar") - @test read(path / "test.txt", String) == "Foobar" +end + +function TestPaths.test_touch(ps::PathSet) + @testset "touch" begin + newfile = ps.root / "newfile" + touch(newfile) + @test exists(newfile) + @test ispath(newfile) + rm(newfile) + end +end + +function TestPaths.test_tmpname(ps::PathSet) + @testset "tmpname" begin + @test isa(tmpname(), AbstractPath) + @test hasparent(tmpname()) + @test exists(parent(tmpname())) + end +end + +function TestPaths.test_tmpdir(ps::PathSet) + @testset "tmpname" begin + @test isa(tmpdir(), AbstractPath) + @test exists(tmpdir()) + @test isdir(tmpdir()) + end +end + +function TestPaths.test_mktmp(ps::PathSet) + @testset "mktmp" begin + mktmp(ps.root) do path, io + @test exists(path) + @test iswritable(io) + write(io, "Foobar") + seekstart(io) + @test read(io, String) == "Foobar" + end + end +end + +function TestPaths.test_mktmpdir(ps::PathSet) + @testset "mktmpdir" begin + mktmpdir(ps.root) do path + @test exists(path) + write(path / "test.txt", "Foobar") + @test read(path / "test.txt", String) == "Foobar" + end + end +end + +function TestPaths.test_chown(ps::PathSet) + @testset "chown" begin + newfile = ps.root / "newfile" + touch(newfile) + + if haskey(ENV, "USER") + if ENV["USER"] == "root" + chown(newfile, "nobody", "nogroup"; recursive=true) + elseif VERSION >= v"1.2" # Error type changed in Julia 1.2 + @test_throws ProcessFailedException chown( + newfile, "nobody", "nogroup"; recursive=true + ) + else + @test_throws ErrorException chown( + newfile, "nobody", "nogroup"; recursive=true + ) end end - end - function TestPaths.test_chown(ps::PathSet) - @testset "chown" begin - newfile = ps.root / "newfile" - touch(newfile) - - if haskey(ENV, "USER") - if ENV["USER"] == "root" - chown(newfile, "nobody", "nogroup"; recursive=true) - elseif VERSION >= v"1.2" # Error type changed in Julia 1.2 - @test_throws ProcessFailedException chown(newfile, "nobody", "nogroup"; recursive=true) - else - @test_throws ErrorException chown(newfile, "nobody", "nogroup"; recursive=true) - end - end - - rm(newfile) - end + rm(newfile) end +end - function TestPaths.test_chmod(ps::PathSet) - @testset "chmod" begin - newfile = ps.root / "newfile" - newpath = ps.root / "thud" +function TestPaths.test_chmod(ps::PathSet) + @testset "chmod" begin + newfile = ps.root / "newfile" + newpath = ps.root / "thud" - touch(newfile) - mkdir(newpath) - chmod(newfile, user=(READ+WRITE+EXEC), group=(READ+EXEC), other=READ) - @test string(mode(newfile)) == "-rwxr-xr--" - @test isexecutable(newfile) - @test iswritable(newfile) - @test isreadable(newfile) + touch(newfile) + mkdir(newpath) + chmod(newfile; user=(READ + WRITE + EXEC), group=(READ + EXEC), other=READ) + @test string(mode(newfile)) == "-rwxr-xr--" + @test isexecutable(newfile) + @test iswritable(newfile) + @test isreadable(newfile) - chmod(newfile, "-x") - @test !isexecutable(newfile) + chmod(newfile, "-x") + @test !isexecutable(newfile) - @test string(mode(newfile)) == "-rw-r--r--" - chmod(newfile, "+x") - write(newfile, "foobar") - @test read(newfile, String) == "foobar" - chmod(newfile, "u=rwx") - - open(newfile, "r") do io - @test read(io, String) == "foobar" - end + @test string(mode(newfile)) == "-rw-r--r--" + chmod(newfile, "+x") + write(newfile, "foobar") + @test read(newfile, String) == "foobar" + chmod(newfile, "u=rwx") - chmod(newpath, mode(newfile); recursive=true) + open(newfile, "r") do io + @test read(io, String) == "foobar" end + + chmod(newpath, mode(newfile); recursive=true) end +end - function TestPaths.test_download(ps::PathSet) - @testset "download" begin - rm(ps.foo / "README.md"; force=true) - download( - "https://github.com/rofinn/FilePathsBase.jl/blob/master/README.md", - ps.foo / "README.md" - ) - @test exists(ps.foo / "README.md") +function TestPaths.test_download(ps::PathSet) + @testset "download" begin + rm(ps.foo / "README.md"; force=true) + download( + "https://github.com/rofinn/FilePathsBase.jl/blob/master/README.md", + ps.foo / "README.md", + ) + @test exists(ps.foo / "README.md") - # Test downloading from another path - download(ps.foo / "README.md", ps.qux / "README.md") - @test exists(ps.qux / "README.md") + # Test downloading from another path + download(ps.foo / "README.md", ps.qux / "README.md") + @test exists(ps.qux / "README.md") - # Test downloading to a string - download(ps.foo / "README.md", string(ps.fred / "README.md")) - @test exists(ps.fred / "README.md") - rm.([ps.foo / "README.md", ps.qux / "README.md", ps.fred / "README.md"]) - end + # Test downloading to a string + download(ps.foo / "README.md", string(ps.fred / "README.md")) + @test exists(ps.fred / "README.md") + rm.([ps.foo / "README.md", ps.qux / "README.md", ps.fred / "README.md"]) end +end - function TestPaths.test_include(ps::PathSet) - @testset "include" begin - write(ps.quux, "2 + 2\n") - res = include(ps.quux) - @test res == 4 - end +function TestPaths.test_include(ps::PathSet) + @testset "include" begin + write(ps.quux, "2 + 2\n") + res = include(ps.quux) + @test res == 4 end -end \ No newline at end of file +end +end diff --git a/src/FilePathsBase.jl b/src/FilePathsBase.jl index 38b8269..1b4e13d 100644 --- a/src/FilePathsBase.jl +++ b/src/FilePathsBase.jl @@ -13,7 +13,7 @@ end #https://github.com/JuliaLang/julia/pull/53699 makes isexecutable public. #we overload that method if available. -@static if isdefined(Base,:isexecutable) +@static if isdefined(Base, :isexecutable) import Base: isexecutable end @@ -76,14 +76,13 @@ export WRITE, EXEC - export isexecutable const PATH_TYPES = Type[] function __init__() # Register the default fallback path type based on the os. - register(Sys.iswindows() ? WindowsPath : PosixPath) + return register(Sys.iswindows() ? WindowsPath : PosixPath) end """ @@ -120,7 +119,7 @@ function register(T::Type{<:AbstractPath}) # We add the type to the beginning of our PATH_TYPES, # so that they can take precedence over the Posix and # Windows paths. - pushfirst!(PATH_TYPES, T) + return pushfirst!(PATH_TYPES, T) end """ @@ -144,7 +143,7 @@ include("windows.jl") include("test_stub.jl") include("deprecates.jl") -if !isdefined(Base,:get_extension) +if !isdefined(Base, :get_extension) include("../ext/FilePathsBaseMmapExt.jl") include("../ext/FilePathsBaseTestExt.jl") end diff --git a/src/aliases.jl b/src/aliases.jl index e265099..18a539b 100644 --- a/src/aliases.jl +++ b/src/aliases.jl @@ -4,7 +4,6 @@ /(args...) = Base.:/(args...) join(args...) = Base.join(args...) - # Aliases for Base.Filesystem API # Filesystem methods that were renamed should still have an alias for better interop. Base.abspath(fp::AbstractPath) = absolute(fp) @@ -13,7 +12,9 @@ Base.dirname(fp::AbstractPath) = parent(fp) Base.filemode(fp::AbstractPath) = mode(fp) Base.isabspath(fp::AbstractPath) = isabsolute(fp) Base.ispath(fp::AbstractPath) = exists(fp) -Base.joinpath(root::AbstractPath, pieces::Union{AbstractPath, AbstractString}...) = join(root, pieces...) +function Base.joinpath(root::AbstractPath, pieces::Union{AbstractPath,AbstractString}...) + return join(root, pieces...) +end Base.mkpath(fp::AbstractPath) = mkdir(fp; recursive=true, exist_ok=true) Base.mtime(fp::AbstractPath) = datetime2unix(modified(fp)) Base.normpath(fp::AbstractPath) = normalize(fp) diff --git a/src/buffer.jl b/src/buffer.jl index 48bc4b5..0df713f 100644 --- a/src/buffer.jl +++ b/src/buffer.jl @@ -56,14 +56,14 @@ function Base.read(buffer::FileBuffer) isreadable(buffer) || throw(ArgumentError("read failed, FileBuffer is not readable")) _read(buffer) seekstart(buffer) - read(buffer.io) + return read(buffer.io) end function Base.read(buffer::FileBuffer, ::Type{String}) isreadable(buffer) || throw(ArgumentError("read failed, FileBuffer is not readable")) _read(buffer) seekstart(buffer) - read(buffer.io, String) + return read(buffer.io, String) end function Base.read(buffer::FileBuffer, ::Type{UInt8}) @@ -71,7 +71,7 @@ function Base.read(buffer::FileBuffer, ::Type{UInt8}) write(buffer.io, read(buffer.path)) seekstart(buffer) end - read(buffer.io, UInt8) + return read(buffer.io, UInt8) end #= @@ -79,17 +79,17 @@ NOTE: We need to define multiple methods because of ambiguity error with base IO =# function Base.write(buffer::FileBuffer, x::Vector{UInt8}) iswritable(buffer) || throw(ArgumentError("write failed, FileBuffer is not writeable")) - write(buffer.io, x) + return write(buffer.io, x) end function Base.write(buffer::FileBuffer, x::String) iswritable(buffer) || throw(ArgumentError("write failed, FileBuffer is not writeable")) - write(buffer.io, x) + return write(buffer.io, x) end function Base.write(buffer::FileBuffer, x::UInt8) iswritable(buffer) || throw(ArgumentError("write failed, FileBuffer is not writeable")) - write(buffer.io, x) + return write(buffer.io, x) end function Base.flush(buffer::FileBuffer) @@ -101,5 +101,5 @@ end function Base.close(buffer::FileBuffer) flush(buffer) - close(buffer.io) + return close(buffer.io) end diff --git a/src/constants.jl b/src/constants.jl index 53a7d68..bbc6a26 100644 --- a/src/constants.jl +++ b/src/constants.jl @@ -1,20 +1,19 @@ # Posix separator and regex -const POSIX_PATH_SEPARATOR = "/" +const POSIX_PATH_SEPARATOR = "/" const POSIX_PATH_SEPARATOR_RE = r"/+" -const POSIX_PATH_ABSOLUTE_RE = r"^/" +const POSIX_PATH_ABSOLUTE_RE = r"^/" const POSIX_PATH_DIRECTORY_RE = r"(?:^|/)\.{0,2}$" const POSIX_PATH_DIR_SPLITTER = r"^(.*?)(/+)([^/]*)$" const POSIX_PATH_EXT_SPLITTER = r"^((?:.*/)?(?:\.|[^/\.])[^/]*?)(\.[^/\.]*|)$" # Windows separator and regex -const WIN_PATH_SEPARATOR = "\\" +const WIN_PATH_SEPARATOR = "\\" const WIN_PATH_SEPARATOR_RE = r"[/\\]+" -const WIN_PATH_ABSOLUTE_RE = r"^(?:\w+:)?[/\\]" +const WIN_PATH_ABSOLUTE_RE = r"^(?:\w+:)?[/\\]" const WIN_PATH_DIRECTORY_RE = r"(?:^|[/\\])\.{0,2}$" const WIN_PATH_DIR_SPLITTER = r"^(.*?)([/\\]+)([^/\\]*)$" const WIN_PATH_EXT_SPLITTER = r"^((?:.*[/\\])?(?:\.|[^/\\\.])[^/\\]*?)(\.[^/\\\.]*|)$" - # The common READ, WRITE and EXEC constants # to make working with file permissions easier. const READ = 0o4 @@ -27,12 +26,12 @@ const OTHER_COEFF = 0o0001 # Stardard C style file mode constants taken from cpython's # stat.py file -const S_IFDIR = 0o040000 # directory -const S_IFCHR = 0o020000 # character device -const S_IFBLK = 0o060000 # block device -const S_IFREG = 0o100000 # regular file -const S_IFIFO = 0o010000 # fifo (named pipe) -const S_IFLNK = 0o120000 # symbolic link +const S_IFDIR = 0o040000 # directory +const S_IFCHR = 0o020000 # character device +const S_IFBLK = 0o060000 # block device +const S_IFREG = 0o100000 # regular file +const S_IFIFO = 0o010000 # fifo (named pipe) +const S_IFLNK = 0o120000 # symbolic link const S_IFSOCK = 0o140000 # socket file const S_ISUID = 0o4000 # set UID bit @@ -56,30 +55,23 @@ const S_IWOTH = 0o0002 # write by others const S_IXOTH = 0o0001 # execute by others const FILEMODE_TABLE = ( - ((S_IFLNK, 'l'), - (S_IFREG, '-'), - (S_IFBLK, 'b'), - (S_IFDIR, 'd'), - (S_IFCHR, 'c'), - (S_IFIFO, 'p')), - - ((S_IRUSR, 'r'),), - ((S_IWUSR, 'w'),), - ((S_IXUSR|S_ISUID, 's'), - (S_ISUID, 'S'), - (S_IXUSR, 'x')), - - ((S_IRGRP, 'r'),), - ((S_IWGRP, 'w'),), - ((S_IXGRP|S_ISGID, 's'), - (S_ISGID, 'S'), - (S_IXGRP, 'x')), - - ((S_IROTH, 'r'),), - ((S_IWOTH, 'w'),), - ((S_IXOTH|S_ISVTX, 't'), - (S_ISVTX, 'T'), - (S_IXOTH, 'x')) + ( + (S_IFLNK, 'l'), + (S_IFREG, '-'), + (S_IFBLK, 'b'), + (S_IFDIR, 'd'), + (S_IFCHR, 'c'), + (S_IFIFO, 'p'), + ), + ((S_IRUSR, 'r'),), + ((S_IWUSR, 'w'),), + ((S_IXUSR | S_ISUID, 's'), (S_ISUID, 'S'), (S_IXUSR, 'x')), + ((S_IRGRP, 'r'),), + ((S_IWGRP, 'w'),), + ((S_IXGRP | S_ISGID, 's'), (S_ISGID, 'S'), (S_IXGRP, 'x')), + ((S_IROTH, 'r'),), + ((S_IWOTH, 'w'),), + ((S_IXOTH | S_ISVTX, 't'), (S_ISVTX, 'T'), (S_IXOTH, 'x')), ) const DATA_SUFFIX = ["", "K", "M", "G", "T", "P", "E", "Z", "Y"] diff --git a/src/libc.jl b/src/libc.jl index 4cf7691..60fa49c 100644 --- a/src/libc.jl +++ b/src/libc.jl @@ -14,13 +14,13 @@ end elseif Sys.islinux() struct Cpasswd - pw_name::Cstring - pw_passwd::Cstring - pw_uid::Cint - pw_gid::Cint - pw_gecos::Cstring - pw_dir::Cstring - pw_shell::Cstring + pw_name::Cstring + pw_passwd::Cstring + pw_uid::Cint + pw_gid::Cint + pw_gecos::Cstring + pw_dir::Cstring + pw_shell::Cstring end else struct Cpasswd @@ -51,12 +51,12 @@ struct User end function User(ps::Cpasswd) - User( + return User( unsafe_string(ps.pw_name), UInt64(ps.pw_uid), UInt64(ps.pw_gid), unsafe_string(ps.pw_dir), - unsafe_string(ps.pw_shell) + unsafe_string(ps.pw_shell), ) end diff --git a/src/mode.jl b/src/mode.jl index 35ec764..0521ecd 100644 --- a/src/mode.jl +++ b/src/mode.jl @@ -19,10 +19,10 @@ julia> Mode("-rwxr-x--x") -rwxr-x--x ``` """ -function Mode(;user::UInt8=0o0, group::UInt8=0o0, other::UInt8=0o0,) +function Mode(; user::UInt8=0o0, group::UInt8=0o0, other::UInt8=0o0) @assert user <= 0o7 && group <= 0o7 && other <= 0o7 - Mode(user * USER_COEFF | group * GROUP_COEFF | other * OTHER_COEFF) + return Mode(user * USER_COEFF | group * GROUP_COEFF | other * OTHER_COEFF) end function Mode(mode::UInt8, usr_grps::Symbol...) @@ -39,14 +39,16 @@ function Mode(mode::UInt8, usr_grps::Symbol...) elseif usr_grp == :OTHER other = mode else - throw(ArgumentError( - "$usr_grp not a valid symbol only " * - ":ALL, :USER, :GROUP and :OTHER are accepted." - )) + throw( + ArgumentError( + "$usr_grp not a valid symbol only " * + ":ALL, :USER, :GROUP and :OTHER are accepted.", + ), + ) end end - return Mode(user=user, group=group, other=other) + return Mode(; user=user, group=group, other=other) end Mode(s::AbstractString) = parse(Mode, s) @@ -55,9 +57,11 @@ function Base.parse(::Type{Mode}, x::AbstractString) n = length(FILEMODE_TABLE) if length(x) != n - throw(ArgumentError( - "Expected a mode permission string with $n characters (e.g., '-rwxrwxrwx')" - )) + throw( + ArgumentError( + "Expected a mode permission string with $n characters (e.g., '-rwxrwxrwx')" + ), + ) end m = zero(UInt64) @@ -76,9 +80,11 @@ function Base.parse(::Type{Mode}, x::AbstractString) end if !found options = last.(table) - throw(ArgumentError( - "Unknown character '$c' at position $i, expected one of $options." - )) + throw( + ArgumentError( + "Unknown character '$c' at position $i, expected one of $options." + ), + ) end end @@ -105,11 +111,11 @@ function Base.print(io::IO, mode::Mode) end end - print(io, String(perm)) + return print(io, String(perm)) end function Base.show(io::IO, mode::Mode) - get(io, :compact, false) ? print(io, mode) : print(io, "Mode(\"$mode\")") + return get(io, :compact, false) ? print(io, mode) : print(io, "Mode(\"$mode\")") end Base.:-(a::Mode, b::Mode) = Mode(a.m & ~b.m) diff --git a/src/path.jl b/src/path.jl index 62f8e8d..7b07310 100644 --- a/src/path.jl +++ b/src/path.jl @@ -1,5 +1,4 @@ - """ Path() -> SystemPath Path(fp::Tuple) -> SystemPath @@ -12,11 +11,15 @@ Windows systems respectively) """ function Path end -function Path(fp::T; overrides...) where T<:AbstractPath +function Path(fp::T; overrides...) where {T<:AbstractPath} override_fields = keys(overrides) override_vals = values(overrides) - T((s in override_fields ? override_vals[s] : getfield(fp, s) for s in fieldnames(T))...) + return T( + ( + s in override_fields ? override_vals[s] : getfield(fp, s) for s in fieldnames(T) + )..., + ) end Path(str::AbstractString) = parse(AbstractPath, str) @@ -31,7 +34,7 @@ macro p_str(fp) return :(Path($fp)) end -function Base.getproperty(fp::T, attr::Symbol) where T <: AbstractPath +function Base.getproperty(fp::T, attr::Symbol) where {T<:AbstractPath} if isdefined(fp, attr) return getfield(fp, attr) elseif attr === :drive @@ -49,7 +52,7 @@ function Base.getproperty(fp::T, attr::Symbol) where T <: AbstractPath end end -function Base.propertynames(::T, private::Bool=false) where T <: AbstractPath +function Base.propertynames(::T, private::Bool=false) where {T<:AbstractPath} public_names = (:drive, :root, :anchor, :separator) return private ? Base.merge_names(public_names, fieldnames(T)) : public_names end @@ -59,11 +62,11 @@ We only want to print the macro string syntax when compact is true and we want print to just return the string (this allows `string` to work normally) =# function Base.print(io::IO, fp::AbstractPath) - print(io, fp.anchor * join(fp.segments, fp.separator)) + return print(io, fp.anchor * join(fp.segments, fp.separator)) end function Base.show(io::IO, fp::AbstractPath) - get(io, :compact, false) ? print(io, fp) : print(io, "p\"$fp\"") + return get(io, :compact, false) ? print(io, fp) : print(io, "p\"$fp\"") end # Needed for path Cmd interpolation to work correctly. @@ -74,7 +77,7 @@ Base.arg_gen(fp::AbstractPath) = Base.arg_gen(string(fp)) # Default string constructors for AbstractPath types should fall back to calling `parse`. (::Type{T})(str::AbstractString) where {T<:AbstractPath} = parse(T, str) -function Base.parse(::Type{P}, str::AbstractString; kwargs...) where P<:AbstractPath +function Base.parse(::Type{P}, str::AbstractString; kwargs...) where {P<:AbstractPath} result = tryparse(P, str; kwargs...) result === nothing && throw(ArgumentError("$str cannot be parsed as $P")) return result @@ -105,7 +108,7 @@ function Base.tryparse(::Type{AbstractPath}, str::AbstractString; debug=false) @debug( string( "Found multiple path types that could parse the string specified ($types). ", - "Please use a specific `parse` method if $(first(types)) is not the correct type." + "Please use a specific `parse` method if $(first(types)) is not the correct type.", ) ) end @@ -116,7 +119,7 @@ end Base.convert(T::Type{<:AbstractPath}, x::AbstractString) = parse(T, x) Base.convert(::Type{String}, x::AbstractPath) = string(x) Base.promote_rule(::Type{String}, ::Type{<:AbstractPath}) = String -Base.isless(a::P, b::P) where P<:AbstractPath = isless(a.segments, b.segments) +Base.isless(a::P, b::P) where {P<:AbstractPath} = isless(a.segments, b.segments) """ cwd() -> SystemPath @@ -229,11 +232,14 @@ julia> parents(p".") p"." ``` """ -function parents(fp::T) where {T <: AbstractPath} +function parents(fp::T) where {T<:AbstractPath} if hasparent(fp) # Iterate from 1:n-1 or 0:n-1 for relative and absolute paths respectively. # (i.e., include fp.root when applicable) - return [Path(fp; segments=fp.segments[1:i]) for i in isrelative(fp):length(fp.segments) - 1] + return [ + Path(fp; segments=fp.segments[1:i]) for + i in isrelative(fp):(length(fp.segments) - 1) + ] elseif fp.segments == tuple(".") || !isempty(fp.root) return [fp] else @@ -254,7 +260,7 @@ julia> p"foo" * "bar" p"foobar" ``` """ -function Base.:(*)(a::T, b::Union{T, AbstractString, AbstractChar}...) where T <: AbstractPath +function Base.:(*)(a::T, b::Union{T,AbstractString,AbstractChar}...) where {T<:AbstractPath} return parse(T, *(string(a), string.(b)...)) end @@ -272,8 +278,7 @@ julia> p"foo" / "bar" / "baz" p"foo/bar/baz" ``` """ -/(root::AbstractPath, pieces::Union{AbstractPath, AbstractString}...) = join(root, pieces...) - +/(root::AbstractPath, pieces::Union{AbstractPath,AbstractString}...) = join(root, pieces...) """ join(root::AbstractPath, pieces::Union{AbstractPath, AbstractString}...) -> AbstractPath @@ -286,7 +291,7 @@ julia> join(p"~/.julia/v0.6", "REQUIRE") p"~/.julia/v0.6/REQUIRE" ``` """ -function join(prefix::AbstractPath, pieces::Union{AbstractPath, AbstractString}...) +function join(prefix::AbstractPath, pieces::Union{AbstractPath,AbstractString}...) pre = prefix segments = String[pre.segments...] @@ -390,7 +395,7 @@ Base.isempty(fp::AbstractPath) = isempty(fp.segments) normalizes a path by removing "." and ".." entries. """ -function normalize(fp::T) where {T <: AbstractPath} +function normalize(fp::T) where {T<:AbstractPath} p = fp.segments result = String[] rem = length(p) @@ -465,9 +470,11 @@ function relative(fp::T, start::T=cwd()) where {T<:AbstractPath} pathpart = p[(i + 1):findlast(x -> !isempty(x), p)] prefix_num = findlast(x -> !isempty(x), s) - i - 1 if prefix_num >= 0 - relpath_ = isempty(pathpart) ? - tuple(fill(pardir, prefix_num + 1)...) : + relpath_ = if isempty(pathpart) + tuple(fill(pardir, prefix_num + 1)...) + else tuple(fill(pardir, prefix_num + 1)..., pathpart...) + end else relpath_ = tuple(pathpart...) end @@ -601,10 +608,12 @@ true if the `src` should be sent. This may be useful if you'd like to use a chec comparison. """ function sync(src::AbstractPath, dst::AbstractPath; kwargs...) - sync(should_sync, src, dst; kwargs...) + return sync(should_sync, src, dst; kwargs...) end -function sync(f::Function, src::AbstractPath, dst::AbstractPath; delete=false, overwrite=true) +function sync( + f::Function, src::AbstractPath, dst::AbstractPath; delete=false, overwrite=true +) # Throw an error if the source path doesn't exist at all exists(src) || throw(ArgumentError("$src does not exist")) @@ -655,7 +664,11 @@ function sync(f::Function, src::AbstractPath, dst::AbstractPath; delete=false, o index_pairs = collect(pairs(index)) index_pairs = index_pairs[sortperm(last.(index_pairs))] for (seg, i) in index_pairs - cp(src_paths[i], Path(dst, segments=tuple(dst.segments..., seg...)); force=true) + cp( + src_paths[i], + Path(dst; segments=tuple(dst.segments..., seg...)); + force=true, + ) end else cp(src, dst) @@ -670,8 +683,8 @@ function should_sync(src::AbstractPath, dst::AbstractPath) if src_stat.size != dst_stat.size || src_stat.mtime > dst_stat.mtime @debug( "syncing: $src -> $dst, " * - "size: $(src_stat.size) -> $(dst_stat.size), " * - "modified_time: $(src_stat.mtime) -> $(dst_stat.mtime)" + "size: $(src_stat.size) -> $(dst_stat.size), " * + "modified_time: $(src_stat.mtime) -> $(dst_stat.mtime)" ) return true else @@ -687,7 +700,7 @@ Download a file from the remote `url` and save it to the `localfile` path. NOTE: Not downloading into a `localfile` directory matches the base Julia behaviour. https://github.com/rofinn/FilePathsBase.jl/issues/48 """ -function Base.download(url::AbstractString, localfile::P) where P <: AbstractPath +function Base.download(url::AbstractString, localfile::P) where {P<:AbstractPath} mktemp(P) do fp, io download(url, fp) cp(fp, localfile; force=false) @@ -704,7 +717,7 @@ end """ readpath(fp::P; join=true, sort=true) where {P <: AbstractPath} -> Vector{P} """ -function readpath(p::P; join=true, sort=true)::Vector{P} where P <: AbstractPath +function readpath(p::P; join=true, sort=true)::Vector{P} where {P<:AbstractPath} @static if VERSION < v"1.4" results = readdir(p) sort && sort!(results) @@ -719,8 +732,10 @@ end Performs a depth first search through the directory structure """ -function walkpath(fp::P; topdown=true, follow_symlinks=false, onerror=throw) where P <: AbstractPath - return Channel(ctype=P) do chnl +function walkpath( + fp::P; topdown=true, follow_symlinks=false, onerror=throw +) where {P<:AbstractPath} + return Channel(; ctype=P) do chnl for p in readpath(fp) topdown && put!(chnl, p) if isdir(p) && (follow_symlinks || !islink(p)) @@ -765,7 +780,6 @@ function Base.open(fp::AbstractPath, mode) end end - # Fallback read write methods Base.read(fp::AbstractPath, ::Type{T}) where {T} = open(io -> read(io, T), fp) Base.write(fp::AbstractPath, x) = open(io -> write(io, x), fp, "w") @@ -821,7 +835,7 @@ function Base.mktempdir(fn::Function, parent::AbstractPath) try fn(tmpdir) finally - rm(tmpdir, recursive=true) + rm(tmpdir; recursive=true) end end @@ -833,14 +847,14 @@ mktmpdir(arg1, args...) = mktempdir(arg1, args...) Returns `true` if `fp` is within the directory tree of the `asc`. """ -isdescendant(fp::P, asc::P) where {P <: AbstractPath} = fp == asc || asc in parents(fp) +isdescendant(fp::P, asc::P) where {P<:AbstractPath} = fp == asc || asc in parents(fp) """ isascendant(fp::P, desc::P) where {P <: AbstractPath} -> Bool Returns `true` if `fp` is a directory containing `desc`. """ -isascendant(fp::P, desc::P) where {P <: AbstractPath} = isdescendant(desc, fp) +isascendant(fp::P, desc::P) where {P<:AbstractPath} = isdescendant(desc, fp) """ diskusage(fp::AbstractPath) @@ -859,10 +873,10 @@ Base.include(m::Module, path::AbstractPath) = Base.include(identity, m, path) function Base.include(mapexpr::Function, m::Module, path::AbstractPath) tmp_file = cwd() / string(uuid4(), "-", basename(path)) try - cp(path, tmp_file; force = true) + cp(path, tmp_file; force=true) Base.include(mapexpr, m, string(tmp_file)) finally - rm(tmp_file; force = true) + rm(tmp_file; force=true) end end diff --git a/src/posix.jl b/src/posix.jl index bcae42e..b4eb6a4 100644 --- a/src/posix.jl +++ b/src/posix.jl @@ -38,7 +38,7 @@ function Base.Filesystem.contractuser(fp::PosixPath) return PosixPath("~") else n = length(h.segments) - return PosixPath(("~", fp.segments[n+1:end]...)) + return PosixPath(("~", fp.segments[(n + 1):end]...)) end end diff --git a/src/status.jl b/src/status.jl index 89d7c61..94d8910 100644 --- a/src/status.jl +++ b/src/status.jl @@ -18,7 +18,7 @@ struct Status end function Status(s::StatStruct) - Status( + return Status( s.device, s.inode, Mode(s.mode), @@ -30,12 +30,13 @@ function Status(s::StatStruct) s.blksize, s.blocks, unix2datetime(s.mtime), - unix2datetime(s.ctime) + unix2datetime(s.ctime), ) end function Base.show(io::IO, s::Status) - output = "Status(\n" * + output = + "Status(\n" * " device = $(s.device),\n" * " inode = $(s.inode),\n" * " mode = $(s.mode),\n" * @@ -49,5 +50,5 @@ function Base.show(io::IO, s::Status) " mtime = $(s.mtime),\n" * " ctime = $(s.ctime),\n)" - print(io, output) + return print(io, output) end diff --git a/src/system.jl b/src/system.jl index 139a8c7..fc19fea 100644 --- a/src/system.jl +++ b/src/system.jl @@ -7,8 +7,9 @@ methods that wrap base functionality. abstract type SystemPath <: AbstractPath end Path() = @static Sys.isunix() ? PosixPath() : WindowsPath() -Path(pieces::Tuple{Vararg{String}}) = +function Path(pieces::Tuple{Vararg{String}}) @static Sys.isunix() ? PosixPath(pieces) : WindowsPath(pieces) +end """ @__PATH__ -> SystemPath @@ -113,8 +114,8 @@ function isexecutable(fp::SystemPath) return ( isexecutable(s.mode, :ALL) || isexecutable(s.mode, :OTHER) || - ( usr.uid == s.user.uid && isexecutable(s.mode, :USER) ) || - ( usr.gid == s.group.gid && isexecutable(s.mode, :GROUP) ) + (usr.uid == s.user.uid && isexecutable(s.mode, :USER)) || + (usr.gid == s.group.gid && isexecutable(s.mode, :GROUP)) ) end @@ -130,8 +131,8 @@ function Base.iswritable(fp::SystemPath) return ( iswritable(s.mode, :ALL) || iswritable(s.mode, :OTHER) || - ( usr.uid == s.user.uid && iswritable(s.mode, :USER) ) || - ( usr.gid == s.group.gid && iswritable(s.mode, :GROUP) ) + (usr.uid == s.user.uid && iswritable(s.mode, :USER)) || + (usr.gid == s.group.gid && iswritable(s.mode, :GROUP)) ) end @@ -147,8 +148,8 @@ function Base.isreadable(fp::SystemPath) return ( isreadable(s.mode, :ALL) || isreadable(s.mode, :OTHER) || - ( usr.uid == s.user.uid && isreadable(s.mode, :USER) ) || - ( usr.gid == s.group.gid && isreadable(s.mode, :GROUP) ) + (usr.uid == s.user.uid && isreadable(s.mode, :USER)) || + (usr.gid == s.group.gid && isreadable(s.mode, :GROUP)) ) end @@ -162,7 +163,7 @@ function Base.ismount(fp::SystemPath) # directory must be a mount point (s1.device != s2.device) && return true (s1.inode == s2.inode) && return true - false + return false end #= @@ -184,12 +185,14 @@ function Base.cd(fn::Function, dir::SystemPath) try cd(dir) fn() - finally + finally cd(old) end end -function Base.mkdir(fp::T; mode=0o777, recursive=false, exist_ok=false) where T<:SystemPath +function Base.mkdir( + fp::T; mode=0o777, recursive=false, exist_ok=false +) where {T<:SystemPath} if exists(fp) if exist_ok return fp @@ -198,24 +201,24 @@ function Base.mkdir(fp::T; mode=0o777, recursive=false, exist_ok=false) where T< end else if hasparent(fp) && !exists(parent(fp)) - if recursive - mkdir(parent(fp); mode=mode, recursive=recursive, exist_ok=exist_ok) - else - error( - "The parent of $fp does not exist. " * - "Pass recursive=true to create it." - ) - end + if recursive + mkdir(parent(fp); mode=mode, recursive=recursive, exist_ok=exist_ok) + else + error( + "The parent of $fp does not exist. " * + "Pass recursive=true to create it.", + ) + end end - return parse(T, mkdir(string(fp), mode=mode)) + return parse(T, mkdir(string(fp); mode=mode)) end end function Base.symlink(src::SystemPath, dest::SystemPath; exist_ok=false, overwrite=false) if exists(src) if exists(dest) && exist_ok && overwrite - rm(dest, recursive=true) + rm(dest; recursive=true) end if !exists(dest) @@ -229,22 +232,24 @@ function Base.symlink(src::SystemPath, dest::SystemPath; exist_ok=false, overwri end function Base.rm(fp::SystemPath; kwargs...) - rm(string(fp); kwargs...) + return rm(string(fp); kwargs...) end Base.touch(fp::T) where {T<:SystemPath} = parse(T, touch(string(fp))) -function Base.mktemp(parent::T) where T<:SystemPath +function Base.mktemp(parent::T) where {T<:SystemPath} fp, io = mktemp(string(parent)) return parse(T, fp), io end -Base.mktempdir(parent::T) where {T<:SystemPath}= parse(T, mktempdir(string(parent))) +Base.mktempdir(parent::T) where {T<:SystemPath} = parse(T, mktempdir(string(parent))) """ chown(fp::SystemPath, user::AbstractString, group::AbstractString; recursive=false) Change the `user` and `group` of the `fp`. """ -function Base.chown(fp::SystemPath, user::AbstractString, group::AbstractString; recursive=false) +function Base.chown( + fp::SystemPath, user::AbstractString, group::AbstractString; recursive=false +) @static if Sys.isunix() chown_cmd = String["chown"] if recursive @@ -299,7 +304,7 @@ julia> mode(p"newfile") -rw-r--r-- ``` """ -function Base.chmod(fp::T, mode::Mode; recursive=false) where T<:SystemPath +function Base.chmod(fp::T, mode::Mode; recursive=false) where {T<:SystemPath} chmod_path = string(fp) chmod_mode = raw(mode) @@ -309,15 +314,17 @@ function Base.chmod(fp::T, mode::Mode; recursive=false) where T<:SystemPath end end - parse(T, chmod(chmod_path, chmod_mode)) + return parse(T, chmod(chmod_path, chmod_mode)) end function Base.chmod(fp::SystemPath, mode::Integer; recursive=false) - chmod(fp, Mode(mode); recursive=recursive) + return chmod(fp, Mode(mode); recursive=recursive) end -function Base.chmod(fp::SystemPath; user::UInt8=0o0, group::UInt8=0o0, other::UInt8=0o0, recursive=false) - chmod(fp, Mode(user=user, group=group, other=other); recursive=recursive) +function Base.chmod( + fp::SystemPath; user::UInt8=0o0, group::UInt8=0o0, other::UInt8=0o0, recursive=false +) + return chmod(fp, Mode(; user=user, group=group, other=other); recursive=recursive) end function Base.chmod(fp::SystemPath, symbolic_mode::AbstractString; recursive=false) @@ -373,11 +380,11 @@ end Base.open(fp::SystemPath, args...) = open(string(fp), args...) function Base.open(f::Function, fp::SystemPath, args...; kwargs...) - open(f, string(fp), args...; kwargs...) + return open(f, string(fp), args...; kwargs...) end Base.read(fp::SystemPath) = read(string(fp)) -function Base.write(fp::SystemPath, x::Union{String, Vector{UInt8}}, mode="w") +function Base.write(fp::SystemPath, x::Union{String,Vector{UInt8}}, mode="w") open(fp, mode) do f write(f, x) end @@ -386,15 +393,15 @@ end Base.readdir(fp::SystemPath; kwargs...) = readdir(string(fp); kwargs...) #TODO: Base.download is deprecated; use Downloads.download instead (FilePathsBase.jl#173) -function Base.download(url::AbstractString, dest::T) where T<:SystemPath +function Base.download(url::AbstractString, dest::T) where {T<:SystemPath} return parse(T, download(url, string(dest))) end -function Base.readlink(fp::T) where T<:SystemPath +function Base.readlink(fp::T) where {T<:SystemPath} return parse(T, readlink(string(fp))) end -function canonicalize(fp::T) where T<:SystemPath +function canonicalize(fp::T) where {T<:SystemPath} return parse(T, realpath(string(fp))) end @@ -402,4 +409,4 @@ end function Base.include(mapexpr::Function, m::Module, path::SystemPath) return Base.include(mapexpr, m, string(path)) end -end \ No newline at end of file +end diff --git a/src/test_stub.jl b/src/test_stub.jl index 612e428..6d098c4 100644 --- a/src/test_stub.jl +++ b/src/test_stub.jl @@ -61,252 +61,252 @@ test(ps, testsets) ``` """ module TestPaths - using Dates: Dates - using FilePathsBase - using FilePathsBase: /, join +using Dates: Dates +using FilePathsBase +using FilePathsBase: /, join - export PathSet, - TESTALL, - test, - test_registration, - test_show, - test_cmd, - test_parse, - test_convert, - test_components, - test_indexing, - test_iteration, - test_parents, - test_descendants_and_ascendants, - test_join, - test_splitext, - test_basename, - test_splitdir, - test_filename, - test_extensions, - test_isempty, - test_normalize, - test_canonicalize, - test_relative, - test_absolute, - test_isdir, - test_isfile, - test_stat, - test_filesize, - test_modified, - test_created, - test_issocket, - test_isfifo, - test_ischardev, - test_isblockdev, - test_ismount, - test_isexecutable, - test_isreadable, - test_iswritable, - test_cd, - test_readpath, - test_walkpath, - test_read, - test_write, - test_mkdir, - test_cp, - test_mv, - test_sync, - test_symlink, - test_touch, - test_tmpname, - test_tmpdir, - test_mktmp, - test_mktmpdir, - test_chown, - test_chmod, - test_download, - test_include +export PathSet, + TESTALL, + test, + test_registration, + test_show, + test_cmd, + test_parse, + test_convert, + test_components, + test_indexing, + test_iteration, + test_parents, + test_descendants_and_ascendants, + test_join, + test_splitext, + test_basename, + test_splitdir, + test_filename, + test_extensions, + test_isempty, + test_normalize, + test_canonicalize, + test_relative, + test_absolute, + test_isdir, + test_isfile, + test_stat, + test_filesize, + test_modified, + test_created, + test_issocket, + test_isfifo, + test_ischardev, + test_isblockdev, + test_ismount, + test_isexecutable, + test_isreadable, + test_iswritable, + test_cd, + test_readpath, + test_walkpath, + test_read, + test_write, + test_mkdir, + test_cp, + test_mv, + test_sync, + test_symlink, + test_touch, + test_tmpname, + test_tmpdir, + test_mktmp, + test_mktmpdir, + test_chown, + test_chmod, + test_download, + test_include - """ - PathSet(root::AbstractPath=tmpdir(); symlink=false) +""" + PathSet(root::AbstractPath=tmpdir(); symlink=false) - Constructs a common test path hierarchy to running shared API tests. +Constructs a common test path hierarchy to running shared API tests. - Hierarchy: +Hierarchy: - ``` - root - |-- foo - | |-- baz.txt - |-- bar - | |-- qux - | |-- quux.tar.gz - |-- fred - | |-- plugh - ``` - """ - struct PathSet{P<:AbstractPath} - root::P - foo::P - baz::P - bar::P - qux::P - quux::P - fred::P - plugh::P - link::Bool - end +``` +root +|-- foo +| |-- baz.txt +|-- bar +| |-- qux +| |-- quux.tar.gz +|-- fred +| |-- plugh +``` +""" +struct PathSet{P<:AbstractPath} + root::P + foo::P + baz::P + bar::P + qux::P + quux::P + fred::P + plugh::P + link::Bool +end - function PathSet(root=tmpdir() / "pathset_root"; symlink=false) - root = absolute(root) +function PathSet(root=tmpdir() / "pathset_root"; symlink=false) + root = absolute(root) - PathSet( - root, - root / "foo", - root / "foo" / "baz.txt", - root / "bar", - root / "bar" / "qux", - root / "bar" / "qux" / "quux.tar.gz", - root / "fred", - root / "fred" / "plugh", - symlink, - ) - end + return PathSet( + root, + root / "foo", + root / "foo" / "baz.txt", + root / "bar", + root / "bar" / "qux", + root / "bar" / "qux" / "quux.tar.gz", + root / "fred", + root / "fred" / "plugh", + symlink, + ) +end - function initialize(ps::PathSet) - @info "Initializing $(typeof(ps))" - mkdir.([ps.foo, ps.qux, ps.fred]; recursive=true, exist_ok=true) - write(ps.baz, "Hello World!") - write(ps.quux, "Hello Again!") +function initialize(ps::PathSet) + @info "Initializing $(typeof(ps))" + mkdir.([ps.foo, ps.qux, ps.fred]; recursive=true, exist_ok=true) + write(ps.baz, "Hello World!") + write(ps.quux, "Hello Again!") - # If link is true then plugh is a symlink to foo - if ps.link - symlink(ps.foo, ps.plugh) - else - touch(ps.plugh) - end + # If link is true then plugh is a symlink to foo + if ps.link + symlink(ps.foo, ps.plugh) + else + touch(ps.plugh) end +end - # NOTE: Most paths should test their own constructors as necessary. +# NOTE: Most paths should test their own constructors as necessary. - function test_registration end - function test_show end - function test_cmd end - function test_parse end - function test_convert end - function test_components end - function test_indexing end - function test_iteration end - function test_parents end - function test_descendants_and_ascendants end - function test_join end - function test_splitext end - function test_basename end - function test_splitdir end - function test_filename end - function test_extensions end - function test_isempty end - function test_normalize end - function test_canonicalize end - function test_relative end - function test_absolute end - function test_isdir end - function test_isfile end - function test_stat end +function test_registration end +function test_show end +function test_cmd end +function test_parse end +function test_convert end +function test_components end +function test_indexing end +function test_iteration end +function test_parents end +function test_descendants_and_ascendants end +function test_join end +function test_splitext end +function test_basename end +function test_splitdir end +function test_filename end +function test_extensions end +function test_isempty end +function test_normalize end +function test_canonicalize end +function test_relative end +function test_absolute end +function test_isdir end +function test_isfile end +function test_stat end - # Minimal testing of issocket, isfifo, ischardev, isblockdev and ismount which - # people won't typically include. - function test_issocket end - function test_isfifo end - function test_ischardev end - function test_isblockdev end - function test_ismount end - function test_filesize end - function test_modified end - function test_created end - function test_isexecutable end - function test_isreadable end - function test_iswritable end - function test_cd end - function test_readpath end - function test_walkpath end - function test_read end - function test_write end - function test_mkdir end - function test_cp end - function test_mv end - function test_sync end - function test_symlink end - function test_touch end - function test_tmpname end - function test_tmpdir end - function test_mktmp end - function test_mktmpdir end - function test_chown end - function test_chmod end - function test_download end - function test_include end +# Minimal testing of issocket, isfifo, ischardev, isblockdev and ismount which +# people won't typically include. +function test_issocket end +function test_isfifo end +function test_ischardev end +function test_isblockdev end +function test_ismount end +function test_filesize end +function test_modified end +function test_created end +function test_isexecutable end +function test_isreadable end +function test_iswritable end +function test_cd end +function test_readpath end +function test_walkpath end +function test_read end +function test_write end +function test_mkdir end +function test_cp end +function test_mv end +function test_sync end +function test_symlink end +function test_touch end +function test_tmpname end +function test_tmpdir end +function test_mktmp end +function test_mktmpdir end +function test_chown end +function test_chmod end +function test_download end +function test_include end - TESTALL = [ - test_registration, - test_show, - test_cmd, - test_parse, - test_convert, - test_components, - test_indexing, - test_iteration, - test_parents, - test_descendants_and_ascendants, - test_join, - test_splitext, - test_basename, - test_filename, - test_extensions, - test_isempty, - test_normalize, - test_canonicalize, - test_relative, - test_absolute, - test_isdir, - test_isfile, - test_stat, - test_filesize, - test_modified, - test_created, - test_issocket, - test_isfifo, - test_ischardev, - test_isblockdev, - test_ismount, - test_isexecutable, - test_isreadable, - test_iswritable, - test_cd, - test_readpath, - test_walkpath, - test_read, - test_write, - test_mkdir, - test_cp, - test_mv, - test_symlink, - test_touch, - test_tmpname, - test_tmpdir, - test_mktmp, - test_mktmpdir, - test_chown, - test_chmod, - test_download, - test_include, - ] +TESTALL = [ + test_registration, + test_show, + test_cmd, + test_parse, + test_convert, + test_components, + test_indexing, + test_iteration, + test_parents, + test_descendants_and_ascendants, + test_join, + test_splitext, + test_basename, + test_filename, + test_extensions, + test_isempty, + test_normalize, + test_canonicalize, + test_relative, + test_absolute, + test_isdir, + test_isfile, + test_stat, + test_filesize, + test_modified, + test_created, + test_issocket, + test_isfifo, + test_ischardev, + test_isblockdev, + test_ismount, + test_isexecutable, + test_isreadable, + test_iswritable, + test_cd, + test_readpath, + test_walkpath, + test_read, + test_write, + test_mkdir, + test_cp, + test_mv, + test_symlink, + test_touch, + test_tmpname, + test_tmpdir, + test_mktmp, + test_mktmpdir, + test_chown, + test_chmod, + test_download, + test_include, +] - function test(ps::PathSet, test_sets=TESTALL) - try - initialize(ps) +function test(ps::PathSet, test_sets=TESTALL) + try + initialize(ps) - for ts in test_sets - ts(ps) - end - finally - rm(ps.root; recursive=true, force=true) + for ts in test_sets + ts(ps) end + finally + rm(ps.root; recursive=true, force=true) end +end end #module diff --git a/src/utils.jl b/src/utils.jl index 4f02ff7..c81c79a 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -2,7 +2,7 @@ function uuid4() u = rand(UInt128) u &= 0xffffffffffff0fff3fffffffffffffff u |= 0x00000000000040008000000000000000 - UUID(u) + return UUID(u) end # Mostly copied from https://github.com/IainNZ/Humanize.jl/blob/master/src/Humanize.jl#L27 function _datasize(bytes::Number) @@ -12,12 +12,12 @@ function _datasize(bytes::Number) suffix = first(DATA_SUFFIX) for (i, s) in enumerate(DATA_SUFFIX) - unit = base ^ i + unit = base^i if nbytes < unit suffix = s break end end - return string(round(base * nbytes / unit,digits = 1)) * suffix + return string(round(base * nbytes / unit; digits=1)) * suffix end diff --git a/src/windows.jl b/src/windows.jl index e0da293..f82dc74 100644 --- a/src/windows.jl +++ b/src/windows.jl @@ -18,8 +18,11 @@ struct WindowsPath <: SystemPath end function _win_splitdrive(fp::String) - m = match(r"^([^\\]+:|\\\\[^\\]+\\[^\\]+|\\\\\?\\UNC\\[^\\]+\\[^\\]+|\\\\\?\\[^\\]+:|)(.*)$", fp) - String(m.captures[1]), String(m.captures[2]) + m = match( + r"^([^\\]+:|\\\\[^\\]+\\[^\\]+|\\\\\?\\UNC\\[^\\]+\\[^\\]+|\\\\\?\\[^\\]+:|)(.*)$", + fp, + ) + return String(m.captures[1]), String(m.captures[2]) end WindowsPath() = WindowsPath(tuple(), "", "") @@ -70,8 +73,8 @@ end function Base.:(==)(a::WindowsPath, b::WindowsPath) return lowercase.(a.segments) == lowercase.(b.segments) && - lowercase(a.root) == lowercase(b.root) && - lowercase(a.drive) == lowercase(b.drive) + lowercase(a.root) == lowercase(b.root) && + lowercase(a.drive) == lowercase(b.drive) end function Base.show(io::IO, fp::WindowsPath) @@ -80,7 +83,7 @@ function Base.show(io::IO, fp::WindowsPath) print(io, replace(fp.anchor, "\\" => "/")) end print(io, join(fp.segments, "/")) - print(io, "\"") + return print(io, "\"") end isabsolute(fp::WindowsPath) = (!isempty(fp.drive) || !isempty(fp.root)) diff --git a/test/buffer.jl b/test/buffer.jl index e90092d..7401b2b 100644 --- a/test/buffer.jl +++ b/test/buffer.jl @@ -85,7 +85,7 @@ using FilePathsBase: FileBuffer p2 = p"README.md" cp(p1, p2) - io = FileBuffer(p2; read=true,write=true) + io = FileBuffer(p2; read=true, write=true) try @test isopen(io) @test isreadable(io) @@ -111,7 +111,7 @@ using FilePathsBase: FileBuffer rm(p2) - io = FileBuffer(p2; read=true,write=true) + io = FileBuffer(p2; read=true, write=true) try write(io, read(p1)) flush(io) @@ -162,7 +162,6 @@ using FilePathsBase: FileBuffer end end - open(p"hello_world.jlso") do io data = UInt8[] push!(data, read(io, UInt8)) diff --git a/test/mode.jl b/test/mode.jl index daae564..cd557ac 100644 --- a/test/mode.jl +++ b/test/mode.jl @@ -1,9 +1,5 @@ @testset "Mode Tests" begin - m = Mode( - user=(READ + WRITE + EXEC), - group=(READ + EXEC), - other=EXEC - ) + m = Mode(; user=(READ + WRITE + EXEC), group=(READ + EXEC), other=EXEC) x = executable(:ALL) r = readable(:USER, :GROUP) diff --git a/test/runtests.jl b/test/runtests.jl index daee8fb..ddbf65e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -24,7 +24,8 @@ include(p"testpkg.jl") @testset "`propertynames`" begin @test propertynames(ps.root) == (:drive, :root, :anchor, :separator) - @test propertynames(ps.root, true) == (:drive, :root, :anchor, :separator, :segments) + @test propertynames(ps.root, true) == + (:drive, :root, :anchor, :separator, :segments) end @testset "$(typeof(ps.root))" begin diff --git a/test/system.jl b/test/system.jl index 6eb1159..03e3bc5 100644 --- a/test/system.jl +++ b/test/system.jl @@ -62,7 +62,7 @@ ps = PathSet(; symlink=true) test_iswritable, test_chown, test_chmod, - ] + ], ) end @@ -106,7 +106,8 @@ ps = PathSet(; symlink=true) @test exists(p) @test !isabsolute(p) - @test string(normalize(p"../src/../src/FilePathsBase.jl")) == normpath("../src/../src/FilePathsBase.jl") + @test string(normalize(p"../src/../src/FilePathsBase.jl")) == + normpath("../src/../src/FilePathsBase.jl") @test string(absolute(p)) == abspath(string(p)) @test sprint(show, p"../README.md") == "p\"../README.md\"" @@ -196,7 +197,8 @@ ps = PathSet(; symlink=true) end @testset "basename" begin - @test basename(p"../src/FilePathsBase.jl") == basename("../src/FilePathsBase.jl") + @test basename(p"../src/FilePathsBase.jl") == + basename("../src/FilePathsBase.jl") end @testset "splitext" begin diff --git a/test/testpkg.jl b/test/testpkg.jl index 72bd260..c4abf45 100644 --- a/test/testpkg.jl +++ b/test/testpkg.jl @@ -63,7 +63,9 @@ Base.isreadable(fp::TestPath) = isreadable(test2posix(fp)) Base.cd(fp::TestPath) = cd(test2posix(fp)) Base.cd(f::Function, fp::TestPath) = cd(f, test2posix(fp)) Base.mkdir(fp::TestPath; kwargs...) = posix2test(mkdir(test2posix(fp); kwargs...)) -Base.symlink(src::TestPath, dest::TestPath; kwargs...) = symlink(test2posix(src), test2posix(dest); kwargs...) +function Base.symlink(src::TestPath, dest::TestPath; kwargs...) + return symlink(test2posix(src), test2posix(dest); kwargs...) +end Base.rm(fp::TestPath; kwargs...) = rm(test2posix(fp); kwargs...) Base.read(fp::TestPath) = read(test2posix(fp)) Base.write(fp::TestPath, x) = write(test2posix(fp), x)