static void test_mapentry_check() { upb_status s = UPB_STATUS_INIT; upb_msgdef *m = upb_msgdef_new(&m); upb_fielddef *f = upb_fielddef_new(&f); upb_symtab *symtab = upb_symtab_new(&symtab); upb_msgdef *subm = upb_msgdef_new(&subm); upb_def *defs[2]; upb_msgdef_setfullname(m, "TestMessage", &s); upb_fielddef_setname(f, "field1", &s); upb_fielddef_setnumber(f, 1, &s); upb_fielddef_setlabel(f, UPB_LABEL_OPTIONAL); upb_fielddef_settype(f, UPB_TYPE_MESSAGE); upb_fielddef_setsubdefname(f, ".MapEntry", &s); upb_msgdef_addfield(m, f, &f, &s); ASSERT(upb_ok(&s)); upb_msgdef_setfullname(subm, "MapEntry", &s); upb_msgdef_setmapentry(subm, true); defs[0] = upb_msgdef_upcast_mutable(m); defs[1] = upb_msgdef_upcast_mutable(subm); upb_symtab_add(symtab, defs, 2, NULL, &s); /* Should not have succeeded: non-repeated field pointing to a MapEntry. */ ASSERT(!upb_ok(&s)); upb_fielddef_setlabel(f, UPB_LABEL_REPEATED); upb_symtab_add(symtab, defs, 2, NULL, &s); ASSERT(upb_ok(&s)); upb_symtab_unref(symtab, &symtab); upb_msgdef_unref(subm, &subm); upb_msgdef_unref(m, &m); }
static void test_descriptor_flags() { upb_msgdef *m = upb_msgdef_new(&m); upb_msgdef *m2; upb_status s = UPB_STATUS_INIT; ASSERT(upb_msgdef_mapentry(m) == false); upb_msgdef_setfullname(m, "TestMessage", &s); ASSERT(upb_ok(&s)); upb_msgdef_setmapentry(m, true); ASSERT(upb_msgdef_mapentry(m) == true); m2 = upb_msgdef_dup(m, &m2); ASSERT(upb_msgdef_mapentry(m2) == true); upb_msgdef_unref(m, &m); upb_msgdef_unref(m2, &m2); }
/* * call-seq: * MessageBuilderContext.map(name, key_type, value_type, number, * value_type_class = nil) * * Defines a new map field on this message type with the given key and value * types, tag number, and type class (for message and enum value types). The key * type must be :int32/:uint32/:int64/:uint64, :bool, or :string. The value type * type must be a Ruby symbol (as accepted by FieldDescriptor#type=) and the * type_class must be a string, if present (as accepted by * FieldDescriptor#submsg_name=). */ VALUE MessageBuilderContext_map(int argc, VALUE* argv, VALUE _self) { DEFINE_SELF(MessageBuilderContext, self, _self); if (argc < 4) { rb_raise(rb_eArgError, "Expected at least 4 arguments."); } VALUE name = argv[0]; VALUE key_type = argv[1]; VALUE value_type = argv[2]; VALUE number = argv[3]; VALUE type_class = (argc > 4) ? argv[4] : Qnil; // Validate the key type. We can't accept enums, messages, or floats/doubles // as map keys. (We exclude these explicitly, and the field-descriptor setter // below then ensures that the type is one of the remaining valid options.) if (SYM2ID(key_type) == rb_intern("float") || SYM2ID(key_type) == rb_intern("double") || SYM2ID(key_type) == rb_intern("enum") || SYM2ID(key_type) == rb_intern("message")) { rb_raise(rb_eArgError, "Cannot add a map field with a float, double, enum, or message " "type."); } // Create a new message descriptor for the map entry message, and create a // repeated submessage field here with that type. VALUE mapentry_desc = rb_class_new_instance(0, NULL, cDescriptor); VALUE mapentry_desc_name = rb_funcall(self->descriptor, rb_intern("name"), 0); mapentry_desc_name = rb_str_cat2(mapentry_desc_name, "_MapEntry_"); mapentry_desc_name = rb_str_cat2(mapentry_desc_name, rb_id2name(SYM2ID(name))); Descriptor_name_set(mapentry_desc, mapentry_desc_name); // The 'mapentry' attribute has no Ruby setter because we do not want the user // attempting to DIY the setup below; we want to ensure that the fields are // correct. So we reach into the msgdef here to set the bit manually. Descriptor* mapentry_desc_self = ruby_to_Descriptor(mapentry_desc); upb_msgdef_setmapentry((upb_msgdef*)mapentry_desc_self->msgdef, true); // optional <type> key = 1; VALUE key_field = rb_class_new_instance(0, NULL, cFieldDescriptor); FieldDescriptor_name_set(key_field, rb_str_new2("key")); FieldDescriptor_label_set(key_field, ID2SYM(rb_intern("optional"))); FieldDescriptor_number_set(key_field, INT2NUM(1)); FieldDescriptor_type_set(key_field, key_type); Descriptor_add_field(mapentry_desc, key_field); // optional <type> value = 2; VALUE value_field = rb_class_new_instance(0, NULL, cFieldDescriptor); FieldDescriptor_name_set(value_field, rb_str_new2("value")); FieldDescriptor_label_set(value_field, ID2SYM(rb_intern("optional"))); FieldDescriptor_number_set(value_field, INT2NUM(2)); FieldDescriptor_type_set(value_field, value_type); if (type_class != Qnil) { VALUE submsg_name = rb_str_new2("."); // prepend '.' to make name absolute. submsg_name = rb_str_append(submsg_name, type_class); FieldDescriptor_submsg_name_set(value_field, submsg_name); } Descriptor_add_field(mapentry_desc, value_field); // Add the map-entry message type to the current builder, and use the type to // create the map field itself. Builder* builder_self = ruby_to_Builder(self->builder); rb_ary_push(builder_self->pending_list, mapentry_desc); VALUE map_field = rb_class_new_instance(0, NULL, cFieldDescriptor); VALUE name_str = rb_str_new2(rb_id2name(SYM2ID(name))); FieldDescriptor_name_set(map_field, name_str); FieldDescriptor_number_set(map_field, number); FieldDescriptor_label_set(map_field, ID2SYM(rb_intern("repeated"))); FieldDescriptor_type_set(map_field, ID2SYM(rb_intern("message"))); VALUE submsg_name = rb_str_new2("."); // prepend '.' to make name absolute. submsg_name = rb_str_append(submsg_name, mapentry_desc_name); FieldDescriptor_submsg_name_set(map_field, submsg_name); Descriptor_add_field(self->descriptor, map_field); return Qnil; }