aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/environment.c27
-rw-r--r--src/environment.h1
-rw-r--r--src/stdlib/datatypes.h35
-rw-r--r--src/stdlib/optionals.h2
-rw-r--r--src/stdlib/paths.c111
-rw-r--r--src/stdlib/paths.h22
-rw-r--r--src/stdlib/result.c65
-rw-r--r--src/stdlib/result.h9
-rw-r--r--src/stdlib/tomo.h1
9 files changed, 199 insertions, 74 deletions
diff --git a/src/environment.c b/src/environment.c
index bdbc8a4f..f84b6697 100644
--- a/src/environment.c
+++ b/src/environment.c
@@ -18,6 +18,8 @@ public
type_t *PATH_TYPE = NULL;
public
type_t *PRESENT_TYPE = NULL;
+public
+type_t *RESULT_TYPE = NULL;
static type_t *declare_type(env_t *env, const char *def_str) {
ast_t *ast = parse_file_str(def_str);
@@ -68,6 +70,7 @@ env_t *global_env(bool source_mapping) {
PATH_TYPE = declare_type(
env,
"enum Path(AbsolutePath(components:[Text]), RelativePath(components:[Text]), HomePath(components:[Text]))");
+ RESULT_TYPE = declare_type(env, "enum Result(Success, Failure(reason:Text))");
PRESENT_TYPE = declare_type(env, "struct Present()");
@@ -90,6 +93,7 @@ env_t *global_env(bool source_mapping) {
MAKE_TYPE("Abort", Type(AbortType), Text("void"), Text("Abort$info")),
MAKE_TYPE("Memory", Type(MemoryType), Text("void"), Text("Memory$info")),
MAKE_TYPE("Present", PRESENT_TYPE, Text("Present$$type"), Text("Present$$info")),
+ MAKE_TYPE("Result", RESULT_TYPE, Text("Result_t"), Text("Result$$info")),
MAKE_TYPE( //
"Bool", Type(BoolType), Text("Bool_t"), Text("Bool$info"),
{"parse", "Bool$parse", "func(text:Text, remainder:&Text?=none -> Bool?)"}),
@@ -293,8 +297,9 @@ env_t *global_env(bool source_mapping) {
MAKE_TYPE( //
"Path", PATH_TYPE, Text("Path_t"), Text("Path$info"), //
{"accessed", "Path$accessed", "func(path:Path, follow_symlinks=yes -> Int64?)"}, //
- {"append", "Path$append", "func(path:Path, text:Text, permissions=Int32(0o644))"}, //
- {"append_bytes", "Path$append_bytes", "func(path:Path, bytes:[Byte], permissions=Int32(0o644))"}, //
+ {"append", "Path$append", "func(path:Path, text:Text, permissions=Int32(0o644) -> Result)"}, //
+ {"append_bytes", "Path$append_bytes",
+ "func(path:Path, bytes:[Byte], permissions=Int32(0o644) -> Result)"}, //
{"base_name", "Path$base_name", "func(path:Path -> Text)"}, //
{"by_line", "Path$by_line", "func(path:Path -> func(->Text?)?)"}, //
{"can_execute", "Path$can_execute", "func(path:Path -> Bool)"}, //
@@ -306,7 +311,7 @@ env_t *global_env(bool source_mapping) {
{"concatenated_with", "Path$concat", "func(a,b:Path -> Path)"}, //
{"components", "Path$components", "func(path:Path -> [Text])"}, //
{"create_directory", "Path$create_directory",
- "func(path:Path, permissions=Int32(0o755), recursive=yes)"}, //
+ "func(path:Path, permissions=Int32(0o755), recursive=yes -> Result)"}, //
{"current_dir", "Path$current_dir", "func(->Path)"}, //
{"exists", "Path$exists", "func(path:Path -> Bool)"}, //
{"expand_home", "Path$expand_home", "func(path:Path -> Path)"}, //
@@ -324,21 +329,21 @@ env_t *global_env(bool source_mapping) {
{"lines", "Path$lines", "func(path:Path -> [Text]?)"}, //
{"modified", "Path$modified", "func(path:Path, follow_symlinks=yes -> Int64?)"}, //
{"owner", "Path$owner", "func(path:Path, follow_symlinks=yes -> Text?)"}, //
- {"parent", "Path$parent", "func(path:Path -> Path)"}, //
+ {"parent", "Path$parent", "func(path:Path -> Path?)"}, //
{"read", "Path$read", "func(path:Path -> Text?)"}, //
{"read_bytes", "Path$read_bytes", "func(path:Path, limit:Int?=none -> [Byte]?)"}, //
{"relative_to", "Path$relative_to", "func(path:Path, relative_to:Path -> Path)"}, //
- {"remove", "Path$remove", "func(path:Path, ignore_missing=no)"}, //
+ {"remove", "Path$remove", "func(path:Path, ignore_missing=no -> Result)"}, //
{"resolved", "Path$resolved", "func(path:Path, relative_to=(./) -> Path)"}, //
- {"set_owner", "Path$set_owner", //
- "func(path:Path, owner:Text?=none, group:Text?=none, follow_symlinks=yes)"}, //
+ {"set_owner", "Path$set_owner",
+ "func(path:Path, owner:Text?=none, group:Text?=none, follow_symlinks=yes -> Result)"}, //
{"sibling", "Path$sibling", "func(path:Path, name:Text -> Path)"}, //
{"subdirectories", "Path$children", "func(path:Path, include_hidden=no -> [Path])"}, //
{"unique_directory", "Path$unique_directory", "func(path:Path -> Path)"}, //
- {"write", "Path$write", "func(path:Path, text:Text, permissions=Int32(0o644))"}, //
- {"write_bytes", "Path$write_bytes", "func(path:Path, bytes:[Byte], permissions=Int32(0o644))"}, //
- {"write_unique", "Path$write_unique", "func(path:Path, text:Text -> Path)"}, //
- {"write_unique_bytes", "Path$write_unique_bytes", "func(path:Path, bytes:[Byte] -> Path)"}),
+ {"write", "Path$write", "func(path:Path, text:Text, permissions=Int32(0o644) -> Result)"}, //
+ {"write_bytes", "Path$write_bytes", "func(path:Path, bytes:[Byte], permissions=Int32(0o644) -> Result)"}, //
+ {"write_unique", "Path$write_unique", "func(path:Path, text:Text -> Path?)"}, //
+ {"write_unique_bytes", "Path$write_unique_bytes", "func(path:Path, bytes:[Byte] -> Path?)"}),
MAKE_TYPE( //
"Text", TEXT_TYPE, Text("Text_t"), Text("Text$info"), //
{"as_c_string", "Text$as_c_string", "func(text:Text -> CString)"}, //
diff --git a/src/environment.h b/src/environment.h
index 88d36d42..ba036f2e 100644
--- a/src/environment.h
+++ b/src/environment.h
@@ -87,3 +87,4 @@ binding_t *get_namespace_binding(env_t *env, ast_t *self, const char *name);
extern type_t *TEXT_TYPE;
extern type_t *PATH_TYPE;
extern type_t *PRESENT_TYPE;
+extern type_t *RESULT_TYPE;
diff --git a/src/stdlib/datatypes.h b/src/stdlib/datatypes.h
index 49c4d835..3cd99f38 100644
--- a/src/stdlib/datatypes.h
+++ b/src/stdlib/datatypes.h
@@ -152,6 +152,41 @@ typedef struct {
};
} Path_t;
+#define $OptionalPath$$type Path_t
+#define OptionalPath_t Path_t
+
+typedef struct Result$Success$$struct {
+} Result$Success$$type;
+
+typedef struct {
+ Result$Success$$type value;
+ bool has_value;
+} $OptionalResult$Success$$type;
+
+typedef struct Result$Failure$$struct {
+ Text_t reason;
+} Result$Failure$$type;
+
+typedef struct {
+ Result$Failure$$type value;
+ bool has_value;
+} $OptionalResult$Failure$$type;
+
+#define Result$Success ((Result$$type){.$tag = Result$tag$Success})
+#define SuccessResult Result$Success
+#define Result$tagged$Failure(msg) ((Result$$type){.$tag = Result$tag$Failure, .Failure.reason = msg})
+#define FailureResult(...) Result$tagged$Failure(Texts(__VA_ARGS__))
+
+typedef struct Result$$struct {
+ enum { Result$tag$none, Result$tag$Success, Result$tag$Failure } $tag;
+ union {
+ Result$Success$$type Success;
+ Result$Failure$$type Failure;
+ };
+} Result$$type;
+
+#define Result_t Result$$type
+
#define OptionalBool_t uint8_t
#define OptionalList_t List_t
#define OptionalTable_t Table_t
diff --git a/src/stdlib/optionals.h b/src/stdlib/optionals.h
index 985d6611..700a4ada 100644
--- a/src/stdlib/optionals.h
+++ b/src/stdlib/optionals.h
@@ -15,7 +15,7 @@
#define NONE_TABLE ((OptionalTable_t){.entries.data = NULL})
#define NONE_CLOSURE ((OptionalClosure_t){.fn = NULL})
#define NONE_TEXT ((OptionalText_t){.tag = TEXT_NONE})
-#define NONE_PATH ((Path_t){.$tag = Path$tag$none})
+#define NONE_PATH ((OptionalPath_t){.$tag = Path$tag$none})
PUREFUNC bool is_none(const void *obj, const TypeInfo_t *non_optional_type);
PUREFUNC uint64_t Optional$hash(const void *obj, const TypeInfo_t *type);
diff --git a/src/stdlib/paths.c b/src/stdlib/paths.c
index 9fb37ee3..ed8383fd 100644
--- a/src/stdlib/paths.c
+++ b/src/stdlib/paths.c
@@ -128,8 +128,10 @@ Path_t Path$_concat(int n, Path_t items[n]) {
public
Path_t Path$resolved(Path_t path, Path_t relative_to) {
- if (path.$tag == Path$tag$RelativePath
- && !(relative_to.$tag == Path$tag$RelativePath && relative_to.components.length == 0)) {
+ if (path.$tag == Path$tag$HomePath) {
+ return Path$expand_home(path);
+ } else if (path.$tag == Path$tag$RelativePath
+ && !(relative_to.$tag == Path$tag$RelativePath && relative_to.components.length == 0)) {
Path_t result = {
.$tag = relative_to.$tag,
.components = relative_to.components,
@@ -144,16 +146,18 @@ Path_t Path$resolved(Path_t path, Path_t relative_to) {
public
Path_t Path$relative_to(Path_t path, Path_t relative_to) {
- if (path.$tag != relative_to.$tag)
- fail("Cannot create a path relative to a different path with a mismatching type: (", path, ") relative to (",
- relative_to, ")");
+ if (path.$tag != relative_to.$tag) {
+ path = Path$resolved(path, Path$current_dir());
+ relative_to = Path$resolved(relative_to, Path$current_dir());
+ }
Path_t result = Path$tagged$RelativePath(EMPTY_LIST);
int64_t shared = 0;
- for (; shared < (int64_t)path.components.length && shared < (int64_t)relative_to.components.length; shared++) {
+ while (shared < (int64_t)path.components.length && shared < (int64_t)relative_to.components.length) {
Text_t *p = (Text_t *)(path.components.data + shared * path.components.stride);
Text_t *r = (Text_t *)(relative_to.components.data + shared * relative_to.components.stride);
if (!Text$equal_values(*p, *r)) break;
+ shared += 1;
}
for (int64_t i = shared; i < (int64_t)relative_to.components.length; i++)
@@ -163,7 +167,6 @@ Path_t Path$relative_to(Path_t path, Path_t relative_to) {
Text_t *p = (Text_t *)(path.components.data + i * path.components.stride);
List$insert(&result.components, p, I(0), sizeof(Text_t));
}
- // clean_components(&result.components);
return result;
}
@@ -277,7 +280,7 @@ OptionalInt64_t Path$changed(Path_t path, bool follow_symlinks) {
return (OptionalInt64_t){.value = (int64_t)sb.st_ctime};
}
-static void _write(Path_t path, List_t bytes, int mode, int permissions) {
+static Result_t _write(Path_t path, List_t bytes, int mode, int permissions) {
path = Path$expand_home(path);
const char *path_str = Path$as_c_string(path);
int fd = open(path_str, mode, permissions);
@@ -287,36 +290,38 @@ static void _write(Path_t path, List_t bytes, int mode, int permissions) {
// be closed by GC finalizers.
GC_gcollect();
fd = open(path_str, mode, permissions);
- if (fd == -1) fail("Could not write to file: ", path_str, "\n", strerror(errno));
}
+ if (fd == -1) return FailureResult("Could not write to file: ", path_str, " (", strerror(errno), ")");
}
if (bytes.stride != 1) List$compact(&bytes, 1);
ssize_t written = write(fd, bytes.data, (size_t)bytes.length);
- if (written != (ssize_t)bytes.length) fail("Could not write to file: ", path_str, "\n", strerror(errno));
+ if (written != (ssize_t)bytes.length)
+ return FailureResult("Could not write to file: ", path_str, " (", strerror(errno), ")");
close(fd);
+ return SuccessResult;
}
public
-void Path$write(Path_t path, Text_t text, int permissions) {
+Result_t Path$write(Path_t path, Text_t text, int permissions) {
List_t bytes = Text$utf8(text);
- _write(path, bytes, O_WRONLY | O_CREAT | O_TRUNC, permissions);
+ return _write(path, bytes, O_WRONLY | O_CREAT | O_TRUNC, permissions);
}
public
-void Path$write_bytes(Path_t path, List_t bytes, int permissions) {
- _write(path, bytes, O_WRONLY | O_CREAT | O_TRUNC, permissions);
+Result_t Path$write_bytes(Path_t path, List_t bytes, int permissions) {
+ return _write(path, bytes, O_WRONLY | O_CREAT | O_TRUNC, permissions);
}
public
-void Path$append(Path_t path, Text_t text, int permissions) {
+Result_t Path$append(Path_t path, Text_t text, int permissions) {
List_t bytes = Text$utf8(text);
- _write(path, bytes, O_WRONLY | O_APPEND | O_CREAT, permissions);
+ return _write(path, bytes, O_WRONLY | O_APPEND | O_CREAT, permissions);
}
public
-void Path$append_bytes(Path_t path, List_t bytes, int permissions) {
- _write(path, bytes, O_WRONLY | O_APPEND | O_CREAT, permissions);
+Result_t Path$append_bytes(Path_t path, List_t bytes, int permissions) {
+ return _write(path, bytes, O_WRONLY | O_APPEND | O_CREAT, permissions);
}
public
@@ -347,8 +352,7 @@ OptionalList_t Path$read_bytes(Path_t path, OptionalInt_t count) {
memcpy(content, mem, (size_t)sb.st_size);
content[sb.st_size] = '\0';
close(fd);
- if (count.small && (int64_t)sb.st_size < target_count)
- fail("Could not read ", target_count, " bytes from ", path, " (only got ", (uint64_t)sb.st_size, ")");
+ if (count.small && (int64_t)sb.st_size < target_count) return NONE_LIST;
int64_t len = count.small ? target_count : (int64_t)sb.st_size;
return (List_t){.data = content, .atomic = 1, .stride = 1, .length = (uint64_t)len};
} else {
@@ -376,8 +380,7 @@ OptionalList_t Path$read_bytes(Path_t path, OptionalInt_t count) {
len += (size_t)just_read;
}
close(fd);
- if (count.small != 0 && (int64_t)len < target_count)
- fail("Could not read ", target_count, " bytes from ", path, " (only got ", (uint64_t)len, ")");
+ if (count.small != 0 && (int64_t)len < target_count) return NONE_LIST;
return (List_t){.data = content, .atomic = 1, .stride = 1, .length = (uint64_t)len};
}
}
@@ -408,23 +411,24 @@ OptionalText_t Path$group(Path_t path, bool follow_symlinks) {
}
public
-void Path$set_owner(Path_t path, OptionalText_t owner, OptionalText_t group, bool follow_symlinks) {
+Result_t Path$set_owner(Path_t path, OptionalText_t owner, OptionalText_t group, bool follow_symlinks) {
uid_t owner_id = (uid_t)-1;
if (owner.tag == TEXT_NONE) {
struct passwd *pwd = getpwnam(Text$as_c_string(owner));
- if (pwd == NULL) fail("Not a valid user: ", owner);
+ if (pwd == NULL) return FailureResult("Not a valid user: ", owner);
owner_id = pwd->pw_uid;
}
gid_t group_id = (gid_t)-1;
if (group.tag == TEXT_NONE) {
struct group *grp = getgrnam(Text$as_c_string(group));
- if (grp == NULL) fail("Not a valid group: ", group);
+ if (grp == NULL) return FailureResult("Not a valid group: ", group);
group_id = grp->gr_gid;
}
const char *path_str = Path$as_c_string(path);
int result = follow_symlinks ? chown(path_str, owner_id, group_id) : lchown(path_str, owner_id, group_id);
- if (result < 0) fail("Could not set owner!");
+ if (result < 0) return FailureResult("Could not set owner!");
+ return SuccessResult;
}
static int _remove_files(const char *path, const struct stat *sbuf, int type, struct FTW *ftwb) {
@@ -446,29 +450,30 @@ static int _remove_files(const char *path, const struct stat *sbuf, int type, st
}
public
-void Path$remove(Path_t path, bool ignore_missing) {
+Result_t Path$remove(Path_t path, bool ignore_missing) {
path = Path$expand_home(path);
const char *path_str = Path$as_c_string(path);
struct stat sb;
if (lstat(path_str, &sb) != 0) {
- if (!ignore_missing) fail("Could not remove file: ", path_str, " (", strerror(errno), ")");
- return;
+ if (!ignore_missing) return FailureResult("Could not remove file: ", path_str, " (", strerror(errno), ")");
+ return SuccessResult;
}
if ((sb.st_mode & S_IFMT) == S_IFREG || (sb.st_mode & S_IFMT) == S_IFLNK) {
if (unlink(path_str) != 0 && !ignore_missing)
- fail("Could not remove file: ", path_str, " (", strerror(errno), ")");
+ return FailureResult("Could not remove file: ", path_str, " (", strerror(errno), ")");
} else if ((sb.st_mode & S_IFMT) == S_IFDIR) {
const int num_open_fd = 10;
if (nftw(path_str, _remove_files, num_open_fd, FTW_DEPTH | FTW_MOUNT | FTW_PHYS) < 0)
- fail("Could not remove directory: %s (%s)", path_str, strerror(errno));
+ return FailureResult("Could not remove directory: ", path_str, " (", strerror(errno), ")");
} else {
- fail("Could not remove path: ", path_str, " (not a file or directory)");
+ return FailureResult("Could not remove path: ", path_str, " (not a file or directory)");
}
+ return SuccessResult;
}
public
-void Path$create_directory(Path_t path, int permissions, bool recursive) {
+Result_t Path$create_directory(Path_t path, int permissions, bool recursive) {
retry:
path = Path$expand_home(path);
const char *c_path = Path$as_c_string(path);
@@ -478,19 +483,20 @@ retry:
Path$create_directory(Path$parent(path), permissions, recursive);
goto retry;
} else if (errno != EEXIST) {
- fail("Could not create directory: ", c_path, " (", strerror(errno), ")");
+ return FailureResult("Could not create directory: ", c_path, " (", strerror(errno), ")");
}
}
+ return SuccessResult;
}
-static List_t _filtered_children(Path_t path, bool include_hidden, mode_t filter) {
+static OptionalList_t _filtered_children(Path_t path, bool include_hidden, mode_t filter) {
path = Path$expand_home(path);
struct dirent *dir;
List_t children = EMPTY_LIST;
const char *path_str = Path$as_c_string(path);
size_t path_len = strlen(path_str);
DIR *d = opendir(path_str);
- if (!d) fail("Could not open directory: ", path, " (", strerror(errno), ")");
+ if (!d) return NONE_LIST;
if (path_str[path_len - 1] == '/') --path_len;
@@ -511,18 +517,22 @@ static List_t _filtered_children(Path_t path, bool include_hidden, mode_t filter
}
public
-List_t Path$children(Path_t path, bool include_hidden) { return _filtered_children(path, include_hidden, (mode_t)-1); }
+OptionalList_t Path$children(Path_t path, bool include_hidden) {
+ return _filtered_children(path, include_hidden, (mode_t)-1);
+}
public
-List_t Path$files(Path_t path, bool include_hidden) { return _filtered_children(path, include_hidden, S_IFREG); }
+OptionalList_t Path$files(Path_t path, bool include_hidden) {
+ return _filtered_children(path, include_hidden, S_IFREG);
+}
public
-List_t Path$subdirectories(Path_t path, bool include_hidden) {
+OptionalList_t Path$subdirectories(Path_t path, bool include_hidden) {
return _filtered_children(path, include_hidden, S_IFDIR);
}
public
-Path_t Path$unique_directory(Path_t path) {
+OptionalPath_t Path$unique_directory(Path_t path) {
path = Path$expand_home(path);
const char *path_str = Path$as_c_string(path);
size_t len = strlen(path_str);
@@ -532,12 +542,12 @@ Path_t Path$unique_directory(Path_t path) {
buf[len] = '\0';
if (buf[len - 1] == '/') buf[--len] = '\0';
char *created = mkdtemp(buf);
- if (!created) fail("Failed to create temporary directory: ", path_str, " (", strerror(errno), ")");
+ if (!created) return NONE_PATH;
return Path$from_str(created);
}
public
-Path_t Path$write_unique_bytes(Path_t path, List_t bytes) {
+OptionalPath_t Path$write_unique_bytes(Path_t path, List_t bytes) {
path = Path$expand_home(path);
const char *path_str = Path$as_c_string(path);
size_t len = strlen(path_str);
@@ -553,23 +563,23 @@ Path_t Path$write_unique_bytes(Path_t path, List_t bytes) {
++suffixlen;
int fd = mkstemps(buf, suffixlen);
- if (fd == -1) fail("Could not write to unique file: ", buf, "\n", strerror(errno));
+ if (fd == -1) return NONE_PATH;
if (bytes.stride != 1) List$compact(&bytes, 1);
ssize_t written = write(fd, bytes.data, (size_t)bytes.length);
- if (written != (ssize_t)bytes.length) fail("Could not write to file: ", buf, "\n", strerror(errno));
+ if (written != (ssize_t)bytes.length) fail("Could not write to file: ", buf, " (", strerror(errno), ")");
close(fd);
return Path$from_str(buf);
}
public
-Path_t Path$write_unique(Path_t path, Text_t text) { return Path$write_unique_bytes(path, Text$utf8(text)); }
+OptionalPath_t Path$write_unique(Path_t path, Text_t text) { return Path$write_unique_bytes(path, Text$utf8(text)); }
public
-Path_t Path$parent(Path_t path) {
+OptionalPath_t Path$parent(Path_t path) {
if (path.$tag == Path$tag$AbsolutePath && path.components.length == 0) {
- return path;
+ return NONE_PATH;
} else if (path.components.length > 0
&& !Text$equal_values(
*(Text_t *)(path.components.data + path.components.stride * ((int64_t)path.components.length - 1)),
@@ -631,11 +641,10 @@ public
Path_t Path$sibling(Path_t path, Text_t name) { return Path$child(Path$parent(path), name); }
public
-Path_t Path$with_extension(Path_t path, Text_t extension, bool replace) {
- if (path.components.length == 0) fail("A path with no components can't have an extension!");
+OptionalPath_t Path$with_extension(Path_t path, Text_t extension, bool replace) {
+ if (path.components.length == 0) return NONE_PATH;
- if (Text$has(extension, Text("/")) || Text$has(extension, Text(";")))
- fail("Path extension has invalid characters: ", extension);
+ if (Text$has(extension, Text("/")) || Text$has(extension, Text(";"))) return NONE_PATH;
Path_t result = {
.$tag = path.$tag,
diff --git a/src/stdlib/paths.h b/src/stdlib/paths.h
index 7a175099..4e52866e 100644
--- a/src/stdlib/paths.h
+++ b/src/stdlib/paths.h
@@ -34,24 +34,24 @@ bool Path$can_execute(Path_t path);
OptionalInt64_t Path$modified(Path_t path, bool follow_symlinks);
OptionalInt64_t Path$accessed(Path_t path, bool follow_symlinks);
OptionalInt64_t Path$changed(Path_t path, bool follow_symlinks);
-void Path$write(Path_t path, Text_t text, int permissions);
-void Path$write_bytes(Path_t path, List_t bytes, int permissions);
-void Path$append(Path_t path, Text_t text, int permissions);
-void Path$append_bytes(Path_t path, List_t bytes, int permissions);
+Result_t Path$write(Path_t path, Text_t text, int permissions);
+Result_t Path$write_bytes(Path_t path, List_t bytes, int permissions);
+Result_t Path$append(Path_t path, Text_t text, int permissions);
+Result_t Path$append_bytes(Path_t path, List_t bytes, int permissions);
OptionalText_t Path$read(Path_t path);
OptionalList_t Path$read_bytes(Path_t path, OptionalInt_t limit);
-void Path$set_owner(Path_t path, OptionalText_t owner, OptionalText_t group, bool follow_symlinks);
+Result_t Path$set_owner(Path_t path, OptionalText_t owner, OptionalText_t group, bool follow_symlinks);
OptionalText_t Path$owner(Path_t path, bool follow_symlinks);
OptionalText_t Path$group(Path_t path, bool follow_symlinks);
-void Path$remove(Path_t path, bool ignore_missing);
-void Path$create_directory(Path_t path, int permissions, bool recursive);
+Result_t Path$remove(Path_t path, bool ignore_missing);
+Result_t Path$create_directory(Path_t path, int permissions, bool recursive);
List_t Path$children(Path_t path, bool include_hidden);
List_t Path$files(Path_t path, bool include_hidden);
List_t Path$subdirectories(Path_t path, bool include_hidden);
-Path_t Path$unique_directory(Path_t path);
-Path_t Path$write_unique(Path_t path, Text_t text);
-Path_t Path$write_unique_bytes(Path_t path, List_t bytes);
-Path_t Path$parent(Path_t path);
+OptionalPath_t Path$unique_directory(Path_t path);
+OptionalPath_t Path$write_unique(Path_t path, Text_t text);
+OptionalPath_t Path$write_unique_bytes(Path_t path, List_t bytes);
+OptionalPath_t Path$parent(Path_t path);
Text_t Path$base_name(Path_t path);
Text_t Path$extension(Path_t path, bool full);
bool Path$has_extension(Path_t path, Text_t extension);
diff --git a/src/stdlib/result.c b/src/stdlib/result.c
new file mode 100644
index 00000000..8fd2ca1e
--- /dev/null
+++ b/src/stdlib/result.c
@@ -0,0 +1,65 @@
+// Result (Success/Failure) type info
+#include <err.h>
+#include <gc.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/param.h>
+
+#include "enums.h"
+#include "structs.h"
+#include "text.h"
+#include "util.h"
+
+public
+const TypeInfo_t Result$Success$$info = {
+ .size = sizeof(Result$Success$$type),
+ .align = __alignof__(Result$Success$$type),
+ .tag = StructInfo,
+ .StructInfo =
+ {
+ .name = "Success",
+ .num_fields = 0,
+ },
+ .metamethods = Struct$metamethods,
+};
+
+public
+const TypeInfo_t Result$Failure$$info = {
+ .size = sizeof(Result$Failure$$type),
+ .align = __alignof__(Result$Failure$$type),
+ .tag = StructInfo,
+ .StructInfo =
+ {
+ .name = "Failure",
+ .num_fields = 1,
+ .fields =
+ (NamedType_t[1]){
+ {.name = "reason", .type = &Text$info},
+ },
+ },
+ .metamethods = Struct$metamethods,
+};
+
+public
+const TypeInfo_t Result$$info = {
+ .size = sizeof(Result_t),
+ .align = __alignof__(Result_t),
+ .tag = EnumInfo,
+ .EnumInfo =
+ {
+ .name = "Result",
+ .num_tags = 2,
+ .tags =
+ (NamedType_t[2]){
+ {
+ .name = "Success",
+ .type = &Result$Success$$info,
+ },
+ {
+ .name = "Failure",
+ .type = &Result$Failure$$info,
+ },
+ },
+ },
+ .metamethods = Enum$metamethods,
+};
diff --git a/src/stdlib/result.h b/src/stdlib/result.h
new file mode 100644
index 00000000..328480e7
--- /dev/null
+++ b/src/stdlib/result.h
@@ -0,0 +1,9 @@
+#pragma once
+
+// Result type for Success/Failure
+
+#include "types.h"
+
+extern const TypeInfo_t Result$Success$$info;
+extern const TypeInfo_t Result$Failure$$info;
+extern const TypeInfo_t Result$$info;
diff --git a/src/stdlib/tomo.h b/src/stdlib/tomo.h
index 1ff065b9..ff8f90a2 100644
--- a/src/stdlib/tomo.h
+++ b/src/stdlib/tomo.h
@@ -25,6 +25,7 @@
#include "paths.h" // IWYU pragma: export
#include "pointers.h" // IWYU pragma: export
#include "print.h" // IWYU pragma: export
+#include "result.h" // IWYU pragma: export
#include "siphash.h" // IWYU pragma: export
#include "stacktrace.h" // IWYU pragma: export
#include "structs.h" // IWYU pragma: export