iyi

Standard library, 0.18.0

std/file

File: comprehensive file operations, metadata inspection, and manipulation.

import std/file to use it. Source: src/std/file.iyi at v0.18.0.

Extends the prelude's core File with File::Info, File::Type, File::Permissions, path queries, permissions, timestamps, symlinks, and iteration without external C libraries.

Filealias

std/fileiyi doc src/std/file.iyi
pub alias File = ::File

Fileclass, reopened

std/fileiyi doc src/std/file.iyi
class ::File
  def self.basename(path : String | Path, suffix : String | ::Nil = nil) : String
  def self.chmod(path : String | Path, mode : Int32) : Nil
  def self.chown(path : String | Path, uid : Int32, gid : Int32) : Nil
  def self.directory?(path : String | Path) : Bool
  def self.dirname(path : String | Path) : String
  # A line at a time, off the file as it is read: it read the whole file
  # into an array of its lines first, so a log of any size was the
  # program's memory twice over before the first line was seen. Lines as
  # `read_lines` gives them, without their `\n` or a `\r` before it.
  def self.each_line(path : String | Path, &block : (String -> Nil)) : Nil
  def self.empty?(path : String | Path) : Bool
  # On Windows a file runs by its extension, one of PATHEXT's - what cmd
  # and `CreateProcess`'s search go by - and a directory is searchable
  # when it can be read. `cmd.exe` answered false: the made-up mode gave
  # the execute bit to directories alone.
  def self.executable?(path : String | Path) : Bool
  def self.expand_path(path : String | Path, base : String | ::Nil = nil) : String
  def self.extname(path : String | Path) : String
  def self.file?(path : String | Path) : Bool
  def self.info(path : String | Path, follow_symlinks : Bool = true) : Info
  def self.info?(path : String | Path, follow_symlinks : Bool = true) : Info | ::Nil
  def self.join(first : String | Path, second : String | Path, third : String | Path) : String
  def self.join(first : String | Path, second : String | Path) : String
  def self.join(parts : Array(String)) : String
  def self.link(old_path : String | Path, new_path : String | Path) : Nil
  # Whether *path* matches the shell-style *pattern*: `*` any run of
  # characters within a segment, `**` any run of segments (a whole
  # segment of the pattern), `?` one character but a separator, `[abc]`,
  # `[a-z]` and `[^...]` one character of a set, `{a,b}` one of the
  # branches (nested to ten), and `\` the next character literally. Only
  # `/` in the pattern matches a separator. On Windows a name's `\` is a
  # separator as well, and a pattern's `\` is one too rather than an
  # escape: `Dir.glob` answers with either.
  #
  # The other library's algorithm (a port of the glob-match crate, after
  # research.swtch.com/glob), linear in the name but for braces. This
  # had `*` and `?` alone, byte by byte: `*` crossed separators, `?` took
  # half of a two-byte character, `[a]` and `{a,b}` were literal text,
  # and `*a*a*a*a*a*a*a*a*a*a*b` against forty `a`s did not finish.
  def self.match?(path : String | Path, pattern : String) : Bool
  def self.read_lines(path : String | Path) : Array(String)
  def self.readable?(path : String | Path) : Bool
  def self.readlink(path : String | Path) : String
  def self.real_path(path : String | Path) : String
  def self.rename(old_path : String | Path, new_path : String | Path) : Nil
  def self.same?(path1 : String | Path, path2 : String | Path) : Bool
  def self.same_content?(path1 : String | Path, path2 : String | Path) : Bool
  def self.size(path : String | Path) : Int64
  def self.symlink(old_path : String | Path, new_path : String | Path) : Nil
  def self.symlink?(path : String | Path) : Bool
  # A new, empty file in the temporary directory, and its path.
  #
  # Made with an exclusive create - `O_CREAT | O_EXCL`, `CREATE_NEW` on
  # Windows - which fails when anything is at the path, a symlink
  # included, rather than asked whether something is there and written
  # after. The name was the process id and a counter, and the file was
  # written wherever the path led if nothing was there when asked: a
  # symlink planted at the next name in a shared `/tmp` between the
  # question and the write sent an upload wherever its owner pointed it.
  # The name is sixteen hex digits of the system's randomness now, so it
  # cannot be planted in advance either; Windows' `%TEMP%` belongs to the
  # user, and there the create's refusal is the guarantee and the name
  # needs only to be new. The file is the owner's alone, mode 0600.
  def self.tempfile(prefix : String = "tmp", suffix : String = "") : String
  # The file is deleted after the block if it is still there: the block
  # may have renamed it into place - write to a temporary name, then
  # rename, is what the name is for - and `delete` of a path that is gone
  # panicked after the block had done its work.
  def self.tempfile(prefix : String = "tmp", suffix : String = "", &block : (String -> Nil)) : Nil
  # *time* 0 is "now"; any other value is the Unix second both atime and
  # mtime are set to.
  #
  # The darwin branch passed a null `times` to `utimes`, which is the
  # kernel's spelling of "now" — so `touch(path, 1)` set the clock's time
  # there and the epoch second on Linux, one name with two behaviours
  # decided by the platform (SPEC.md III.1.7a). It writes the pair now, in
  # the same shape the Linux branch writes its `timespec`s: `timeval` is an
  # `Int64` second and an `Int32` microsecond padded to eight, so four
  # `Int64` slots are the two structures and the odd slots are the
  # microseconds, zero.
  def self.touch(path : String | Path, time : Int64 = 0_i64) : Nil
  def self.touch?(path : String | Path, time : Int64 = 0_i64) : Bool
  def self.truncate(path : String | Path, size : Int64 = 0_i64) : Nil
  # Why the last call failed, in Windows' own words. A message that names
  # only what was attempted — "Cannot symlink A to B" — leaves the caller
  # with nothing to act on, and on Windows the difference between "a
  # required privilege is not held" and "the file already exists" is the
  # difference between a machine setting and a bug in the program. The
  # code is kept beside the text because the text is localised and the
  # number is not.
  #
  # FORMAT_MESSAGE_FROM_SYSTEM is 0x1000 and FORMAT_MESSAGE_IGNORE_INSERTS
  # 0x200: without the second, a message with an insert in it fails rather
  # than printing the placeholder. Windows ends these sentences with CRLF
  # and sometimes a trailing space, which is a line break in the middle of
  # a panic, so the tail comes off. `std/dir`'s refusals say it too.
  def self.win_error : String
  def self.writable?(path : String | Path) : Bool
end

File::Flagsstruct, reopened

std/fileiyi doc src/std/file.iyi
struct ::File::Flags
  def initialize(value : Int32 = 0)
  def set_group? : Bool
  def set_user? : Bool
  def sticky? : Bool
  def value : Int32
end

File::Infostruct, reopened

std/fileiyi doc src/std/file.iyi
struct ::File::Info
  def dev : Int64
  def directory? : Bool
  def executable? : Bool
  def file? : Bool
  def flags : Flags
  def group_id : String
  def initialize(size : Int64, permissions : Permissions, type : Type, flags : Flags, modification_time : Int64, owner_id : String, group_id : String, dev : Int64 = 0_i64, ino : UInt64 = 0_u64)
  def ino : UInt64
  def modification_time : Int64
  def owner_id : String
  def permissions : Permissions
  def readable? : Bool
  # An identity of 0 is none: Windows answers a path it could not open
  # with the attributes alone (`info?`), and two such files - different
  # sizes, different contents - were the same file.
  def same_file?(other : Info) : Bool
  def size : Int64
  def symlink? : Bool
  def type : Type
  def writable? : Bool
end

File::Permissionsstruct, reopened

std/fileiyi doc src/std/file.iyi
struct ::File::Permissions
  def group_execute? : Bool
  def group_read? : Bool
  def group_write? : Bool
  def initialize(value : Int32)
  def other_execute? : Bool
  def other_read? : Bool
  def other_write? : Bool
  def owner_execute? : Bool
  def owner_read? : Bool
  def owner_write? : Bool
  def to_i : Int32
  def to_s(io : IO) : Nil
  def to_s : String
  def value : Int32
end

File::Typeenum, reopened

std/fileiyi doc src/std/file.iyi
enum ::File::Type : UInt8
  # Returns `true` if this enum value equals `BlockDevice`
  def block_device? : Bool
  # Returns `true` if this enum value equals `CharacterDevice`
  def character_device? : Bool
  def device? : Bool
  # Returns `true` if this enum value equals `Directory`
  def directory? : Bool
  # Returns `true` if this enum value equals `File`
  def file? : Bool
  # Returns `true` if this enum value equals `Pipe`
  def pipe? : Bool
  # Returns `true` if this enum value equals `Socket`
  def socket? : Bool
  # Returns `true` if this enum value equals `Symlink`
  def symlink? : Bool
end

IyiIOclass, reopened

std/fileiyi doc src/std/file.iyi
class ::IyiIO
  # Advisory locks on the whole file, the other library's `flock_*`: a
  # shared one many holders may have at once, an exclusive one a single
  # holder. The holder is this open file - another `File.open` of the same
  # path, in this process or another, is another holder - until
  # `flock_unlock`, or until the file is closed.
  #
  #     File.open("app.lock", "w") do |f|
  #       f.flock_exclusive { run_alone }.or_panic
  #     end
  #
  # A lock another holder has is waited for a task at a time: tried again
  # after 1, 2, 4 ... 64 ms, the thread's other tasks running meanwhile,
  # and cancellable. `try_flock_*` answers at once whether it took it.
  #
  # Linux and darwin lock with `flock(2)`, which only other locks heed, and
  # turn a shared lock this file holds into an exclusive one in place.
  # Windows locks every byte with `LockFileEx`, which is mandatory - while
  # one handle holds it exclusively, another handle's reads and writes of
  # the file fail - and counts each lock a handle takes: unlock a shared
  # lock before taking the exclusive one.
  def flock_exclusive : Nil | Cancelled
  # The block's answer, run with the lock held and unlocked after it, even
  # when the block panics.
  def flock_exclusive(&block : (-> R)) : R | Cancelled forall R
  def flock_shared : Nil | Cancelled
  def flock_shared(&block : (-> R)) : R | Cancelled forall R
  # Gives the lock back; one this file does not hold is no error.
  def flock_unlock : Nil
  # The kernel's move: the new offset, or a negative error. A stream with
  # no descriptor - a `Memory` - is -1, which Windows' `to_u64` of it
  # panicked "arithmetic overflow" on; it is EBADF everywhere, as Linux's
  # kernel answers it.
  protected def self.lseek(fd : Int64, offset : Int64, whence : Int32) : Int64
  # Bytes from the start to where the next read or write happens.
  def pos : Int64
  def pos=(position : Int64) : Int64
  # Moves to *offset* from the start, from here, or from the end, and
  # answers the new position.
  def seek(offset : Int64, whence : Seek = Seek::Set) : Int64
  def seek(offset : Int32, whence : Seek = Seek::Set) : Int64
  def try_flock_exclusive : Bool
  def try_flock_shared : Bool
end

IyiIO::Seekenum, reopened

std/fileiyi doc src/std/file.iyi
enum ::IyiIO::Seek : Int32
end