diff --git a/src/cli/cli.odin b/src/cli/cli.odin index 08a6c9b..243e75d 100644 --- a/src/cli/cli.odin +++ b/src/cli/cli.odin @@ -5,6 +5,8 @@ import "core:fmt" import "core:os" import "core:strings" +// ----------------------------------------- + Options :: struct { verbose: bool `args:"name=verbose" usage:"Enable verbose output"`, compression: u16 `args:"name=compression" usage:"Compression level, 0-12"`, @@ -14,25 +16,31 @@ Options :: struct { // overflow: [dynamic]string, } -parse :: proc(working_dir: string) -> Options { +Error :: union #shared_nil { + flags.Error, +} + +// ----------------------------------------- + +parse :: proc(working_dir: string) -> (Options, Error) { opts: Options err := flags.parse(&opts, os.args[1:], .Unix) if err != nil { flags.print_errors(typeid_of(Options), err, os.args[0], .Unix) - os.exit(1) + return {}, err } verify(opts, working_dir) - return opts + return opts, nil } @(private) -verify :: proc(opts: Options, working_dir: string) { +verify :: proc(opts: Options, working_dir: string) -> Error { if opts.compression > 12 { - throw_error( + return throw_error( `Invalid compression "%d". Compression higher than allowed maximum.`, opts.compression, ) @@ -40,13 +48,16 @@ verify :: proc(opts: Options, working_dir: string) { input_path := os.name(opts.input) if !os.is_dir(input_path) { - throw_error(`Invalid input "%v". Should be a directory`, input_path) + return throw_error( + `Invalid input "%v". Should be a directory`, + input_path, + ) } if opts.output != nil { output_path := os.name(opts.output) if !os.is_dir(output_path) { - throw_error( + return throw_error( `Invalid output "%v". Should be a directory`, output_path, ) @@ -54,18 +65,20 @@ verify :: proc(opts: Options, working_dir: string) { } if !strings.has_prefix(input_path, working_dir) { - throw_error( + return throw_error( `Invalid input "%v". Should be a subdirectory of the working directory.`, input_path, ) } + + return nil } @(private) -throw_error :: proc(fmt_str: string, args: ..any) { +throw_error :: proc(fmt_str: string, args: ..any) -> Error { err := flags.Validation_Error { message = fmt.tprintf(fmt_str, ..args), } flags.print_errors(typeid_of(Options), err, os.args[0], .Unix) - os.exit(1) + return err } diff --git a/src/file/file.odin b/src/file/file.odin index 9c38eae..818726d 100644 --- a/src/file/file.odin +++ b/src/file/file.odin @@ -5,12 +5,26 @@ import "core:os" // ----------------------------------------- -get_working_dir :: proc(allocator := context.allocator) -> string { +Error :: union #shared_nil { + os.Error, + File_Error, +} + +File_Error :: enum u8 { + None = 0, + Not_A_Directory = 1, + Unimplemented = 127, + Okay = None, +} + +// ----------------------------------------- + +get_working_dir :: proc(allocator := context.allocator) -> (string, Error) { wd, err := os.get_working_directory(allocator) if err != nil { fmt.eprintln("error: get working directory failed:", err) - os.exit(1) + return {}, err } - return wd + return wd, nil } diff --git a/src/file/list_dir.odin b/src/file/list_dir.odin index 378bbb6..dfbf2f9 100644 --- a/src/file/list_dir.odin +++ b/src/file/list_dir.odin @@ -19,9 +19,17 @@ list_dir_recursive :: proc( f: ^os.File, working_dir: string, allocator := context.allocator, -) -> [dynamic]File_Entry { +) -> ( + entries: [dynamic]File_Entry, + err: Error, +) { path := os.name(f) - return list_dir_recursive_by_path_impl(path, working_dir, allocator) + result := list_dir_recursive_by_path_impl( + path, + working_dir, + allocator, + ) or_return + return result, nil } // Returning `[dynamic]File_Entry` and each relpath `string` inside it are owned by the caller. @@ -29,8 +37,16 @@ list_dir_recursive_by_path :: proc( path: string, working_dir: string, allocator := context.allocator, -) -> [dynamic]File_Entry { - return list_dir_recursive_by_path_impl(path, working_dir, allocator) +) -> ( + entries: [dynamic]File_Entry, + err: Error, +) { + result := list_dir_recursive_by_path_impl( + path, + working_dir, + allocator, + ) or_return + return result, nil } // Frees every owning relpath string then the dynamic array itself. @@ -46,10 +62,13 @@ list_dir_recursive_by_path_impl :: proc( path: string, working_dir: string, allocator: runtime.Allocator, -) -> [dynamic]File_Entry { +) -> ( + [dynamic]File_Entry, + Error, +) { if !os.is_dir(path) { fmt.eprintln("error: path is not a directory:", path) - os.exit(1) + return {}, .Not_A_Directory } result := make([dynamic]File_Entry, allocator) @@ -62,7 +81,7 @@ list_dir_recursive_by_path_impl :: proc( list_dir_queue(&queue, &result, working_dir, allocator) } - return result // owning [dynamic] + return result, nil // owning [dynamic] } @(private) @@ -71,7 +90,7 @@ list_dir_queue :: proc( result: ^[dynamic]File_Entry, working_dir: string, allocator: runtime.Allocator, -) { +) -> Error { assert(len(queue) > 0, "queue must not be empty") path := pop(queue) @@ -80,14 +99,14 @@ list_dir_queue :: proc( f, open_err := os.open(path, {.Read}) if open_err != nil { fmt.eprintln("error: open failed:", open_err) - os.exit(1) + return open_err } defer os.close(f) entries, read_err := os.read_all_directory(f, allocator) if read_err != nil { fmt.eprintln("error: read directory failed:", read_err) - os.exit(1) + return read_err } defer os.file_info_slice_delete(entries, allocator) @@ -118,4 +137,6 @@ list_dir_queue :: proc( case: // skip other types } } + + return nil } diff --git a/src/main.odin b/src/main.odin index ae263b3..28daff5 100644 --- a/src/main.odin +++ b/src/main.odin @@ -8,39 +8,50 @@ import "src:file" VERSION :: #config(VERSION, "dev") +CLI_ERR :: 1 +WORKING_DIR_ERR :: 2 +LIST_DIR_ERR :: 3 +WRITE_ASSETS_ERR :: 4 +WRITE_METADATA_ERR :: 5 + +// ----------------------------------------- + main :: proc() { - working_dir := file.get_working_dir() + working_dir, wd_err := file.get_working_dir() + if wd_err != nil do os.exit(WORKING_DIR_ERR) defer delete(working_dir) - opts := cli.parse(working_dir) + opts, c_err := cli.parse(working_dir) + if c_err != nil do os.exit(CLI_ERR) // fmt.println("verbose:", opts.verbose) // fmt.println("compression:", opts.compression) // fmt.println("size:", opts.size) - entries := file.list_dir_recursive(opts.input, working_dir) + entries, e_err := file.list_dir_recursive(opts.input, working_dir) + if e_err != nil do os.exit(LIST_DIR_ERR) defer file.delete_entries(&entries) w := chunks.writer_init(cast(u64)len(entries)) defer chunks.writer_destroy(&w) - err := chunks.write_assets( + wa_err := chunks.write_assets( &w, opts.output, entries[:], opts.size, opts.compression, ) - if err != nil do os.exit(1) + if wa_err != nil do os.exit(WRITE_ASSETS_ERR) - chunks.write_metadata( + wm_err := chunks.write_metadata( &w, opts.output, entries[:], opts.size, opts.compression, ) - if err != nil do os.exit(2) + if wm_err != nil do os.exit(WRITE_METADATA_ERR) } // TODO: .chunkignore