Skip to content

Commit 618137e

Browse files
committed
feat(config): add flexible dependency parsing for string and map formats
1 parent bd3b065 commit 618137e

File tree

2 files changed

+120
-0
lines changed

2 files changed

+120
-0
lines changed

pkg/config/config.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,42 @@ type Dependency struct {
100100
Container *Container `yaml:"container"`
101101
}
102102

103+
// UnmarshalYAML is a custom unmarshaler that lets you handle both string and map forms.
104+
func (d *Dependency) UnmarshalYAML(node *yaml.Node) error {
105+
switch node.Tag {
106+
case "!!str":
107+
// If the node is just a string (e.g. "postgres:16"), parse it.
108+
// You can define your own logic here: how to set Name vs. Image, etc.
109+
// For instance, if you want Name = the first part, and Image = the entire string:
110+
value := node.Value
111+
parts := strings.SplitN(value, ":", 2)
112+
if len(parts) > 1 {
113+
d.Name = parts[0]
114+
d.Image = value // e.g. "postgres:16"
115+
} else {
116+
// If there's no colon, define your fallback:
117+
// e.g. "postgres" => use "postgres" for both Name and Image
118+
d.Name = value
119+
d.Image = value
120+
}
121+
return nil
122+
123+
case "!!map":
124+
// If the node is a map, just decode into the struct in the usual way.
125+
type dependencyAlias Dependency
126+
var tmp dependencyAlias
127+
if err := node.Decode(&tmp); err != nil {
128+
return fmt.Errorf("failed to decode dependency map: %w", err)
129+
}
130+
*d = Dependency(tmp)
131+
return nil
132+
133+
default:
134+
// If there's some other type, return an error or handle as needed
135+
return fmt.Errorf("unsupported YAML type for Dependency: %s", node.Tag)
136+
}
137+
}
138+
103139
type Volume struct {
104140
Name string `yaml:"name" validate:"required"`
105141
Path string `yaml:"path" validate:"required,unix_path"`

pkg/config/config_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,3 +257,87 @@ services:
257257
assert.Error(suite.T(), err)
258258
assert.Nil(suite.T(), config)
259259
}
260+
261+
func (suite *ConfigTestSuite) TestParseConfig_StringDependency() {
262+
yamlData := []byte(`
263+
project:
264+
name: "string-dep-project"
265+
domain: "example.com"
266+
email: "test@example.com"
267+
server:
268+
host: "example.com"
269+
port: 22
270+
user: "user"
271+
ssh_key: "~/.ssh/id_rsa"
272+
services:
273+
- name: "web"
274+
image: "nginx:latest"
275+
port: 80
276+
routes:
277+
- path: "/"
278+
dependencies:
279+
- "postgres:16"
280+
- "redis"
281+
`)
282+
283+
config, err := ParseConfig(yamlData)
284+
assert.NoError(suite.T(), err)
285+
assert.NotNil(suite.T(), config)
286+
287+
// We expect 2 dependencies
288+
assert.Len(suite.T(), config.Dependencies, 2)
289+
290+
// Dependency #1: "postgres:16"
291+
assert.Equal(suite.T(), "postgres", config.Dependencies[0].Name)
292+
assert.Equal(suite.T(), "postgres:16", config.Dependencies[0].Image)
293+
294+
// Dependency #2: "redis"
295+
// No colon, so we fall back to same name and image
296+
assert.Equal(suite.T(), "redis", config.Dependencies[1].Name)
297+
assert.Equal(suite.T(), "redis", config.Dependencies[1].Image)
298+
}
299+
300+
func (suite *ConfigTestSuite) TestParseConfig_MixedDependencies() {
301+
yamlData := []byte(`
302+
project:
303+
name: "mixed-deps-project"
304+
domain: "example.com"
305+
email: "test@example.com"
306+
server:
307+
host: "example.com"
308+
port: 22
309+
user: "user"
310+
ssh_key: "~/.ssh/id_rsa"
311+
services:
312+
- name: "web"
313+
image: "nginx:latest"
314+
port: 80
315+
routes:
316+
- path: "/"
317+
dependencies:
318+
- "mysql:8"
319+
- name: "redis"
320+
image: "redis:6"
321+
- "elasticsearch"
322+
`)
323+
324+
config, err := ParseConfig(yamlData)
325+
assert.NoError(suite.T(), err)
326+
assert.NotNil(suite.T(), config)
327+
328+
// We expect 3 dependencies
329+
assert.Len(suite.T(), config.Dependencies, 3)
330+
331+
// Dependency #1: "mysql:8"
332+
assert.Equal(suite.T(), "mysql", config.Dependencies[0].Name)
333+
assert.Equal(suite.T(), "mysql:8", config.Dependencies[0].Image)
334+
335+
// Dependency #2: normal map-based dependency
336+
assert.Equal(suite.T(), "redis", config.Dependencies[1].Name)
337+
assert.Equal(suite.T(), "redis:6", config.Dependencies[1].Image)
338+
339+
// Dependency #3: "elasticsearch" (no colon)
340+
// We expect same name / image
341+
assert.Equal(suite.T(), "elasticsearch", config.Dependencies[2].Name)
342+
assert.Equal(suite.T(), "elasticsearch", config.Dependencies[2].Image)
343+
}

0 commit comments

Comments
 (0)