Skip to content

Commit 007241e

Browse files
add readdirx which returns more object info from dir scan
Co-Authored-By: Cody Tapscott <[email protected]>
1 parent 77c0672 commit 007241e

File tree

5 files changed

+94
-5
lines changed

5 files changed

+94
-5
lines changed

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ New library functions
3333
---------------------
3434

3535
* `logrange(start, stop; length)` makes a range of constant ratio, instead of constant step ([#39071])
36+
* `readdirx` for returning directory contents along with the type of the entries in a vector of new `DirEntry`
37+
objects ([#53377])
3638

3739
New library features
3840
--------------------

base/exports.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,7 @@ export
841841
eof,
842842
fd,
843843
fdio,
844+
DirEntry,
844845
flush,
845846
gethostname,
846847
htol,
@@ -865,6 +866,7 @@ export
865866
readbytes!,
866867
readchomp,
867868
readdir,
869+
readdirx,
868870
readline,
869871
readlines,
870872
readuntil,

base/file.jl

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export
1919
rename,
2020
readlink,
2121
readdir,
22+
readdirx,
2223
rm,
2324
samefile,
2425
sendfile,
@@ -29,6 +30,7 @@ export
2930
unlink,
3031
walkdir
3132

33+
3234
# get and set current directory
3335

3436
"""
@@ -929,7 +931,78 @@ julia> readdir(abspath("base"), join=true)
929931
"/home/JuliaUser/dev/julia/base/weakkeydict.jl"
930932
```
931933
"""
932-
function readdir(dir::AbstractString; join::Bool=false, sort::Bool=true)
934+
readdir(; join::Bool=false, kwargs...) = readdir(join ? pwd() : "."; join, kwargs...)::Vector{String}
935+
readdir(dir::AbstractString; kwargs...) = _readdir(dir; return_objects=false, kwargs...)::Vector{String}
936+
937+
# this might be better as an Enum but they're not available here
938+
# UV_DIRENT_T
939+
const UV_DIRENT_UNKNOWN = Cint(0)
940+
const UV_DIRENT_FILE = Cint(1)
941+
const UV_DIRENT_DIR = Cint(2)
942+
const UV_DIRENT_LINK = Cint(3)
943+
const UV_DIRENT_FIFO = Cint(4)
944+
const UV_DIRENT_SOCKET = Cint(5)
945+
const UV_DIRENT_CHAR = Cint(6)
946+
const UV_DIRENT_BLOCK = Cint(7)
947+
948+
"""
949+
DirEntry
950+
951+
A type representing a filesystem entry that contains the name of the entry, the directory, and
952+
the raw type of the entry. The full path of the entry can be obtained lazily by accessing the
953+
`path` field. The type of the entry can be checked for by calling [`isfile`](@ref), [`isdir`](@ref),
954+
[`islink`](@ref), [`isfifo`](@ref), [`issocket`](@ref), [`ischardev`](@ref), and [`isblockdev`](@ref)
955+
"""
956+
struct DirEntry
957+
dir::String
958+
name::String
959+
rawtype::Cint
960+
end
961+
function Base.getproperty(obj::DirEntry, p::Symbol)
962+
if p === :path
963+
return joinpath(obj.dir, obj.name)
964+
else
965+
return getfield(obj, p)
966+
end
967+
end
968+
Base.propertynames(::DirEntry) = (:dir, :name, :path, :rawtype)
969+
Base.isless(a::DirEntry, b::DirEntry) = a.dir == b.dir ? isless(a.name, b.name) : isless(a.dir, b.dir)
970+
Base.hash(o::DirEntry, h::UInt) = hash(o.dir, hash(o.name, hash(o.rawtype, h)))
971+
Base.:(==)(a::DirEntry, b::DirEntry) = a.name == b.name && a.dir == b.dir && a.rawtype == b.rawtype
972+
joinpath(obj::DirEntry, args...) = joinpath(obj.path, args...)
973+
isunknown(obj::DirEntry) = obj.rawtype == UV_DIRENT_UNKNOWN
974+
islink(obj::DirEntry) = isunknown(obj) ? islink(obj.path) : obj.rawtype == UV_DIRENT_LINK
975+
isfile(obj::DirEntry) = (isunknown(obj) || islink(obj)) ? isfile(obj.path) : obj.rawtype == UV_DIRENT_FILE
976+
isdir(obj::DirEntry) = (isunknown(obj) || islink(obj)) ? isdir(obj.path) : obj.rawtype == UV_DIRENT_DIR
977+
isfifo(obj::DirEntry) = (isunknown(obj) || islink(obj)) ? isfifo(obj.path) : obj.rawtype == UV_DIRENT_FIFO
978+
issocket(obj::DirEntry) = (isunknown(obj) || islink(obj)) ? issocket(obj.path) : obj.rawtype == UV_DIRENT_SOCKET
979+
ischardev(obj::DirEntry) = (isunknown(obj) || islink(obj)) ? ischardev(obj.path) : obj.rawtype == UV_DIRENT_CHAR
980+
isblockdev(obj::DirEntry) = (isunknown(obj) || islink(obj)) ? isblockdev(obj.path) : obj.rawtype == UV_DIRENT_BLOCK
981+
982+
"""
983+
readdirx(dir::AbstractString=pwd(); sort::Bool = true) -> Vector{DirEntry}
984+
985+
Return a vector of [`DirEntry`](@ref) objects representing the contents of the directory `dir`,
986+
or the current working directory if not given. If `sort` is true, the returned vector is
987+
sorted by name.
988+
989+
Unlike [`readdir`](@ref), `readdirx` returns [`DirEntry`](@ref) objects, which contain the name of the
990+
file, the directory it is in, and the type of the file which is determined during the
991+
directory scan. This means that calls to [`isfile`](@ref), [`isdir`](@ref), [`islink`](@ref), [`isfifo`](@ref),
992+
[`issocket`](@ref), [`ischardev`](@ref), and [`isblockdev`](@ref) can be made on the
993+
returned objects without further stat calls. However, for some filesystems, the type of the file
994+
cannot be determined without a stat call. In these cases the `rawtype` field of the [`DirEntry`](@ref))
995+
object will be 0 (`UV_DIRENT_UNKNOWN`) and [`isfile`](@ref) etc. will fall back to a `stat` call.
996+
997+
```julia
998+
for obj in readdirx()
999+
isfile(obj) && println("$(obj.name) is a file with path $(obj.path)")
1000+
end
1001+
```
1002+
"""
1003+
readdirx(dir::AbstractString=pwd(); sort::Bool=true) = _readdir(dir; return_objects=true, sort)::Vector{DirEntry}
1004+
1005+
function _readdir(dir::AbstractString; return_objects::Bool=false, join::Bool=false, sort::Bool=true)
9331006
# Allocate space for uv_fs_t struct
9341007
req = Libc.malloc(_sizeof_uv_fs)
9351008
try
@@ -939,11 +1012,16 @@ function readdir(dir::AbstractString; join::Bool=false, sort::Bool=true)
9391012
err < 0 && uv_error("readdir($(repr(dir)))", err)
9401013

9411014
# iterate the listing into entries
942-
entries = String[]
1015+
entries = return_objects ? DirEntry[] : String[]
9431016
ent = Ref{uv_dirent_t}()
9441017
while Base.UV_EOF != ccall(:uv_fs_scandir_next, Cint, (Ptr{Cvoid}, Ptr{uv_dirent_t}), req, ent)
9451018
name = unsafe_string(ent[].name)
946-
push!(entries, join ? joinpath(dir, name) : name)
1019+
if return_objects
1020+
rawtype = ent[].typ
1021+
push!(entries, DirEntry(dir, name, rawtype))
1022+
else
1023+
push!(entries, join ? joinpath(dir, name) : name)
1024+
end
9471025
end
9481026

9491027
# Clean up the request string
@@ -957,8 +1035,6 @@ function readdir(dir::AbstractString; join::Bool=false, sort::Bool=true)
9571035
Libc.free(req)
9581036
end
9591037
end
960-
readdir(; join::Bool=false, sort::Bool=true) =
961-
readdir(join ? pwd() : ".", join=join, sort=sort)
9621038

9631039
"""
9641040
walkdir(dir; topdown=true, follow_symlinks=false, onerror=throw)

doc/src/base/file.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ Base.Filesystem.pwd
77
Base.Filesystem.cd(::AbstractString)
88
Base.Filesystem.cd(::Function)
99
Base.Filesystem.readdir
10+
Base.Filesystem.readdirx
11+
Base.Filesystem.DirEntry
1012
Base.Filesystem.walkdir
1113
Base.Filesystem.mkdir
1214
Base.Filesystem.mkpath

test/file.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,15 @@ if !Sys.iswindows() || Sys.windows_version() >= Sys.WINDOWS_VISTA_VER
3131
symlink(subdir, dirlink)
3232
@test stat(dirlink) == stat(subdir)
3333
@test readdir(dirlink) == readdir(subdir)
34+
@test readdirx(dirlink) == readdirx(subdir)
3435

3536
# relative link
3637
relsubdirlink = joinpath(subdir, "rel_subdirlink")
3738
reldir = joinpath("..", "adir2")
3839
symlink(reldir, relsubdirlink)
3940
@test stat(relsubdirlink) == stat(subdir2)
4041
@test readdir(relsubdirlink) == readdir(subdir2)
42+
@test readdirx(relsubdirlink) == readdirx(subdir2)
4143

4244
# creation of symlink to directory that does not yet exist
4345
new_dir = joinpath(subdir, "new_dir")
@@ -56,6 +58,7 @@ if !Sys.iswindows() || Sys.windows_version() >= Sys.WINDOWS_VISTA_VER
5658
mkdir(new_dir)
5759
touch(foo_file)
5860
@test readdir(new_dir) == readdir(nedlink)
61+
@test readdirx(new_dir) == readdirx(nedlink)
5962

6063
rm(foo_file)
6164
rm(new_dir)
@@ -1441,6 +1444,10 @@ rm(dirwalk, recursive=true)
14411444
touch(randstring())
14421445
end
14431446
@test issorted(readdir())
1447+
@test issorted(readdirx())
1448+
@test map(o->o.name, readdirx()) == readdir()
1449+
@test map(o->o.path, readdirx()) == readdir(join=true)
1450+
@test count(isfile, readdir(join=true)) == count(isfile, readdirx())
14441451
end
14451452
end
14461453
end

0 commit comments

Comments
 (0)