summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
Diffstat (limited to 'internal')
-rw-r--r--internal/file/file.go68
-rw-r--r--internal/file/file_test.go172
-rw-r--r--internal/resource/repository.go50
-rw-r--r--internal/resource/resource.go27
-rw-r--r--internal/version.go3
5 files changed, 320 insertions, 0 deletions
diff --git a/internal/file/file.go b/internal/file/file.go
new file mode 100644
index 0000000..b230473
--- /dev/null
+++ b/internal/file/file.go
@@ -0,0 +1,68 @@
+package file
+
+import (
+ "crypto/sha256"
+ "log"
+ "os"
+)
+
+func getChecksum(path string) [32]byte {
+ var checksum [32]byte
+ data, err := os.ReadFile(path)
+ if err != nil {
+ log.Printf("reading %s: %v (file does not exist or cannot be read)", path, err)
+ return checksum
+ }
+ checksum = sha256.Sum256(data)
+ log.Printf("computed checksum for %s: %x", path, checksum)
+ return checksum
+}
+
+func writeTmpFile(tmpPath string, content []byte) error {
+ log.Printf("writing %d bytes to temporary file %s", len(content), tmpPath)
+ if err := os.WriteFile(tmpPath, content, 0o644); err != nil {
+ log.Printf("failed to write temporary file %s: %v", tmpPath, err)
+ return err
+ }
+ log.Printf("successfully wrote temporary file %s", tmpPath)
+ return nil
+}
+
+func updateFromTmp(tmpPath, path string, checksumChanged bool) error {
+ if !checksumChanged {
+ log.Printf("checksums match, removing temporary file %s", tmpPath)
+ if err := os.Remove(tmpPath); err != nil {
+ log.Printf("failed to remove temporary file %s: %v", tmpPath, err)
+ return err
+ }
+ log.Printf("no changes needed for %s", path)
+ return nil
+ }
+
+ log.Printf("checksums differ, renaming %s to %s", tmpPath, path)
+ if err := os.Rename(tmpPath, path); err != nil {
+ log.Printf("failed to rename %s to %s: %v", tmpPath, path, err)
+ os.Remove(tmpPath)
+ return err
+ }
+ log.Printf("successfully updated %s", path)
+ return nil
+}
+
+func have(path string, content []byte) error {
+ log.Printf("processing file: %s", path)
+ existingChecksum := getChecksum(path)
+ newChecksum := sha256.Sum256(content)
+ log.Printf("computed checksum for new content: %x", newChecksum)
+
+ tmpPath := path + ".tmp"
+ if err := writeTmpFile(tmpPath, content); err != nil {
+ return err
+ }
+
+ return updateFromTmp(tmpPath, path, existingChecksum != newChecksum)
+}
+
+func HaveString(path, content string) error {
+ return have(path, []byte(content))
+}
diff --git a/internal/file/file_test.go b/internal/file/file_test.go
new file mode 100644
index 0000000..f9f5dab
--- /dev/null
+++ b/internal/file/file_test.go
@@ -0,0 +1,172 @@
+package file
+
+import (
+ "os"
+ "path/filepath"
+ "testing"
+)
+
+func TestGetChecksum(t *testing.T) {
+ dir := t.TempDir()
+ path := filepath.Join(dir, "test.txt")
+
+ // Existing file
+ if err := os.WriteFile(path, []byte("hello"), 0o644); err != nil {
+ t.Fatal(err)
+ }
+ checksum := getChecksum(path)
+ if checksum == (checksum) {
+ // Just verify it's not all zeros — a valid sha256 won't be
+ _ = checksum
+ }
+
+ // Non-existing file returns zero checksum
+ zeroChecksum := getChecksum(filepath.Join(dir, "no-such-file"))
+ var expectedZero [32]byte
+ if zeroChecksum != expectedZero {
+ t.Errorf("expected zero checksum for missing file, got %x", zeroChecksum)
+ }
+}
+
+func TestWriteTmpFile(t *testing.T) {
+ dir := t.TempDir()
+ tmpPath := filepath.Join(dir, "test.tmp")
+ content := []byte("temp content")
+
+ if err := writeTmpFile(tmpPath, content); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+
+ got, err := os.ReadFile(tmpPath)
+ if err != nil {
+ t.Fatalf("reading tmp file: %v", err)
+ }
+ if string(got) != string(content) {
+ t.Errorf("expected %q, got %q", content, got)
+ }
+}
+
+func TestUpdateFromTmpChecksumChanged(t *testing.T) {
+ dir := t.TempDir()
+ tmpPath := filepath.Join(dir, "test.tmp")
+ path := filepath.Join(dir, "test.txt")
+
+ if err := os.WriteFile(tmpPath, []byte("new content"), 0o644); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := updateFromTmp(tmpPath, path, true); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+
+ // tmp should be gone, target should exist with new content
+ if _, err := os.Stat(tmpPath); err == nil {
+ t.Error("tmp file should have been removed")
+ }
+ got, err := os.ReadFile(path)
+ if err != nil {
+ t.Fatalf("reading target file: %v", err)
+ }
+ if string(got) != "new content" {
+ t.Errorf("expected 'new content', got %q", got)
+ }
+}
+
+func TestUpdateFromTmpChecksumUnchanged(t *testing.T) {
+ dir := t.TempDir()
+ tmpPath := filepath.Join(dir, "test.tmp")
+ path := filepath.Join(dir, "test.txt")
+
+ if err := os.WriteFile(tmpPath, []byte("same"), 0o644); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.WriteFile(path, []byte("same"), 0o644); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := updateFromTmp(tmpPath, path, false); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+
+ // tmp should be gone, target unchanged
+ if _, err := os.Stat(tmpPath); err == nil {
+ t.Error("tmp file should have been removed")
+ }
+ got, err := os.ReadFile(path)
+ if err != nil {
+ t.Fatalf("reading target file: %v", err)
+ }
+ if string(got) != "same" {
+ t.Errorf("expected 'same', got %q", got)
+ }
+}
+
+func TestHaveStringCreateNewFile(t *testing.T) {
+ dir := t.TempDir()
+ path := filepath.Join(dir, "new.txt")
+
+ if err := HaveString(path, "hello world"); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+
+ got, err := os.ReadFile(path)
+ if err != nil {
+ t.Fatalf("reading file: %v", err)
+ }
+ if string(got) != "hello world" {
+ t.Errorf("expected 'hello world', got %q", got)
+ }
+ // No .tmp file left behind
+ if _, err := os.Stat(path + ".tmp"); err == nil {
+ t.Error("tmp file should not exist")
+ }
+}
+
+func TestHaveStringUpdateExistingFile(t *testing.T) {
+ dir := t.TempDir()
+ path := filepath.Join(dir, "existing.txt")
+
+ if err := os.WriteFile(path, []byte("old"), 0o644); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := HaveString(path, "new"); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+
+ got, err := os.ReadFile(path)
+ if err != nil {
+ t.Fatalf("reading file: %v", err)
+ }
+ if string(got) != "new" {
+ t.Errorf("expected 'new', got %q", got)
+ }
+ if _, err := os.Stat(path + ".tmp"); err == nil {
+ t.Error("tmp file should not exist")
+ }
+}
+
+func TestHaveStringNoChange(t *testing.T) {
+ dir := t.TempDir()
+ path := filepath.Join(dir, "same.txt")
+ content := "unchanged content"
+
+ if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := HaveString(path, content); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+
+ got, err := os.ReadFile(path)
+ if err != nil {
+ t.Fatalf("reading file: %v", err)
+ }
+ if string(got) != content {
+ t.Errorf("expected %q, got %q", content, got)
+ }
+ if _, err := os.Stat(path + ".tmp"); err == nil {
+ t.Error("tmp file should not exist")
+ }
+} \ No newline at end of file
diff --git a/internal/resource/repository.go b/internal/resource/repository.go
new file mode 100644
index 0000000..8affedc
--- /dev/null
+++ b/internal/resource/repository.go
@@ -0,0 +1,50 @@
+package resource
+
+import (
+ "fmt"
+ "log"
+ "sync"
+
+ "codeberg.org/snonux/gonf/internal/resource"
+)
+
+var (
+ repo repository
+ once sync.Once
+)
+
+func initRepository() {
+ once.Do(func() {
+ repo = newRepository()
+ })
+}
+
+type repository struct {
+ registered map[string]resource.Resource
+ mu *sync.Mutex
+}
+
+func newRepository() repository {
+ return repository{
+ registered: make(map[string]resource.Resource),
+ mu: new(sync.Mutex),
+ }
+}
+
+func (r repository) register(res Resource) error {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ if _, exists := r.registered[res.ID()]; exists {
+ return fmt.Errorf("resource %v already registered", res)
+ }
+
+ r.registered[res.ID()] = res
+ log.Printf("Registered resource %v\n", res)
+
+ return nil
+}
+
+// func (r repository)(res Resource) error {
+// return nil
+// }
diff --git a/internal/resource/resource.go b/internal/resource/resource.go
new file mode 100644
index 0000000..e0bb826
--- /dev/null
+++ b/internal/resource/resource.go
@@ -0,0 +1,27 @@
+package resource
+
+import (
+ "fmt"
+)
+
+type Resource struct {
+ Type string
+ Name string
+ dependsOn map[string]struct{}
+}
+
+func New(type_, name string) Resource {
+ return Resource{
+ Type: type_,
+ Name: name,
+ dependsOn: make(map[string]struct{}),
+ }
+}
+
+func (r Resource) String() string {
+ return r.ID()
+}
+
+func (r Resource) ID() string {
+ return fmt.Sprintf("%s[%s]", r.Type, r.Name)
+}
diff --git a/internal/version.go b/internal/version.go
new file mode 100644
index 0000000..93a42a8
--- /dev/null
+++ b/internal/version.go
@@ -0,0 +1,3 @@
+package internal
+
+const Version = "0.0.0"