package main import "core:bufio" import "core:fmt" import "core:io" import "core:mem" import "core:os" import "core:strings" Command :: struct { name: string, args: [dynamic]string, flags: map[string]string, bool_set: map[string]bool, } CommandInfo :: struct { name: string, usage: string, short: string, long: string, aliases: []string, } COMMANDS := []CommandInfo { { "init", "envr init", "Set up envr", "The init command generates your initial config and saves it to\n~/.envr/config in JSON format.\n\nDuring setup, you will be prompted to select one or more ssh keys with which to\nencrypt your databse. **Make 100% sure** that you have **a remote copy** of this\nkey somewhere, otherwise your data could be lost forever.", {}, }, {"scan", "envr scan", "Find and select .env files for backup", "", {}}, {"sync", "envr sync", "Update or restore your env backups", "", {}}, {"backup", "envr backup ", "Import a .env file into envr", "", {"add"}}, {"restore", "envr restore ", "Restore a .env file from the database", "", {}}, {"list", "envr list", "View your tracked files", "", {}}, {"remove", "envr remove ", "Remove a .env file from your database", "", {}}, {"check", "envr check [path]", "Check if files are backed up", "", {}}, { "deps", "envr deps", "Check for missing binaries", "envr relies on external binaries for certain functionality.\n\nThe check command reports on which binaries are available and which are not.", {}, }, {"version", "envr version", "Show envr's version", "", {}}, {"edit-config", "envr edit-config", "Edit your config with your default editor", "", {}}, {"nushell-completion", "envr nushell-completion", "Generate custom completions for nushell", "", {}}, } parse_args :: proc() -> (cmd: Command, ok: bool) { args := os.args if len(args) < 2 { print_usage() return Command{}, false } cmd.name = args[1] if cmd.name == "--help" || cmd.name == "-h" { print_usage() return Command{}, false } cmd.args = make([dynamic]string) cmd.flags = make(map[string]string) cmd.bool_set = make(map[string]bool) i := 2 for i < len(args) { arg := args[i] if strings.starts_with(arg, "--") { key := arg[2:] if i + 1 < len(args) && !strings.starts_with(args[i + 1], "-") { cmd.flags[key] = args[i + 1] i += 2 } else { cmd.bool_set[key] = true i += 1 } } else if strings.starts_with(arg, "-") && len(arg) == 2 { key_slice := arg[1:2] if i + 1 < len(args) && !strings.starts_with(args[i + 1], "-") { cmd.flags[key_slice] = args[i + 1] i += 2 } else { cmd.bool_set[key_slice] = true i += 1 } } else { append(&cmd.args, arg) i += 1 } } if has_flag(&cmd, "help") { print_command_help(cmd.name) return Command{}, false } return cmd, true } has_flag :: proc(cmd: ^Command, name: string) -> bool { _, ok := cmd.flags[name] if ok { return true } _, ok2 := cmd.bool_set[name] return ok2 } find_command :: proc(name: string) -> (CommandInfo, bool) { for c in COMMANDS { if c.name == name { return c, true } for a in c.aliases { if a == name { return c, true } } } return CommandInfo{}, false } write_command_help :: proc(name: string, w: io.Writer) -> bool { info, found := find_command(name) if !found { return false } fmt.wprintf(w, "Usage: %s [flags]\n\n", info.usage, flush = false) fmt.wprintf(w, "%s\n", info.short, flush = false) if len(info.aliases) > 0 { fmt.wprintf(w, "\nAliases:\n %s", info.name, flush = false) for a in info.aliases { fmt.wprintf(w, ", %s", a, flush = false) } fmt.wprintf(w, "\n", flush = false) } if len(info.long) > 0 { fmt.wprintf(w, "\n%s\n", info.long, flush = false) } fmt.wprintf(w, "\nFlags:\n -h, --help help for %s\n", info.name, flush = false) return true } print_command_help :: proc(name: string) { bw: bufio.Writer bufio.writer_init(&bw, io.to_writer(os.to_writer(os.stdout)), mem.DEFAULT_PAGE_SIZE) defer bufio.writer_destroy(&bw) w := bufio.writer_to_writer(&bw) ok := write_command_help(name, w) if !ok { fmt.printf("Unknown command: %s\n", name) print_usage() } bufio.writer_flush(&bw) } write_usage :: proc(w: io.Writer) { fmt.wprintf( w, `envr keeps your .env synced to a local, age encrypted database. Is a safe and easy way to gather all your .env files in one place where they can easily be backed by another tool such as restic or git. All your data is stored in ~/data.age Getting started is easy: 1. Create your configuration file and set up encrypted storage: > envr init 2. Scan for existing .env files: > envr scan Select the files you want to back up from the interactive list. 3. Verify that it worked: > envr list 4. After changing any of your .env files, update the backup with: > envr sync 5. If you lose a repository, after re-cloning the repo into the same path it was at before, restore your backup with: > envr restore ~//.env Usage: envr [command] Available Commands: `, flush = false, ) for c in COMMANDS { name_start := len(c.name) fmt.wprintf(w, "%s", c.name, flush = false) for a in c.aliases { fmt.wprintf(w, ", %s", a, flush = false) name_start += len(a) + 2 } padding := 20 - name_start if padding > 0 { for _ in 0 ..< padding { io.write_byte(w, ' ') } } fmt.wprintf(w, " %s\n", c.short, flush = false) } fmt.wprintf( w, ` Flags: -h, --help help for envr Use "envr [command] --help" for more information about a command. `, flush = false, ) } // TODO: Look at usages,might want to pass a writer print_usage :: proc() { bw: bufio.Writer bufio.writer_init(&bw, io.to_writer(os.to_writer(os.stdout)), mem.DEFAULT_PAGE_SIZE) defer bufio.writer_destroy(&bw) defer bufio.writer_flush(&bw) write_usage(bufio.writer_to_writer(&bw)) }