summaryrefslogtreecommitdiff
path: root/internal/resource/opt/opt.go
blob: 23f076d3a2b8c2ddb8cd55f456d08a3fa9d19f3d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// Package opt provides interface-based, resource-agnostic configuration
// options shared by the file, dir, and link resource packages.
//
// It is designed to be dot-imported at call sites so options read as bare
// WithMode(...) / WithOwner(...) calls regardless of the resource type:
//
//	import (
//		"codeberg.org/snonux/gonf/internal/resource/file"
//		. "codeberg.org/snonux/gonf/internal/resource/opt"
//	)
//
//	file.Have("file.txt", WithOwner("paul"), WithMode(0o755))
//
// Each option targets a small capability interface (Owner, Moded, ...). A
// resource implements only the setters it supports. Applying an option to a
// resource that lacks the matching interface (e.g. WithPrune on a file) is a
// programming error in the resource declaration and aborts the program via
// log.Fatalf.
package opt

import (
	"log"
	"os"
)

// Option configures a resource. It is applied to the concrete resource value
// (e.g. *file.File) during construction.
type Option func(any)

// Capability interfaces. A resource implements only the setters it supports.
type (
	Owner      interface{ SetOwner(string) }
	Grouped    interface{ SetGroup(string) }
	Moded      interface{ SetMode(os.FileMode) }
	Sourced    interface{ SetSource(string) }
	Contented  interface{ SetContent(string) }
	FileModed  interface{ SetFileMode(os.FileMode) }
	Prunable   interface{ SetPrune() }
	Absentable interface{ SetAbsent() }
	Linkable   interface {
		SetSymlink(target string)
		SetHardlink(target string)
	}
)

// WithOwner sets the owning user of the resource.
func WithOwner(owner string) Option {
	return func(t any) {
		r, ok := t.(Owner)
		if !ok {
			log.Fatalf("%T does not support WithOwner", t)
		}
		r.SetOwner(owner)
	}
}

// WithGroup sets the owning group of the resource.
func WithGroup(group string) Option {
	return func(t any) {
		r, ok := t.(Grouped)
		if !ok {
			log.Fatalf("%T does not support WithGroup", t)
		}
		r.SetGroup(group)
	}
}

// WithMode sets the resource's own file mode.
func WithMode(mode os.FileMode) Option {
	return func(t any) {
		r, ok := t.(Moded)
		if !ok {
			log.Fatalf("%T does not support WithMode", t)
		}
		r.SetMode(mode)
	}
}

// WithSource sets the source path the resource is populated from.
func WithSource(source string) Option {
	return func(t any) {
		r, ok := t.(Sourced)
		if !ok {
			log.Fatalf("%T does not support WithSource", t)
		}
		r.SetSource(source)
	}
}

// WithContent sets literal content for the resource.
func WithContent(content string) Option {
	return func(t any) {
		r, ok := t.(Contented)
		if !ok {
			log.Fatalf("%T does not support WithContent", t)
		}
		r.SetContent(content)
	}
}

// WithFileMode sets the mode applied to regular files copied from a source
// tree (distinct from the resource's own mode).
func WithFileMode(mode os.FileMode) Option {
	return func(t any) {
		r, ok := t.(FileModed)
		if !ok {
			log.Fatalf("%T does not support WithFileMode", t)
		}
		r.SetFileMode(mode)
	}
}

// WithPrune enables reconciliation of extra destination entries during a
// source copy, and recursive removal during IsAbsent().
func WithPrune() Option {
	return func(t any) {
		r, ok := t.(Prunable)
		if !ok {
			log.Fatalf("%T does not support WithPrune", t)
		}
		r.SetPrune()
	}
}

// IsAbsent marks the resource for removal.
func IsAbsent() Option {
	return func(t any) {
		r, ok := t.(Absentable)
		if !ok {
			log.Fatalf("%T does not support IsAbsent", t)
		}
		r.SetAbsent()
	}
}

// WithSymlink makes the resource a symbolic link pointing at target.
func WithSymlink(target string) Option {
	return func(t any) {
		r, ok := t.(Linkable)
		if !ok {
			log.Fatalf("%T does not support WithSymlink", t)
		}
		r.SetSymlink(target)
	}
}

// WithHardlink makes the resource a hard link pointing at target.
func WithHardlink(target string) Option {
	return func(t any) {
		r, ok := t.(Linkable)
		if !ok {
			log.Fatalf("%T does not support WithHardlink", t)
		}
		r.SetHardlink(target)
	}
}