1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;;; Free Software published under an MIT-like license. See LICENSE ;;;
5 ;;; Copyright (c) 2012 Google, 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 (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=)
138 ((symbol) "(~A)~@[ = ~A~]")
139 ((boolean) "(~A)~@[ = ~(~A~)~]")
140 (otherwise "(~A)~@[ = ~S~]")))
143 ((symbol) "~A~@[ = ~A~]")
144 ((boolean) "~A~@[ = ~(~A~)~]")
145 (otherwise "~A~@[ = ~S~]"))))))
146 (format stream fmt-control (proto-name option) (proto-value option))))
147 (atsign-p ;~@/protobuf-option/ -- .lisp format
148 (let ((fmt-control (if (eq type 'symbol) "~(~S~) ~A" "~(~S~) ~S")))
149 (format stream fmt-control (proto-name option) (proto-value option))))
150 (t ;~/protobuf-option/ -- keyword/value format
151 (let ((fmt-control (if (eq type 'symbol) "~(:~A~) ~A" "~(:~A~) ~S")))
152 (format stream fmt-control (proto-name option) (proto-value option)))))))
154 (defmethod write-schema-as ((type (eql :proto)) (enum protobuf-enum) stream
155 &key (indentation 0) more)
156 (declare (ignore more))
157 (with-prefixed-accessors (name class alias-for documentation options) (proto- enum)
159 (write-schema-documentation type documentation stream :indentation indentation))
160 (format stream "~&~@[~VT~]enum ~A {~%"
161 (and (not (zerop indentation)) indentation) name)
162 (let ((other (and class (not (string-equal name (class-name->proto class))) class)))
164 (format stream "~&~VToption (lisp_name) = \"~A:~A\";~%"
165 (+ indentation 2) (package-name (symbol-package class)) (symbol-name class))))
167 (format stream "~&~VToption (lisp_alias) = \"~A:~A\";~%"
168 (+ indentation 2) (package-name (symbol-package alias-for)) (symbol-name alias-for)))
169 (dolist (option options)
170 (format stream "~&option ~:/protobuf-option/;~%" option))
171 (loop for (value . more) on (proto-values enum) doing
172 (write-schema-as type value stream :indentation (+ indentation 2) :more more))
173 (format stream "~&~@[~VT~]}~%"
174 (and (not (zerop indentation)) indentation))))
176 (defparameter *protobuf-enum-comment-column* 56)
177 (defmethod write-schema-as ((type (eql :proto)) (val protobuf-enum-value) stream
178 &key (indentation 0) more)
179 (declare (ignore more))
180 (with-prefixed-accessors (name documentation index) (proto- val)
181 (format stream "~&~@[~VT~]~A = ~D;~:[~*~*~;~VT// ~A~]~%"
182 (and (not (zerop indentation)) indentation) name index
183 documentation *protobuf-enum-comment-column* documentation)))
186 (defmethod write-schema-as ((type (eql :proto)) (message protobuf-message) stream
187 &key (indentation 0) more index arity)
188 (declare (ignore more arity))
189 (let ((*protobuf* message))
190 (with-prefixed-accessors (name class alias-for message-type documentation options) (proto- message)
191 (cond ((eq message-type :group)
192 ;; If we've got a group, the printer for fields has already
193 ;; printed a partial line (nice modularity, huh?)
194 (format stream "group ~A = ~D {~%" name index)
195 (let ((other (and class (not (string-equal name (class-name->proto class))) class)))
197 (format stream "~&~VToption (lisp_name) = \"~A:~A\";~%"
198 (+ indentation 2) (package-name (symbol-package class)) (symbol-name class))))
200 (format stream "~&~VToption (lisp_alias) = \"~A:~A\";~%"
201 (+ indentation 2) (package-name (symbol-package alias-for)) (symbol-name alias-for)))
202 (dolist (option options)
203 (format stream "~&~VToption ~:/protobuf-option/;~%"
204 (+ indentation 2) option))
205 (loop for (enum . more) on (proto-enums message) doing
206 (write-schema-as type enum stream :indentation (+ indentation 2) :more more))
207 (loop for (field . more) on (proto-fields message) doing
208 (write-schema-as type field stream
209 :indentation (+ indentation 2) :more more :message message))
210 (format stream "~&~@[~VT~]}~%"
211 (and (not (zerop indentation)) indentation)))
214 (write-schema-documentation type documentation stream :indentation indentation))
215 (format stream "~&~@[~VT~]~A ~A {~%"
216 (and (not (zerop indentation)) indentation)
217 (if (eq message-type :message) "message" "extend")
218 (maybe-qualified-name message))
219 (let ((other (and class (not (string-equal name (class-name->proto class))) class)))
221 (format stream "~&~VToption (lisp_name) = \"~A:~A\";~%"
222 (+ indentation 2) (package-name (symbol-package class)) (symbol-name class))))
224 (format stream "~&~VToption (lisp_alias) = \"~A:~A\";~%"
225 (+ indentation 2) (package-name (symbol-package alias-for)) (symbol-name alias-for)))
226 (dolist (option options)
227 (format stream "~&~VToption ~:/protobuf-option/;~%"
228 (+ indentation 2) option))
229 (cond ((eq message-type :extends)
230 (loop for (field . more) on (proto-extended-fields message) doing
231 (write-schema-as type field stream
232 :indentation (+ indentation 2) :more more
235 (loop for (enum . more) on (proto-enums message) doing
236 (write-schema-as type enum stream :indentation (+ indentation 2) :more more))
237 (loop for (msg . more) on (proto-messages message) doing
238 (unless (eq (proto-message-type msg) :group)
239 (write-schema-as type msg stream :indentation (+ indentation 2) :more more)))
240 (loop for (field . more) on (proto-fields message) doing
241 (write-schema-as type field stream
242 :indentation (+ indentation 2) :more more
244 (loop for (extension . more) on (proto-extensions message) doing
245 (write-schema-as type extension stream :indentation (+ indentation 2) :more more))))
246 (format stream "~&~@[~VT~]}~%"
247 (and (not (zerop indentation)) indentation)))))))
249 (defun maybe-qualified-name (message &optional name)
250 "Given a message, return a fully qualified name if the short name
251 is not sufficient to name the message in the current scope."
254 (cond ((string= (make-qualified-name (proto-parent message) (proto-name message))
255 (proto-qualified-name message))
256 (proto-name message))
258 (proto-qualified-name message))))
259 ((or null protobuf-enum)
262 (defparameter *protobuf-field-comment-column* 56)
263 (defmethod write-schema-as ((type (eql :proto)) (field protobuf-field) stream
264 &key (indentation 0) more message)
265 (declare (ignore more))
266 (with-prefixed-accessors (name documentation required type index packed options) (proto- field)
267 (let* ((class (if (eq (proto-class field) 'boolean) :bool (proto-class field)))
268 (msg (and (not (keywordp class))
269 (or (find-message message class) (find-enum message class)))))
270 (cond ((and (typep msg 'protobuf-message)
271 (eq (proto-message-type msg) :group))
272 (format stream "~&~@[~VT~]~(~A~) "
273 (and (not (zerop indentation)) indentation) required)
274 (write-schema-as :proto msg stream :indentation indentation :index index :arity required))
276 (let* ((defaultp (if (proto-alias-for message)
277 ;; Special handling for imported CLOS classes
278 (if (eq (proto-required field) :optional)
280 (and (proto-default field)
281 (not (equalp (proto-default field) #()))
282 (not (empty-default-p field))))
283 (not (empty-default-p field))))
284 (default (proto-default field))
285 (default (and defaultp
286 (cond ((and (typep msg 'protobuf-enum)
287 (or (stringp default) (symbolp default)))
288 (let ((e (find default (proto-values msg)
289 :key #'proto-name :test #'string=)))
290 (and e (proto-name e))))
292 (if (boolean-true-p default) "true" "false"))
294 (default (and defaultp
295 (if (stringp default) (escape-string default) default))))
296 (format stream (if (and (keywordp class) (not (eq class :bool)))
297 ;; Keyword class means a primitive type, print default with ~S
298 "~&~@[~VT~]~(~A~) ~A ~A = ~D~
299 ~:[~*~; [default = ~S]~]~@[ [packed = true]~*~]~{ [~:/protobuf-option/]~};~
300 ~:[~*~*~;~VT// ~A~]~%"
301 ;; Non-keyword class means an enum type, print default with ~A"
302 "~&~@[~VT~]~(~A~) ~A ~A = ~D~
303 ~:[~*~; [default = ~A]~]~@[ [packed = true]~*~]~{ [~:/protobuf-option/]~};~
304 ~:[~*~*~;~VT// ~A~]~%")
305 (and (not (zerop indentation)) indentation)
306 required (maybe-qualified-name msg type) name index
307 defaultp default packed options
308 documentation *protobuf-field-comment-column* documentation)))))))
310 (defun escape-string (string)
311 (if (every #'(lambda (ch) (and (standard-char-p ch) (graphic-char-p ch))) string)
313 (with-output-to-string (s)
314 (loop for ch across string
315 as esc = (escape-char ch)
316 do (format s "~A" esc)))))
318 (defmethod write-schema-as ((type (eql :proto)) (extension protobuf-extension) stream
319 &key (indentation 0) more)
320 (declare (ignore more))
321 (with-prefixed-accessors (from to) (proto-extension- extension)
322 (format stream "~&~@[~VT~]extensions ~D~:[~*~; to ~D~];~%"
323 (and (not (zerop indentation)) indentation)
324 from (not (eql from to)) (if (eql to #.(1- (ash 1 29))) "max" to))))
327 (defmethod write-schema-as ((type (eql :proto)) (service protobuf-service) stream
328 &key (indentation 0) more)
329 (declare (ignore more))
330 (with-prefixed-accessors (name documentation) (proto- service)
332 (write-schema-documentation type documentation stream :indentation indentation))
333 (format stream "~&~@[~VT~]service ~A {~%"
334 (and (not (zerop indentation)) indentation) name)
335 (loop for (method . more) on (proto-methods service) doing
336 (write-schema-as type method stream :indentation (+ indentation 2) :more more))
337 (format stream "~&~@[~VT~]}~%"
338 (and (not (zerop indentation)) indentation))))
340 (defmethod write-schema-as ((type (eql :proto)) (method protobuf-method) stream
341 &key (indentation 0) more)
342 (declare (ignore more))
343 (with-prefixed-accessors
344 (name documentation input-name output-name options) (proto- method)
345 (let* ((imsg (find-message *protobuf* input-name))
346 (omsg (find-message *protobuf* output-name))
347 (iname (maybe-qualified-name imsg))
348 (oname (maybe-qualified-name omsg)))
350 (write-schema-documentation type documentation stream :indentation indentation))
351 (format stream "~&~@[~VT~]rpc ~A (~@[~A~])~@[ returns (~A)~]"
352 (and (not (zerop indentation)) indentation)
355 (format stream " {~%")
356 (dolist (option options)
357 (format stream "~&~@[~VT~]option ~:/protobuf-option/;~%"
358 (+ indentation 2) option))
359 (format stream "~@[~VT~]}"
360 (and (not (zerop indentation)) indentation)))
362 (format stream ";~%"))))))
365 ;;; Pretty print a schema as a .lisp file
367 (defvar *show-lisp-enum-indexes* t)
368 (defvar *show-lisp-field-indexes* t)
370 (defmethod write-schema-as ((type (eql :lisp)) (schema protobuf-schema) stream
372 (show-field-indexes *show-lisp-field-indexes*)
373 (show-enum-indexes *show-lisp-enum-indexes*))
374 (with-prefixed-accessors (name class documentation package lisp-package imports) (proto- schema)
375 (let* ((optimize (let ((opt (find-option schema "optimize_for")))
376 (and opt (cond ((string= opt "SPEED") :speed)
377 ((string= opt "CODE_SIZE") :space)
379 (options (remove-if #'(lambda (x) (string= (proto-name x) "optimize_for"))
380 (proto-options schema)))
381 (pkg (and package (if (stringp package) package (string package))))
382 (lisp-pkg (and lisp-package (if (stringp lisp-package) lisp-package (string lisp-package))))
383 (*show-lisp-enum-indexes* show-enum-indexes)
384 (*show-lisp-field-indexes* show-field-indexes)
385 (*protobuf-package* (or (find-proto-package lisp-pkg) *package*))
386 (*package* *protobuf-package*))
387 (when (or lisp-pkg pkg)
388 (let ((pkg (string-upcase (or lisp-pkg pkg))))
389 (format stream "~&(cl:eval-when (:execute :compile-toplevel :load-toplevel) ~
390 ~% (unless (cl:find-package \"~A\") ~
391 ~% (cl:defpackage ~A (:use :COMMON-LISP)))) ~
392 ~%(cl:in-package \"~A\") ~
393 ~%(cl:export '(~{~A~^~% ~}))~%~%"
394 pkg pkg pkg (collect-exports schema))))
396 (write-schema-documentation type documentation stream :indentation indentation))
397 (format stream "~&(proto:define-schema ~(~A~)" (or class name))
398 (if (or pkg lisp-pkg imports optimize options documentation)
399 (format stream "~% (")
400 (format stream " ("))
403 (format stream "~A:package \"~A\"" spaces pkg)
404 (when (or lisp-pkg imports optimize options documentation)
408 (format stream "~A:lisp-package \"~A\"" spaces lisp-pkg)
409 (when (or imports optimize options documentation)
413 (cond ((= (length imports) 1)
414 (format stream "~A:import \"~A\"" spaces (car imports)))
416 (format stream "~A:import (~{\"~A\"~^ ~})" spaces imports)))
417 (when (or optimize options documentation)
421 (format stream "~A:optimize ~(~S~)" spaces optimize)
422 (when (or options documentation)
426 (format stream "~A:options (~{~@/protobuf-option/~^ ~})" spaces options)
431 (format stream "~A:documentation ~S" spaces documentation)))
433 (loop for (enum . more) on (proto-enums schema) doing
434 (write-schema-as type enum stream :indentation 2 :more more))
435 (loop for (msg . more) on (proto-messages schema) doing
436 (write-schema-as type msg stream :indentation 2 :more more))
437 (loop for (svc . more) on (proto-services schema) doing
438 (write-schema-as type svc stream :indentation 2 :more more)))
439 (format stream ")~%")))
441 (defmethod write-schema-documentation ((type (eql :lisp)) docstring stream
442 &key (indentation 0))
443 (let ((lines (split-string docstring :separators '(#\newline #\return))))
445 (format stream "~&~@[~VT~];; ~A~%"
446 (and (not (zerop indentation)) indentation) line))))
448 (defmethod write-schema-header ((type (eql :lisp)) (schema protobuf-schema) stream)
449 (declare (ignorable type stream))
452 (defmethod write-schema-as ((type (eql :lisp)) (enum protobuf-enum) stream
453 &key (indentation 0) more)
454 (declare (ignore more))
456 (with-prefixed-accessors (name class alias-for documentation) (proto- enum)
458 (write-schema-documentation type documentation stream :indentation indentation))
459 (format stream "~@[~VT~](proto:define-enum ~(~S~)"
460 (and (not (zerop indentation)) indentation) class)
461 (let ((other (and name (not (string-equal name (class-name->proto class))) name)))
462 (cond ((or other alias-for documentation)
463 (format stream "~%~@[~VT~](~:[~*~*~;:name ~(~S~)~@[~%~VT~]~]~
464 ~:[~*~*~;:alias-for ~(~S~)~@[~%~VT~]~]~
465 ~:[~*~;:documentation ~S~])"
467 other other (and (or alias-for documentation) (+ indentation 5))
468 alias-for alias-for (and documentation (+ indentation 5))
469 documentation documentation))
471 (format stream " ()"))))
472 (loop for (value . more) on (proto-values enum) doing
473 (write-schema-as type value stream :indentation (+ indentation 2) :more more)
476 (format stream ")")))
478 (defmethod write-schema-as ((type (eql :lisp)) (val protobuf-enum-value) stream
479 &key (indentation 0) more)
480 (declare (ignore more))
481 (with-prefixed-accessors (value index) (proto- val)
482 (if *show-lisp-enum-indexes*
483 (format stream "~&~@[~VT~](~(~A~) ~D)"
484 (and (not (zerop indentation)) indentation) value index)
485 (format stream "~&~@[~VT~]~(~A~)"
486 (and (not (zerop indentation)) indentation) value))))
489 (defmethod write-schema-as ((type (eql :lisp)) (message protobuf-message) stream
490 &key (indentation 0) more index arity)
491 (declare (ignore more))
492 (let ((*protobuf* message))
493 (with-prefixed-accessors (name class alias-for conc-name message-type documentation) (proto- message)
494 (cond ((eq message-type :group)
496 (write-schema-documentation type documentation stream :indentation indentation))
497 (format stream "~&~@[~VT~](proto:define-group ~(~S~)"
498 (and (not (zerop indentation)) indentation) class)
499 (let ((other (and name (not (string-equal name (class-name->proto class))) name)))
500 (format stream "~%~@[~VT~](:index ~D~@[~%~VT~]~
501 :arity ~(~S~)~@[~%~VT~]~
502 ~:[~*~*~;:name ~(~S~)~@[~%~VT~]~]~
503 ~:[~*~*~;:alias-for ~(~S~)~@[~%~VT~]~]~
504 ~:[~*~*~;:conc-name ~(~S~)~@[~%~VT~]~]~
505 ~:[~*~;:documentation ~S~])"
507 index (+ indentation 5)
508 arity (and (or other alias-for conc-name documentation) (+ indentation 5))
509 other other (and (or alias-for conc-name documentation) (+ indentation 5))
510 alias-for alias-for (and (or documentation conc-name) (+ indentation 5))
511 conc-name conc-name (and documentation (+ indentation 5))
512 documentation documentation))
513 (loop for (enum . more) on (proto-enums message) doing
514 (write-schema-as type enum stream :indentation (+ indentation 2) :more more)
517 (loop for (field . more) on (proto-fields message) doing
518 (write-schema-as type field stream
519 :indentation (+ indentation 2) :more more
525 (write-schema-documentation type documentation stream :indentation indentation))
526 (format stream "~&~@[~VT~](proto:define-~A ~(~S~)"
527 (and (not (zerop indentation)) indentation)
528 (if (eq message-type :message) "message" "extend") class)
529 (let ((other (and name (not (string-equal name (class-name->proto class))) name)))
530 (cond ((eq message-type :extends)
531 (format stream " ()"))
532 ((or other alias-for conc-name documentation)
533 (format stream "~%~@[~VT~](~:[~*~*~;:name ~(~S~)~@[~%~VT~]~]~
534 ~:[~*~*~;:alias-for ~(~S~)~@[~%~VT~]~]~
535 ~:[~*~*~;:conc-name ~(~S~)~@[~%~VT~]~]~
536 ~:[~*~;:documentation ~S~])"
538 other other (and (or alias-for conc-name documentation) (+ indentation 5))
539 alias-for alias-for (and (or documentation conc-name) (+ indentation 5))
540 conc-name conc-name (and documentation (+ indentation 5))
541 documentation documentation))
543 (format stream " ()"))))
544 (cond ((eq message-type :extends)
545 (loop for (field . more) on (proto-extended-fields message) doing
546 (write-schema-as type field stream
547 :indentation (+ indentation 2) :more more
552 (loop for (enum . more) on (proto-enums message) doing
553 (write-schema-as type enum stream :indentation (+ indentation 2) :more more)
556 (loop for (msg . more) on (proto-messages message) doing
557 (unless (eq (proto-message-type msg) :group)
558 (write-schema-as type msg stream :indentation (+ indentation 2) :more more)
561 (loop for (field . more) on (proto-fields message) doing
562 (write-schema-as type field stream
563 :indentation (+ indentation 2) :more more
567 (loop for (extension . more) on (proto-extensions message) doing
568 (write-schema-as type extension stream :indentation (+ indentation 2) :more more)
570 (terpri stream)))))))
571 (format stream ")"))))
573 (defparameter *protobuf-slot-comment-column* 56)
574 (defmethod write-schema-as ((type (eql :lisp)) (field protobuf-field) stream
575 &key (indentation 0) more message)
576 (with-prefixed-accessors (value required index packed options documentation) (proto- field)
577 (let* ((class (if (eq (proto-class field) 'boolean) :bool (proto-class field)))
578 (msg (and (not (keywordp class))
579 (or (find-message message class) (find-enum message class))))
580 (type (let ((cl (case class
587 ((:fixed32) 'fixed32)
588 ((:fixed64) 'fixed64)
589 ((:sfixed32) 'sfixed32)
590 ((:sfixed64) 'sfixed64)
592 ((:double) 'double-float)
595 ((:bytes) 'byte-vector)
598 (cond ((eq required :optional)
600 ((eq required :repeated)
601 (if (vector-field-p field)
605 (cond ((and (typep msg 'protobuf-message)
606 (eq (proto-message-type msg) :group))
607 (write-schema-as :lisp msg stream :indentation indentation :index index :arity required))
609 (let* ((defaultp (if (proto-alias-for message)
610 (if (eq (proto-required field) :optional)
612 (and (proto-default field)
613 (not (equalp (proto-default field) #()))
614 (not (empty-default-p field))))
615 (not (empty-default-p field))))
616 (default (proto-default field))
617 (default (and defaultp
618 (cond ((and (typep msg 'protobuf-enum)
619 (or (stringp default) (symbolp default)))
620 (let ((e (find default (proto-values msg)
621 :key #'proto-name :test #'string=)))
622 (and e (proto-value e))))
624 (boolean-true-p default))
626 (default (and defaultp
627 (if (stringp default) (escape-string default) default)))
628 (conc-name (proto-conc-name message))
629 (reader (when (and (not (eq (proto-reader field) value))
630 (not (string-equal (proto-reader field)
631 (format nil "~A~A" conc-name value))))
632 (proto-reader field)))
633 (writer (when (and (not (eq (proto-writer field) value))
634 (not (string-equal (proto-writer field)
635 (format nil "~A~A" conc-name value))))
636 (proto-writer field)))
637 (slot-name (if *show-lisp-field-indexes*
638 (format nil "(~(~S~) ~D)" value index)
639 (format nil "~(~S~)" value))))
640 (format stream (if (and (keywordp class) (not (eq class :bool)))
641 ;; Keyword class means a primitive type, print default with ~S
642 "~&~@[~VT~](~A :type ~(~S~)~:[~*~; :default ~S~]~
643 ~@[ :reader ~(~S~)~]~@[ :writer ~(~S~)~]~@[ :packed ~(~S~)~]~
644 ~@[ :options (~{~@/protobuf-option/~^ ~})~])~
646 ;; Non-keyword class means an enum type, print default with ~(~S~)
647 "~&~@[~VT~](~A :type ~(~S~)~:[~*~; :default ~(~S~)~]~
648 ~@[ :reader ~(~S~)~]~@[ :writer ~(~S~)~]~@[ :packed ~(~S~)~]~
649 ~@[ :options (~{~@/protobuf-option/~^ ~})~])~
651 (and (not (zerop indentation)) indentation)
652 slot-name type defaultp default reader writer packed options
653 ;; Don't write the comment if we'll insert a close paren after it
654 (and more documentation) *protobuf-slot-comment-column* documentation)))))))
656 (defmethod write-schema-as ((type (eql :lisp)) (extension protobuf-extension) stream
657 &key (indentation 0) more)
658 (declare (ignore more))
659 (with-prefixed-accessors (from to) (proto-extension- extension)
660 (format stream "~&~@[~VT~](proto:define-extension ~D ~D)"
661 (and (not (zerop indentation)) indentation)
662 from (if (eql to #.(1- (ash 1 29))) "max" to))))
665 (defmethod write-schema-as ((type (eql :lisp)) (service protobuf-service) stream
666 &key (indentation 0) more)
667 (declare (ignore more))
668 (with-prefixed-accessors (class documentation) (proto- service)
670 (write-schema-documentation type documentation stream :indentation indentation))
671 (format stream "~&~@[~VT~](proto:define-service ~(~S~)"
672 (and (not (zerop indentation)) indentation) (proto-class service))
674 (format stream "~%~@[~VT~](:documentation ~S)"
675 (+ indentation 4) documentation))
677 (format stream " ()")))
678 (loop for (method . more) on (proto-methods service) doing
679 (write-schema-as type method stream :indentation (+ indentation 2) :more more)
682 (format stream ")")))
684 (defmethod write-schema-as ((type (eql :lisp)) (method protobuf-method) stream
685 &key (indentation 0) more)
686 (declare (ignore more))
687 (with-prefixed-accessors
688 (class documentation input-type output-type options) (proto- method)
690 (write-schema-documentation type documentation stream :indentation indentation))
691 (format stream "~&~@[~VT~](~(~S~) (~(~S~) ~(~S~))"
692 (and (not (zerop indentation)) indentation)
693 class input-type output-type)
695 (format stream "~%~VT:options (~{~@/protobuf-option/~^ ~})"
696 (+ indentation 2) options))
697 (format stream ")")))
700 ;;; Collect symbols to be exported
702 (defgeneric collect-exports (schema)
704 "Collect all the symbols that should be exported from a Protobufs package"))
706 (defmethod collect-exports ((schema protobuf-schema))
709 (append (mapcan #'collect-exports (proto-enums schema))
710 (mapcan #'collect-exports (proto-messages schema))
711 (mapcan #'collect-exports (proto-services schema))))
714 ;; Export just the type name
715 (defmethod collect-exports ((enum protobuf-enum))
716 (list (proto-class enum)))
718 ;; Export the class name and all of the accessor names
719 (defmethod collect-exports ((message protobuf-message))
720 (append (list (proto-class message))
721 (mapcan #'collect-exports (proto-fields message))))
723 ;; Export just the slot accessor name
724 (defmethod collect-exports ((field protobuf-field))
725 (list (proto-slot field)))
727 ;; Export the names of all the methods
728 (defmethod collect-exports ((service protobuf-service))
729 (mapcan #'collect-exports (proto-methods service)))
731 ;; Export just the method name
732 (defmethod collect-exports ((method protobuf-method))
733 (list (proto-client-stub method) (proto-server-stub method)))