feat(manifest): add .core/view.yml types and parser
Manifest struct, Permissions, Parse() from YAML, SlotNames() helper. Foundation for Phase 4 module system. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
bac77fb2c4
commit
5e9f61acf4
2 changed files with 115 additions and 0 deletions
50
pkg/manifest/manifest.go
Normal file
50
pkg/manifest/manifest.go
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
package manifest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Manifest represents a .core/view.yml application manifest.
|
||||||
|
type Manifest struct {
|
||||||
|
Code string `yaml:"code"`
|
||||||
|
Name string `yaml:"name"`
|
||||||
|
Version string `yaml:"version"`
|
||||||
|
Sign string `yaml:"sign"`
|
||||||
|
Layout string `yaml:"layout"`
|
||||||
|
Slots map[string]string `yaml:"slots"`
|
||||||
|
|
||||||
|
Permissions Permissions `yaml:"permissions"`
|
||||||
|
Modules []string `yaml:"modules"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Permissions declares the I/O capabilities a module requires.
|
||||||
|
type Permissions struct {
|
||||||
|
Read []string `yaml:"read"`
|
||||||
|
Write []string `yaml:"write"`
|
||||||
|
Net []string `yaml:"net"`
|
||||||
|
Run []string `yaml:"run"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse decodes YAML bytes into a Manifest.
|
||||||
|
func Parse(data []byte) (*Manifest, error) {
|
||||||
|
var m Manifest
|
||||||
|
if err := yaml.Unmarshal(data, &m); err != nil {
|
||||||
|
return nil, fmt.Errorf("manifest.Parse: %w", err)
|
||||||
|
}
|
||||||
|
return &m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SlotNames returns a deduplicated list of component names from slots.
|
||||||
|
func (m *Manifest) SlotNames() []string {
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
var names []string
|
||||||
|
for _, name := range m.Slots {
|
||||||
|
if !seen[name] {
|
||||||
|
seen[name] = true
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return names
|
||||||
|
}
|
||||||
65
pkg/manifest/manifest_test.go
Normal file
65
pkg/manifest/manifest_test.go
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
package manifest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParse_Good(t *testing.T) {
|
||||||
|
raw := `
|
||||||
|
code: photo-browser
|
||||||
|
name: Photo Browser
|
||||||
|
version: 0.1.0
|
||||||
|
sign: dGVzdHNpZw==
|
||||||
|
|
||||||
|
layout: HLCRF
|
||||||
|
slots:
|
||||||
|
H: nav-breadcrumb
|
||||||
|
L: folder-tree
|
||||||
|
C: photo-grid
|
||||||
|
R: metadata-panel
|
||||||
|
F: status-bar
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
read: ["./photos/"]
|
||||||
|
write: []
|
||||||
|
net: []
|
||||||
|
run: []
|
||||||
|
|
||||||
|
modules:
|
||||||
|
- core/media
|
||||||
|
- core/fs
|
||||||
|
`
|
||||||
|
m, err := Parse([]byte(raw))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "photo-browser", m.Code)
|
||||||
|
assert.Equal(t, "Photo Browser", m.Name)
|
||||||
|
assert.Equal(t, "0.1.0", m.Version)
|
||||||
|
assert.Equal(t, "dGVzdHNpZw==", m.Sign)
|
||||||
|
assert.Equal(t, "HLCRF", m.Layout)
|
||||||
|
assert.Equal(t, "nav-breadcrumb", m.Slots["H"])
|
||||||
|
assert.Equal(t, "photo-grid", m.Slots["C"])
|
||||||
|
assert.Len(t, m.Permissions.Read, 1)
|
||||||
|
assert.Equal(t, "./photos/", m.Permissions.Read[0])
|
||||||
|
assert.Len(t, m.Modules, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParse_Bad(t *testing.T) {
|
||||||
|
_, err := Parse([]byte("not: valid: yaml: ["))
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManifest_SlotNames_Good(t *testing.T) {
|
||||||
|
m := Manifest{
|
||||||
|
Slots: map[string]string{
|
||||||
|
"H": "nav-bar",
|
||||||
|
"C": "main-content",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
names := m.SlotNames()
|
||||||
|
assert.Contains(t, names, "nav-bar")
|
||||||
|
assert.Contains(t, names, "main-content")
|
||||||
|
assert.Len(t, names, 2)
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue