1package parser
2
3import (
4 "encoding/json"
5 "fmt"
6 "os"
7
8 "github.com/bnema/purego-pipewire/cmd/pipewiregen/internal/model"
9)
10
11// Load reads and parses a binding model JSON file.
12func Load(path string) (*model.Model, error) {
13 b, err := os.ReadFile(path)
14 if err != nil {
15 return nil, fmt.Errorf("failed to read file %s: %w", path, err)
16 }
17 var m model.Model
18 if err := json.Unmarshal(b, &m); err != nil {
19 return nil, fmt.Errorf("failed to unmarshal JSON from %s: %w", path, err)
20 }
21 if err := validate(&m); err != nil {
22 return nil, fmt.Errorf("validation failed: %w", err)
23 }
24 return &m, nil
25}
26
27func validate(m *model.Model) error {
28 // Build set of valid library names
29 libNames := make(map[string]bool)
30 for i, lib := range m.Libraries {
31 if lib.Name == "" {
32 return fmt.Errorf("library at index %d has empty name", i)
33 }
34 if lib.SOName == "" {
35 return fmt.Errorf("library %q has empty soname", lib.Name)
36 }
37 libNames[lib.Name] = true
38 }
39
40 // Build set of valid group names
41 groupNames := make(map[string]bool)
42 for i, group := range m.Groups {
43 if group.Name == "" {
44 return fmt.Errorf("group at index %d has empty name", i)
45 }
46 if group.Interface == "" {
47 return fmt.Errorf("group %q has empty interface", group.Name)
48 }
49 groupNames[group.Name] = true
50 }
51
52 // Validate symbols
53 for i, sym := range m.Symbols {
54 if sym.Name == "" {
55 return fmt.Errorf("symbol at index %d has empty name", i)
56 }
57 if sym.Library == "" {
58 return fmt.Errorf("symbol %q has empty library", sym.Name)
59 }
60 if sym.Signature == "" {
61 return fmt.Errorf("symbol %q has empty signature", sym.Name)
62 }
63 if !libNames[sym.Library] {
64 return fmt.Errorf("symbol %q references unknown library %q", sym.Name, sym.Library)
65 }
66 if sym.Group == "" {
67 return fmt.Errorf("symbol %q has empty group", sym.Name)
68 }
69 if !groupNames[sym.Group] {
70 return fmt.Errorf("symbol %q references unknown group %q", sym.Name, sym.Group)
71 }
72 }
73
74 // Validate callbacks
75 for i, cb := range m.Callbacks {
76 if cb.Name == "" {
77 return fmt.Errorf("callback at index %d has empty name", i)
78 }
79 if cb.Signature == "" {
80 return fmt.Errorf("callback %q has empty signature", cb.Name)
81 }
82 if cb.Group == "" {
83 return fmt.Errorf("callback %q has empty group", cb.Name)
84 }
85 if !groupNames[cb.Group] {
86 return fmt.Errorf("callback %q references unknown group %q", cb.Name, cb.Group)
87 }
88 }
89
90 // Validate event structs
91 for i, es := range m.EventStructs {
92 if es.Name == "" {
93 return fmt.Errorf("event struct at index %d has empty name", i)
94 }
95 if es.Group == "" {
96 return fmt.Errorf("event struct %q has empty group", es.Name)
97 }
98 if !groupNames[es.Group] {
99 return fmt.Errorf("event struct %q references unknown group %q", es.Name, es.Group)
100 }
101 }
102
103 return nil
104}