Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions src/FilePathsBase.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,8 @@ export
Status,

# Methods
anchor,
cwd,
drive,
home,
components,
root,
hasparent,
parents,
filename,
Expand Down Expand Up @@ -76,13 +72,16 @@ end
"""
AbstractPath

Defines an abstract filesystem path. Subtypes of `AbstractPath` should implement the
following methods:
Defines an abstract filesystem path. Subtypes of `AbstractPath` should have the following
fields:

- fp.segments # tuple of path segments
- fp.root # fs root (defaults to "/")
- fp.drive # fs drive (defaults to "")
- fp.separator # symbol for separating path segments (defaults to "/")

And the following methods defined
- `Base.print(io, p)` (default: call base julia's joinpath with drive and path parts)
- `FilePathsBase.path(p)`
- `FilePathsBase.root(p)`
- `FilePathsBase.drive(p)`
- `FilePathsBase.ispathtype(::Type{MyPath}, x::AbstractString) = true`
"""
abstract type AbstractPath end # Define the AbstractPath here to avoid circular include dependencies
Expand Down
6 changes: 4 additions & 2 deletions src/deprecates.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,7 @@ import Base:
@deprecate filemode(fp::AbstractPath) mode(fp)
@deprecate isabspath(fp::AbstractPath) isabs(fp)
@deprecate mkpath(fp::AbstractPath) mkdir(fp; recursive=true, exist_ok=true)
@deprecate mv(src::AbstractPath, dest::AbstractPath; kwargs...) move(src, dest; kwargs...)
@deprecate rm(fp::AbstractPath; kwargs...) remove(fp; kwargs...)
@deprecate parts(fp::AbstractPath) fp.segments
@deprecate drive(fp::Abstractpath) fp.drive
@deprecate root(fp::AbstractPath) fp.root
@deprecate anchor(fp::AbstractPath) fp.anchor
21 changes: 10 additions & 11 deletions src/libc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,16 @@ struct User
gid::UInt64
dir::String
shell::String
end

function User(ps::Cpasswd)
new(
unsafe_string(ps.pw_name),
UInt64(ps.pw_uid),
UInt64(ps.pw_gid),
unsafe_string(ps.pw_dir),
unsafe_string(ps.pw_shell)
)
end
function User(ps::Cpasswd)
User(
unsafe_string(ps.pw_name),
UInt64(ps.pw_uid),
UInt64(ps.pw_gid),
unsafe_string(ps.pw_dir),
unsafe_string(ps.pw_shell)
)
end

User(passwd::Ptr{Cpasswd}) = User(unsafe_load(passwd))
Expand Down Expand Up @@ -94,10 +94,9 @@ end
struct Group
name::String
gid::UInt64

Group(gr::Cgroup) = new(unsafe_string(gr.gr_name), UInt64(gr.gr_gid))
end

Group(gr::Cgroup) = Group(unsafe_string(gr.gr_name), UInt64(gr.gr_gid))
Group(group::Ptr{Cgroup}) = Group(unsafe_load(group))

function Base.show(io::IO, group::Group)
Expand Down
79 changes: 51 additions & 28 deletions src/path.jl
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@


"""
Path()
Path(fp::AbstractPath)
Path(fp::Tuple)
Path(fp::AbstractString)
Path() -> SystemPath
Path(fp::Tuple) -> SystemPath
Path(fp::P) where P <: AbstractPath) -> P
Path(fp::AbstractString) -> AbstractPath
Path(fp::P, segments::Tuple) -> P

Responsible for creating the appropriate platform specific path
(e.g., `PosixPath` and `WindowsPath` for Unix and Windows systems respectively)
Expand Down Expand Up @@ -32,6 +33,10 @@ function Path(str::AbstractString; debug=false)
return first(types)(str)
end

function Path(fp::T, segments::Tuple{Vararg{String}}) where T <: AbstractPath
T((s === :segments ? segments : getfield(fp, s) for s in fieldnames(T))...)
end

"""
@p_str -> Path

Expand All @@ -42,13 +47,31 @@ macro p_str(fp)
return :(Path($fp))
end

==(a::P, b::P) where P <: AbstractPath = components(a) == components(b)
function Base.getproperty(fp::T, attr::Symbol) where T <: AbstractPath
if isdefined(fp, attr)
return getfield(fp, attr)
elseif attr === :drive
return ""
elseif attr === :root
return POSIX_PATH_SEPARATOR
elseif attr === :anchor
return fp.drive * fp.root
elseif attr === :separator
return POSIX_PATH_SEPARATOR
else
# Call getfield even though we know it'll error
# so the message is consistent.
return getfield(fp, attr)
end
end

#=
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)
=#
Base.print(io::IO, fp::AbstractPath) = print(io, anchor(fp) * joinpath(path(fp)...))
function Base.print(io::IO, fp::AbstractPath)
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\"")
Expand All @@ -62,8 +85,7 @@ Base.promote_rule(::Type{String}, ::Type{<:AbstractPath}) = String
cwd() = Path(pwd())
home() = Path(homedir())

anchor(fp::AbstractPath) = drive(fp) * root(fp)
components(fp::AbstractPath) = tuple(drive(fp), root(fp), path(fp)...)
# components(fp::AbstractPath) = tuple(drive(fp), root(fp), path(fp)...)

#=
Path Modifiers
Expand All @@ -76,7 +98,7 @@ path components

Returns whether there is a parent directory component to the supplied path.
"""
hasparent(fp::AbstractPath) = length(path(fp)) > 1
hasparent(fp::AbstractPath) = length(fp.segments) > 1

"""
parent{T<:AbstractPath}(fp::T) -> T
Expand Down Expand Up @@ -111,9 +133,7 @@ julia> parents(p"~/.julia/v0.6/REQUIRE")
"""
function parents(fp::T) where {T <: AbstractPath}
if hasparent(fp)
return map(2:length(components(fp))-1) do i
T(tuple(components(fp)[1:i]...))
end
return [Path(fp, fp.segments[1:i]) for i in 1:length(fp.segments)-1]
else
error("$fp has no parents")
end
Expand Down Expand Up @@ -164,21 +184,24 @@ p"~/.julia/v0.6/REQUIRE"
```
"""
function Base.join(prefix::T, pieces::Union{AbstractPath, AbstractString}...) where T <: AbstractPath
all_parts = String[]
push!(all_parts, components(prefix)...)
segments = String[prefix.segments...]

for p in map(Path, pieces)
push!(all_parts, components(p)...)
for p in pieces
if isa(p, AbstractPath)
push!(segments, p.segments...)
else
push!(segments, Path(p).segments...)
end
end

return T(tuple(all_parts...))
return Path(prefix, tuple(segments...))
end

function Base.joinpath(root::AbstractPath, pieces::Union{AbstractPath, AbstractString}...)
return join(root, pieces...)
end

Base.basename(fp::AbstractPath) = path(fp)[end]
Base.basename(fp::AbstractPath) = fp.segments[end]

"""
filename(fp::AbstractPath) -> AbstractString
Expand Down Expand Up @@ -250,15 +273,15 @@ Returns whether or not a path is empty.
NOTE: Empty paths are usually only created by `Path()`, as `p""` and `Path("")` will
default to using the current directory (or `p"."`).
"""
Base.isempty(fp::AbstractPath) = isempty(path(fp))
Base.isempty(fp::AbstractPath) = isempty(fp.segments)

"""
norm(fp::AbstractPath) -> AbstractPath

Normalizes a path by removing "." and ".." entries.
"""
function LinearAlgebra.norm(fp::T) where {T <: AbstractPath}
p = path(fp)
p = fp.segments
result = String[]
rem = length(p)
count = 0
Expand All @@ -281,7 +304,7 @@ function LinearAlgebra.norm(fp::T) where {T <: AbstractPath}
count += 1
end

return T(tuple(drive(fp), root(fp), fill("..", del)..., reverse(result)...))
return Path(fp, tuple(fill("..", del)..., reverse(result)...))
end

"""
Expand All @@ -300,7 +323,7 @@ function Base.abs(fp::AbstractPath)
end

function isabs(fp::AbstractPath)
return !isempty(drive(fp)) && !isempty(root(fp))
return !isempty(fp.drive) && !isempty(fp.root)
end

"""
Expand All @@ -312,8 +335,8 @@ function relative(fp::T, start::T=T(".")) where {T <: AbstractPath}
curdir = "."
pardir = ".."

p = path(abs(fp))
s = path(abs(start))
p = abs(fp).segments
s = abs(start).segments

p == s && return T(curdir)

Expand All @@ -340,9 +363,9 @@ function relative(fp::T, start::T=T(".")) where {T <: AbstractPath}
tuple(fill(pardir, prefix_num + 1)...) :
tuple(fill(pardir, prefix_num + 1)..., pathpart...)
else
relpath_ = pathpart
relpath_ = tuple(pathpart...)
end
return isempty(relpath_) ? T(curdir) : T(relpath_)
return isempty(relpath_) ? T(curdir) : Path(fp, relpath_)
end

"""
Expand Down Expand Up @@ -397,11 +420,11 @@ Download a file from the remote url and save it to the localfile path.
function Base.download(url::AbstractString, localfile::AbstractPath)
mktmp() do fp, io
download(url, fp)
cp(fp, localfile)
cp(fp, localfile; force=true)
end
end

Base.download(url::AbstractPath, localfile::AbstractPath) = cp(url, localfile)
Base.download(url::AbstractPath, localfile::AbstractPath) = cp(url, localfile; force=true)

"""
readpath(fp::P) where {P <: AbstractPath} -> Vector{P}
Expand Down
19 changes: 3 additions & 16 deletions src/posix.jl
Original file line number Diff line number Diff line change
@@ -1,20 +1,10 @@
struct PosixPath <: AbstractPath
path::Tuple{Vararg{String}}
segments::Tuple{Vararg{String}}
root::String
end

PosixPath() = PosixPath(tuple(), "")

function PosixPath(components::Tuple)
components = map(String, Iterators.filter(!isempty, components))

if components[1]==POSIX_PATH_SEPARATOR
return PosixPath(tuple(components[2:end]...), POSIX_PATH_SEPARATOR)
else
return PosixPath(tuple(components...), "")
end
end

function PosixPath(str::AbstractString)
str = string(str)
root = ""
Expand All @@ -30,14 +20,11 @@ function PosixPath(str::AbstractString)
return PosixPath(tuple(map(String, filter!(!isempty, tokenized))...), root)
end

path(fp::PosixPath) = fp.path
root(fp::PosixPath) = fp.root
drive(fp::PosixPath) = ""
ispathtype(::Type{PosixPath}, str::AbstractString) = Sys.isunix()
isabs(fp::PosixPath) = !isempty(root(fp))
isabs(fp::PosixPath) = !isempty(fp.root)

function Base.expanduser(fp::PosixPath)
p = path(fp)
p = fp.segments

if p[1] == "~"
if length(p) > 1
Expand Down
32 changes: 16 additions & 16 deletions src/status.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,23 @@ struct Status
blocks::Int64
mtime::DateTime
ctime::DateTime
end

function Status(s::StatStruct)
new(
s.device,
s.inode,
Mode(s.mode),
s.nlink,
User(s.uid),
Group(s.gid),
s.rdev,
s.size,
s.blksize,
s.blocks,
unix2datetime(s.mtime),
unix2datetime(s.ctime)
)
end
function Status(s::StatStruct)
Status(
s.device,
s.inode,
Mode(s.mode),
s.nlink,
User(s.uid),
Group(s.gid),
s.rdev,
s.size,
s.blksize,
s.blocks,
unix2datetime(s.mtime),
unix2datetime(s.ctime)
)
end

function Base.show(io::IO, s::Status)
Expand Down
9 changes: 4 additions & 5 deletions src/system.jl
Original file line number Diff line number Diff line change
Expand Up @@ -225,17 +225,16 @@ function Base.symlink(src::SystemPath, dest::SystemPath; exist_ok=false, overwri
end
end

Base.rm(fp::SystemPath; kwargs...) = rm(string(fp); kwargs...)
function Base.rm(fp::SystemPath; kwargs...)
rm(string(fp); kwargs...)
end
Base.touch(fp::SystemPath) = touch(string(fp))
function mktmp(parent::SystemPath=Path(tempdir()))
fp, io = mktemp(string(parent))
return Path(fp), io
end

function mktmpdir(parent::SystemPath=tmpdir())
@show string(parent)
Path(mktempdir(string(parent)))
end
mktmpdir(parent::SystemPath=tmpdir()) = Path(mktempdir(string(parent)))

"""
chown(fp::SystemPath, user::AbstractString, group::AbstractString; recursive=false)
Expand Down
Loading