123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- package flags
- import "base:runtime"
- import "core:fmt"
- import "core:io"
- import "core:os"
- import "core:reflect"
- import "core:slice"
- import "core:strconv"
- import "core:strings"
- _, _, _, _, _, _, _, _ :: runtime, fmt, io, os, reflect, slice, strconv, strings
- // Write out the documentation for the command-line arguments.
- write_usage :: proc(out: io.Writer, data: ^$T, program: string = "") {
- Flag :: struct {
- name: string,
- usage: string,
- name_with_type: string,
- pos: int,
- is_positional: bool,
- is_required: bool,
- is_boolean: bool,
- is_hidden: bool,
- }
- sort_flags :: proc(a, b: Flag) -> slice.Ordering {
- if a.is_positional && b.is_positional {
- return slice.cmp(a.pos, b.pos)
- }
- if a.is_required && !b.is_required {
- return .Less
- } else if !a.is_required && b.is_required {
- return .Greater
- }
- if a.is_positional && !b.is_positional {
- return .Less
- } else if b.is_positional && !a.is_positional {
- return .Greater
- }
- return slice.cmp(a.name, b.name)
- }
- flags: [dynamic]Flag
- defer delete(flags)
- longest_flag_length: int
- for field in reflect.struct_fields_zipped(T) {
- flag: Flag
- flag.name = get_field_name(field)
- #partial switch t in field.type.variant {
- case runtime.Type_Info_Map:
- flag.name_with_type = fmt.tprintf("%s:<%v>=<%v>", flag.name, t.key.id, t.value.id)
- case runtime.Type_Info_Dynamic_Array:
- flag.name_with_type = fmt.tprintf("%s:<%v, ...>", flag.name, t.elem.id)
- case:
- flag.name_with_type = fmt.tprintf("%s:<%v>", flag.name, field.type.id)
- }
- if usage, ok := reflect.struct_tag_lookup(field.tag, TAG_USAGE); ok {
- flag.usage = usage
- } else {
- flag.usage = UNDOCUMENTED_FLAG
- }
- if args_tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS); ok {
- if pos_str, is_pos := get_struct_subtag(args_tag, SUBTAG_POS); is_pos {
- flag.is_positional = true
- if pos, ok := strconv.parse_int(pos_str); ok && pos >= 0 {
- flag.pos = pos
- } else {
- fmt.panicf("%v has incorrect pos subtag specifier `%s`", typeid_of(T), pos_str)
- }
- }
- if _, is_required := get_struct_subtag(args_tag, SUBTAG_REQUIRED); is_required {
- flag.is_required = true
- }
- if reflect.type_kind(field.type.id) == .Boolean {
- flag.is_boolean = true
- }
- if _, is_hidden := get_struct_subtag(args_tag, SUBTAG_HIDDEN); is_hidden {
- flag.is_hidden = true
- }
- }
- if !flag.is_hidden {
- longest_flag_length = max(longest_flag_length, len(flag.name_with_type))
- }
- append(&flags, flag)
- }
- slice.sort_by_cmp(flags[:], sort_flags)
- if len(program) > 0 {
- fmt.wprintf(out, "Usage:\n\t%s", program)
- for flag in flags {
- if flag.is_hidden {
- continue
- }
- io.write_byte(out, ' ')
- if flag.name == SUBTAG_POS {
- io.write_string(out, "...")
- continue
- }
- if !flag.is_required { io.write_byte(out, '[') }
- if !flag.is_positional { io.write_byte(out, '-') }
- io.write_string(out, flag.name)
- if !flag.is_required { io.write_byte(out, ']') }
- }
- io.write_byte(out, '\n')
- }
- fmt.wprintln(out, "Flags:")
- for flag in flags {
- if flag.is_hidden {
- continue
- }
- spacing := strings.repeat(" ",
- (MINIMUM_SPACING + longest_flag_length) - len(flag.name_with_type),
- context.temp_allocator)
- fmt.wprintf(out, "\t-%s%s%s\n", flag.name_with_type, spacing, flag.usage)
- }
- }
- // Print out the documentation for the command-line arguments.
- print_usage :: proc(data: ^$T, program: string = "") {
- write_usage(os.stream_from_handle(os.stdout), data, program)
- }
|