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 ;;; Protobufs schema pretty printing
16 (defun write-schema (protobuf &rest keys
17 &key (stream *standard-output*) (type :proto) &allow-other-keys)
18 "Writes the object 'protobuf' (schema, message, enum, etc) onto the
19 stream 'stream'in the format given by 'type' (:proto, :text, etc)."
20 (let ((*protobuf* protobuf))
21 (apply #'write-schema-as type protobuf stream keys)))
23 (defgeneric write-schema-as (type protobuf stream &key indentation &allow-other-keys)
25 "Writes the protobuf object 'protobuf' (schema, message, enum, etc) onto
26 the given stream 'stream' in the format given by 'type' (:proto, :text, etc).
27 If 'more' is true, this means there are more enum values, fields, etc to
28 be written after the current one."))
30 (defgeneric write-schema-header (type schema stream)
32 "Writes a header for the schema onto the given stream 'stream'
33 in the format given by 'type' (:proto, :text, etc)."))
35 (defgeneric write-schema-documentation (type docstring stream &key indentation)
37 "Writes the docstring as a \"block comment\" onto the given stream 'stream'
38 in the format given by 'type' (:proto, :text, etc)."))
41 ;;; Pretty print a schema as a .proto file
43 (defmethod write-schema-as ((type (eql :proto)) (schema protobuf-schema) stream
45 (with-prefixed-accessors (name documentation syntax package imports options) (proto- schema)
47 (write-schema-documentation type documentation stream :indentation indentation))
49 (format stream "~&syntax = \"~A\";~%~%" syntax))
51 (format stream "~&package ~A;~%~%" (substitute #\_ #\- package)))
53 (dolist (import imports)
54 (format stream "~&import \"~A\";~%" import))
56 (write-schema-header type schema stream)
58 (dolist (option options)
59 (format stream "~&option ~:/protobuf-option/;~%" option))
61 (loop for (enum . more) on (proto-enums schema) doing
62 (write-schema-as type enum stream :indentation indentation :more more)
64 (loop for (msg . more) on (proto-messages schema) doing
65 (write-schema-as type msg stream :indentation indentation :more more)
67 (loop for (svc . more) on (proto-services schema) doing
68 (write-schema-as type svc stream :indentation indentation :more more)
71 (defmethod write-schema-documentation ((type (eql :proto)) docstring stream
73 (let ((lines (split-string docstring :separators '(#\newline #\return))))
75 (format stream "~&~@[~VT~]// ~A~%"
76 (and (not (zerop indentation)) indentation) line))))
78 ;; Lisp was born in 1958 :-)
79 (defvar *lisp-options* '(("lisp_package" string 195801)
80 ("lisp_name" string 195802)
81 ("lisp_alias" string 195803)
82 ("lisp_type" string 195804)
83 ("lisp_class" string 195805)
84 ("lisp_slot" string 195806)))
86 (defvar *option-types* '(("optimize_for" symbol)
88 ;; Keep the rest of these in alphabetical order
89 ("cc_api_version" integer)
90 ("cc_generic_services" symbol)
93 ("java_api_version" integer)
94 ("java_generic_services" symbol)
95 ("java_java5_enums" boolean)
96 ("java_multiple_files" boolean)
97 ("java_outer_classname" string)
98 ("java_package" string)
99 ("java_use_javaproto2" boolean)
100 ("py_api_version" integer)
101 ("py_generic_services" symbol)))
103 (defmethod write-schema-header ((type (eql :proto)) (schema protobuf-schema) stream)
104 (when (any-lisp-option schema)
105 (format stream "~&import \"net/proto2/proto/descriptor.proto\";~%~%")
106 (format stream "~&extend proto2.MessageOptions {~%")
107 (loop for (option type index) in *lisp-options* doing
108 (format stream "~& optional ~(~A~) ~A = ~D;~%" type option index))
109 (format stream "~&}~%~%")))
111 (defgeneric any-lisp-option (schema)
113 "Returns true iff there is anything in the schema that would require that
114 the .proto file include and extend 'MessageOptions'.")
115 (:method ((schema protobuf-schema))
116 (labels ((find-one (protobuf)
117 (dolist (enum (proto-enums protobuf))
118 (with-prefixed-accessors (name class alias-for) (proto- enum)
120 (and class (not (string-equal name (class-name->proto class))) class))
121 (return-from any-lisp-option t))))
122 (dolist (msg (proto-messages protobuf))
123 (with-prefixed-accessors (name class alias-for) (proto- msg)
125 (and class (not (string-equal name (class-name->proto class))) class))
126 (return-from any-lisp-option t))))
127 (map () #'find-one (proto-messages protobuf))))
131 (defun cl-user::protobuf-option (stream option colon-p atsign-p)
132 (let ((type (or (second (find (proto-name option) *option-types* :key #'first :test #'string=))
133 (proto-type option))))
134 (cond (colon-p ;~:/protobuf-option/ -- .proto format
136 (cond ((find (proto-name option) *lisp-options* :key #'first :test #'string=)
137 (if (eq type 'symbol) "(~A)~@[ = ~A~]" "(~A)~@[ = ~S~]"))
139 (if (eq type 'symbol) "~A~@[ = ~A~]" "~A~@[ = ~S~]")))))
140 (format stream fmt-control (proto-name option) (proto-value option))))
141 (atsign-p ;~@/protobuf-option/ -- .lisp format
142 (let ((fmt-control (if (eq type 'symbol) "~(~S~) ~A" "~(~S~) ~S")))
143 (format stream fmt-control (proto-name option) (proto-value option))))
144 (t ;~/protobuf-option/ -- keyword/value format
145 (let ((fmt-control (if (eq type 'symbol) "~(:~A~) ~A" "~(:~A~) ~S")))
146 (format stream fmt-control (proto-name option) (proto-value option)))))))
148 (defmethod write-schema-as ((type (eql :proto)) (enum protobuf-enum) stream
149 &key (indentation 0) more)
150 (declare (ignore more))
151 (with-prefixed-accessors (name class alias-for documentation options) (proto- enum)
153 (write-schema-documentation type documentation stream :indentation indentation))
154 (format stream "~&~@[~VT~]enum ~A {~%"
155 (and (not (zerop indentation)) indentation) name)
156 (let ((other (and class (not (string-equal name (class-name->proto class))) class)))
158 (format stream "~&~VToption (lisp_name) = \"~A:~A\";~%"
159 (+ indentation 2) (package-name (symbol-package class)) (symbol-name class))))
161 (format stream "~&~VToption (lisp_alias) = \"~A:~A\";~%"
162 (+ indentation 2) (package-name (symbol-package alias-for)) (symbol-name alias-for)))
163 (dolist (option options)
164 (format stream "~&option ~:/protobuf-option/;~%" option))
165 (loop for (value . more) on (proto-values enum) doing
166 (write-schema-as type value stream :indentation (+ indentation 2) :more more))
167 (format stream "~&~@[~VT~]}~%"
168 (and (not (zerop indentation)) indentation))))
170 (defparameter *protobuf-enum-comment-column* 56)
171 (defmethod write-schema-as ((type (eql :proto)) (val protobuf-enum-value) stream
172 &key (indentation 0) more)
173 (declare (ignore more))
174 (with-prefixed-accessors (name documentation index) (proto- val)
175 (format stream "~&~@[~VT~]~A = ~D;~:[~*~*~;~VT// ~A~]~%"
176 (and (not (zerop indentation)) indentation) name index
177 documentation *protobuf-enum-comment-column* documentation)))
180 (defmethod write-schema-as ((type (eql :proto)) (message protobuf-message) stream
181 &key (indentation 0) more index arity)
182 (declare (ignore more arity))
183 (with-prefixed-accessors (name class alias-for message-type documentation options) (proto- message)
184 (cond ((eq message-type :group)
185 ;; If we've got a group, the printer for fields has already
186 ;; printed a partial line (nice modularity, huh?)
187 (format stream "group ~A = ~D {~%" name index)
188 (let ((other (and class (not (string-equal name (class-name->proto class))) class)))
190 (format stream "~&~VToption (lisp_name) = \"~A:~A\";~%"
191 (+ indentation 2) (package-name (symbol-package class)) (symbol-name class))))
193 (format stream "~&~VToption (lisp_alias) = \"~A:~A\";~%"
194 (+ indentation 2) (package-name (symbol-package alias-for)) (symbol-name alias-for)))
195 (dolist (option options)
196 (format stream "~&~VToption ~:/protobuf-option/;~%"
197 (+ indentation 2) option))
198 (loop for (enum . more) on (proto-enums message) doing
199 (write-schema-as type enum stream :indentation (+ indentation 2) :more more))
200 (loop for (field . more) on (proto-fields message) doing
201 (write-schema-as type field stream
202 :indentation (+ indentation 2) :more more :message message))
203 (format stream "~&~@[~VT~]}~%"
204 (and (not (zerop indentation)) indentation)))
207 (write-schema-documentation type documentation stream :indentation indentation))
208 (format stream "~&~@[~VT~]~A ~A {~%"
209 (and (not (zerop indentation)) indentation)
210 (if (eq message-type :message) "message" "extend") name)
211 (let ((other (and class (not (string-equal name (class-name->proto class))) class)))
213 (format stream "~&~VToption (lisp_name) = \"~A:~A\";~%"
214 (+ indentation 2) (package-name (symbol-package class)) (symbol-name class))))
216 (format stream "~&~VToption (lisp_alias) = \"~A:~A\";~%"
217 (+ indentation 2) (package-name (symbol-package alias-for)) (symbol-name alias-for)))
218 (dolist (option options)
219 (format stream "~&~VToption ~:/protobuf-option/;~%"
220 (+ indentation 2) option))
221 (cond ((eq message-type :extends)
222 (loop for (field . more) on (proto-extended-fields message) doing
223 (write-schema-as type field stream
224 :indentation (+ indentation 2) :more more
227 (loop for (enum . more) on (proto-enums message) doing
228 (write-schema-as type enum stream :indentation (+ indentation 2) :more more))
229 (loop for (msg . more) on (proto-messages message) doing
230 (unless (eq (proto-message-type msg) :group)
231 (write-schema-as type msg stream :indentation (+ indentation 2) :more more)))
232 (loop for (field . more) on (proto-fields message) doing
233 (write-schema-as type field stream
234 :indentation (+ indentation 2) :more more
236 (loop for (extension . more) on (proto-extensions message) doing
237 (write-schema-as type extension stream :indentation (+ indentation 2) :more more))))
238 (format stream "~&~@[~VT~]}~%"
239 (and (not (zerop indentation)) indentation))))))
241 (defparameter *protobuf-field-comment-column* 56)
242 (defmethod write-schema-as ((type (eql :proto)) (field protobuf-field) stream
243 &key (indentation 0) more message)
244 (declare (ignore more))
245 (with-prefixed-accessors (name documentation required type index packed options) (proto- field)
246 (let* ((class (if (eq (proto-class field) 'boolean) :bool (proto-class field)))
247 (msg (and (not (keywordp class))
248 (or (find-message message class) (find-enum message class)))))
249 (cond ((and (typep msg 'protobuf-message)
250 (eq (proto-message-type msg) :group))
251 (format stream "~&~@[~VT~]~(~A~) "
252 (and (not (zerop indentation)) indentation) required)
253 (write-schema-as :proto msg stream :indentation indentation :index index :arity required))
255 (let* ((defaultp (if (proto-alias-for message)
256 ;; Special handling for imported CLOS classes
257 (if (eq (proto-required field) :optional)
259 (and (proto-default field)
260 (not (equalp (proto-default field) #()))
261 (not (empty-default-p field))))
262 (not (empty-default-p field))))
263 (default (proto-default field))
264 (default (and defaultp
265 (cond ((and (typep msg 'protobuf-enum)
266 (or (stringp default) (symbolp default)))
267 (let ((e (find default (proto-values msg)
268 :key #'proto-name :test #'string=)))
269 (and e (proto-name e))))
271 (if (boolean-true-p default) "true" "false"))
273 (default (and defaultp
274 (if (stringp default) (escape-string default) default))))
275 (format stream (if (and (keywordp class) (not (eq class :bool)))
276 ;; Keyword class means a primitive type, print default with ~S
277 "~&~@[~VT~]~(~A~) ~A ~A = ~D~
278 ~:[~*~; [default = ~S]~]~@[ [packed = true]~*~]~{ [~:/protobuf-option/]~};~
279 ~:[~*~*~;~VT// ~A~]~%"
280 ;; Non-keyword class means an enum type, print default with ~A"
281 "~&~@[~VT~]~(~A~) ~A ~A = ~D~
282 ~:[~*~; [default = ~A]~]~@[ [packed = true]~*~]~{ [~:/protobuf-option/]~};~
283 ~:[~*~*~;~VT// ~A~]~%")
284 (and (not (zerop indentation)) indentation)
285 required type name index defaultp default packed options
286 documentation *protobuf-field-comment-column* documentation)))))))
288 (defun escape-string (string)
289 (if (every #'(lambda (ch) (and (standard-char-p ch) (graphic-char-p ch))) string)
291 (with-output-to-string (s)
292 (loop for ch across string
293 as esc = (escape-char ch)
294 do (format s "~A" esc)))))
296 (defmethod write-schema-as ((type (eql :proto)) (extension protobuf-extension) stream
297 &key (indentation 0) more)
298 (declare (ignore more))
299 (with-prefixed-accessors (from to) (proto-extension- extension)
300 (format stream "~&~@[~VT~]extensions ~D~:[~*~; to ~D~];~%"
301 (and (not (zerop indentation)) indentation)
302 from (not (eql from to)) (if (eql to #.(1- (ash 1 29))) "max" to))))
305 (defmethod write-schema-as ((type (eql :proto)) (service protobuf-service) stream
306 &key (indentation 0) more)
307 (declare (ignore more))
308 (with-prefixed-accessors (name documentation) (proto- service)
310 (write-schema-documentation type documentation stream :indentation indentation))
311 (format stream "~&~@[~VT~]service ~A {~%"
312 (and (not (zerop indentation)) indentation) name)
313 (loop for (method . more) on (proto-methods service) doing
314 (write-schema-as type method stream :indentation (+ indentation 2) :more more))
315 (format stream "~&~@[~VT~]}~%"
316 (and (not (zerop indentation)) indentation))))
318 (defmethod write-schema-as ((type (eql :proto)) (method protobuf-method) stream
319 &key (indentation 0) more)
320 (declare (ignore more))
321 (with-prefixed-accessors (name documentation input-name output-name options) (proto- method)
323 (write-schema-documentation type documentation stream :indentation indentation))
324 (format stream "~&~@[~VT~]rpc ~A (~@[~A~])~@[ returns (~A)~]"
325 (and (not (zerop indentation)) indentation)
326 name input-name output-name)
328 (format stream " {~%")
329 (dolist (option options)
330 (format stream "~&~@[~VT~]option ~:/protobuf-option/;~%"
331 (+ indentation 2) option))
332 (format stream "~@[~VT~]}"
333 (and (not (zerop indentation)) indentation)))
335 (format stream ";~%")))))
338 ;;; Pretty print a schema as a .lisp file
340 (defvar *show-lisp-enum-indexes* t)
341 (defvar *show-lisp-field-indexes* t)
343 (defmethod write-schema-as ((type (eql :lisp)) (schema protobuf-schema) stream
345 (show-field-indexes *show-lisp-field-indexes*)
346 (show-enum-indexes *show-lisp-enum-indexes*))
347 (with-prefixed-accessors (name class documentation package lisp-package imports) (proto- schema)
348 (let* ((optimize (let ((opt (find-option schema "optimize_for")))
349 (and opt (cond ((string= opt "SPEED") :speed)
350 ((string= opt "CODE_SIZE") :space)
352 (options (remove-if #'(lambda (x) (string= (proto-name x) "optimize_for"))
353 (proto-options schema)))
354 (pkg (and package (if (stringp package) package (string package))))
355 (lisp-pkg (and lisp-package (if (stringp lisp-package) lisp-package (string lisp-package))))
356 (*show-lisp-enum-indexes* show-enum-indexes)
357 (*show-lisp-field-indexes* show-field-indexes)
358 (*protobuf-package* (or (find-package lisp-pkg)
359 (find-package (string-upcase lisp-pkg))
361 (*package* *protobuf-package*))
362 (when (or lisp-pkg pkg)
363 (let ((pkg (string-upcase (or lisp-pkg pkg))))
364 (format stream "~&(eval-when (:execute :compile-toplevel :load-toplevel) ~
365 ~% (unless (find-package \"~A\") ~
366 ~% (defpackage ~A (:use :COMMON-LISP :PROTOBUFS)))) ~
367 ~%(in-package \"~A\")~%~%"
370 (write-schema-documentation type documentation stream :indentation indentation))
371 (format stream "~&(proto:define-schema ~(~A~)" (or class name))
372 (if (or pkg lisp-pkg imports optimize options documentation)
373 (format stream "~% (")
374 (format stream " ("))
377 (format stream "~A:package \"~A\"" spaces pkg)
378 (when (or lisp-pkg imports optimize options documentation)
382 (format stream "~A:lisp-package \"~A\"" spaces lisp-pkg)
383 (when (or imports optimize options documentation)
387 (cond ((= (length imports) 1)
388 (format stream "~A:import \"~A\"" spaces (car imports)))
390 (format stream "~A:import (~{\"~A\"~^ ~})" spaces imports)))
391 (when (or optimize options documentation)
395 (format stream "~A:optimize ~(~S~)" spaces optimize)
396 (when (or options documentation)
400 (format stream "~A:options (~{~@/protobuf-option/~^ ~})" spaces options)
405 (format stream "~A:documentation ~S" spaces documentation)))
407 (loop for (enum . more) on (proto-enums schema) doing
408 (write-schema-as type enum stream :indentation 2 :more more))
409 (loop for (msg . more) on (proto-messages schema) doing
410 (write-schema-as type msg stream :indentation 2 :more more))
411 (loop for (svc . more) on (proto-services schema) doing
412 (write-schema-as type svc stream :indentation 2 :more more)))
413 (format stream ")~%")))
415 (defmethod write-schema-documentation ((type (eql :lisp)) docstring stream
416 &key (indentation 0))
417 (let ((lines (split-string docstring :separators '(#\newline #\return))))
419 (format stream "~&~@[~VT~];; ~A~%"
420 (and (not (zerop indentation)) indentation) line))))
422 (defmethod write-schema-header ((type (eql :lisp)) (schema protobuf-schema) stream)
423 (declare (ignorable type stream))
426 (defmethod write-schema-as ((type (eql :lisp)) (enum protobuf-enum) stream
427 &key (indentation 0) more)
428 (declare (ignore more))
430 (with-prefixed-accessors (name class alias-for documentation) (proto- enum)
432 (write-schema-documentation type documentation stream :indentation indentation))
433 (format stream "~@[~VT~](proto:define-enum ~(~S~)"
434 (and (not (zerop indentation)) indentation) class)
435 (let ((other (and name (not (string-equal name (class-name->proto class))) name)))
436 (cond ((or other alias-for documentation)
437 (format stream "~%~@[~VT~](~:[~*~*~;:name ~(~S~)~@[~%~VT~]~]~
438 ~:[~*~*~;:alias-for ~(~S~)~@[~%~VT~]~]~
439 ~:[~*~;:documentation ~S~])"
441 other other (and (or alias-for documentation) (+ indentation 5))
442 alias-for alias-for (and documentation (+ indentation 5))
443 documentation documentation))
445 (format stream " ()"))))
446 (loop for (value . more) on (proto-values enum) doing
447 (write-schema-as type value stream :indentation (+ indentation 2) :more more)
450 (format stream ")")))
452 (defmethod write-schema-as ((type (eql :lisp)) (val protobuf-enum-value) stream
453 &key (indentation 0) more)
454 (declare (ignore more))
455 (with-prefixed-accessors (value index) (proto- val)
456 (if *show-lisp-enum-indexes*
457 (format stream "~&~@[~VT~](~(~A~) ~D)"
458 (and (not (zerop indentation)) indentation) value index)
459 (format stream "~&~@[~VT~]~(~A~)"
460 (and (not (zerop indentation)) indentation) value))))
463 (defmethod write-schema-as ((type (eql :lisp)) (message protobuf-message) stream
464 &key (indentation 0) more index arity)
465 (declare (ignore more))
466 (with-prefixed-accessors (name class alias-for conc-name message-type documentation) (proto- message)
467 (cond ((eq message-type :group)
469 (write-schema-documentation type documentation stream :indentation indentation))
470 (format stream "~&~@[~VT~](proto:define-group ~(~S~)"
471 (and (not (zerop indentation)) indentation) class)
472 (let ((other (and name (not (string-equal name (class-name->proto class))) name)))
473 (format stream "~%~@[~VT~](:index ~D~@[~%~VT~]~
474 :arity ~(~S~)~@[~%~VT~]~
475 ~:[~*~*~;:name ~(~S~)~@[~%~VT~]~]~
476 ~:[~*~*~;:alias-for ~(~S~)~@[~%~VT~]~]~
477 ~:[~*~*~;:conc-name ~(~S~)~@[~%~VT~]~]~
478 ~:[~*~;:documentation ~S~])"
480 index (+ indentation 5)
481 arity (and (or other alias-for conc-name documentation) (+ indentation 5))
482 other other (and (or alias-for conc-name documentation) (+ indentation 5))
483 alias-for alias-for (and (or documentation conc-name) (+ indentation 5))
484 conc-name conc-name (and documentation (+ indentation 5))
485 documentation documentation))
486 (loop for (enum . more) on (proto-enums message) doing
487 (write-schema-as type enum stream :indentation (+ indentation 2) :more more)
490 (loop for (field . more) on (proto-fields message) doing
491 (write-schema-as type field stream
492 :indentation (+ indentation 2) :more more
498 (write-schema-documentation type documentation stream :indentation indentation))
499 (format stream "~&~@[~VT~](proto:define-~A ~(~S~)"
500 (and (not (zerop indentation)) indentation)
501 (if (eq message-type :message) "message" "extend") class)
502 (let ((other (and name (not (string-equal name (class-name->proto class))) name)))
503 (cond ((eq message-type :extends)
504 (format stream " ()"))
505 ((or other alias-for conc-name documentation)
506 (format stream "~%~@[~VT~](~:[~*~*~;:name ~(~S~)~@[~%~VT~]~]~
507 ~:[~*~*~;:alias-for ~(~S~)~@[~%~VT~]~]~
508 ~:[~*~*~;:conc-name ~(~S~)~@[~%~VT~]~]~
509 ~:[~*~;:documentation ~S~])"
511 other other (and (or alias-for conc-name documentation) (+ indentation 5))
512 alias-for alias-for (and (or documentation conc-name) (+ indentation 5))
513 conc-name conc-name (and documentation (+ indentation 5))
514 documentation documentation))
516 (format stream " ()"))))
517 (cond ((eq message-type :extends)
518 (loop for (field . more) on (proto-extended-fields message) doing
519 (write-schema-as type field stream
520 :indentation (+ indentation 2) :more more
525 (loop for (enum . more) on (proto-enums message) doing
526 (write-schema-as type enum stream :indentation (+ indentation 2) :more more)
529 (loop for (msg . more) on (proto-messages message) doing
530 (unless (eq (proto-message-type msg) :group)
531 (write-schema-as type msg stream :indentation (+ indentation 2) :more more)
534 (loop for (field . more) on (proto-fields message) doing
535 (write-schema-as type field stream
536 :indentation (+ indentation 2) :more more
540 (loop for (extension . more) on (proto-extensions message) doing
541 (write-schema-as type extension stream :indentation (+ indentation 2) :more more)
543 (terpri stream)))))))
544 (format stream ")")))
546 (defparameter *protobuf-slot-comment-column* 56)
547 (defmethod write-schema-as ((type (eql :lisp)) (field protobuf-field) stream
548 &key (indentation 0) more message)
549 (with-prefixed-accessors (value required index packed options documentation) (proto- field)
550 (let* ((class (if (eq (proto-class field) 'boolean) :bool (proto-class field)))
551 (msg (and (not (keywordp class))
552 (or (find-message message class) (find-enum message class))))
553 (type (let ((cl (case class
560 ((:fixed32) 'fixed32)
561 ((:fixed64) 'fixed64)
562 ((:sfixed32) 'sfixed32)
563 ((:sfixed64) 'sfixed64)
565 ((:double) 'double-float)
568 ((:bytes) 'byte-vector)
571 (cond ((eq required :optional)
573 ((eq required :repeated)
574 (if (eq (proto-default field) $empty-vector)
578 (cond ((and (typep msg 'protobuf-message)
579 (eq (proto-message-type msg) :group))
580 (write-schema-as :lisp msg stream :indentation indentation :index index :arity required))
582 (let* ((defaultp (if (proto-alias-for message)
583 (if (eq (proto-required field) :optional)
585 (and (proto-default field)
586 (not (equalp (proto-default field) #()))
587 (not (empty-default-p field))))
588 (not (empty-default-p field))))
589 (default (proto-default field))
590 (default (and defaultp
591 (cond ((and (typep msg 'protobuf-enum)
592 (or (stringp default) (symbolp default)))
593 (let ((e (find default (proto-values msg)
594 :key #'proto-name :test #'string=)))
595 (and e (proto-value e))))
597 (boolean-true-p default))
599 (default (and defaultp
600 (if (stringp default) (escape-string default) default)))
601 (reader (unless (eq (proto-reader field) value) (proto-reader field)))
602 (writer (unless (eq (proto-writer field) value) (proto-writer field)))
603 (slot-name (if *show-lisp-field-indexes*
604 (format nil "(~(~S~) ~D)" value index)
605 (format nil "~(~S~)" value))))
606 (format stream (if (and (keywordp class) (not (eq class :bool)))
607 ;; Keyword class means a primitive type, print default with ~S
608 "~&~@[~VT~](~A :type ~(~S~)~:[~*~; :default ~S~]~
609 ~@[ :reader ~(~S~)~]~@[ :writer ~(~S~)~]~@[ :packed ~(~S~)~]~
610 ~@[ :options (~{~@/protobuf-option/~^ ~})~])~
612 ;; Non-keyword class means an enum type, print default with ~(~S~)
613 "~&~@[~VT~](~A :type ~(~S~)~:[~*~; :default ~(~S~)~]~
614 ~@[ :reader ~(~S~)~]~@[ :writer ~(~S~)~]~@[ :packed ~(~S~)~]~
615 ~@[ :options (~{~@/protobuf-option/~^ ~})~])~
617 (and (not (zerop indentation)) indentation)
618 slot-name type defaultp default reader writer packed options
619 ;; Don't write the comment if we'll insert a close paren after it
620 (and more documentation) *protobuf-slot-comment-column* documentation)))))))
622 (defmethod write-schema-as ((type (eql :lisp)) (extension protobuf-extension) stream
623 &key (indentation 0) more)
624 (declare (ignore more))
625 (with-prefixed-accessors (from to) (proto-extension- extension)
626 (format stream "~&~@[~VT~](proto:define-extension ~D ~D)"
627 (and (not (zerop indentation)) indentation)
628 from (if (eql to #.(1- (ash 1 29))) "max" to))))
631 (defmethod write-schema-as ((type (eql :lisp)) (service protobuf-service) stream
632 &key (indentation 0) more)
633 (declare (ignore more))
634 (with-prefixed-accessors (class documentation conc-name) (proto- service)
636 (write-schema-documentation type documentation stream :indentation indentation))
637 (format stream "~&~@[~VT~](proto:define-service ~(~S~)"
638 (and (not (zerop indentation)) indentation) (proto-class service))
640 (format stream "~%~@[~VT~](:documentation ~S)"
641 (+ indentation 4) documentation))
643 (format stream " ()")))
644 (loop for (method . more) on (proto-methods service) doing
645 (write-schema-as type method stream :indentation (+ indentation 2) :more more)
648 (format stream ")")))
650 (defmethod write-schema-as ((type (eql :lisp)) (method protobuf-method) stream
651 &key (indentation 0) more)
652 (declare (ignore more))
653 (with-prefixed-accessors
654 (function documentation input-type output-type options) (proto- method)
656 (write-schema-documentation type documentation stream :indentation indentation))
657 (format stream "~&~@[~VT~](~(~S~) (~(~S~) ~(~S~))"
658 (and (not (zerop indentation)) indentation)
659 function input-type output-type)
661 (format stream "~%~VT:options (~{~@/protobuf-option/~^ ~})"
662 (+ indentation 2) options))
663 (format stream ")")))