From 65b8fd210aed8954515f45bcb740919bd5293f90 Mon Sep 17 00:00:00 2001 From: Lukas Wurzinger Date: Wed, 4 Dec 2024 21:37:26 +0100 Subject: [PATCH] init --- .envrc | 3 ++ .gitignore | 6 +++ devenv.lock | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++ devenv.nix | 3 ++ devenv.yaml | 3 ++ go.mod | 3 ++ gonfig.go | 74 +++++++++++++++++++++++++++++++++ 7 files changed, 208 insertions(+) create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 devenv.lock create mode 100644 devenv.nix create mode 100644 devenv.yaml create mode 100644 go.mod create mode 100644 gonfig.go diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..894571b --- /dev/null +++ b/.envrc @@ -0,0 +1,3 @@ +source_url "https://raw.githubusercontent.com/cachix/devenv/82c0147677e510b247d8b9165c54f73d32dfd899/direnvrc" "sha256-7u4iDd1nZpxL4tCzmPG0dQgC5V+/44Ba+tHkPob1v2k=" + +use devenv diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4b07a82 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.devenv* +devenv.local.nix + +.direnv + +.pre-commit-config.yaml diff --git a/devenv.lock b/devenv.lock new file mode 100644 index 0000000..38be17e --- /dev/null +++ b/devenv.lock @@ -0,0 +1,116 @@ +{ + "nodes": { + "devenv": { + "locked": { + "dir": "src/modules", + "lastModified": 1732896163, + "owner": "cachix", + "repo": "devenv", + "rev": "2c928a199d56191d7a53f29ccafa56238c8ce4e5", + "type": "github" + }, + "original": { + "dir": "src/modules", + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1732722421, + "owner": "edolstra", + "repo": "flake-compat", + "rev": "9ed2ac151eada2306ca8c418ebd97807bb08f6ac", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1733212471, + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "55d15ad12a74eb7d4646254e13638ad0c4128776", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1733220138, + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "bcb68885668cccec12276bbb379f8f2557aa06ce", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1732021966, + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "3308484d1a443fc5bc92012435d79e80458fe43c", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "nixpkgs": "nixpkgs", + "pre-commit-hooks": "pre-commit-hooks" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/devenv.nix b/devenv.nix new file mode 100644 index 0000000..18951a2 --- /dev/null +++ b/devenv.nix @@ -0,0 +1,3 @@ +{ + languages.go.enable = true; +} diff --git a/devenv.yaml b/devenv.yaml new file mode 100644 index 0000000..c6d2894 --- /dev/null +++ b/devenv.yaml @@ -0,0 +1,3 @@ +inputs: + nixpkgs: + url: github:NixOS/nixpkgs/nixos-unstable diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..3185c98 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/lukaswrz/gonfig + +go 1.23.3 diff --git a/gonfig.go b/gonfig.go new file mode 100644 index 0000000..579f712 --- /dev/null +++ b/gonfig.go @@ -0,0 +1,74 @@ +// Package gonfig provides utilities for reading, unmarshaling, and validating +// configuration files. +package gonfig + +import ( + "errors" + "fmt" + "os" +) + +// Validator defines an interface for validating a configuration of type T. It +// ensures that the provided configuration meets required constraints. +type Validator[T any] interface { + // Validate checks the provided configuration and returns an error if it is + // invalid. + Validate(config T) []error +} + +// ReadConfig reads a configuration file, unmarshals its content into the given +// configuration object, and validates it using the provided validator. If the +// primary path is empty, it searches for the configuration file in the +// fallback paths. The function returns the resolved path to the configuration +// file or a list of errors if the file cannot be located, read, unmarshaled, +// or validated. +func ReadConfig[T any](path string, paths []string, c *T, unmarshal func([]byte, *T) error, validator Validator[T]) (string, []error) { + var err error + + path, err = findConfig(path, paths) + if err != nil { + return "", []error{err} + } + + content, err := os.ReadFile(path) + if err != nil { + return "", []error{fmt.Errorf("unable to read configuration file %s: %w", path, err)} + } + + err = unmarshal(content, c) + if err != nil { + return "", []error{fmt.Errorf("unable to unmarshal configuration file %s: %w", path, err)} + } + + return path, validator.Validate(*c) +} + +// findConfig determines the path to the configuration file by using the +// provided primary path or searching through a list of fallback paths if the +// primary path is empty. If no valid file is found, or if the specified file is +// inaccessible, an error is returned. +func findConfig(path string, paths []string) (string, error) { + var err error + + if path == "" { + for _, p := range paths { + _, err = os.Stat(p) + if err != nil { + continue + } + + path = p + } + + if path == "" { + return "", errors.New("could not locate configuration file") + } + } else { + _, err = os.Stat(path) + if err != nil { + return "", fmt.Errorf("could not stat configuration file %s: %w", path, err) + } + } + + return path, nil +}