1package spec
2
3import (
4 "encoding/xml"
5 "fmt"
6)
7
8// Protocol is a parsed Wayland protocol XML document.
9type Protocol struct {
10 XMLName xml.Name `xml:"protocol"`
11 Name string `xml:"name,attr"`
12 Copyright string `xml:"copyright"`
13 Description *Description `xml:"description"`
14 Interfaces []Interface `xml:"interface"`
15}
16
17// Interface is a versioned Wayland interface.
18type Interface struct {
19 Name string `xml:"name,attr"`
20 Version int `xml:"version,attr"`
21 Description *Description `xml:"description"`
22 Requests []Message `xml:"request"`
23 Events []Message `xml:"event"`
24 Enums []Enum `xml:"enum"`
25}
26
27// Message is a request (client to server) or an event (server to client).
28type Message struct {
29 Name string `xml:"name,attr"`
30 Type string `xml:"type,attr"` // "destructor" or empty
31 Since int `xml:"since,attr"`
32 Description *Description `xml:"description"`
33 Args []Arg `xml:"arg"`
34}
35
36// Arg is a message argument.
37type Arg struct {
38 Name string `xml:"name,attr"`
39 Type string `xml:"type,attr"`
40 Summary string `xml:"summary,attr"`
41 Interface string `xml:"interface,attr"`
42 AllowNull bool `xml:"allow-null,attr"`
43 Enum string `xml:"enum,attr"`
44}
45
46// Enum is an enumeration or bitfield.
47type Enum struct {
48 Name string `xml:"name,attr"`
49 Since int `xml:"since,attr"`
50 Bitfield bool `xml:"bitfield,attr"`
51 Description *Description `xml:"description"`
52 Entries []Entry `xml:"entry"`
53}
54
55// Entry is an enum value.
56type Entry struct {
57 Name string `xml:"name,attr"`
58 Value string `xml:"value,attr"`
59 Summary string `xml:"summary,attr"`
60 Since int `xml:"since,attr"`
61}
62
63// Description is protocol documentation.
64type Description struct {
65 Summary string `xml:"summary,attr"`
66 Text string `xml:",chardata"`
67}
68
69// Parse decodes a protocol XML document.
70func Parse(data []byte) (*Protocol, error) {
71 var p Protocol
72 if err := xml.Unmarshal(data, &p); err != nil {
73 return nil, fmt.Errorf("parse protocol: %w", err)
74 }
75 if p.Name == "" {
76 return nil, fmt.Errorf("parse protocol: missing protocol name")
77 }
78 return &p, nil
79}
80
81// ReferencedInterfaces returns the interfaces named by arguments of p, in
82// first-seen order, without duplicates.
83func (p *Protocol) ReferencedInterfaces() []string {
84 seen := map[string]bool{}
85 var out []string
86 add := func(ms []Message) {
87 for _, m := range ms {
88 for _, a := range m.Args {
89 if a.Interface != "" && !seen[a.Interface] {
90 seen[a.Interface] = true
91 out = append(out, a.Interface)
92 }
93 }
94 }
95 }
96 for _, it := range p.Interfaces {
97 add(it.Requests)
98 add(it.Events)
99 }
100 return out
101}