diff --git a/flag/flag.odin b/flag/flag.odin deleted file mode 100644 index ab0f747..0000000 --- a/flag/flag.odin +++ /dev/null @@ -1,227 +0,0 @@ -/* -zlib License - -(C) 2026 Simon Kellet - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -#+vet explicit-allocators -package fleg - -import "base:runtime" -import "core:fmt" -import "core:os" -import "core:strconv" -import "core:strings" - -FORCE_HELP_ON_EMPTY_ARGS := false -FLAG_START_CHAR := "-" -FLAG_SEP_CHAR := "=" - -Flag_Value_Ptr :: union { - ^bool, - ^int, - ^string, - ^f32, - ^f64, -} - -Flag :: struct { - name: string, - value: Flag_Value_Ptr, - usage: string, - parsed: bool, // sck: flag to check if it has been parsed - required: bool, // sck: required flag -} - -// Global dynamic array of Flags -all_flags: [dynamic]Flag - -// runtime allocator for the flags -flag_allocator: runtime.Allocator - -// sck: Used to create a customer allocator -init_custom_allocator :: proc(allocator: runtime.Allocator) { - destroy() // sck: make sure to destory the old allocations - - flag_allocator = allocator - all_flags = make([dynamic]Flag, flag_allocator) -} - -destroy :: proc() { - free_all(flag_allocator) - delete(all_flags) - all_flags = {} -} - -print_flags :: proc() { - for f in all_flags { - fmt.printfln("%s: %s (required=%v)", f.name, f.usage, f.required) - } -} - -print_flag_format :: proc(){ - fmt.printfln("Want %s%s\n", FLAG_START_CHAR, FLAG_SEP_CHAR) -} - -print_usage :: proc() { - fmt.println("\tUsage: ") - fmt.println("\n\tFlag format for this program:") - fmt.printfln("\t%s%s\n", FLAG_START_CHAR, FLAG_SEP_CHAR) - - for f in all_flags { - req_msg: string - if f.required { req_msg = " (required)"} - switch v in f.value { - - case ^bool: - fmt.printfln("\t%s%s:\n\t\t%s (default: %v)%s\n", FLAG_START_CHAR, f.name, f.usage, v^, req_msg) - case ^int: - fmt.printfln("\t%s%s:\n\t\t%s (default: %d)%s\n", FLAG_START_CHAR, f.name, f.usage, v^, req_msg) - case ^string: - fmt.printfln("\t%s%s:\n\t\t%s (default: %s)%s\n", FLAG_START_CHAR, f.name, f.usage, v^, req_msg) - case ^f32: - fmt.printfln("\t%s%s:\n\t\t%s (default: %f)%s\n", FLAG_START_CHAR, f.name, f.usage, v^, req_msg) - case ^f64: - fmt.printfln("\t%s%s:\n\t\t%s (default: %f)%s\n", FLAG_START_CHAR, f.name, f.usage, v^, req_msg) - } - } -} - -BoolVar :: proc(ptr: ^bool, name: string, default: bool, usage: string, required := false) { - ptr^ = default - append(&all_flags, Flag{name = name, value = ptr, usage = usage, required = required}) -} - -IntVar :: proc(ptr: ^int, name: string, default: int, usage: string, required := false) { - ptr^ = default - append(&all_flags, Flag{name = name, value = ptr, usage = usage, required = required}) -} - -StringVar :: proc(ptr: ^string, name: string, default: string, usage: string, required := false) { - ptr^ = default - append(&all_flags, Flag{name = name, value = ptr, usage = usage, required = required}) -} - -Float32Var :: proc(ptr: ^f32, name: string, default: f32, usage: string, required := false) { - ptr^ = default - append(&all_flags, Flag{name = name, value = ptr, usage = usage, required = required}) -} - -Float64Var :: proc(ptr: ^f64, name: string, default: f64, usage: string, required := false) { - ptr^ = default - append(&all_flags, Flag{name = name, value = ptr, usage = usage, required = required}) -} - -parse_flags :: proc() { - // sck: We cannot have the start and seperator formats being the same - if FLAG_START_CHAR == FLAG_SEP_CHAR { - fmt.println("[ERROR]: FLAG_START_CHAR and FLAG_SEP_CHAR cannot be the same!") - fmt.printfln("\t FLAG_START_CHAR= \"%s\"\t FLAG_SEP_CHAR= \"%s\"", FLAG_START_CHAR, FLAG_SEP_CHAR) - os.exit(1) - } - if len(os.args) < 2 && FORCE_HELP_ON_EMPTY_ARGS {print_usage()} - - // sck: skip the first arg (1:) - for &a in os.args[1:] { - if a == "-h" || a == "-help" || a == "--help" {print_usage(); os.exit(0)} - // sck: if the user has a custom flag format, copy that to the help flag too. - if a == fmt.aprintf("%s%s", FLAG_START_CHAR, "help", allocator = context.temp_allocator) {print_usage(); os.exit(0)} - if a == fmt.aprintf("%s%s", FLAG_START_CHAR, "h", allocator = context.temp_allocator) {print_usage(); os.exit(0)} - defer free_all(context.temp_allocator) //clean up! - - // Begin parsing flags... - for &f in all_flags { - if f.parsed {continue} - - a = strings.trim_prefix(a, FLAG_START_CHAR) - name := a - value: string - - split := strings.index_any(a, FLAG_SEP_CHAR) - if split >= 0 { - name = a[:split] - value = a[split + 1:] - } else { - name = a - fmt.printf("[INFO]: Could not read flag: %s. ", name) - print_flag_format() - break - } - - // sck: We are going to parse all the flags - if name == f.name && !f.parsed { - f.parsed = true - switch &v in f.value { - case ^bool: - if value == "" {break} - parsed, ok := strconv.parse_bool(value) - if !ok { - fmt.fprintfln(os.stderr, "[ERROR] Failed to parse flag %s: got %T, wanted bool", - f.name, value) - os.exit(1) - } - v^ = parsed - - case ^int: - if value == "" {break} - parsed, ok := strconv.parse_int(value) - if !ok { - fmt.fprintfln(os.stderr, "[ERROR] Failed to parse flag %s: got %T, wanted int", - f.name, value) - os.exit(1) - } - v^ = parsed - - case ^string: - if value == "" {break} - // TODO: validate string? - v^ = value - - case ^f32: - if value == "" {break} - parsed, ok := strconv.parse_f32(value) - if !ok { - fmt.fprintfln(os.stderr, "[ERROR] Failed to parse flag %s: got %T, wanted f32", - f.name, value) - os.exit(1) - } - v^ = parsed - - case ^f64: - if value == "" {break} - parsed, ok := strconv.parse_f64(value) - if !ok { - fmt.fprintfln(os.stderr, "[ERROR] Failed to parse flag %s: got %T, wanted f64", - f.name, value) - os.exit(1) - } - v^ = parsed - } - } - } - } - - for f in all_flags { - if f.required && !f.parsed { - fmt.fprintfln(os.stderr, "[ERROR] Missing required flag: %s%s", FLAG_START_CHAR, f.name) - fmt.fprintfln(os.stderr, "use -help or -h to view all flags") - os.exit(1) - } - } -} diff --git a/flag/flag.odin b/flag/flag.odin new file mode 120000 index 0000000..a3cb1dc --- /dev/null +++ b/flag/flag.odin @@ -0,0 +1 @@ +../../flags/fleg/flag.odin \ No newline at end of file