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-documentation (type docstring stream &key indentation)
32 "Writes the docstring as a \"block comment\" onto the given stream 'stream'
33 in the format given by 'type' (:proto, :text, etc)."))
36 ;;; Pretty print a schema as a .proto file
38 (defmethod write-schema-as ((type (eql :proto)) (schema protobuf-schema) stream
40 (with-prefixed-accessors (name documentation syntax package imports options) (proto- schema)
42 (write-schema-documentation type documentation stream :indentation indentation))
44 (format stream "~&syntax = \"~A\";~%~%" syntax))
46 (format stream "~&package ~A;~%~%" (substitute #\_ #\- package)))
48 (dolist (import imports)
49 (format stream "~&import \"~A\";~%" import))
51 (write-schema-header type schema stream)
53 (dolist (option options)
54 (format stream "~&option ~:/protobuf-option/;~%" option))
56 (loop for (enum . more) on (proto-enums schema) doing
57 (write-schema-as type enum stream :indentation indentation :more more)
59 (loop for (msg . more) on (proto-messages schema) doing
60 (write-schema-as type msg stream :indentation indentation :more more)
62 (loop for (svc . more) on (proto-services schema) doing
63 (write-schema-as type svc stream :indentation indentation :more more)
66 (defmethod write-schema-documentation ((type (eql :proto)) docstring stream
68 (let ((lines (split-string docstring :separators '(#\newline #\return))))
70 (format stream "~&~@[~VT~]// ~A~%"
71 (and (not (zerop indentation)) indentation) line))))
73 ;; Lisp was born in 1958 :-)
74 (defvar *lisp-options* '(("lisp_package" string 195801)
75 ("lisp_name" string 195802)
76 ("lisp_alias" string 195803)
77 ("lisp_type" string 195804)
78 ("lisp_class" string 195805)
79 ("lisp_slot" string 195806)))
81 (defvar *option-types* '(("optimize_for" symbol)
83 ;; Keep the rest of these in alphabetical order
84 ("cc_api_version" integer)
85 ("cc_generic_services" symbol)
88 ("java_api_version" integer)
89 ("java_generic_services" symbol)
90 ("java_java5_enums" boolean)
91 ("java_multiple_files" boolean)
92 ("java_outer_classname" string)
93 ("java_package" string)
94 ("java_use_javaproto2" boolean)
95 ("py_api_version" integer)
96 ("py_generic_services" symbol)))
98 (defmethod write-schema-header ((type (eql :proto)) (schema protobuf-schema) stream)
99 (when (any-lisp-option schema)
100 (format stream "~&import \"net/proto2/proto/descriptor.proto\";~%~%")
101 (format stream "~&extend proto2.MessageOptions {~%")
102 (loop for (option type index) in *lisp-options* doing
103 (format stream "~& optional ~(~A~) ~A = ~D;~%" type option index))
104 (format stream "~&}~%~%")))
106 (defmethod any-lisp-option ((schema protobuf-schema))
107 (labels ((find-one (protobuf)
108 (dolist (enum (proto-enums protobuf))
109 (with-prefixed-accessors (name class alias-for) (proto- enum)
111 (and class (not (string-equal name (class-name->proto class))) class))
112 (return-from any-lisp-option t))))
113 (dolist (msg (proto-messages protobuf))
114 (with-prefixed-accessors (name class alias-for) (proto- msg)
116 (and class (not (string-equal name (class-name->proto class))) class))
117 (return-from any-lisp-option t))))
118 (map () #'find-one (proto-messages protobuf))))
122 (defun cl-user::protobuf-option (stream option colon-p atsign-p)
123 (let ((type (or (second (find (proto-name option) *option-types* :key #'first :test #'string=))
124 (proto-type option))))
125 (cond (colon-p ;~:/protobuf-option/ -- .proto format
127 (cond ((find (proto-name option) *lisp-options* :key #'first :test #'string=)
128 (if (eq type 'symbol) "(~A)~@[ = ~A~]" "(~A)~@[ = ~S~]"))
130 (if (eq type 'symbol) "~A~@[ = ~A~]" "~A~@[ = ~S~]")))))
131 (format stream fmt-control (proto-name option) (proto-value option))))
132 (atsign-p ;~@/protobuf-option/ -- .lisp format
133 (let ((fmt-control (if (eq type 'symbol) "~(~S~) ~A" "~(~S~) ~S")))
134 (format stream fmt-control (proto-name option) (proto-value option))))
135 (t ;~/protobuf-option/ -- keyword/value format
136 (let ((fmt-control (if (eq type 'symbol) "~(:~A~) ~A" "~(:~A~) ~S")))
137 (format stream fmt-control (proto-name option) (proto-value option)))))))
139 (defmethod write-schema-as ((type (eql :proto)) (enum protobuf-enum) stream
140 &key (indentation 0) more)
141 (declare (ignore more))
142 (with-prefixed-accessors (name class alias-for documentation options) (proto- enum)
144 (write-schema-documentation type documentation stream :indentation indentation))
145 (format stream "~&~@[~VT~]enum ~A {~%"
146 (and (not (zerop indentation)) indentation) name)
147 (let ((other (and class (not (string-equal name (class-name->proto class))) class)))
149 (format stream "~&~VToption (lisp_name) = \"~A:~A\";~%"
150 (+ indentation 2) (package-name (symbol-package class)) (symbol-name class))))
152 (format stream "~&~VToption (lisp_alias) = \"~A:~A\";~%"
153 (+ indentation 2) (package-name (symbol-package alias-for)) (symbol-name alias-for)))
154 (dolist (option options)
155 (format stream "~&option ~:/protobuf-option/;~%" option))
156 (loop for (value . more) on (proto-values enum) doing
157 (write-schema-as type value stream :indentation (+ indentation 2) :more more))
158 (format stream "~&~@[~VT~]}~%"
159 (and (not (zerop indentation)) indentation))))
161 (defparameter *protobuf-enum-comment-column* 56)
162 (defmethod write-schema-as ((type (eql :proto)) (val protobuf-enum-value) stream
163 &key (indentation 0) more)
164 (declare (ignore more))
165 (with-prefixed-accessors (name documentation index) (proto- val)
166 (format stream "~&~@[~VT~]~A = ~D;~:[~*~*~;~VT// ~A~]~%"
167 (and (not (zerop indentation)) indentation) name index
168 documentation *protobuf-enum-comment-column* documentation)))
171 (defmethod write-schema-as ((type (eql :proto)) (message protobuf-message) stream
172 &key (indentation 0) more index arity)
173 (declare (ignore more arity))
174 (with-prefixed-accessors (name class alias-for message-type documentation options) (proto- message)
175 (cond ((eq message-type :group)
176 ;; If we've got a group, the printer for fields has already
177 ;; printed a partial line (nice modularity, huh?)
178 (format stream "group ~A = ~D {~%" name index)
179 (let ((other (and class (not (string-equal name (class-name->proto class))) class)))
181 (format stream "~&~VToption (lisp_name) = \"~A:~A\";~%"
182 (+ indentation 2) (package-name (symbol-package class)) (symbol-name class))))
184 (format stream "~&~VToption (lisp_alias) = \"~A:~A\";~%"
185 (+ indentation 2) (package-name (symbol-package alias-for)) (symbol-name alias-for)))
186 (dolist (option options)
187 (format stream "~&~VToption ~:/protobuf-option/;~%"
188 (+ indentation 2) option))
189 (loop for (enum . more) on (proto-enums message) doing
190 (write-schema-as type enum stream :indentation (+ indentation 2) :more more))
191 (loop for (field . more) on (proto-fields message) doing
192 (write-schema-as type field stream
193 :indentation (+ indentation 2) :more more :message message))
194 (format stream "~&~@[~VT~]}~%"
195 (and (not (zerop indentation)) indentation)))
198 (write-schema-documentation type documentation stream :indentation indentation))
199 (format stream "~&~@[~VT~]~A ~A {~%"
200 (and (not (zerop indentation)) indentation)
201 (if (eq message-type :message) "message" "extend") name)
202 (let ((other (and class (not (string-equal name (class-name->proto class))) class)))
204 (format stream "~&~VToption (lisp_name) = \"~A:~A\";~%"
205 (+ indentation 2) (package-name (symbol-package class)) (symbol-name class))))
207 (format stream "~&~VToption (lisp_alias) = \"~A:~A\";~%"
208 (+ indentation 2) (package-name (symbol-package alias-for)) (symbol-name alias-for)))
209 (dolist (option options)
210 (format stream "~&~VToption ~:/protobuf-option/;~%"
211 (+ indentation 2) option))
212 (cond ((eq message-type :extends)
213 (loop for (field . more) on (proto-extended-fields message) doing
214 (write-schema-as type field stream
215 :indentation (+ indentation 2) :more more
218 (loop for (enum . more) on (proto-enums message) doing
219 (write-schema-as type enum stream :indentation (+ indentation 2) :more more))
220 (loop for (msg . more) on (proto-messages message) doing
221 (unless (eq (proto-message-type msg) :group)
222 (write-schema-as type msg stream :indentation (+ indentation 2) :more more)))
223 (loop for (field . more) on (proto-fields message) doing
224 (write-schema-as type field stream
225 :indentation (+ indentation 2) :more more
227 (loop for (extension . more) on (proto-extensions message) doing
228 (write-schema-as type extension stream :indentation (+ indentation 2) :more more))))
229 (format stream "~&~@[~VT~]}~%"
230 (and (not (zerop indentation)) indentation))))))
232 (defparameter *protobuf-field-comment-column* 56)
233 (defmethod write-schema-as ((type (eql :proto)) (field protobuf-field) stream
234 &key (indentation 0) more message)
235 (declare (ignore more))
236 (with-prefixed-accessors (name documentation required type index packed options) (proto- field)
237 (let* ((class (if (eq (proto-class field) 'boolean) :bool (proto-class field)))
238 (msg (and (not (keywordp class))
239 (or (find-message message class) (find-enum message class)))))
240 (cond ((and (typep msg 'protobuf-message)
241 (eq (proto-message-type msg) :group))
242 (format stream "~&~@[~VT~]~(~A~) "
243 (and (not (zerop indentation)) indentation) required)
244 (write-schema-as :proto msg stream :indentation indentation :index index :arity required))
246 (let* ((defaultp (if (proto-alias-for message)
247 ;; Special handling for imported CLOS classes
248 (if (eq (proto-required field) :optional)
250 (and (proto-default field)
251 (not (equalp (proto-default field) #()))
252 (not (empty-default-p field))))
253 (not (empty-default-p field))))
254 (default (proto-default field))
255 (default (and defaultp
256 (cond ((and (typep msg 'protobuf-enum)
257 (or (stringp default) (symbolp default)))
258 (let ((e (find default (proto-values msg)
259 :key #'proto-name :test #'string=)))
260 (and e (proto-name e))))
262 (if (boolean-true-p default) "true" "false"))
264 (default (and defaultp
265 (if (stringp default) (escape-string default) default))))
266 (format stream (if (and (keywordp class) (not (eq class :bool)))
267 ;; Keyword class means a primitive type, print default with ~S
268 "~&~@[~VT~]~(~A~) ~A ~A = ~D~
269 ~:[~*~; [default = ~S]~]~@[ [packed = true]~*~]~{ [~:/protobuf-option/]~};~
270 ~:[~*~*~;~VT// ~A~]~%"
271 ;; Non-keyword class means an enum type, print default with ~A"
272 "~&~@[~VT~]~(~A~) ~A ~A = ~D~
273 ~:[~*~; [default = ~A]~]~@[ [packed = true]~*~]~{ [~:/protobuf-option/]~};~
274 ~:[~*~*~;~VT// ~A~]~%")
275 (and (not (zerop indentation)) indentation)
276 required type name index defaultp default packed options
277 documentation *protobuf-field-comment-column* documentation)))))))
279 (defun escape-string (string)
280 (if (every #'(lambda (ch) (and (standard-char-p ch) (graphic-char-p ch))) string)
282 (with-output-to-string (s)
283 (loop for ch across string
284 as esc = (escape-char ch)
285 do (format s "~A" esc)))))
287 (defmethod write-schema-as ((type (eql :proto)) (extension protobuf-extension) stream
288 &key (indentation 0) more)
289 (declare (ignore more))
290 (with-prefixed-accessors (from to) (proto-extension- extension)
291 (format stream "~&~@[~VT~]extensions ~D~:[~*~; to ~D~];~%"
292 (and (not (zerop indentation)) indentation)
293 from (not (eql from to)) (if (eql to #.(1- (ash 1 29))) "max" to))))
296 (defmethod write-schema-as ((type (eql :proto)) (service protobuf-service) stream
297 &key (indentation 0) more)
298 (declare (ignore more))
299 (with-prefixed-accessors (name documentation) (proto- service)
301 (write-schema-documentation type documentation stream :indentation indentation))
302 (format stream "~&~@[~VT~]service ~A {~%"
303 (and (not (zerop indentation)) indentation) name)
304 (loop for (method . more) on (proto-methods service) doing
305 (write-schema-as type method stream :indentation (+ indentation 2) :more more))
306 (format stream "~&~@[~VT~]}~%"
307 (and (not (zerop indentation)) indentation))))
309 (defmethod write-schema-as ((type (eql :proto)) (method protobuf-method) stream
310 &key (indentation 0) more)
311 (declare (ignore more))
312 (with-prefixed-accessors (name documentation input-name output-name options) (proto- method)
314 (write-schema-documentation type documentation stream :indentation indentation))
315 (format stream "~&~@[~VT~]rpc ~A (~@[~A~])~@[ returns (~A)~]"
316 (and (not (zerop indentation)) indentation)
317 name input-name output-name)
319 (format stream " {~%")
320 (dolist (option options)
321 (format stream "~&~@[~VT~]option ~:/protobuf-option/;~%"
322 (+ indentation 2) option))
323 (format stream "~@[~VT~]}"
324 (and (not (zerop indentation)) indentation)))
326 (format stream ";~%")))))
329 ;;; Pretty print a schema as a .lisp file
331 (defvar *show-lisp-enum-indexes* t)
332 (defvar *show-lisp-field-indexes* t)
334 (defmethod write-schema-as ((type (eql :lisp)) (schema protobuf-schema) stream
336 (show-field-indexes *show-lisp-field-indexes*)
337 (show-enum-indexes *show-lisp-enum-indexes*))
338 (with-prefixed-accessors (name class documentation package lisp-package imports) (proto- schema)
339 (let* ((optimize (let ((opt (find-option schema "optimize_for")))
340 (and opt (cond ((string= opt "SPEED") :speed)
341 ((string= opt "CODE_SIZE") :space)
343 (options (remove-if #'(lambda (x) (string= (proto-name x) "optimize_for"))
344 (proto-options schema)))
345 (pkg (and package (if (stringp package) package (string package))))
346 (lisp-pkg (and lisp-package (if (stringp lisp-package) lisp-package (string lisp-package))))
347 (*show-lisp-enum-indexes* show-enum-indexes)
348 (*show-lisp-field-indexes* show-field-indexes)
349 (*protobuf-package* (or (find-package lisp-pkg)
350 (find-package (string-upcase lisp-pkg))
352 (*package* *protobuf-package*))
353 (when (or lisp-pkg pkg)
354 (let ((pkg (string-upcase (or lisp-pkg pkg))))
355 (format stream "~&(eval-when (:execute :compile-toplevel :load-toplevel) ~
356 ~% (unless (find-package \"~A\") ~
357 ~% (defpackage ~A (:use :COMMON-LISP :PROTOBUFS)))) ~
358 ~%(in-package \"~A\")~%~%"
361 (write-schema-documentation type documentation stream :indentation indentation))
362 (format stream "~&(proto:define-schema ~(~A~)" (or class name))
363 (if (or pkg lisp-pkg imports optimize options documentation)
364 (format stream "~% (")
365 (format stream " ("))
368 (format stream "~A:package \"~A\"" spaces pkg)
369 (when (or lisp-pkg imports optimize options documentation)
373 (format stream "~A:lisp-package \"~A\"" spaces lisp-pkg)
374 (when (or imports optimize options documentation)
378 (cond ((= (length imports) 1)
379 (format stream "~A:import \"~A\"" spaces (car imports)))
381 (format stream "~A:import (~{\"~A\"~^ ~})" spaces imports)))
382 (when (or optimize options documentation)
386 (format stream "~A:optimize ~(~S~)" spaces optimize)
387 (when (or options documentation)
391 (format stream "~A:options (~{~@/protobuf-option/~^ ~})" spaces options)
396 (format stream "~A:documentation ~S" spaces documentation)))
398 (loop for (enum . more) on (proto-enums schema) doing
399 (write-schema-as type enum stream :indentation 2 :more more))
400 (loop for (msg . more) on (proto-messages schema) doing
401 (write-schema-as type msg stream :indentation 2 :more more))
402 (loop for (svc . more) on (proto-services schema) doing
403 (write-schema-as type svc stream :indentation 2 :more more)))
404 (format stream ")~%")))
406 (defmethod write-schema-documentation ((type (eql :lisp)) docstring stream
407 &key (indentation 0))
408 (let ((lines (split-string docstring :separators '(#\newline #\return))))
410 (format stream "~&~@[~VT~];; ~A~%"
411 (and (not (zerop indentation)) indentation) line))))
413 (defmethod write-schema-header ((type (eql :lisp)) (schema protobuf-schema) stream)
414 (declare (ignorable type stream))
417 (defmethod write-schema-as ((type (eql :lisp)) (enum protobuf-enum) stream
418 &key (indentation 0) more)
419 (declare (ignore more))
421 (with-prefixed-accessors (name class alias-for documentation) (proto- enum)
423 (write-schema-documentation type documentation stream :indentation indentation))
424 (format stream "~@[~VT~](proto:define-enum ~(~S~)"
425 (and (not (zerop indentation)) indentation) class)
426 (let ((other (and name (not (string-equal name (class-name->proto class))) name)))
427 (cond ((or other alias-for documentation)
428 (format stream "~%~@[~VT~](~:[~*~*~;:name ~(~S~)~@[~%~VT~]~]~
429 ~:[~*~*~;:alias-for ~(~S~)~@[~%~VT~]~]~
430 ~:[~*~;:documentation ~S~])"
432 other other (and (or alias-for documentation) (+ indentation 5))
433 alias-for alias-for (and documentation (+ indentation 5))
434 documentation documentation))
436 (format stream " ()"))))
437 (loop for (value . more) on (proto-values enum) doing
438 (write-schema-as type value stream :indentation (+ indentation 2) :more more)
441 (format stream ")")))
443 (defmethod write-schema-as ((type (eql :lisp)) (val protobuf-enum-value) stream
444 &key (indentation 0) more)
445 (declare (ignore more))
446 (with-prefixed-accessors (value index) (proto- val)
447 (if *show-lisp-enum-indexes*
448 (format stream "~&~@[~VT~](~(~A~) ~D)"
449 (and (not (zerop indentation)) indentation) value index)
450 (format stream "~&~@[~VT~]~(~A~)"
451 (and (not (zerop indentation)) indentation) value))))
454 (defmethod write-schema-as ((type (eql :lisp)) (message protobuf-message) stream
455 &key (indentation 0) more index arity)
456 (declare (ignore more))
457 (with-prefixed-accessors (name class alias-for conc-name message-type documentation) (proto- message)
458 (cond ((eq message-type :group)
460 (write-schema-documentation type documentation stream :indentation indentation))
461 (format stream "~&~@[~VT~](proto:define-group ~(~S~)"
462 (and (not (zerop indentation)) indentation) class)
463 (let ((other (and name (not (string-equal name (class-name->proto class))) name)))
464 (format stream "~%~@[~VT~](:index ~D~@[~%~VT~]~
465 :arity ~(~S~)~@[~%~VT~]~
466 ~:[~*~*~;:name ~(~S~)~@[~%~VT~]~]~
467 ~:[~*~*~;:alias-for ~(~S~)~@[~%~VT~]~]~
468 ~:[~*~*~;:conc-name ~(~S~)~@[~%~VT~]~]~
469 ~:[~*~;:documentation ~S~])"
471 index (+ indentation 5)
472 arity (and (or other alias-for conc-name documentation) (+ indentation 5))
473 other other (and (or alias-for conc-name documentation) (+ indentation 5))
474 alias-for alias-for (and (or documentation conc-name) (+ indentation 5))
475 conc-name conc-name (and documentation (+ indentation 5))
476 documentation documentation))
477 (loop for (enum . more) on (proto-enums message) doing
478 (write-schema-as type enum stream :indentation (+ indentation 2) :more more)
481 (loop for (field . more) on (proto-fields message) doing
482 (write-schema-as type field stream
483 :indentation (+ indentation 2) :more more
489 (write-schema-documentation type documentation stream :indentation indentation))
490 (format stream "~&~@[~VT~](proto:define-~A ~(~S~)"
491 (and (not (zerop indentation)) indentation)
492 (if (eq message-type :message) "message" "extend") class)
493 (let ((other (and name (not (string-equal name (class-name->proto class))) name)))
494 (cond ((eq message-type :extends)
495 (format stream " ()"))
496 ((or other alias-for conc-name documentation)
497 (format stream "~%~@[~VT~](~:[~*~*~;:name ~(~S~)~@[~%~VT~]~]~
498 ~:[~*~*~;:alias-for ~(~S~)~@[~%~VT~]~]~
499 ~:[~*~*~;:conc-name ~(~S~)~@[~%~VT~]~]~
500 ~:[~*~;:documentation ~S~])"
502 other other (and (or alias-for conc-name documentation) (+ indentation 5))
503 alias-for alias-for (and (or documentation conc-name) (+ indentation 5))
504 conc-name conc-name (and documentation (+ indentation 5))
505 documentation documentation))
507 (format stream " ()"))))
508 (cond ((eq message-type :extends)
509 (loop for (field . more) on (proto-extended-fields message) doing
510 (write-schema-as type field stream
511 :indentation (+ indentation 2) :more more
516 (loop for (enum . more) on (proto-enums message) doing
517 (write-schema-as type enum stream :indentation (+ indentation 2) :more more)
520 (loop for (msg . more) on (proto-messages message) doing
521 (unless (eq (proto-message-type msg) :group)
522 (write-schema-as type msg stream :indentation (+ indentation 2) :more more)
525 (loop for (field . more) on (proto-fields message) doing
526 (write-schema-as type field stream
527 :indentation (+ indentation 2) :more more
531 (loop for (extension . more) on (proto-extensions message) doing
532 (write-schema-as type extension stream :indentation (+ indentation 2) :more more)
534 (terpri stream)))))))
535 (format stream ")")))
537 (defparameter *protobuf-slot-comment-column* 56)
538 (defmethod write-schema-as ((type (eql :lisp)) (field protobuf-field) stream
539 &key (indentation 0) more message)
540 (with-prefixed-accessors (value required index packed options documentation) (proto- field)
541 (let* ((class (if (eq (proto-class field) 'boolean) :bool (proto-class field)))
542 (msg (and (not (keywordp class))
543 (or (find-message message class) (find-enum message class))))
544 (type (let ((cl (case class
551 ((:fixed32) 'fixed32)
552 ((:fixed64) 'fixed64)
553 ((:sfixed32) 'sfixed32)
554 ((:sfixed64) 'sfixed64)
556 ((:double) 'double-float)
559 ((:bytes) 'byte-vector)
562 (cond ((eq required :optional)
564 ((eq required :repeated)
565 (if (eq (proto-default field) $empty-vector)
569 (cond ((and (typep msg 'protobuf-message)
570 (eq (proto-message-type msg) :group))
571 (write-schema-as :lisp msg stream :indentation indentation :index index :arity required))
573 (let* ((defaultp (if (proto-alias-for message)
574 (if (eq (proto-required field) :optional)
576 (and (proto-default field)
577 (not (equalp (proto-default field) #()))
578 (not (empty-default-p field))))
579 (not (empty-default-p field))))
580 (default (proto-default field))
581 (default (and defaultp
582 (cond ((and (typep msg 'protobuf-enum)
583 (or (stringp default) (symbolp default)))
584 (let ((e (find default (proto-values msg)
585 :key #'proto-name :test #'string=)))
586 (and e (proto-value e))))
588 (boolean-true-p default))
590 (default (and defaultp
591 (if (stringp default) (escape-string default) default)))
592 (reader (unless (eq (proto-reader field) value) (proto-reader field)))
593 (writer (unless (eq (proto-writer field) value) (proto-writer field)))
594 (slot-name (if *show-lisp-field-indexes*
595 (format nil "(~(~S~) ~D)" value index)
596 (format nil "~(~S~)" value))))
597 (format stream (if (and (keywordp class) (not (eq class :bool)))
598 ;; Keyword class means a primitive type, print default with ~S
599 "~&~@[~VT~](~A :type ~(~S~)~:[~*~; :default ~S~]~
600 ~@[ :reader ~(~S~)~]~@[ :writer ~(~S~)~]~@[ :packed ~(~S~)~]~
601 ~@[ :options (~{~@/protobuf-option/~^ ~})~])~
603 ;; Non-keyword class means an enum type, print default with ~(~S~)
604 "~&~@[~VT~](~A :type ~(~S~)~:[~*~; :default ~(~S~)~]~
605 ~@[ :reader ~(~S~)~]~@[ :writer ~(~S~)~]~@[ :packed ~(~S~)~]~
606 ~@[ :options (~{~@/protobuf-option/~^ ~})~])~
608 (and (not (zerop indentation)) indentation)
609 slot-name type defaultp default reader writer packed options
610 ;; Don't write the comment if we'll insert a close paren after it
611 (and more documentation) *protobuf-slot-comment-column* documentation)))))))
613 (defmethod write-schema-as ((type (eql :lisp)) (extension protobuf-extension) stream
614 &key (indentation 0) more)
615 (declare (ignore more))
616 (with-prefixed-accessors (from to) (proto-extension- extension)
617 (format stream "~&~@[~VT~](proto:define-extension ~D ~D)"
618 (and (not (zerop indentation)) indentation)
619 from (if (eql to #.(1- (ash 1 29))) "max" to))))
622 (defmethod write-schema-as ((type (eql :lisp)) (service protobuf-service) stream
623 &key (indentation 0) more)
624 (declare (ignore more))
625 (with-prefixed-accessors (class documentation conc-name) (proto- service)
627 (write-schema-documentation type documentation stream :indentation indentation))
628 (format stream "~&~@[~VT~](proto:define-service ~(~S~)"
629 (and (not (zerop indentation)) indentation) (proto-class service))
631 (format stream "~%~@[~VT~](:documentation ~S)"
632 (+ indentation 4) documentation))
634 (format stream " ()")))
635 (loop for (method . more) on (proto-methods service) doing
636 (write-schema-as type method stream :indentation (+ indentation 2) :more more)
639 (format stream ")")))
641 (defmethod write-schema-as ((type (eql :lisp)) (method protobuf-method) stream
642 &key (indentation 0) more)
643 (declare (ignore more))
644 (with-prefixed-accessors
645 (function documentation input-type output-type options) (proto- method)
647 (write-schema-documentation type documentation stream :indentation indentation))
648 (format stream "~&~@[~VT~](~(~S~) (~(~S~) ~(~S~))"
649 (and (not (zerop indentation)) indentation)
650 function input-type output-type)
652 (format stream "~%~VT:options (~{~@/protobuf-option/~^ ~})"
653 (+ indentation 2) options))
654 (format stream ")")))