mirror of
https://github.com/sbrow/envr.git
synced 2026-06-27 10:38:33 -04:00
refactor: Fixed a number of memory leaks.
This commit is contained in:
66
cli.odin
66
cli.odin
@@ -1,6 +1,9 @@
|
||||
package main
|
||||
|
||||
import "core:bufio"
|
||||
import "core:fmt"
|
||||
import "core:io"
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import "core:strings"
|
||||
|
||||
@@ -20,9 +23,13 @@ CommandInfo :: struct {
|
||||
}
|
||||
|
||||
COMMANDS := []CommandInfo {
|
||||
{"init", "envr init", "Set up envr",
|
||||
{
|
||||
"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 <path>", "Import a .env file into envr", "", {"add"}},
|
||||
@@ -30,9 +37,13 @@ COMMANDS := []CommandInfo{
|
||||
{"list", "envr list", "View your tracked files", "", {}},
|
||||
{"remove", "envr remove <path>", "Remove a .env file from your database", "", {}},
|
||||
{"check", "envr check [path]", "Check if files are backed up", "", {}},
|
||||
{"deps", "envr deps", "Check for missing binaries",
|
||||
{
|
||||
"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", "", {}},
|
||||
}
|
||||
@@ -113,45 +124,43 @@ find_command :: proc(name: string) -> (CommandInfo, bool) {
|
||||
return CommandInfo{}, false
|
||||
}
|
||||
|
||||
command_help_text :: proc(name: string) -> (string, bool) {
|
||||
write_command_help :: proc(name: string, w: io.Writer) -> bool {
|
||||
info, found := find_command(name)
|
||||
if !found {
|
||||
return "", false
|
||||
return false
|
||||
}
|
||||
|
||||
b: strings.Builder
|
||||
strings.builder_init(&b)
|
||||
|
||||
fmt.sbprintf(&b, "Usage: %s [flags]\n\n", info.usage)
|
||||
fmt.sbprintf(&b, "%s\n", info.short)
|
||||
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.sbprintf(&b, "\nAliases:\n %s", info.name)
|
||||
fmt.wprintf(w, "\nAliases:\n %s", info.name, flush = false)
|
||||
for a in info.aliases {
|
||||
fmt.sbprintf(&b, ", %s", a)
|
||||
fmt.wprintf(w, ", %s", a, flush = false)
|
||||
}
|
||||
fmt.sbprintf(&b, "\n")
|
||||
fmt.wprintf(w, "\n", flush = false)
|
||||
}
|
||||
|
||||
if len(info.long) > 0 {
|
||||
fmt.sbprintf(&b, "\n%s\n", info.long)
|
||||
fmt.wprintf(w, "\n%s\n", info.long, flush = false)
|
||||
}
|
||||
|
||||
fmt.sbprintf(&b, "\nFlags:\n -h, --help help for %s\n", info.name)
|
||||
|
||||
s := strings.clone(strings.to_string(b))
|
||||
strings.builder_destroy(&b)
|
||||
return s, true
|
||||
fmt.wprintf(w, "\nFlags:\n -h, --help help for %s\n", info.name, flush = false)
|
||||
return true
|
||||
}
|
||||
|
||||
print_command_help :: proc(name: string) {
|
||||
text, ok := command_help_text(name)
|
||||
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()
|
||||
return
|
||||
}
|
||||
fmt.println(text)
|
||||
bufio.writer_flush(&bw)
|
||||
}
|
||||
|
||||
usage_text :: proc() -> string {
|
||||
@@ -159,7 +168,10 @@ usage_text :: proc() -> string {
|
||||
strings.builder_init(&b)
|
||||
|
||||
fmt.sbprintf(&b, "envr keeps your .env synced to a local, age encrypted database.\n")
|
||||
fmt.sbprintf(&b, "Is a safe and easy way to gather all your .env files in one place where they can\n")
|
||||
fmt.sbprintf(
|
||||
&b,
|
||||
"Is a safe and easy way to gather all your .env files in one place where they can\n",
|
||||
)
|
||||
fmt.sbprintf(&b, "easily be backed by another tool such as restic or git.\n")
|
||||
fmt.sbprintf(&b, "\n")
|
||||
fmt.sbprintf(&b, "All your data is stored in ~/data.age\n")
|
||||
@@ -184,7 +196,10 @@ usage_text :: proc() -> string {
|
||||
fmt.sbprintf(&b, "\n")
|
||||
fmt.sbprintf(&b, "> envr sync\n")
|
||||
fmt.sbprintf(&b, "\n")
|
||||
fmt.sbprintf(&b, "5. If you lose a repository, after re-cloning the repo into the same path it was\n")
|
||||
fmt.sbprintf(
|
||||
&b,
|
||||
"5. If you lose a repository, after re-cloning the repo into the same path it was\n",
|
||||
)
|
||||
fmt.sbprintf(&b, "at before, restore your backup with:\n")
|
||||
fmt.sbprintf(&b, "\n")
|
||||
fmt.sbprintf(&b, "> envr restore ~/<path to repository>/.env\n")
|
||||
@@ -224,3 +239,4 @@ usage_text :: proc() -> string {
|
||||
print_usage :: proc() {
|
||||
fmt.print(usage_text())
|
||||
}
|
||||
|
||||
|
||||
@@ -34,11 +34,7 @@ test_usage_text_contains_steps :: proc(t: ^testing.T) {
|
||||
testing.expect(t, strings.contains(text, "4."), "missing step 4")
|
||||
testing.expect(t, strings.contains(text, "5."), "missing step 5")
|
||||
testing.expect(t, strings.contains(text, "> envr sync\n"), "step 4 missing 'envr sync'")
|
||||
testing.expect(
|
||||
t,
|
||||
strings.contains(text, "> envr restore"),
|
||||
"step 5 missing 'envr restore'",
|
||||
)
|
||||
testing.expect(t, strings.contains(text, "> envr restore"), "step 5 missing 'envr restore'")
|
||||
}
|
||||
|
||||
@(test)
|
||||
@@ -47,42 +43,37 @@ test_usage_text_contains_flags_and_help_hint :: proc(t: ^testing.T) {
|
||||
|
||||
testing.expect(t, strings.contains(text, "Flags:"), "missing Flags section")
|
||||
testing.expect(t, strings.contains(text, "--help"), "missing --help flag")
|
||||
testing.expect(
|
||||
t,
|
||||
strings.contains(text, "Use \"envr [command] --help\""),
|
||||
"missing help hint",
|
||||
)
|
||||
testing.expect(t, strings.contains(text, "Use \"envr [command] --help\""), "missing help hint")
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_command_help_backup :: proc(t: ^testing.T) {
|
||||
text, ok := command_help_text("backup")
|
||||
testing.expect(t, ok, "command_help_text(\"backup\") returned false")
|
||||
b: strings.Builder
|
||||
strings.builder_init(&b)
|
||||
defer strings.builder_destroy(&b)
|
||||
|
||||
ok := write_command_help("backup", strings.to_writer(&b))
|
||||
testing.expect(t, ok, "write_command_help(\"backup\") returned false")
|
||||
|
||||
text := strings.to_string(b)
|
||||
testing.expect(t, strings.contains(text, "Usage:"), "missing Usage line")
|
||||
testing.expect(
|
||||
t,
|
||||
strings.contains(text, "envr backup <path>"),
|
||||
"missing usage pattern",
|
||||
)
|
||||
testing.expect(
|
||||
t,
|
||||
strings.contains(text, "Aliases:"),
|
||||
"missing Aliases section",
|
||||
)
|
||||
testing.expect(t, strings.contains(text, "envr backup <path>"), "missing usage pattern")
|
||||
testing.expect(t, strings.contains(text, "Aliases:"), "missing Aliases section")
|
||||
testing.expect(t, strings.contains(text, "add"), "missing 'add' alias")
|
||||
testing.expect(t, strings.contains(text, "Flags:"), "missing Flags section")
|
||||
testing.expect(
|
||||
t,
|
||||
strings.contains(text, "--help"),
|
||||
"missing --help in flags",
|
||||
)
|
||||
testing.expect(t, strings.contains(text, "--help"), "missing --help in flags")
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_command_help_add_alias :: proc(t: ^testing.T) {
|
||||
text, ok := command_help_text("add")
|
||||
testing.expect(t, ok, "command_help_text(\"add\") returned false")
|
||||
b: strings.Builder
|
||||
strings.builder_init(&b)
|
||||
defer strings.builder_destroy(&b)
|
||||
|
||||
ok := write_command_help("add", strings.to_writer(&b))
|
||||
testing.expect(t, ok, "write_command_help(\"add\") returned false")
|
||||
|
||||
text := strings.to_string(b)
|
||||
testing.expect(
|
||||
t,
|
||||
strings.contains(text, "envr backup <path>"),
|
||||
@@ -93,34 +84,43 @@ test_command_help_add_alias :: proc(t: ^testing.T) {
|
||||
|
||||
@(test)
|
||||
test_command_help_init_no_aliases :: proc(t: ^testing.T) {
|
||||
text, ok := command_help_text("init")
|
||||
testing.expect(t, ok, "command_help_text(\"init\") returned false")
|
||||
b: strings.Builder
|
||||
strings.builder_init(&b)
|
||||
defer strings.builder_destroy(&b)
|
||||
|
||||
ok := write_command_help("init", strings.to_writer(&b))
|
||||
testing.expect(t, ok, "write_command_help(\"init\") returned false")
|
||||
|
||||
text := strings.to_string(b)
|
||||
testing.expect(t, strings.contains(text, "Usage:"), "missing Usage line")
|
||||
testing.expect(
|
||||
t,
|
||||
!strings.contains(text, "Aliases:"),
|
||||
"init should not have Aliases section",
|
||||
)
|
||||
testing.expect(t, !strings.contains(text, "Aliases:"), "init should not have Aliases section")
|
||||
testing.expect(t, strings.contains(text, "Flags:"), "missing Flags section")
|
||||
testing.expect(
|
||||
t,
|
||||
strings.contains(text, "help for init"),
|
||||
"missing 'help for init'",
|
||||
)
|
||||
testing.expect(t, strings.contains(text, "help for init"), "missing 'help for init'")
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_command_help_unknown :: proc(t: ^testing.T) {
|
||||
text, ok := command_help_text("nonexistent")
|
||||
testing.expect(t, !ok, "command_help_text(\"nonexistent\") should return false")
|
||||
b: strings.Builder
|
||||
strings.builder_init(&b)
|
||||
defer strings.builder_destroy(&b)
|
||||
|
||||
ok := write_command_help("nonexistent", strings.to_writer(&b))
|
||||
testing.expect(t, !ok, "write_command_help(\"nonexistent\") should return false")
|
||||
|
||||
text := strings.to_string(b)
|
||||
testing.expect(t, len(text) == 0, "text should be empty for unknown command")
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_command_help_version :: proc(t: ^testing.T) {
|
||||
text, ok := command_help_text("version")
|
||||
testing.expect(t, ok, "command_help_text(\"version\") returned false")
|
||||
b: strings.Builder
|
||||
strings.builder_init(&b)
|
||||
defer strings.builder_destroy(&b)
|
||||
|
||||
ok := write_command_help("version", strings.to_writer(&b))
|
||||
testing.expect(t, ok, "write_command_help(\"version\") returned false")
|
||||
|
||||
text := strings.to_string(b)
|
||||
testing.expect(t, strings.contains(text, "Usage:"), "missing Usage line")
|
||||
testing.expect(
|
||||
t,
|
||||
@@ -128,3 +128,4 @@ test_command_help_version :: proc(t: ^testing.T) {
|
||||
"version should not have Aliases section",
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ import "core:path/filepath"
|
||||
import "core:strings"
|
||||
|
||||
cmd_check :: proc(cmd: ^Command) {
|
||||
feats := check_features()
|
||||
|
||||
check_path: string
|
||||
if len(cmd.args) > 0 {
|
||||
check_path = cmd.args[0]
|
||||
@@ -41,8 +43,10 @@ cmd_check :: proc(cmd: ^Command) {
|
||||
files_in_path: [dynamic]string
|
||||
|
||||
if is_dir {
|
||||
if !can_scan() {
|
||||
fmt.println("Error: please install fd to use the check command (https://github.com/sharkdp/fd)")
|
||||
if cant_scan(feats) {
|
||||
fmt.println(
|
||||
"Error: please install fd to use the check command (https://github.com/sharkdp/fd)",
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -77,3 +81,4 @@ cmd_check :: proc(cmd: ^Command) {
|
||||
fmt.println("\nRun 'envr sync' to back up these files.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,11 @@ test_find_unbacked_finds_missing :: proc(t: ^testing.T) {
|
||||
result := find_unbacked(local, db[:])
|
||||
testing.expect(t, len(result) == 1, fmt.aprintf("expected 1 unbacked, got %d", len(result)))
|
||||
if len(result) > 0 {
|
||||
testing.expect(t, result[0] == "/c/.env", fmt.aprintf("expected /c/.env, got %s", result[0]))
|
||||
testing.expect(
|
||||
t,
|
||||
result[0] == "/c/.env",
|
||||
fmt.aprintf("expected /c/.env, got %s", result[0]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,3 +45,4 @@ test_find_unbacked_none_backed :: proc(t: ^testing.T) {
|
||||
result := find_unbacked(local, db[:])
|
||||
testing.expect(t, len(result) == 2, fmt.aprintf("expected 2 unbacked, got %d", len(result)))
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@ import "core:encoding/json"
|
||||
import "core:fmt"
|
||||
|
||||
cmd_scan :: proc(cmd: ^Command) {
|
||||
if !can_scan() {
|
||||
feats := check_features()
|
||||
if cant_scan(feats) {
|
||||
fmt.println(
|
||||
"Error: please install fd to use the scan command (https://github.com/sharkdp/fd)",
|
||||
)
|
||||
|
||||
@@ -6,8 +6,8 @@ import "core:testing"
|
||||
|
||||
@(test)
|
||||
test_find_binary_exists :: proc(t: ^testing.T) {
|
||||
path := os.get_env("PATH", context.allocator)
|
||||
paths := strings.split(path, ":")
|
||||
path := os.get_env("PATH", context.temp_allocator)
|
||||
paths := strings.split(path, ":", context.temp_allocator)
|
||||
|
||||
result := find_binary(paths, "sh")
|
||||
testing.expect(t, result != "", "sh should be found on PATH")
|
||||
@@ -15,7 +15,7 @@ test_find_binary_exists :: proc(t: ^testing.T) {
|
||||
|
||||
@(test)
|
||||
test_find_binary_not_exists :: proc(t: ^testing.T) {
|
||||
old_path := os.get_env("PATH", context.allocator)
|
||||
old_path := os.get_env("PATH", context.temp_allocator)
|
||||
defer {
|
||||
if old_path != "" {
|
||||
os.set_env("PATH", old_path)
|
||||
@@ -24,8 +24,8 @@ test_find_binary_not_exists :: proc(t: ^testing.T) {
|
||||
|
||||
os.set_env("PATH", "/tmp/envr-nope")
|
||||
|
||||
path := os.get_env("PATH", context.allocator)
|
||||
paths := strings.split(path, ":")
|
||||
path := os.get_env("PATH", context.temp_allocator)
|
||||
paths := strings.split(path, ":", context.temp_allocator)
|
||||
|
||||
|
||||
result := find_binary(paths, "no_such_binary_xyz")
|
||||
|
||||
109
scan.odin
109
scan.odin
@@ -2,23 +2,49 @@ package main
|
||||
|
||||
import "core:fmt"
|
||||
import "core:os"
|
||||
import "core:path/filepath"
|
||||
import "core:strings"
|
||||
import "core:sync"
|
||||
|
||||
fd_counter: sync.Atomic_Mutex
|
||||
fd_seq: int
|
||||
|
||||
next_fd_tmp_path :: proc() -> string {
|
||||
sync.atomic_mutex_lock(&fd_counter)
|
||||
n := fd_seq
|
||||
fd_seq += 1
|
||||
sync.atomic_mutex_unlock(&fd_counter)
|
||||
return fmt.aprintf("/tmp/envr-fd-%d-%d", os.get_pid(), n)
|
||||
// Caller is responsible for freeing paths
|
||||
scan_path :: proc(search_path: string, cfg: Config) -> (paths: [dynamic]string, ok: bool) {
|
||||
if is_tty() {
|
||||
fmt.printf("Searching for all files in \"%s\"...\n", search_path)
|
||||
}
|
||||
all_files, all_ok := run_fd(build_fd_args(search_path, cfg, true))
|
||||
if !all_ok {
|
||||
return
|
||||
}
|
||||
|
||||
if is_tty() {
|
||||
fmt.printf("Search for unignored fies in \"%s\"...\n", search_path)
|
||||
}
|
||||
unignored_files, unignored_ok := run_fd(build_fd_args(search_path, cfg, false))
|
||||
if !unignored_ok {
|
||||
return
|
||||
}
|
||||
|
||||
unignored_set := make(map[string]bool, len(unignored_files), context.temp_allocator)
|
||||
for file in unignored_files {
|
||||
unignored_set[file] = true
|
||||
}
|
||||
|
||||
for file in all_files {
|
||||
if !(file in unignored_set) {
|
||||
append(&paths, file)
|
||||
}
|
||||
}
|
||||
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
@(private = "file")
|
||||
build_fd_args :: proc(search_path: string, cfg: Config, include_ignored: bool) -> []string {
|
||||
args := make([dynamic]string, 0, 3 + 2 * len(cfg.ScanConfig.Exclude) + 2)
|
||||
args_len := 3 + 2 * len(cfg.ScanConfig.Exclude) + 2
|
||||
args := make([dynamic]string, 0, args_len, context.temp_allocator)
|
||||
append(&args, "fd")
|
||||
append(&args, "-a")
|
||||
append(&args, cfg.ScanConfig.Matcher)
|
||||
@@ -38,7 +64,7 @@ build_fd_args :: proc(search_path: string, cfg: Config, include_ignored: bool) -
|
||||
return args[:]
|
||||
}
|
||||
|
||||
run_fd :: proc(args: []string) -> (lines: [dynamic]string, ok: bool) {
|
||||
run_fd :: proc(args: []string) -> (lines: []string, ok: bool) {
|
||||
tmp_path := next_fd_tmp_path()
|
||||
tmp_file, tmp_err := os.open(tmp_path, os.O_CREATE | os.O_WRONLY | os.O_TRUNC)
|
||||
if tmp_err != nil {
|
||||
@@ -64,7 +90,7 @@ run_fd :: proc(args: []string) -> (lines: [dynamic]string, ok: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
data, read_err := os.read_entire_file_from_path(tmp_path, context.allocator)
|
||||
data, read_err := os.read_entire_file_from_path(tmp_path, context.temp_allocator)
|
||||
os.remove(tmp_path)
|
||||
if read_err != nil {
|
||||
return
|
||||
@@ -77,69 +103,44 @@ run_fd :: proc(args: []string) -> (lines: [dynamic]string, ok: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
raw_lines := strings.split(output, "\n")
|
||||
raw_lines := strings.split(output, "\n", context.temp_allocator)
|
||||
result := make([dynamic]string, 0, len(raw_lines), context.temp_allocator)
|
||||
for line in raw_lines {
|
||||
trimmed, _ := strings.clone(strings.trim_space(line))
|
||||
trimmed := strings.trim_space(line)
|
||||
if len(trimmed) > 0 {
|
||||
append(&lines, trimmed)
|
||||
append(&result, trimmed)
|
||||
}
|
||||
}
|
||||
|
||||
ok = true
|
||||
return
|
||||
return result[:], true
|
||||
}
|
||||
|
||||
scan_path :: proc(search_path: string, cfg: Config) -> (paths: [dynamic]string, ok: bool) {
|
||||
if is_tty() {
|
||||
fmt.printf("Searching for all files in \"%s\"...\n", search_path)
|
||||
}
|
||||
all_args := build_fd_args(search_path, cfg, true)
|
||||
all_files, all_ok := run_fd(all_args)
|
||||
if !all_ok {
|
||||
return
|
||||
@(private = "file")
|
||||
next_fd_tmp_path :: proc() -> string {
|
||||
sync.atomic_mutex_lock(&fd_counter)
|
||||
n := fd_seq
|
||||
fd_seq += 1
|
||||
sync.atomic_mutex_unlock(&fd_counter)
|
||||
return fmt.aprintf("/tmp/envr-fd-%d-%d", os.get_pid(), n, allocator = context.temp_allocator)
|
||||
}
|
||||
|
||||
if is_tty() {
|
||||
fmt.printf("Search for unignored fies in \"%s\"...\n", search_path)
|
||||
}
|
||||
unignored_args := build_fd_args(search_path, cfg, false)
|
||||
unignored_files, unignored_ok := run_fd(unignored_args)
|
||||
if !unignored_ok {
|
||||
return
|
||||
cant_scan :: proc(feats: AvailableFeatures) -> bool {
|
||||
return Feature.Fd not_in feats
|
||||
}
|
||||
|
||||
unignored_set: map[string]bool
|
||||
for file in unignored_files {
|
||||
unignored_set[file] = true
|
||||
}
|
||||
|
||||
for file in all_files {
|
||||
if !(file in unignored_set) {
|
||||
append(&paths, file)
|
||||
}
|
||||
}
|
||||
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
can_scan :: proc() -> bool {
|
||||
feats := check_features()
|
||||
return Feature.Fd in feats
|
||||
}
|
||||
|
||||
find_unbacked :: proc(local_files: []string, db_files: []EnvFile) -> [dynamic]string {
|
||||
backed_set: map[string]bool
|
||||
find_unbacked :: proc(local_files: []string, db_files: []EnvFile) -> []string {
|
||||
// Lives until the end of the function
|
||||
backed_set := make(map[string]bool, len(db_files), context.temp_allocator)
|
||||
for file in db_files {
|
||||
backed_set[file.Path] = true
|
||||
}
|
||||
|
||||
unbacked: [dynamic]string
|
||||
unbacked := make([dynamic]string, 0, len(db_files) / 2, context.temp_allocator)
|
||||
for file in local_files {
|
||||
if !(file in backed_set) {
|
||||
append(&unbacked, file)
|
||||
}
|
||||
}
|
||||
return unbacked
|
||||
return unbacked[:]
|
||||
}
|
||||
|
||||
|
||||
@@ -7,9 +7,8 @@ import "core:testing"
|
||||
|
||||
@(test)
|
||||
test_scan_path_finds_gitignored_env_files :: proc(t: ^testing.T) {
|
||||
if !can_scan() {
|
||||
return
|
||||
}
|
||||
feats := check_features()
|
||||
testing.expect(t, cant_scan(feats) == false)
|
||||
|
||||
base := fmt.aprintf("/tmp/envr-scan-test-%d", os.get_pid())
|
||||
os.mkdir_all(base)
|
||||
@@ -38,14 +37,11 @@ test_scan_path_finds_gitignored_env_files :: proc(t: ^testing.T) {
|
||||
_ = os.write_entire_file(fmt.aprintf("%s/config.yaml", base), "key: value")
|
||||
|
||||
cfg := Config {
|
||||
ScanConfig = ScanConfig{
|
||||
Matcher = "\\.env",
|
||||
Exclude = []string{},
|
||||
Include = []string{},
|
||||
},
|
||||
ScanConfig = ScanConfig{Matcher = "\\.env", Exclude = []string{}, Include = []string{}},
|
||||
}
|
||||
|
||||
results, ok := scan_path(base, cfg)
|
||||
defer delete(results)
|
||||
testing.expect(t, ok, "scan_path should succeed")
|
||||
|
||||
found_env := false
|
||||
@@ -72,23 +68,20 @@ test_scan_path_finds_gitignored_env_files :: proc(t: ^testing.T) {
|
||||
|
||||
@(test)
|
||||
test_scan_path_empty_dir :: proc(t: ^testing.T) {
|
||||
if !can_scan() {
|
||||
return
|
||||
}
|
||||
feats := check_features()
|
||||
testing.expect(t, cant_scan(feats) == false)
|
||||
|
||||
base := fmt.aprintf("/tmp/envr-scan-empty-%d", os.get_pid())
|
||||
os.mkdir_all(base)
|
||||
defer os.remove_all(base)
|
||||
|
||||
cfg := Config {
|
||||
ScanConfig = ScanConfig{
|
||||
Matcher = "\\.env",
|
||||
Exclude = []string{},
|
||||
Include = []string{},
|
||||
},
|
||||
ScanConfig = ScanConfig{Matcher = "\\.env", Exclude = []string{}, Include = []string{}},
|
||||
}
|
||||
|
||||
results, ok := scan_path(base, cfg)
|
||||
defer delete(results)
|
||||
testing.expect(t, ok, "scan_path should succeed")
|
||||
testing.expect(t, len(results) == 0, fmt.aprintf("expected 0 results, got %d", len(results)))
|
||||
}
|
||||
|
||||
|
||||
@@ -77,11 +77,10 @@ render_table :: proc(headers: []string, rows: [][]string) {
|
||||
}
|
||||
|
||||
render_json_rows :: proc(w: io.Writer, headers: []string, rows: [][]string) {
|
||||
entries := make([dynamic]map[string]string, 0, len(rows))
|
||||
defer delete(entries)
|
||||
entries := make([dynamic]map[string]string, 0, len(rows), context.temp_allocator)
|
||||
|
||||
for row in rows {
|
||||
entry: map[string]string
|
||||
entry := make(map[string]string, len(headers), context.temp_allocator)
|
||||
for i in 0 ..< len(headers) {
|
||||
entry[headers[i]] = row[i]
|
||||
}
|
||||
@@ -95,3 +94,4 @@ render_json_rows :: proc(w: io.Writer, headers: []string, rows: [][]string) {
|
||||
}
|
||||
io.write_string(w, string(data))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user