Improve error handling

This commit is contained in:
Riyyi
2026-08-09 21:06:59 +02:00
parent af5db96962
commit 9a4e61626c
9 changed files with 110 additions and 72 deletions
+25
View File
@@ -1,6 +1,7 @@
package chunks package chunks
import "base:runtime" import "base:runtime"
import "core:fmt"
import "core:io" import "core:io"
import "core:os" import "core:os"
@@ -42,3 +43,27 @@ Error :: union #shared_nil {
Read_Error, Read_Error,
Write_Error, Write_Error,
} }
// -----------------------------------------
@(require_results)
format_error :: proc(ferr: Error) -> string {
if ferr == nil do return ""
switch e in ferr {
case nil:
return ""
case runtime.Allocator_Error:
return fmt.tprintf("allocator: {}", e)
case io.Error:
return fmt.tprintf("io: {}", e)
case os.Error:
return fmt.tprintf("os: {}", e)
case Read_Error:
return fmt.tprintf("chunks: {}", read_error_strings[e])
case Write_Error:
return fmt.tprintf("chunks: {}", write_error_strings[e])
}
return "unknown error"
}
+2 -2
View File
@@ -30,7 +30,6 @@ read_file :: proc(
// Check filesystem first, so chunks can be overriden // Check filesystem first, so chunks can be overriden
if os.exists(path) { if os.exists(path) {
// Open file // Open file
fmt.println("searching:", path)
file := os.open(path, {.Read}) or_return file := os.open(path, {.Read}) or_return
defer os.close(file) defer os.close(file)
@@ -92,5 +91,6 @@ read_file :: proc(
return bytes, nil return bytes, nil
} }
return nil, .File_Not_Exist fmt.eprintln("asset: ", path)
return nil, .Asset_Not_Exist
} }
+10 -1
View File
@@ -14,13 +14,22 @@ Reader :: struct {
Read_Error :: enum u8 { Read_Error :: enum u8 {
None = 0, None = 0,
File_Not_A_Chunk = 1, File_Not_A_Chunk = 1,
File_Not_Exist = 2, Asset_Not_Exist = 2,
Unimplemented = 127, Unimplemented = 127,
Okay = None, Okay = None,
} }
// ----------------------------------------- // -----------------------------------------
read_error_strings := #sparse[Read_Error]string { // enumerated array
.None = "",
.File_Not_A_Chunk = "file is not a chunk file",
.Asset_Not_Exist = "asset does not exist",
.Unimplemented = "feature is unimplemented",
}
// -----------------------------------------
reader_init :: proc( reader_init :: proc(
output: ^os.File, output: ^os.File,
allocator := context.allocator, allocator := context.allocator,
+17 -30
View File
@@ -26,8 +26,9 @@ write_assets :: proc(
number_of_assets := len(entries) number_of_assets := len(entries)
if size_per_chunk < w.asset_table.data_offset { if size_per_chunk < w.asset_table.data_offset {
fmt.eprintln( fmt.eprintf(
"error: chunk size too small to hold asset index:", "chunk size: {}\nasset index size: {}\n",
size_per_chunk,
w.asset_table.data_offset, w.asset_table.data_offset,
) )
return .Chunk_Size_Too_Small return .Chunk_Size_Too_Small
@@ -160,22 +161,13 @@ create_new_chunk :: proc(
) or_return ) or_return
defer delete(chunk_path, allocator) defer delete(chunk_path, allocator)
if os.exists(chunk_path) { if os.exists(chunk_path) {
rm_err := os.remove(chunk_path) os.remove(chunk_path) or_return
if rm_err != nil {
fmt.eprintln("error: remove old chunk failed:", rm_err)
return {}, rm_err
}
} }
if w.chunk_file != nil do os.close(w.chunk_file) // close previous if w.chunk_file != nil do os.close(w.chunk_file) // close previous
// Create new chunk // Create new chunk
cf_err: os.Error w.chunk_file = os.create(chunk_path) or_return
w.chunk_file, cf_err = os.create(chunk_path)
if cf_err != nil {
fmt.eprintln("error: create new chunk failed:", cf_err)
return {}, cf_err
}
chunk_stream = os.to_stream(w.chunk_file) chunk_stream = os.to_stream(w.chunk_file)
@@ -183,34 +175,29 @@ create_new_chunk :: proc(
} }
@(private) @(private)
open_asset :: proc(w: ^Writer, path: string) -> (io.Stream, u64, Error) { open_asset :: proc(
w: ^Writer,
path: string,
) -> (
asset_stream: io.Stream,
asset_size: u64,
err: Error,
) {
if w.asset_file != nil do os.close(w.asset_file) // close previous if w.asset_file != nil do os.close(w.asset_file) // close previous
// Open asset // Open asset
asset, open_err := os.open(path, {.Read}) asset := os.open(path, {.Read}) or_return
if open_err != nil {
fmt.eprintln("error: asset open failed:", open_err)
return {}, {}, open_err
}
// Asset size // Asset size
asset_stream := os.to_stream(asset) asset_stream = os.to_stream(asset)
asset_size, size_err := io.size(asset_stream) asset_size = cast(u64)io.size(asset_stream) or_return
if size_err != nil {
fmt.eprintln("error: asset size failed:", size_err)
return {}, {}, size_err
}
return asset_stream, cast(u64)asset_size, nil return asset_stream, cast(u64)asset_size, nil
} }
@(private) @(private)
write_chunk :: proc(dst: io.Stream, src: io.Stream, n: u64) -> Error { write_chunk :: proc(dst: io.Stream, src: io.Stream, n: u64) -> Error {
w, err := io.copy_n(dst, src, cast(i64)n) w := io.copy_n(dst, src, cast(i64)n) or_return
if err != nil {
fmt.eprintln("error: chunk write error:", err)
return err
}
return nil return nil
} }
+5 -17
View File
@@ -24,11 +24,7 @@ write_metadata :: proc(
chunk_path := compute_chunk_path(output_path, 0, allocator) or_return chunk_path := compute_chunk_path(output_path, 0, allocator) or_return
defer delete(chunk_path, allocator) defer delete(chunk_path, allocator)
chunk_file, err := os.open(chunk_path, {.Read, .Write}) chunk_file := os.open(chunk_path, {.Read, .Write}) or_return
if err != nil {
fmt.eprintln("error: open chunk failed: ", err)
return err
}
defer os.close(chunk_file) defer os.close(chunk_file)
write_header(w, chunk_file, number_of_assets, size_per_chunk, compression) write_header(w, chunk_file, number_of_assets, size_per_chunk, compression)
@@ -62,11 +58,7 @@ write_header :: proc(
// Write header // Write header
header_bytes := mem.ptr_to_bytes(&header) header_bytes := mem.ptr_to_bytes(&header)
n, err := os.write_at(chunk_file, header_bytes, 0) n := os.write_at(chunk_file, header_bytes, 0) or_return
if err != nil {
fmt.eprintln("error: chunk write error:", err)
return err
}
return nil return nil
} }
@@ -91,7 +83,7 @@ write_asset_index :: proc(
// TODO: Move to cli validation so it happens sooner // TODO: Move to cli validation so it happens sooner
path_length := len(f.relpath) path_length := len(f.relpath)
if path_length > 512 { if path_length > 512 {
fmt.eprintln("error: path exeeds maximum of 512:", f.relpath) fmt.eprintf("path: {} ({})\n", f.relpath, path_length)
return .Path_Too_Large return .Path_Too_Large
} }
@@ -107,15 +99,11 @@ write_asset_index :: proc(
assert(len(index_bytes) % size_of(Asset_Index) == 0) // clean multiple assert(len(index_bytes) % size_of(Asset_Index) == 0) // clean multiple
// Write asset index // Write asset index
n, err := os.write_at( n := os.write_at(
chunk_file, chunk_file,
index_bytes, index_bytes,
cast(i64)w.asset_table.asset_index_offset, cast(i64)w.asset_table.asset_index_offset,
) ) or_return
if err != nil {
fmt.eprintln("error: chunk write error:", err)
return err
}
return nil return nil
} }
+12 -7
View File
@@ -24,6 +24,15 @@ Write_Error :: enum u8 {
// ----------------------------------------- // -----------------------------------------
write_error_strings := #sparse[Write_Error]string { // enumerated array
.None = "",
.Chunk_Size_Too_Small = "chunk size too small to hold asset index",
.Path_Too_Large = "path exeeds maximum of 512",
.Unimplemented = "feature is unimplemented",
}
// -----------------------------------------
writer_init :: proc( writer_init :: proc(
number_of_assets: u64, number_of_assets: u64,
allocator := context.allocator, allocator := context.allocator,
@@ -55,8 +64,8 @@ compute_chunk_path :: proc(
chunk_index: int, chunk_index: int,
allocator: runtime.Allocator, allocator: runtime.Allocator,
) -> ( ) -> (
string, chunk_path: string,
Error, err: Error,
) { ) {
@(static) buf: [20]u8 @(static) buf: [20]u8
@@ -65,11 +74,7 @@ compute_chunk_path :: proc(
chunk_name := strings.concatenate({CHUNK, chunk_index_str}, allocator) chunk_name := strings.concatenate({CHUNK, chunk_index_str}, allocator)
defer delete(chunk_name, allocator) defer delete(chunk_name, allocator)
chunk_path, c_err := os.join_path({output_path, chunk_name}, allocator) chunk_path = os.join_path({output_path, chunk_name}, allocator) or_return
if c_err != nil {
fmt.eprintln("error: chunk join path failed:", c_err)
return {}, c_err
}
return chunk_path, nil // owning string return chunk_path, nil // owning string
} }
+22
View File
@@ -17,6 +17,12 @@ File_Error :: enum u8 {
Okay = None, Okay = None,
} }
file_error_strings := #sparse[File_Error]string { // enumerated array
.None = "",
.Not_A_Directory = "path is not a directory",
.Unimplemented = "feature is unimplemented",
}
// ----------------------------------------- // -----------------------------------------
get_working_dir :: proc(allocator := context.allocator) -> (string, Error) { get_working_dir :: proc(allocator := context.allocator) -> (string, Error) {
@@ -28,3 +34,19 @@ get_working_dir :: proc(allocator := context.allocator) -> (string, Error) {
return wd, nil return wd, nil
} }
@(require_results)
format_error :: proc(ferr: Error) -> string {
if ferr == nil do return ""
switch e in ferr {
case nil:
return ""
case os.Error:
return fmt.tprintf("os: {}", e)
case File_Error:
return fmt.tprintf("file: {}", file_error_strings[e])
}
return "unknown error"
}
+3 -11
View File
@@ -67,7 +67,7 @@ list_dir_recursive_by_path_impl :: proc(
Error, Error,
) { ) {
if !os.is_dir(path) { if !os.is_dir(path) {
fmt.eprintln("error: path is not a directory:", path) fmt.eprintln("path:", path)
return {}, .Not_A_Directory return {}, .Not_A_Directory
} }
@@ -96,18 +96,10 @@ list_dir_queue :: proc(
path := pop(queue) path := pop(queue)
defer delete(path, queue^.allocator) defer delete(path, queue^.allocator)
f, open_err := os.open(path, {.Read}) f := os.open(path, {.Read}) or_return
if open_err != nil {
fmt.eprintln("error: open failed:", open_err)
return open_err
}
defer os.close(f) defer os.close(f)
entries, read_err := os.read_all_directory(f, allocator) entries := os.read_all_directory(f, allocator) or_return
if read_err != nil {
fmt.eprintln("error: read directory failed:", read_err)
return read_err
}
defer os.file_info_slice_delete(entries, allocator) defer os.file_info_slice_delete(entries, allocator)
// Reserve space for new entries, minor waste as directories are also counted. // Reserve space for new entries, minor waste as directories are also counted.
+14 -4
View File
@@ -1,5 +1,6 @@
package main package main
import "core:fmt"
import "core:os" import "core:os"
import "src:chunks" import "src:chunks"
@@ -28,8 +29,11 @@ main :: proc() {
// fmt.println("compression:", opts.compression) // fmt.println("compression:", opts.compression)
// fmt.println("size:", opts.size) // fmt.println("size:", opts.size)
entries, e_err := file.list_dir_recursive(opts.input, working_dir) entries, ld_err := file.list_dir_recursive(opts.input, working_dir)
if e_err != nil do os.exit(LIST_DIR_ERR) if ld_err != nil {
fmt.eprintln("error:", file.format_error(ld_err))
os.exit(LIST_DIR_ERR)
}
defer file.delete_entries(&entries) defer file.delete_entries(&entries)
w := chunks.writer_init(cast(u64)len(entries)) w := chunks.writer_init(cast(u64)len(entries))
@@ -42,7 +46,10 @@ main :: proc() {
opts.size, opts.size,
opts.compression, opts.compression,
) )
if wa_err != nil do os.exit(WRITE_ASSETS_ERR) if wa_err != nil {
fmt.eprintln("error:", chunks.format_error(wa_err))
os.exit(WRITE_ASSETS_ERR)
}
wm_err := chunks.write_metadata( wm_err := chunks.write_metadata(
&w, &w,
@@ -51,7 +58,10 @@ main :: proc() {
opts.size, opts.size,
opts.compression, opts.compression,
) )
if wm_err != nil do os.exit(WRITE_METADATA_ERR) if wm_err != nil {
fmt.eprintln("error:", chunks.format_error(wm_err))
os.exit(WRITE_METADATA_ERR)
}
} }
// TODO: .chunkignore // TODO: .chunkignore