@@ -33,6 +33,40 @@ type definition struct {
33
33
JSON []byte
34
34
}
35
35
36
+ // definitions that are safe to ignore when a docstring is missing.
37
+ func ignoreWithoutDocstring (buf []byte ) bool {
38
+ switch {
39
+ case bytes .HasPrefix (buf , []byte ("{ 'pragma'" )):
40
+ return true
41
+ case bytes .HasPrefix (buf , []byte ("{ 'include'" )):
42
+ return true
43
+ }
44
+
45
+ return false
46
+ }
47
+
48
+ func importDefinitions (path string , jsonBuf []byte ) ([]definition , error ) {
49
+ v := struct {
50
+ I string `json:"include"`
51
+ }{}
52
+
53
+ if err := json .Unmarshal (jsonBuf , & v ); err != nil {
54
+ return nil , fmt .Errorf ("failed to unmarshal include %q json: %s" , path , err )
55
+ }
56
+
57
+ incPath , err := resolvePath (path , v .I )
58
+ if err != nil {
59
+ return nil , fmt .Errorf ("failed to resolve include %q relative to %q: %s" , v .I , path , err )
60
+ }
61
+
62
+ subdefs , err := readDefinitions (incPath )
63
+ if err != nil {
64
+ return nil , fmt .Errorf ("failed to parse included file %q: %s" , incPath , err )
65
+ }
66
+
67
+ return subdefs , nil
68
+ }
69
+
36
70
// readDefinitions reads the definitions from a QAPI spec file.
37
71
//
38
72
// Includes are processed, so the returned definitions is the full API
@@ -51,32 +85,49 @@ func readDefinitions(path string) ([]definition, error) {
51
85
// input so that we can process it with less gymnastics.
52
86
bs = bytes .Replace (bs , []byte ("}\n ##" ), []byte ("}\n \n ##" ), - 1 )
53
87
88
+ // As mentioned directly above, most of the spec includes two newlines
89
+ // between definitions. This normalizes stacked include statements.
90
+ bs = bytes .Replace (bs , []byte ("}\n { 'include'" ), []byte ("}\n \n { 'include'" ), - 1 )
91
+
54
92
for _ , part := range bytes .Split (bs , []byte ("\n \n " )) {
93
+ if len (part ) < 1 {
94
+ continue
95
+ }
96
+
55
97
fs := bytes .SplitN (part , []byte ("\n {" ), 2 )
56
98
switch len (fs ) {
57
99
default :
58
100
return nil , fmt .Errorf ("unexpected part of spec file %q: %s" , path , string (part ))
59
101
case 1 :
60
- if len (fs ) == 1 && part [0 ] == '{' && ! bytes . HasPrefix (part , [] byte ( "{ 'pragma'" ) ) {
102
+ if len (fs ) == 1 && part [0 ] == '{' && ! ignoreWithoutDocstring (part ) {
61
103
return nil , fmt .Errorf ("found type definition without a docstring in %q: %s" , path , string (part ))
62
104
}
105
+
106
+ // handle 'include' when no docstring is present
107
+ if bytes .HasPrefix (fs [0 ], []byte ("{ 'include'" )) {
108
+ js := pyToJSON (fs [0 ])
109
+
110
+ subdefs , err := importDefinitions (path , js )
111
+ if err != nil {
112
+ return nil , err
113
+
114
+ }
115
+
116
+ ret = append (ret , subdefs ... )
117
+ }
118
+
63
119
// This part looks like a non-docstring comment, just skip it.
64
120
case 2 :
65
121
docstring := string (fs [0 ])
66
122
js := pyToJSON (append ([]byte {'{' }, fs [1 ]... ))
67
123
68
- v := struct {
69
- I string `json:"include"`
70
- }{}
71
- if err = json .Unmarshal (js , & v ); err == nil && v .I != "" {
72
- incPath , err := resolvePath (path , v .I )
73
- if err != nil {
74
- return nil , fmt .Errorf ("failed to resolve include %q relative to %q: %s" , v .I , path , err )
75
- }
76
- subdefs , err := readDefinitions (incPath )
124
+ if bytes .HasPrefix (fs [0 ], []byte ("{ 'include'" )) {
125
+ subdefs , err := importDefinitions (path , js )
77
126
if err != nil {
78
- return nil , fmt .Errorf ("failed to parse included file %q: %s" , incPath , err )
127
+ return nil , err
128
+
79
129
}
130
+
80
131
ret = append (ret , subdefs ... )
81
132
} else {
82
133
ret = append (ret , definition {docstring , js })
@@ -424,6 +475,13 @@ func (n name) SimpleType() bool {
424
475
return ok
425
476
}
426
477
478
+ func (n name ) NullType () bool {
479
+ if n .SimpleType () {
480
+ return false
481
+ }
482
+ return strings .EqualFold (string (n ), "Null" )
483
+ }
484
+
427
485
func (n name ) InterfaceType (api map [name ]interface {}) bool {
428
486
if n .SimpleType () {
429
487
return false
@@ -460,6 +518,7 @@ var upperWords = map[string]bool{
460
518
"fd" : true ,
461
519
"ftp" : true ,
462
520
"ftps" : true ,
521
+ "guid" : true ,
463
522
"http" : true ,
464
523
"https" : true ,
465
524
"id" : true ,
@@ -476,11 +535,13 @@ var upperWords = map[string]bool{
476
535
"qmp" : true ,
477
536
"ram" : true ,
478
537
"sparc" : true ,
538
+ "ssh" : true ,
479
539
"tcp" : true ,
480
540
"tls" : true ,
481
541
"tpm" : true ,
482
542
"ttl" : true ,
483
543
"udp" : true ,
544
+ "uri" : true ,
484
545
"uuid" : true ,
485
546
"vm" : true ,
486
547
"vmdk" : true ,
@@ -527,6 +588,18 @@ func pyToJSON(py []byte) []byte {
527
588
return ret
528
589
}
529
590
591
+ func tryGetVersionFromSpecPath (specPath string ) string {
592
+ retVersion := "UNKNOWN"
593
+ verPath , err := resolvePath (specPath , "VERSION" )
594
+ if err == nil {
595
+ verBuf , err := getQAPI (verPath )
596
+ if err == nil {
597
+ return string (verBuf )
598
+ }
599
+ }
600
+ return retVersion
601
+ }
602
+
530
603
func resolvePath (orig , new string ) (string , error ) {
531
604
u , err := url .Parse (orig )
532
605
if err != nil {
0 commit comments