mirror of
https://github.com/sbrow/envr.git
synced 2026-06-27 18:48:33 -04:00
feat(odin): ported backup command.
This commit is contained in:
2
cli.odin
2
cli.odin
@@ -15,6 +15,8 @@ IMPLEMENTED_COMMANDS := []string{
|
|||||||
"version",
|
"version",
|
||||||
"deps",
|
"deps",
|
||||||
"list",
|
"list",
|
||||||
|
"backup",
|
||||||
|
"add",
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_args :: proc() -> (cmd: Command, ok: bool) {
|
parse_args :: proc() -> (cmd: Command, ok: bool) {
|
||||||
|
|||||||
34
cmd_backup.odin
Normal file
34
cmd_backup.odin
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:strings"
|
||||||
|
|
||||||
|
cmd_backup :: proc(cmd: ^Command) {
|
||||||
|
if len(cmd.args) != 1 {
|
||||||
|
fmt.println("Usage: envr backup <path>")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
path := cmd.args[0]
|
||||||
|
if len(strings.trim_space(path)) == 0 {
|
||||||
|
fmt.println("Error: No path provided")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
file, ok := new_env_file(path)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
db, db_ok := db_open()
|
||||||
|
if !db_ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer db_close(&db)
|
||||||
|
|
||||||
|
if !db_insert(&db, file) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.printf("Saved %s into the database\n", path)
|
||||||
|
}
|
||||||
127
db.odin
127
db.odin
@@ -1,6 +1,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "core:c"
|
import "core:c"
|
||||||
|
import "core:crypto/hash"
|
||||||
|
import "core:encoding/hex"
|
||||||
import "core:encoding/json"
|
import "core:encoding/json"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:os"
|
import "core:os"
|
||||||
@@ -265,6 +267,131 @@ db_attach_and_copy :: proc(mem_db: ^rawptr, src_path: string) -> bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_git_remotes :: proc(dir: string) -> [dynamic]string {
|
||||||
|
remotes: [dynamic]string
|
||||||
|
remote_set: map[string]bool
|
||||||
|
|
||||||
|
b: strings.Builder
|
||||||
|
strings.builder_init(&b)
|
||||||
|
fmt.sbprintf(&b, "%s-git-remotes", make_temp_path())
|
||||||
|
tmp_path := strings.to_string(b)
|
||||||
|
tmp_file, tmp_err := os.open(tmp_path, os.O_CREATE | os.O_WRONLY | os.O_TRUNC)
|
||||||
|
if tmp_err != nil {
|
||||||
|
return remotes
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []string{"git", "remote", "-v"}
|
||||||
|
desc := os.Process_Desc{
|
||||||
|
command = args,
|
||||||
|
stdout = tmp_file,
|
||||||
|
stderr = nil,
|
||||||
|
working_dir = dir,
|
||||||
|
}
|
||||||
|
|
||||||
|
p, start_err := os.process_start(desc)
|
||||||
|
os.close(tmp_file)
|
||||||
|
if start_err != nil {
|
||||||
|
os.remove(tmp_path)
|
||||||
|
return remotes
|
||||||
|
}
|
||||||
|
|
||||||
|
state, wait_err := os.process_wait(p)
|
||||||
|
if wait_err != nil || state.exit_code != 0 {
|
||||||
|
os.remove(tmp_path)
|
||||||
|
return remotes
|
||||||
|
}
|
||||||
|
|
||||||
|
data, read_err := os.read_entire_file_from_path(tmp_path, context.allocator)
|
||||||
|
os.remove(tmp_path)
|
||||||
|
if read_err != nil {
|
||||||
|
return remotes
|
||||||
|
}
|
||||||
|
|
||||||
|
output_str := string(data)
|
||||||
|
lines := strings.split(output_str, "\n")
|
||||||
|
|
||||||
|
for &line in lines {
|
||||||
|
line = strings.trim_space(line)
|
||||||
|
if len(line) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts := strings.fields(line)
|
||||||
|
if len(parts) >= 2 {
|
||||||
|
remote_set[parts[1]] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for remote, _ in remote_set {
|
||||||
|
cloned, _ := strings.clone(remote)
|
||||||
|
append(&remotes, cloned)
|
||||||
|
}
|
||||||
|
|
||||||
|
return remotes
|
||||||
|
}
|
||||||
|
|
||||||
|
new_env_file :: proc(path: string) -> (EnvFile, bool) {
|
||||||
|
abs_path, abs_err := filepath.abs(path)
|
||||||
|
if abs_err != nil {
|
||||||
|
fmt.printf("Error getting absolute path: %v\n", abs_err)
|
||||||
|
return EnvFile{}, false
|
||||||
|
}
|
||||||
|
cloned_path, _ := strings.clone(abs_path)
|
||||||
|
|
||||||
|
dir := filepath.dir(cloned_path)
|
||||||
|
cloned_dir, _ := strings.clone(dir)
|
||||||
|
|
||||||
|
remotes := get_git_remotes(cloned_dir)
|
||||||
|
|
||||||
|
data, read_err := os.read_entire_file_from_path(cloned_path, context.allocator)
|
||||||
|
if read_err != nil {
|
||||||
|
fmt.printf("Error reading file %s: %v\n", cloned_path, read_err)
|
||||||
|
return EnvFile{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
digest := hash.hash_bytes(hash.Algorithm.SHA256, data)
|
||||||
|
hex_bytes, _ := hex.encode(digest)
|
||||||
|
sha_str := string(hex_bytes)
|
||||||
|
|
||||||
|
return EnvFile{
|
||||||
|
Path = cloned_path,
|
||||||
|
Dir = cloned_dir,
|
||||||
|
Remotes = remotes,
|
||||||
|
Sha256 = sha_str,
|
||||||
|
contents = string(data),
|
||||||
|
}, true
|
||||||
|
}
|
||||||
|
|
||||||
|
db_insert :: proc(d: ^Db, file: EnvFile) -> bool {
|
||||||
|
remotes_json, marshal_err := json.marshal(file.Remotes)
|
||||||
|
if marshal_err != nil {
|
||||||
|
fmt.printf("Error marshaling remotes: %v\n", marshal_err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
sql := "INSERT OR REPLACE INTO envr_env_files (path, remotes, sha256, contents) VALUES (?, ?, ?, ?)"
|
||||||
|
stmt: ^rawptr
|
||||||
|
rc := sqlite.prepare_v2(d.db, string_to_cstring(sql), -1, &stmt, nil)
|
||||||
|
if rc != sqlite.OK {
|
||||||
|
fmt.printf("Error preparing insert: %s\n", sqlite.db_errmsg(d.db))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer sqlite.finalize(stmt)
|
||||||
|
|
||||||
|
rc = sqlite.bind_text(stmt, 1, string_to_cstring(file.Path), -1, nil)
|
||||||
|
rc = sqlite.bind_text(stmt, 2, string_to_cstring(string(remotes_json)), -1, nil)
|
||||||
|
rc = sqlite.bind_text(stmt, 3, string_to_cstring(file.Sha256), -1, nil)
|
||||||
|
rc = sqlite.bind_text(stmt, 4, string_to_cstring(file.contents), -1, nil)
|
||||||
|
|
||||||
|
rc = sqlite.step(stmt)
|
||||||
|
if rc != sqlite.DONE {
|
||||||
|
fmt.printf("Error inserting: %s\n", sqlite.db_errmsg(d.db))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
d.changed = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
cstring_to_string :: proc(cs: cstring) -> string {
|
cstring_to_string :: proc(cs: cstring) -> string {
|
||||||
if cs == nil {
|
if cs == nil {
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ main :: proc() {
|
|||||||
cmd_deps(&cmd)
|
cmd_deps(&cmd)
|
||||||
case "list":
|
case "list":
|
||||||
cmd_list(&cmd)
|
cmd_list(&cmd)
|
||||||
|
case "backup", "add":
|
||||||
|
cmd_backup(&cmd)
|
||||||
case:
|
case:
|
||||||
fmt.printf("Unknown command: %s\n", cmd.name)
|
fmt.printf("Unknown command: %s\n", cmd.name)
|
||||||
print_usage()
|
print_usage()
|
||||||
|
|||||||
@@ -14,10 +14,6 @@ cmd_sync :: proc(cmd: ^Command) {
|
|||||||
fmt.println("TODO: sync")
|
fmt.println("TODO: sync")
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd_backup :: proc(cmd: ^Command) {
|
|
||||||
fmt.println("TODO: backup")
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd_restore :: proc(cmd: ^Command) {
|
cmd_restore :: proc(cmd: ^Command) {
|
||||||
fmt.println("TODO: restore")
|
fmt.println("TODO: restore")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user