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 ;;; Print objects using Protobufs text format
16 (defvar *suppress-line-breaks* nil
17 "When true, don't generate line breaks in the text format")
19 (defgeneric print-text-format (object &optional type &key stream suppress-line-breaks)
21 "Prints the object 'object' of type 'type' onto the stream 'stream' using the
23 If 'suppress-line-breaks' is true, all the output is put on a single line."))
25 (defmethod print-text-format (object &optional type
26 &key (stream *standard-output*)
27 (suppress-line-breaks *suppress-line-breaks*))
28 (let* ((type (or type (class-of object)))
29 (message (find-message-for-class type)))
31 "There is no Protobuf message having the type ~S" type)
32 (macrolet ((read-slot (object slot reader)
33 ;; Don't do a boundp check, we assume the object is fully populated
34 ;; Unpopulated slots should be "nullable" and should contain nil
36 (funcall ,reader ,object)
37 (slot-value ,object ,slot))))
38 (labels ((do-field (object trace indent field)
39 ;; We don't do cycle detection here
40 ;; If the client needs it, he can define his own 'print-text-format'
41 ;; method to clean things up first
42 (let* ((type (if (eq (proto-class field) 'boolean) :bool (proto-class field)))
43 (slot (proto-value field))
44 (reader (proto-reader field))
46 (when (or slot reader)
47 (cond ((eq (proto-required field) :repeated)
48 (cond ((keywordp type)
50 (print-prim v type field stream
51 (or suppress-line-breaks indent)))
52 (read-slot object slot reader)))
53 ((typep (setq msg (and type (or (find-message trace type)
54 (find-enum trace type))))
56 (let ((values (if slot (read-slot object slot reader) (list object))))
58 (let ((indent (+ indent 2)))
60 (if suppress-line-breaks
61 (format stream "~A: { " (proto-name field))
62 (format stream "~&~VT~A: {~%" indent (proto-name field)))
63 (map () (curry #'do-field v msg indent)
65 (if suppress-line-breaks
67 (format stream "~&~VT}~%" indent)))))))
68 ((typep msg 'protobuf-enum)
70 (print-enum v msg field stream
71 (or suppress-line-breaks indent)))
72 (read-slot object slot reader)))))
74 (cond ((keywordp type)
75 (let ((v (read-slot object slot reader)))
76 (when (or v (eq type :bool))
77 (print-prim v type field stream
78 (or suppress-line-breaks indent)))))
79 ((typep (setq msg (and type (or (find-message trace type)
80 (find-enum trace type))))
82 (let ((v (if slot (read-slot object slot reader) object)))
84 (let ((indent (+ indent 2)))
85 (if suppress-line-breaks
86 (format stream "~A: { " (proto-name field))
87 (format stream "~&~VT~A: {~%" indent (proto-name field)))
88 (map () (curry #'do-field v msg indent)
90 (if suppress-line-breaks
92 (format stream "~&~VT}~%" indent))))))
93 ((typep msg 'protobuf-enum)
94 (let ((v (read-slot object slot reader)))
96 (print-enum v msg field stream
97 (or suppress-line-breaks indent))))))))))))
98 (declare (dynamic-extent #'do-field))
99 (if suppress-line-breaks
100 (format stream "~A { " (proto-name message))
101 (format stream "~&~A {~%" (proto-name message)))
102 (map () (curry #'do-field object message 0) (proto-fields message))
103 (if suppress-line-breaks
105 (format stream "~&}~%"))
108 (defun print-prim (val type field stream indent)
111 (format stream "~A: " (proto-name field))
112 (format stream "~&~VT~A: " (+ indent 2) (proto-name field)))
114 ((:int32 :uint32 :int64 :uint64 :sint32 :sint64
115 :fixed32 :sfixed32 :fixed64 :sfixed64)
116 (format stream "~D" val))
118 (format stream "\"~A\"" val))
120 (format stream "~S" val))
122 (format stream "~A" (if val "true" "false")))
124 (format stream "~D" val))
125 ;; A few of our homegrown types
127 (let ((val (if (keywordp val)
129 (format nil "~A:~A" (package-name (symbol-package val)) (symbol-name val)))))
130 (format stream "\"~A\"" val)))
131 ((:date :time :datetime :timestamp)
132 (format stream "~D" val)))
135 (format stream "~%"))))
137 (defun print-enum (val enum field stream indent)
140 (format stream "~A: " (proto-name field))
141 (format stream "~&~VT~A: " (+ indent 2) (proto-name field)))
142 (let ((name (let ((e (find val (proto-values enum) :key #'proto-value)))
143 (and e (proto-name e)))))
144 (format stream "~A" name)
147 (format stream "~%")))))
150 ;;; Parse objects that were serialized using the text format
152 (defgeneric parse-text-format (type &key stream)
154 "Parses an object of type 'type' from the stream 'stream' using the textual format."))
156 (defmethod parse-text-format ((type symbol) &key (stream *standard-input*))
157 (let ((message (find-message-for-class type)))
159 "There is no Protobuf message having the type ~S" type)
160 (parse-text-format message :stream stream)))
162 (defmethod parse-text-format ((message protobuf-message) &key (stream *standard-input*))
163 (let ((name (parse-token stream)))
164 (assert (string= name (proto-name message)) ()
165 "The message is not of the expected type ~A" (proto-name message)))
166 (labels ((deserialize (type trace)
167 (let* ((message (find-message trace type))
169 (make-instance (or (proto-alias-for message) (proto-class message)))))
171 (expect-char stream #\{)
173 (skip-whitespace stream)
174 (when (eql (peek-char nil stream nil) #\})
176 (dolist (slot rslots)
177 (setf (slot-value object slot) (nreverse (slot-value object slot))))
178 (return-from deserialize object))
179 (let* ((name (prog1 (parse-token stream)
180 (expect-char stream #\:)))
181 (field (and name (find name (proto-fields message) :key #'proto-name :test #'string=)))
182 (type (and field (if (eq (proto-class field) 'boolean) :bool (proto-class field))))
183 (slot (and field (proto-value field)))
186 ;;---*** This needs to skip a token or a balanced {}-pair
188 (cond ((and field (eq (proto-required field) :repeated))
189 (cond ((keywordp type)
190 (let ((val (case type
191 ((:float :double) (parse-float stream))
192 ((:string) (parse-string stream))
193 ((:bool) (parse-token stream))
194 (otherwise (parse-int stream)))))
196 (pushnew slot rslots)
197 (push val (slot-value object slot)))))
198 ((typep (setq msg (and type (or (find-message trace type)
199 (find-enum trace type))))
201 (let ((obj (deserialize type msg)))
203 (pushnew slot rslots)
204 (push obj (slot-value object slot)))))
205 ((typep msg 'protobuf-enum)
206 (let* ((name (parse-token stream))
207 (enum (find name (proto-values msg) :key #'proto-name :test #'string=))
208 (val (and enum (proto-value enum))))
210 (pushnew slot rslots)
211 (push val (slot-value object slot)))))))
213 (cond ((keywordp type)
214 (let ((val (case type
215 ((:float :double) (parse-float stream))
216 ((:string) (parse-string stream))
217 ((:bool) (parse-token stream))
218 (otherwise (parse-int stream)))))
220 (setf (slot-value object slot) val))))
221 ((typep (setq msg (and type (or (find-message trace type)
222 (find-enum trace type))))
224 (let ((obj (deserialize type msg)))
226 (setf (slot-value object slot) obj))))
227 ((typep msg 'protobuf-enum)
228 (let* ((name (parse-token stream))
229 (enum (find name (proto-values msg) :key #'proto-name :test #'string=))
230 (val (and enum (proto-value enum))))
232 (setf (slot-value object slot) val)))))))))))))
233 (declare (dynamic-extent #'deserialize))
234 (deserialize (proto-class message) message)))