Commits

Jan Borsodi committed 86563d6

Added support for using default values for fields with enums, use field.defaultObject() to access the enum or any other resolved default value. The original raw text can be fetched from field.defaultText().

  • Participants
  • Parent commits e94738a
  • Branches fixes

Comments (0)

Files changed (6)

File hob/parser.py

         self._reset_docs()
         if not hasattr(self._item, "setDefault"):
             raise BuildError("Cannot set default value on current item (%r)" % type(self._item))
-        parsed_value = parse_option_value(value)
-        if isinstance(self._item, Field):
-            if self._item.type == Proto.Bool:
-                if type(parsed_value) != bool:
-                    raise BuildError("Default value for bool field must be either true or false")
-            elif self._item.type in (Proto.Int32, Proto.Sint32, Proto.Uint32, Proto.Int64, Proto.Sint64, Proto.Uint64, Proto.Fixed32, Proto.Fixed64):
-                if type(parsed_value) != int:
-                    raise BuildError("Default value for numeric field must be a number")
-            elif self._item.type == Proto.String:
-                if not isinstance(parsed_value, str):
-                    raise BuildError("Default value for string field must be a string or identifier")
-        self._item.setDefault(parsed_value)
+        self._item.setDefaultText(value)
+
+        # If the field is an enum then the value must be looked up later, the name is enum value of the enum
+        if isinstance(self._item, Field) and self._item.message:
+            def set_default_enum(sym, item):
+                if isinstance(sym.owner.message, Enum):
+                    sym.owner.setDefaultObject(item)
+                else:
+                    raise BuildError("Messages can't have default values")
+            enum_sym = Symbol(value, self._item, None, EnumValue, current=self._item.message, callback=set_default_enum)
+            self._symbols.append(enum_sym)
+        else:
+            parsed_value = parse_option_value(value)
+            if isinstance(self._item, Field):
+                if self._item.type == Proto.Bool:
+                    if type(parsed_value) != bool:
+                        raise BuildError("Default value for bool field must be either true or false")
+                elif self._item.type in (Proto.Int32, Proto.Sint32, Proto.Uint32, Proto.Int64, Proto.Sint64, Proto.Uint64, Proto.Fixed32, Proto.Fixed64):
+                    if type(parsed_value) != int:
+                        raise BuildError("Default value for numeric field must be a number")
+                elif self._item.type == Proto.String:
+                    if not isinstance(parsed_value, str):
+                        raise BuildError("Default value for string field must be a string or identifier")
+            self._item.setDefault(parsed_value)
 
     def on_comment(self, comment):
         self._comments.append(comment)

File hob/proto.py

             q = Quantifier.Required
         self.q = q
         self.default = default
+        self.default_text = str(default)
+        self.default_object = default
         self.message = message
         self.comment = None
 
         return ptype.name
 
     def defaultValue(self):
-        if self.type == Proto.Bool:
+        if self.message and isinstance(self.message, Enum):
+            if isinstance(self.default_object, EnumValue):
+                return self.default_object.name
+            raise ProtoError("Invalid default value for Enum object %s" % ".".join(self.message.path()))
+        elif self.type == Proto.Bool:
             if self.default:
                 return "true"
             else:
         else:
             return str(self.default)
 
+    def defaultObject(self):
+        return self.default_object
+
+    def defaultText(self):
+        return self.default_text
+
     def setDefault(self, value):
         self.default = value
+        self.default_object = value
+
+    def setDefaultText(self, text):
+        self.default_text = text
+
+    def setDefaultObject(self, item):
+        self.default_object = item
 
 class Message(Element):
     fields = []

File templates/proto/message.mako

 <%
   # TODO: Default value must work when options are present, maybe put default as an option?
   def formatDefault(field):
-    if field.default == None:
+    if field.defaultObject() == None:
       return ""
     return " [default = %s]" % field.defaultValue()
   spaces = " "*g.level

File tests/proto/enums.proto

 {
     required WithEnum.Type type = 1;
 }
+
+/**
+ * Enum field with a default enum value.
+ */
+message DefaultEnum
+{
+    required WithEnum.Type type = 1 [default = RED];
+}

File tests/syntax_scope/enums.proto

     required WithEnum.Type type = 1;
 }
 
+/**
+ * Enum field with a default enum value.
+ */
+message DefaultEnum
+{
+    required WithEnum.Type type = 1 [default = RED];
+}
+

File tests/test_parser.py

 import os
 from hob.parser import Lexer, create_lexer_rules, PackageBuilder, Loader, ParseError, BuildError
-from hob.proto import Package, Request, Event, Message
+from hob.proto import Package, Request, Event, Message, Proto
 
 class FakeLoadError(Exception):
     pass
   VIDEO = 6;
 }
 
+message DefaultValue
+{
+  required Corpus corpus1 = 1;
+  required Corpus corpus2 = 2 [default = VIDEO];
+}
+
 """)
     assert len(pkg.enums) == 2
 
     assert enum.values[4].name == values[4][0] and enum.values[4].value == values[4][1]
     assert enum.values[5].name == values[5][0] and enum.values[5].value == values[5][1]
 
+    msg = pkg["DefaultValue"]
+    assert msg["corpus1"].type == Proto.Int32
+    assert msg["corpus1"].message == enum
+    assert msg["corpus2"].type == Proto.Int32
+    assert msg["corpus2"].message == enum
+    assert msg["corpus2"].defaultObject() == enum["VIDEO"]
+
 def test_nestedMessages():
     pkg = loads("""message Foo
 {