dir API

dir

package

API reference for the dir package.

S
struct

Provider

Provider implements configuration.Provider for a directory of JSON files.

core/configuration/source/dir/provider.go:30-39
type Provider struct

Methods

Name
Method

Name returns "dir".

Returns

string
func (*Provider) Name() string
{
	return "dir"
}
Load
Method

Load reads every matching file in the directory and returns a flat key-value map. Each file's content is flattened with colon-separated keys prefixed by the file base name without extension: "tenants/acme.json" -> "acme:quota". Files are processed in sorted name order so the result is deterministic.

Parameters

Returns

map[string]any
error
func (*Provider) Load(ctx context.Context) (map[string]any, error)
{
	if err := ctx.Err(); err != nil {
		return nil, err
	}

	entries, err := os.ReadDir(p.Dir)
	if err != nil {
		return nil, err
	}

	excluded := make(map[string]bool, len(p.Exclude))
	for _, e := range p.Exclude {
		excluded[e] = true
	}

	names := make([]string, 0, len(entries))
	for _, entry := range entries {
		if entry.IsDir() || excluded[entry.Name()] {
			continue
		}
		ok, err := filepath.Match(p.Pattern, entry.Name())
		if err != nil {
			return nil, fmt.Errorf("configuration: invalid dir pattern %q: %w", p.Pattern, err)
		}
		if ok {
			names = append(names, entry.Name())
		}
	}
	sort.Strings(names)

	flat := make(map[string]any)
	for _, name := range names {
		select {
		case <-ctx.Done():
			return nil, ctx.Err()
		default:
		}

		data, err := readLimited(filepath.Join(p.Dir, name))
		if err != nil {
			return nil, fmt.Errorf("configuration: %s: %w", name, err)
		}

		var parsed map[string]any
		if err := json.Unmarshal(data, &parsed); err != nil {
			return nil, fmt.Errorf("configuration: %s: %w", name, err)
		}

		prefix := strings.TrimSuffix(name, filepath.Ext(name))
		flatten(prefix, parsed, flat)
	}
	return flat, nil
}

Fields

Name Type Description
Dir string
Pattern string
Exclude []string
F
function

New

New creates a new directory provider. pattern is a glob like “*.json”.

Parameters

dir
string
pattern
string
exclude
...string

Returns

core/configuration/source/dir/provider.go:42-44
func New(dir, pattern string, exclude ...string) *Provider

{
	return &Provider{Dir: dir, Pattern: pattern, Exclude: exclude}
}
F
function

readLimited

Parameters

path
string

Returns

[]byte
error
core/configuration/source/dir/provider.go:111-125
func readLimited(path string) ([]byte, error)

{
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	data, err := io.ReadAll(io.LimitReader(f, maxFileSize+1))
	if err != nil {
		return nil, err
	}
	if len(data) > maxFileSize {
		return nil, errors.New("file exceeds 4 MiB limit")
	}
	return data, nil
}
F
function

flatten

flatten mirrors source/file: nested objects become colon-separated keys.

Parameters

prefix
string
src
map[string]any
dst
map[string]any
core/configuration/source/dir/provider.go:128-137
func flatten(prefix string, src map[string]any, dst map[string]any)

{
	for k, v := range src {
		key := prefix + ":" + k
		if nested, ok := v.(map[string]any); ok {
			flatten(key, nested, dst)
		} else {
			dst[key] = v
		}
	}
}
F
function

writeFile

Parameters

dir
string
name
string
content
string
core/configuration/source/dir/provider_test.go:11-16
func writeFile(t *testing.T, dir, name, content string)

{
	t.Helper()
	if err := os.WriteFile(filepath.Join(dir, name), []byte(content), 0o644); err != nil {
		t.Fatal(err)
	}
}
F
function

TestLoadFlattensEachFileUnderItsName

Parameters

core/configuration/source/dir/provider_test.go:18-44
func TestLoadFlattensEachFileUnderItsName(t *testing.T)

{
	dir := t.TempDir()
	writeFile(t, dir, "a.example.json", `{"domain":"a.example","port":8080,"ssl":{"enabled":true}}`)
	writeFile(t, dir, "b.example.json", `{"domain":"b.example","port":9090}`)

	p := New(dir, "*.json")
	got, err := p.Load(context.Background())
	if err != nil {
		t.Fatal(err)
	}

	want := map[string]any{
		"a.example:domain":      "a.example",
		"a.example:port":        float64(8080),
		"a.example:ssl:enabled": true,
		"b.example:domain":      "b.example",
		"b.example:port":        float64(9090),
	}
	if len(got) != len(want) {
		t.Fatalf("got %d keys, want %d: %v", len(got), len(want), got)
	}
	for k, v := range want {
		if got[k] != v {
			t.Errorf("key %q: got %v, want %v", k, got[k], v)
		}
	}
}
F
function

TestLoadHonorsPatternAndExclude

Parameters

core/configuration/source/dir/provider_test.go:46-74
func TestLoadHonorsPatternAndExclude(t *testing.T)

{
	dir := t.TempDir()
	writeFile(t, dir, "alpha.json", `{"a":1}`)
	writeFile(t, dir, "conf.global.json", `{"b":2}`)
	writeFile(t, dir, "notes.txt", `{"c":3}`)
	sub := filepath.Join(dir, "nested")
	if err := os.Mkdir(sub, 0o755); err != nil {
		t.Fatal(err)
	}
	writeFile(t, sub, "inner.json", `{"d":4}`)

	p := New(dir, "*.json", "conf.global.json")
	got, err := p.Load(context.Background())
	if err != nil {
		t.Fatal(err)
	}
	if _, ok := got["alpha:a"]; !ok {
		t.Error("alpha.json not loaded")
	}
	if _, ok := got["conf.global:b"]; ok {
		t.Error("excluded file was loaded")
	}
	if _, ok := got["notes:c"]; ok {
		t.Error("non-matching pattern was loaded")
	}
	if _, ok := got["inner:d"]; ok {
		t.Error("subdirectory file was loaded")
	}
}
F
function

TestLoadReportsBadJSONWithFileName

Parameters

core/configuration/source/dir/provider_test.go:76-88
func TestLoadReportsBadJSONWithFileName(t *testing.T)

{
	dir := t.TempDir()
	writeFile(t, dir, "broken.json", `{invalid`)

	p := New(dir, "*.json")
	_, err := p.Load(context.Background())
	if err == nil {
		t.Fatal("expected error")
	}
	if got := err.Error(); !strings.Contains(got, "broken.json") {
		t.Errorf("error %q does not name the file", got)
	}
}
F
function

TestLoadMissingDirectory

Parameters

core/configuration/source/dir/provider_test.go:90-95
func TestLoadMissingDirectory(t *testing.T)

{
	p := New(filepath.Join(t.TempDir(), "nope"), "*.json")
	if _, err := p.Load(context.Background()); err == nil {
		t.Fatal("expected error for missing directory")
	}
}
F
function

TestLoadCanceledContext

Parameters

core/configuration/source/dir/provider_test.go:97-104
func TestLoadCanceledContext(t *testing.T)

{
	ctx, cancel := context.WithCancel(context.Background())
	cancel()
	p := New(t.TempDir(), "*.json")
	if _, err := p.Load(ctx); err == nil {
		t.Fatal("expected context error")
	}
}