-
Notifications
You must be signed in to change notification settings - Fork 3
/
profile.go
197 lines (176 loc) · 4.87 KB
/
profile.go
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
// Copyright 2018 ekino. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package godim
import (
"fmt"
"strings"
)
// AppProfile represents the differents Profile of your application
// see @Profile for Profile detail
type AppProfile struct {
profiles map[string]*Profile
locked bool
}
// Profile a Profile represent an injectable type
//
// It holds the possible dependency tree in order to validate non-cycling app
//
// injection example :
// type UserService struct {
// userRepository *UserRepository inject:"repository:UserRepository"
// }
type Profile struct {
name string
injectIn map[string]bool
}
const defaultStr = "default"
// DefaultProfile specific default profile that allows anything to be injected into anything
var DefaultProfile = Profile{
name: defaultStr,
injectIn: map[string]bool{
defaultStr: true,
},
}
// NewAppProfile returns a new AppProfile that will drive
// injection and dependency throu your application
func newAppProfile() *AppProfile {
return &AppProfile{
profiles: make(map[string]*Profile),
locked: false,
}
}
// StrictHTTPAppProfile return a strict http profile based on :
//
// - handler : manage http input and can call service
//
// - service : manage business logic and can call repositories
//
// - repository : manage data access
//
// - driver : manage resource driver
//
func StrictHTTPAppProfile() *AppProfile {
app := newAppProfile()
app.AddProfileDef("handler")
app.AddProfileDef("service", "handler")
app.AddProfileDef("repository", "service")
app.AddProfileDef("driver", "repository")
app.locked = true
return app
}
// HTTPAppProfile return a less strict http profile based on :
//
// - handler : manage http input and can call services
//
// - service : manage business logic and can call repositories and other services
//
// - repository : manage data access
//
func HTTPAppProfile() *AppProfile {
app := newAppProfile()
app.AddProfileDef("handler")
app.AddProfileDef("service", "handler", "service")
app.AddProfileDef("repository", "service")
app.locked = true
return app
}
// // NewProfile create a new profile
// // - name : the name of the profile
// // - injectIn : where it can be injected
// func NewProfile(name string, injectIn ...string) *Profile {
// injects := make(map[string]bool)
// for _, i := range injectIn {
// injects[i] = true
// }
// return &Profile{
// name: name,
// injectIn: injects,
// }
// }
// AddProfileDef add a new profile definition
//
// - name name to use in injection
//
// - injectIn where it can be injected
//
func (ap *AppProfile) AddProfileDef(name string, injectIn ...string) error {
injects := make(map[string]bool)
for _, i := range injectIn {
injects[i] = true
}
return ap.AddProfile(&Profile{
name: name,
injectIn: injects,
})
}
// AddProfile add a profile
func (ap *AppProfile) AddProfile(p *Profile) error {
if ap.locked {
return newError(fmt.Errorf("AppProfile locked, can't add new profile")).SetErrType(ErrTypeProfile)
}
if ap.profiles[p.name] != nil {
return newError(fmt.Errorf("%s is already declared", p.name)).SetErrType(ErrTypeProfile)
}
ap.profiles[p.name] = p
return nil
}
func (ap *AppProfile) lock() error {
if !ap.locked {
if len(ap.profiles) == 0 {
// inserting DefaultProfile
ap.AddProfile(&DefaultProfile)
} else {
// default profile is prohibited if there is profiles definition
l := len(ap.profiles)
for key := range ap.profiles {
if key == defaultStr && l > 1 {
return newError(fmt.Errorf("default profile can't be used in a multi-profile app")).SetErrType(ErrTypeProfile)
}
}
}
ap.locked = true
}
return nil
}
// IsLocked AppProfile is locked after the first lifecycle phase or with some strict definition
func (ap *AppProfile) isLocked() bool {
return ap.locked
}
func (p *Profile) canBeInjectedIn(other string) bool {
return p.injectIn[other]
}
func (ap *AppProfile) validate(label string) bool {
return ap.profiles[label] != nil
}
func (ap *AppProfile) isDefault() bool {
return ap.profiles[defaultStr] != nil
}
func (ap *AppProfile) validateTag(label, itag string) (string, error) {
if ap.isDefault() {
return itag, nil
}
elts := strings.Split(itag, ":")
if len(elts) != 2 {
return "", newError(fmt.Errorf("wrong number of argument when declaring tag %s", itag)).SetErrType(ErrTypeProfile)
}
p, err := ap.getProfile(elts[0])
if err != nil {
return "", err
}
_, err = ap.getProfile(label)
if err != nil {
return "", err
}
if p.canBeInjectedIn(label) {
return elts[1], nil
}
return "", newError(fmt.Errorf("%s can't be injected in %s", elts[0], label)).SetErrType(ErrTypeProfile)
}
func (ap *AppProfile) getProfile(label string) (*Profile, error) {
p := ap.profiles[label]
if p == nil {
return nil, newError(fmt.Errorf("profile %s does not exist", label)).SetErrType(ErrTypeProfile)
}
return p, nil
}