Update path reading APIs to use optional values instead of erroring
This commit is contained in:
parent
4ad7509013
commit
33876323aa
@ -119,11 +119,12 @@ The base name of the file or directory.
|
||||
### `by_line`
|
||||
|
||||
**Description:**
|
||||
Returns an iterator that can be used to iterate over a file one line at a time.
|
||||
Returns an iterator that can be used to iterate over a file one line at a time,
|
||||
or returns a null value if the file could not be opened.
|
||||
|
||||
**Usage:**
|
||||
```markdown
|
||||
by_line(path: Path) -> func()->Text?
|
||||
by_line(path: Path) -> (func()->Text?)?
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
@ -131,11 +132,20 @@ by_line(path: Path) -> func()->Text?
|
||||
- `path`: The path of the file.
|
||||
|
||||
**Returns:**
|
||||
An iterator that can be used to get lines from a file one at a time.
|
||||
An iterator that can be used to get lines from a file one at a time or a null
|
||||
value if the file couldn't be read.
|
||||
|
||||
**Example:**
|
||||
```markdown
|
||||
for line in (/dev/stdin):by_line():
|
||||
# Safely handle file not being readable:
|
||||
if lines := (./file.txt):by_line():
|
||||
for line in lines:
|
||||
say(line:upper())
|
||||
else:
|
||||
say("Couldn't read file!")
|
||||
|
||||
# Assume the file is readable and error if that's not the case:
|
||||
for line in (/dev/stdin):by_line()!:
|
||||
say(line:upper())
|
||||
```
|
||||
|
||||
@ -414,11 +424,12 @@ The path of the parent directory.
|
||||
### `read`
|
||||
|
||||
**Description:**
|
||||
Reads the contents of the file at the specified path.
|
||||
Reads the contents of the file at the specified path or a null value if the
|
||||
file could not be read.
|
||||
|
||||
**Usage:**
|
||||
```markdown
|
||||
read(path: Path) -> Text
|
||||
read(path: Path) -> Text?
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
@ -426,22 +437,29 @@ read(path: Path) -> Text
|
||||
- `path`: The path of the file to read.
|
||||
|
||||
**Returns:**
|
||||
The contents of the file. If the file does not exist, an error will be raised.
|
||||
The contents of the file. If the file could not be read, a null value will be
|
||||
returned. If the file can be read, but is not valid UTF8 data, an error will be
|
||||
raised.
|
||||
|
||||
**Example:**
|
||||
```markdown
|
||||
content := (./file.txt):read()
|
||||
>> (./hello.txt):read()
|
||||
= "Hello"?
|
||||
|
||||
>> (./nosuchfile.xxx):read()
|
||||
= !Text
|
||||
```
|
||||
---
|
||||
|
||||
### `read_bytes`
|
||||
|
||||
**Description:**
|
||||
Reads the contents of the file at the specified path.
|
||||
Reads the contents of the file at the specified path or a null value if the
|
||||
file could not be read.
|
||||
|
||||
**Usage:**
|
||||
```markdown
|
||||
read_bytes(path: Path) -> [Byte]
|
||||
read_bytes(path: Path) -> [Byte]?
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
@ -449,11 +467,16 @@ read_bytes(path: Path) -> [Byte]
|
||||
- `path`: The path of the file to read.
|
||||
|
||||
**Returns:**
|
||||
The byte contents of the file. If the file does not exist, an error will be raised.
|
||||
The byte contents of the file. If the file cannot be read, a null value will be
|
||||
returned.
|
||||
|
||||
**Example:**
|
||||
```markdown
|
||||
content := (./file.txt):read_bytes()
|
||||
>> (./hello.txt):read()
|
||||
= [72[B], 101[B], 108[B], 108[B], 111[B]]?
|
||||
|
||||
>> (./nosuchfile.xxx):read()
|
||||
= ![Byte]
|
||||
```
|
||||
|
||||
---
|
||||
|
@ -262,7 +262,7 @@ env_t *new_compilation_unit(CORD *libname)
|
||||
{"append", "Path$append", "func(path:Path, text:Text, permissions=0o644[32])"},
|
||||
{"append_bytes", "Path$append_bytes", "func(path:Path, bytes:[Byte], permissions=0o644[32])"},
|
||||
{"base_name", "Path$base_name", "func(path:Path)->Text"},
|
||||
{"by_line", "Path$by_line", "func(path:Path)->func()->Text?"},
|
||||
{"by_line", "Path$by_line", "func(path:Path)->(func()->Text?)?"},
|
||||
{"children", "Path$children", "func(path:Path, include_hidden=no)->[Path]"},
|
||||
{"create_directory", "Path$create_directory", "func(path:Path, permissions=0o644[32])"},
|
||||
{"escape_int", "Int$value_as_text", "func(i:Int)->Path"},
|
||||
@ -277,8 +277,8 @@ env_t *new_compilation_unit(CORD *libname)
|
||||
{"is_socket", "Path$is_socket", "func(path:Path, follow_symlinks=yes)->Bool"},
|
||||
{"is_symlink", "Path$is_symlink", "func(path:Path)->Bool"},
|
||||
{"parent", "Path$parent", "func(path:Path)->Path"},
|
||||
{"read", "Path$read", "func(path:Path)->Text"},
|
||||
{"read_bytes", "Path$read_bytes", "func(path:Path)->[Byte]"},
|
||||
{"read", "Path$read", "func(path:Path)->Text?"},
|
||||
{"read_bytes", "Path$read_bytes", "func(path:Path)->[Byte]?"},
|
||||
{"relative", "Path$relative", "func(path:Path, relative_to=(./))->Path"},
|
||||
{"remove", "Path$remove", "func(path:Path, ignore_missing=no)"},
|
||||
{"resolved", "Path$resolved", "func(path:Path, relative_to=(./))->Path"},
|
||||
|
@ -9,13 +9,17 @@
|
||||
#include "util.h"
|
||||
|
||||
#define OptionalBool_t uint8_t
|
||||
#define OptionalArray_t Array_t
|
||||
#define OptionalTable_t Table_t
|
||||
#define OptionalText_t Text_t
|
||||
#define OptionalClosure_t Closure_t
|
||||
|
||||
extern const OptionalBool_t NULL_BOOL;
|
||||
extern const Table_t NULL_TABLE;
|
||||
extern const Array_t NULL_ARRAY;
|
||||
extern const Int_t NULL_INT;
|
||||
extern const Closure_t NULL_CLOSURE;
|
||||
extern const Text_t NULL_TEXT;
|
||||
extern const OptionalTable_t NULL_TABLE;
|
||||
extern const OptionalArray_t NULL_ARRAY;
|
||||
extern const OptionalInt_t NULL_INT;
|
||||
extern const OptionalClosure_t NULL_CLOSURE;
|
||||
extern const OptionalText_t NULL_TEXT;
|
||||
|
||||
PUREFUNC bool is_null(const void *obj, const TypeInfo *non_optional_type);
|
||||
Text_t Optional$as_text(const void *obj, bool colorize, const TypeInfo *type);
|
||||
|
@ -244,16 +244,16 @@ public void Path$append_bytes(Path_t path, Array_t bytes, int permissions)
|
||||
_write(path, bytes, O_WRONLY | O_APPEND | O_CREAT, permissions);
|
||||
}
|
||||
|
||||
public Array_t Path$read_bytes(Path_t path)
|
||||
public OptionalArray_t Path$read_bytes(Path_t path)
|
||||
{
|
||||
path = Path$_expand_home(path);
|
||||
int fd = open(Text$as_c_string(path), O_RDONLY);
|
||||
if (fd == -1)
|
||||
fail("Could not read file: %k (%s)", &path, strerror(errno));
|
||||
return NULL_ARRAY;
|
||||
|
||||
struct stat sb;
|
||||
if (fstat(fd, &sb) != 0)
|
||||
fail("Could not read file: %k (%s)", &path, strerror(errno));
|
||||
return NULL_ARRAY;
|
||||
|
||||
if ((sb.st_mode & S_IFMT) == S_IFREG) { // Use memory mapping if it's a real file:
|
||||
const char *mem = mmap(NULL, (size_t)sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
@ -269,7 +269,7 @@ public Array_t Path$read_bytes(Path_t path)
|
||||
char chunk[256];
|
||||
ssize_t just_read = read(fd, chunk, sizeof(chunk));
|
||||
if (just_read < 0)
|
||||
fail("Failed while reading file: %k (%s)", &path, strerror(errno));
|
||||
return NULL_ARRAY;
|
||||
else if (just_read == 0) {
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
continue;
|
||||
@ -287,17 +287,14 @@ public Array_t Path$read_bytes(Path_t path)
|
||||
break;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
if (u8_check((uint8_t*)content, len) != NULL)
|
||||
fail("File does not contain valid UTF8 data!");
|
||||
|
||||
return (Array_t){.data=content, .atomic=1, .stride=1, .length=len};
|
||||
}
|
||||
}
|
||||
|
||||
public Text_t Path$read(Path_t path)
|
||||
public OptionalText_t Path$read(Path_t path)
|
||||
{
|
||||
Array_t bytes = Path$read_bytes(path);
|
||||
if (bytes.length < 0) return NULL_TEXT;
|
||||
return Text$from_bytes(bytes);
|
||||
}
|
||||
|
||||
@ -480,13 +477,13 @@ static Text_t _next_line(FILE **f)
|
||||
return line_text;
|
||||
}
|
||||
|
||||
public Closure_t Path$by_line(Path_t path)
|
||||
public OptionalClosure_t Path$by_line(Path_t path)
|
||||
{
|
||||
path = Path$_expand_home(path);
|
||||
|
||||
FILE *f = fopen(Text$as_c_string(path), "r");
|
||||
if (f == NULL)
|
||||
fail("Could not read file: %k (%s)", &path, strerror(errno));
|
||||
return NULL_CLOSURE;
|
||||
|
||||
FILE **wrapper = GC_MALLOC(sizeof(FILE*));
|
||||
*wrapper = f;
|
||||
|
@ -29,8 +29,8 @@ void Path$write(Path_t path, Text_t text, int permissions);
|
||||
void Path$write_bytes(Path_t path, Array_t bytes, int permissions);
|
||||
void Path$append(Path_t path, Text_t text, int permissions);
|
||||
void Path$append_bytes(Path_t path, Array_t bytes, int permissions);
|
||||
Text_t Path$read(Path_t path);
|
||||
Array_t Path$read_bytes(Path_t path);
|
||||
OptionalText_t Path$read(Path_t path);
|
||||
OptionalArray_t Path$read_bytes(Path_t path);
|
||||
void Path$remove(Path_t path, bool ignore_missing);
|
||||
void Path$create_directory(Path_t path, int permissions);
|
||||
Array_t Path$children(Path_t path, bool include_hidden);
|
||||
|
Loading…
Reference in New Issue
Block a user