Skip to content

Commit

Permalink
Simplify Crystal::System interface by adding File.stat? and lstat?
Browse files Browse the repository at this point in the history
By providing these methods we can make the implementation of File.empty? and
File.file? platform-unspecific. This makes the interface to
Crystal::System::File smaller and cleaner.
  • Loading branch information
RX14 committed Jan 7, 2018
1 parent 3cb4b94 commit b015d81
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 41 deletions.
48 changes: 13 additions & 35 deletions src/crystal/system/unix/file.cr
Original file line number Diff line number Diff line change
Expand Up @@ -70,28 +70,28 @@ module Crystal::System::File
tmpdir.rchop(::File::SEPARATOR)
end

def self.stat(path)
def self.stat?(path : String) : ::File::Stat?
if LibC.stat(path.check_no_null_byte, out stat) != 0
raise Errno.new("Unable to get stat for '#{path}'")
if Errno.value == Errno::ENOENT
return nil
else
raise Errno.new("Unable to get stat for '#{path}'")
end
end
::File::Stat.new(stat)
end

def self.lstat(path)
def self.lstat?(path : String) : ::File::Stat?
if LibC.lstat(path.check_no_null_byte, out stat) != 0
raise Errno.new("Unable to get lstat for '#{path}'")
if Errno.value == Errno::ENOENT
return nil
else
raise Errno.new("Unable to get lstat for '#{path}'")
end
end
::File::Stat.new(stat)
end

def self.empty?(path)
begin
stat(path).size == 0
rescue Errno
raise Errno.new("Error determining size of '#{path}'")
end
end

def self.exists?(path)
accessible?(path, LibC::F_OK)
end
Expand All @@ -112,19 +112,8 @@ module Crystal::System::File
LibC.access(path.check_no_null_byte, flag) == 0
end

def self.file?(path) : Bool
if LibC.stat(path.check_no_null_byte, out stat) != 0
if Errno.value == Errno::ENOENT
return false
else
raise Errno.new("stat")
end
end
::File::Stat.new(stat).file?
end

def self.chown(path, uid : Int, gid : Int, follow_symlinks)
ret = if !follow_symlinks && symlink?(path)
ret = if !follow_symlinks && ::File.symlink?(path)
LibC.lchown(path, uid, gid)
else
LibC.chown(path, uid, gid)
Expand Down Expand Up @@ -163,17 +152,6 @@ module Crystal::System::File
ret
end

def self.symlink?(path)
if LibC.lstat(path.check_no_null_byte, out stat) != 0
if Errno.value == Errno::ENOENT
return false
else
raise Errno.new("stat")
end
end
(stat.st_mode & LibC::S_IFMT) == LibC::S_IFLNK
end

def self.rename(old_filename, new_filename)
code = LibC.rename(old_filename.check_no_null_byte, new_filename.check_no_null_byte)
if code != 0
Expand Down
50 changes: 44 additions & 6 deletions src/file.cr
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,32 @@ class File < IO::FileDescriptor

getter path : String

# Returns a `File::Stat` object for the file given by *path* or returns `nil`
# if the file does not exist. Raises `Errno` in case of an error. In case of
# a symbolic link it is followed and information about the target is returned.
#
# ```
# File.write("foo", "foo")
# File.stat("foo").size # => 3
# File.stat("foo").mtime # => 2015-09-23 06:24:19 UTC
# ```
def self.stat?(path : String) : Stat?
Crystal::System::File.stat?(path)
end

# Returns a `File::Stat` object for the file given by *path* or returns `nil`
# if the file does not exist. Raises `Errno` in case of an error. In case of
# a symbolic link information about the link itself is returned.
#
# ```
# File.write("foo", "foo")
# File.lstat("foo").size # => 3
# File.lstat("foo").mtime # => 2015-09-23 06:24:19 UTC
# ```
def self.lstat?(path : String) : Stat?
Crystal::System::File.lstat?(path)
end

# Returns a `File::Stat` object for the file given by *path* or raises
# `Errno` in case of an error. In case of a symbolic link
# it is followed and information about the target is returned.
Expand All @@ -44,20 +70,20 @@ class File < IO::FileDescriptor
# File.stat("foo").mtime # => 2015-09-23 06:24:19 UTC
# ```
def self.stat(path) : Stat
Crystal::System::File.stat(path)
stat?(path) || raise Errno.new("Unable to get stat for #{path.inspect}")
end

# Returns a `File::Stat` object for the file given by *path* or raises
# `Errno` in case of an error. In case of a symbolic link
# information about it is returned.
# information about the link itself is returned.
#
# ```
# File.write("foo", "foo")
# File.lstat("foo").size # => 3
# File.lstat("foo").mtime # => 2015-09-23 06:24:19 UTC
# ```
def self.lstat(path) : Stat
Crystal::System::File.lstat(path)
lstat?(path) || raise Errno.new("Unable to get stat for #{path.inspect}")
end

# Returns `true` if *path* exists else returns `false`
Expand All @@ -82,7 +108,11 @@ class File < IO::FileDescriptor
# File.empty?("foo") # => false
# ```
def self.empty?(path) : Bool
Crystal::System::File.empty?(path)
if stat = stat?(path)
stat.size == 0
else
raise Errno.new("Error determining size of '#{path}'")
end
end

# Returns `true` if *path* is readable by the real user id of this process else returns `false`.
Expand Down Expand Up @@ -125,7 +155,11 @@ class File < IO::FileDescriptor
# File.file?("foobar") # => false
# ```
def self.file?(path) : Bool
Crystal::System::File.file?(path)
if stat = stat?(path)
stat.file?
else
false
end
end

# Returns `true` if the given *path* exists and is a directory.
Expand Down Expand Up @@ -324,7 +358,11 @@ class File < IO::FileDescriptor

# Returns `true` if the *path* is a symbolic link.
def self.symlink?(path) : Bool
Crystal::System::File.symlink?(path)
if stat = lstat?(path)
stat.symlink?
else
false
end
end

# Opens the file named by *filename*. If a file is being created, its initial
Expand Down

0 comments on commit b015d81

Please sign in to comment.