func TestParser(t *testing.T) { testLogger := logger.NewTestLogger(t) for _, fixture := range []struct { String string Length int Definitions []*tag.Definition }{ {`field:foo;complex_field(name:param,name2:param2,name_3:3);field:1;last_field`, 4, []*tag.Definition{ {Name: "field", Value: "foo"}, {Name: "complex_field", Parameters: []tag.Parameter{{Key: "name", Value: "param"}, {Key: "name2", Value: "param2"}, {Key: "name_3", Value: "3"}}}, {Name: "field", Value: "1"}, {Name: "last_field"}, }, }, } { parser := tag.NewParser(strings.NewReader(fixture.String)) parser.SetLogger(testLogger) definitions, err := parser.Parse() test.Fatal(t, err, nil) test.Fatal(t, len(definitions), fixture.Length) test.Fatal(t, reflect.DeepEqual(definitions, fixture.Definitions), true) } }
func ExampleParser() { // Let's parse the meta tag in Title field of type Book type Book struct { Title string `meta:"isbn:11010303030;published;author(firstname:john,lastname:doe);editor:apress"` } // let's get the tag titleField, _ := reflect.TypeOf(Book{}).FieldByName("Title") metaTag := titleField.Tag.Get("meta") // let's create a parser parser := tag.NewParser(strings.NewReader(metaTag)) // do parse definitions, err := parser.Parse() if err != nil { log.Fatal(err) } fmt.Println(definitions) // Output: // [[ Name 'isbn' , Value '11010303030' , Params '[]' ] [ Name 'published' , Value '' , Params '[]' ] [ Name 'author' , Value '' , Params '[{Key:firstname Value:john} {Key:lastname Value:doe}]' ] [ Name 'editor' , Value 'apress' , Params '[]' ]] }
// getTypeMetadatas takes a pointer to struct and returns the metadata // for the struct or an error if the struct tag is invalid. func getTypeMetadatas(value interface{}) (meta metadata, err error) { Value := reflect.Indirect(reflect.ValueOf(value)) Type := Value.Type() // for each field in struct, read its struct tag and // create a metadata for the field if needed for i := 0; i < Value.NumField(); i++ { Field := Type.Field(i) MetaField := field{name: Field.Name, key: strings.ToLower(Field.Name)} // check bson struct tag and extract the document key Tag := Field.Tag.Get("bson") parts := strings.Split(Tag, ",") if len(parts) > 0 { if key := strings.TrimSpace(parts[0]); key != "" { MetaField.key = key if key == "_id" { meta.idField = Field.Name meta.idKey = "_id" } // ignore the field and continue if key == "-" { MetaField.ignore = true continue } } } if len(parts) > 1 { if part := strings.TrimSpace(parts[1]); part == "omitempty" { MetaField.omitempty = true } } Tag = Field.Tag.Get("odm") if Tag == "-" { MetaField.ignore = true continue } parser := tag.NewParser(strings.NewReader(Tag)) var definitions []*tag.Definition definitions, err = parser.Parse() if err != nil { return meta, err } for _, definition := range definitions { switch strings.ToLower(definition.Name) { case "id": meta.idField = Field.Name case "omitempty": MetaField.omitempty = true case "index": MetaField.index = true for _, parameter := range definition.Parameters { switch toLower(parameter.Key) { case "unique": MetaField.unique = true } } case "referencemany", "referenceone": Relation := relation{} switch strings.ToLower(definition.Name) { case "referencemany": Relation.relation = referenceMany MetaField.key = "odm:" + strings.ToLower(Field.Name) + "ids" case "referenceone": Relation.relation = referenceOne MetaField.key = "odm:" + strings.ToLower(Field.Name) + "id" } for _, parameter := range definition.Parameters { switch strings.ToLower(parameter.Key) { case "mappedby": Relation.mapped = mappedBy Relation.mappedField = parameter.Value case "inversedby": Relation.mapped = inversedBy Relation.mappedField = parameter.Value case "targetdocument": Relation.targetDocument = parameter.Value case "cascade": switch strings.ToLower(parameter.Value) { case "persist": Relation.cascade = persist case "remove": Relation.cascade = remove case "all": Relation.cascade = all } case "storeid": Relation.idStorageField = parameter.Value case "load": switch strings.ToLower(parameter.Value) { case "eager": Relation.load = eager } default: return meta, ErrInvalidAnnotation } MetaField.relation = Relation } default: return meta, ErrInvalidAnnotation } } // remove index definition if field has a relation if MetaField.index == true && MetaField.hasRelation() { MetaField.index = false } // use a specific field to store related ids if MetaField.relation.idStorageField != "" { if key := resolveKeyForField(Type, MetaField.relation.idStorageField); key != "" { MetaField.key = key } } meta.fields = append(meta.fields, MetaField) } return }