Jan Borsodi avatar 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().

Comments (0)

Files changed (6)

         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)
             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 = []

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

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];
+}

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];
+}
+

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
 {
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.