1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;;; Confidential and proprietary information of ITA Software, Inc. ;;;
5 ;;; Copyright (c) 2012 ITA Software, Inc. All rights reserved. ;;;
7 ;;; Original author: Scott McKay ;;;
9 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
11 (in-package "PROTO-IMPL")
14 ;;; Can a version of a Protobufs schema be upgraded to a new version
16 ;;--- This should return (a set of) reason(s) if the upgrade will fail
17 (defgeneric protobuf-upgradable (new old)
19 "Returns true if and only if the old protobuf schema (enum, message, etc)
20 can be upgraded to the new schema."))
22 (defmethod protobuf-upgradable ((old protobuf) (new protobuf))
24 ;; Are they named the same?
25 (string= (proto-name old) (proto-name new))
26 (string= (proto-package old) (proto-package new))
27 ;; Is every enum in 'old' upgradable to an enum in 'new'?
28 (loop for old-enum in (proto-enums old)
29 as new-enum = (find (proto-name old-enum) (proto-enums new)
30 :key #'proto-name :test #'string=)
31 always (and new-enum (protobuf-upgradable old-enum new-enum)))
32 ;; Is every message in 'old' upgradable to a message in 'new'?
33 (loop for old-msg in (proto-messages old)
34 as new-msg = (find (proto-name old-msg) (proto-messages new)
35 :key #'proto-name :test #'string=)
36 always (and new-msg (protobuf-upgradable old-msg new-msg)))
37 ;; Is every service in 'old' upgradable to a service in 'new'?
38 (loop for old-svc in (proto-services old)
39 as new-svc = (find (proto-name old-svc) (proto-services new)
40 :key #'proto-name :test #'string=)
41 always (and new-svc (protobuf-upgradable old-svc new-svc)))))
44 (defmethod protobuf-upgradable ((old protobuf-enum) (new protobuf-enum))
46 ;; Are they named the same?
47 (string= (proto-name old) (proto-name new))
48 ;; Is every value in 'old' upgradable to a value in 'new'?
49 (loop for old-val in (proto-values old)
50 as new-val = (find (proto-name old-val) (proto-values new)
51 :key #'proto-name :test #'string=)
52 always (and new-val (protobuf-upgradable old-val new-val)))))
54 (defmethod protobuf-upgradable ((old protobuf-enum-value) (new protobuf-enum-value))
56 ;; Are they named the same?
57 (string= (proto-name old) (proto-name new))
58 ;; Do they have the same index?
59 (= (proto-index old) (proto-index new))))
62 (defmethod protobuf-upgradable ((old protobuf-message) (new protobuf-message))
64 ;; Are they named the same?
65 (string= (proto-name old) (proto-name new))
66 ;; Is every enum in 'old' upgradable to an enum in 'new'?
67 (loop for old-enum in (proto-enums old)
68 as new-enum = (find (proto-name old-enum) (proto-enums new)
69 :key #'proto-name :test #'string=)
70 always (and new-enum (protobuf-upgradable old-enum new-enum)))
71 ;; Is every message in 'old' upgradable to a message in 'new'?
72 (loop for old-msg in (proto-messages old)
73 as new-msg = (find (proto-name old-msg) (proto-messages new)
74 :key #'proto-name :test #'string=)
75 always (and new-msg (protobuf-upgradable old-msg new-msg)))
76 ;; Is every required field in 'old' upgradable to a field in 'new'?
77 ;; (Optional fields are safe to remove)
78 (loop for old-fld in (proto-fields old)
79 as new-fld = (find (proto-name old-fld) (proto-fields new)
80 :key #'proto-name :test #'string=)
82 (protobuf-upgradable old-fld new-fld)
83 (not (eq (proto-required old) :required))))))
85 (defmethod protobuf-upgradable ((old protobuf-field) (new protobuf-field))
86 (flet ((arity-upgradable (old new)
87 ;;--- Handle conversions between non-required fields and extensions
89 (not (eq new :required))))
90 (type-upgradable (old new)
91 ;;--- Handle conversions between embedded messages and bytes
94 ;; These varint types are all compatible
95 (and (member old '("int32" "uint32" "int64" "uint64" "bool") :test #'string=)
96 (member new '("int32" "uint32" "int64" "uint64" "bool") :test #'string=))
97 ;; The two signed integer types are compatible
98 (and (member old '("sint32" "sint64") :test #'string=)
99 (member new '("sint32" "sint64") :test #'string=))
100 ;; Fixed integers are compatible with each other
101 (and (member old '("fixed32" "sfixed32") :test #'string=)
102 (member new '("fixed32" "sfixed32") :test #'string=))
103 (and (member old '("fixed64" "sfixed64") :test #'string=)
104 (member new '("fixed64" "sfixed64") :test #'string=))
105 ;; Strings and bytes are compatible, assuming UTF-8 encoding
106 (and (member old '("string" "bytes") :test #'string=)
107 (member new '("string" "bytes") :test #'string=))))
108 (default-upgradable (old new)
109 (declare (ignore old new))
112 ;; Are they named the same?
113 (string= (proto-name old) (proto-name new))
114 ;; Do they have the same index?
115 (= (proto-index old) (proto-index new))
116 ;; Is the type and arity upgradable
117 (arity-upgradable (proto-required old) (proto-required new))
118 (type-upgradable (proto-type old) (proto-type new))
119 (arity-upgradable (proto-required old) (proto-required new)))))
122 (defmethod protobuf-upgradable ((old protobuf-service) (new protobuf-service))
124 ;; Are they named the same?
125 (string= (proto-name old) (proto-name new))
126 ;; Is every RPC in 'old' upgradable to an RPC in 'new'?
127 (loop for old-rpc in (proto-rpcs old)
128 as new-rpc = (find (proto-name old-rpc) (proto-rpcs new)
129 :key #'proto-name :test #'string=)
130 always (and new-rpc (protobuf-upgradable old-rpc new-rpc)))))
132 (defmethod protobuf-upgradable ((old protobuf-rpc) (new protobuf-rpc))
134 ;; Are they named the same?
135 (string= (proto-name old) (proto-name new))
136 ;; Are their inputs and outputs the same
137 (string= (proto-input-type old) (proto-input-type new))
138 (string= (proto-output-type old) (proto-output-type new))))