<commit_before>package coal

import (
	"reflect"
	"strings"

	"gopkg.in/mgo.v2/bson"
)

var metaCache = make(map[string]*Meta)

var baseType = reflect.TypeOf(Base{})
var toOneType = reflect.TypeOf(bson.ObjectId(""))
var optionalToOneType = reflect.TypeOf(new(bson.ObjectId))
var toManyType = reflect.TypeOf(make([]bson.ObjectId, 0))
var hasOneType = reflect.TypeOf(HasOne{})
var hasManyType = reflect.TypeOf(HasMany{})

// The HasOne type denotes a has-one relationship in a model declaration.
//
// Note: In fire HasOne relationships will be fetched without authorization. This
// means that the query could return ids to resources that would normally not be
// accessible.
type HasOne struct{}

// The HasMany type denotes a has-many relationship in a model declaration.
//
// Note: In fire HasMany relationships will be fetched without authorization. This
// means that the query could return ids to resources that would normally not be
// accessible.
type HasMany struct{}

// A Field contains the meta information about a single field of a model.
type Field struct {
	Name       string
	Type       reflect.Type
	Kind       reflect.Kind
	JSONName   string
	BSONName   string
	Optional   bool
	ToOne      bool
	ToMany     bool
	HasOne     bool
	HasMany    bool
	RelName    string
	RelType    string
	RelInverse string

	index int
}

// Meta stores extracted meta data from a model.
type Meta struct {
	Name       string
	PluralName string
	Collection string
	Fields     []Field

	model Model
}

// NewMeta returns the Meta structure for the passed Model.
//
// Note: This method panics if the passed Model has invalid fields and tags.
func NewMeta(model Model) *Meta {
	// get type and name
	modelType := reflect.TypeOf(model).Elem()
	modelName := modelType.String()

	// check if meta has already been cached
	meta, ok := metaCache[modelName]
	if ok {
		return meta
	}

	// create new meta
	meta = &Meta{
		Name:  modelName,
		model: model,
	}

	// iterate through all fields
	for i := 0; i < modelType.NumField(); i++ {
		structField := modelType.Field(i)

		// get coal tag
		coalStructTag := structField.Tag.Get("coal")

		// check if field is the Base
		if structField.Type == baseType {
			baseTag := strings.Split(coalStructTag, ":")

			// check json tag
			if structField.Tag.Get("json") != "-" {
				panic(`coal: expected to find a tag of the form json:"-" on Base`)
			}

			// check bson tag
			if structField.Tag.Get("bson") != ",inline" {
				panic(`coal: expected to find a tag of the form bson:",inline" on Base`)
			}

			// check tag
			if len(baseTag) > 2 || baseTag[0] == "" {
				panic(`coal: expected to find a tag of the form coal:"plural-name[:collection]" on Base`)
			}

			// infer plural and collection names
			meta.PluralName = baseTag[0]
			meta.Collection = baseTag[0]

			// infer collection
			if len(baseTag) == 2 {
				meta.Collection = baseTag[1]
			}

			continue
		}

		// parse individual tags
		coalTags := strings.Split(coalStructTag, ",")
		if len(coalStructTag) == 0 {
			coalTags = nil
		}

		// get field type
		fieldKind := structField.Type.Kind()
		if fieldKind == reflect.Ptr {
			fieldKind = structField.Type.Elem().Kind()
		}

		// prepare field
		field := Field{
			Name:     structField.Name,
			Type:     structField.Type,
			Kind:     fieldKind,
			JSONName: getJSONFieldName(&structField),
			BSONName: getBSONFieldName(&structField),
			Optional: structField.Type.Kind() == reflect.Ptr,
			index:    i,
		}

		// check if field is a valid to-one relationship
		if structField.Type == toOneType || structField.Type == optionalToOneType {
			if len(coalTags) > 0 && strings.Count(coalTags[0], ":") > 0 {
				// check tag
				if strings.Count(coalTags[0], ":") > 1 {
					panic(`coal: expected to find a tag of the form coal:"name:type" on to-one relationship`)
				}

				// parse special to-one relationship tag
				toOneTag := strings.Split(coalTags[0], ":")

				// set relationship data
				field.ToOne = true
				field.RelName = toOneTag[0]
				field.RelType = toOneTag[1]

				// remove tag
				coalTags = coalTags[1:]
			}
		}

		// check if field is a valid to-many relationship
		if structField.Type == toManyType {
			if len(coalTags) > 0 && strings.Count(coalTags[0], ":") > 0 {
				// check tag
				if strings.Count(coalTags[0], ":") > 1 {
					panic(`coal: expected to find a tag of the form coal:"name:type" on to-many relationship`)
				}

				// parse special to-many relationship tag
				toManyTag := strings.Split(coalTags[0], ":")

				// set relationship data
				field.ToMany = true
				field.RelName = toManyTag[0]
				field.RelType = toManyTag[1]

				// remove tag
				coalTags = coalTags[1:]
			}
		}

		// check if field is a valid has-one relationship
		if structField.Type == hasOneType {
			// check tag
			if len(coalTags) != 1 || strings.Count(coalTags[0], ":") != 2 {
				panic(`coal: expected to find a tag of the form coal:"name:type:inverse" on has-one relationship`)
			}

			// parse special has-one relationship tag
			hasOneTag := strings.Split(coalTags[0], ":")

			// set relationship data
			field.HasOne = true
			field.RelName = hasOneTag[0]
			field.RelType = hasOneTag[1]
			field.RelInverse = hasOneTag[2]

			// remove tag
			coalTags = coalTags[1:]
		}

		// check if field is a valid has-many relationship
		if structField.Type == hasManyType {
			// check tag
			if len(coalTags) != 1 || strings.Count(coalTags[0], ":") != 2 {
				panic(`coal: expected to find a tag of the form coal:"name:type:inverse" on has-many relationship`)
			}

			// parse special has-many relationship tag
			hasManyTag := strings.Split(coalTags[0], ":")

			// set relationship data
			field.HasMany = true
			field.RelName = hasManyTag[0]
			field.RelType = hasManyTag[1]
			field.RelInverse = hasManyTag[2]

			// remove tag
			coalTags = coalTags[1:]
		}

		// panic on any additional tags
		for _, tag := range coalTags {
			panic("coal: unexpected tag " + tag)
		}

		// add field
		meta.Fields = append(meta.Fields, field)
	}

	// cache meta
	metaCache[modelName] = meta

	return meta
}

// FindField returns the first field that has a matching Name, JSONName, or
// BSONName. FindField will return nil if no field has been found.
func (m *Meta) FindField(name string) *Field {
	for _, field := range m.Fields {
		if field.JSONName == name || field.BSONName == name || field.Name == name {
			return &field
		}
	}

	return nil
}

// MustFindField returns the first field that has a matching Name, JSONName, or
// BSONName. MustFindField will panic if no field has been found.
func (m *Meta) MustFindField(name string) *Field {
	field := m.FindField(name)
	if field == nil {
		panic(`coal: field "` + name + `" not found on "` + m.Name + `"`)
	}

	return field
}

// Make returns a pointer to a new zero initialized model e.g. *Post.
//
// Note: Other libraries like mgo might replace the pointer content with a new
// structure, therefore the model eventually needs to be initialized again
// using Init().
func (m *Meta) Make() Model {
	pointer := reflect.New(reflect.TypeOf(m.model).Elem()).Interface()
	return Init(pointer.(Model))
}

// MakeSlice returns a pointer to a zero length slice of the model e.g. *[]*Post.
//
// Note: Don't forget to initialize the slice using InitSlice() after adding
// elements with libraries like mgo.
func (m *Meta) MakeSlice() interface{} {
	slice := reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(m.model)), 0, 0)
	pointer := reflect.New(slice.Type())
	pointer.Elem().Set(slice)
	return pointer.Interface()
}

func getJSONFieldName(field *reflect.StructField) string {
	tag := field.Tag.Get("json")
	values := strings.Split(tag, ",")

	// check for "-"
	if tag == "-" {
		return ""
	}

	// check first value
	if len(tag) > 0 || len(values[0]) > 0 {
		return values[0]
	}

	return field.Name
}

func getBSONFieldName(field *reflect.StructField) string {
	tag := field.Tag.Get("bson")
	values := strings.Split(tag, ",")

	// check for "-"
	if tag == "-" {
		return ""
	}

	// check first value
	if len(tag) > 0 || len(values[0]) > 0 {
		return values[0]
	}

	return strings.ToLower(field.Name)
}
<commit_msg>updated comments<commit_after>package coal

import (
	"reflect"
	"strings"

	"gopkg.in/mgo.v2/bson"
)

var metaCache = make(map[string]*Meta)

var baseType = reflect.TypeOf(Base{})
var toOneType = reflect.TypeOf(bson.ObjectId(""))
var optionalToOneType = reflect.TypeOf(new(bson.ObjectId))
var toManyType = reflect.TypeOf(make([]bson.ObjectId, 0))
var hasOneType = reflect.TypeOf(HasOne{})
var hasManyType = reflect.TypeOf(HasMany{})

// The HasOne type denotes a has-one relationship in a model declaration.
//
// Has-one relationships requires that the referencing side is ensuring that the
// reference is unique. In fire this should be done using a uniqueness validator
// and a unique index on the collection.
//
// Note: In fire has-one relationships will be fetched without authorization.
// This means that the query could return ids to resources that would normally
// not be accessible.
type HasOne struct{}

// The HasMany type denotes a has-many relationship in a model declaration.
//
// Note: In fire has-many relationships will be fetched without authorization.
// This means that the query could return ids to resources that would normally
// not be accessible.
type HasMany struct{}

// A Field contains the meta information about a single field of a model.
type Field struct {
	Name       string
	Type       reflect.Type
	Kind       reflect.Kind
	JSONName   string
	BSONName   string
	Optional   bool
	ToOne      bool
	ToMany     bool
	HasOne     bool
	HasMany    bool
	RelName    string
	RelType    string
	RelInverse string

	index int
}

// Meta stores extracted meta data from a model.
type Meta struct {
	Name       string
	PluralName string
	Collection string
	Fields     []Field

	model Model
}

// NewMeta returns the Meta structure for the passed Model.
//
// Note: This method panics if the passed Model has invalid fields and tags.
func NewMeta(model Model) *Meta {
	// get type and name
	modelType := reflect.TypeOf(model).Elem()
	modelName := modelType.String()

	// check if meta has already been cached
	meta, ok := metaCache[modelName]
	if ok {
		return meta
	}

	// create new meta
	meta = &Meta{
		Name:  modelName,
		model: model,
	}

	// iterate through all fields
	for i := 0; i < modelType.NumField(); i++ {
		structField := modelType.Field(i)

		// get coal tag
		coalStructTag := structField.Tag.Get("coal")

		// check if field is the Base
		if structField.Type == baseType {
			baseTag := strings.Split(coalStructTag, ":")

			// check json tag
			if structField.Tag.Get("json") != "-" {
				panic(`coal: expected to find a tag of the form json:"-" on Base`)
			}

			// check bson tag
			if structField.Tag.Get("bson") != ",inline" {
				panic(`coal: expected to find a tag of the form bson:",inline" on Base`)
			}

			// check tag
			if len(baseTag) > 2 || baseTag[0] == "" {
				panic(`coal: expected to find a tag of the form coal:"plural-name[:collection]" on Base`)
			}

			// infer plural and collection names
			meta.PluralName = baseTag[0]
			meta.Collection = baseTag[0]

			// infer collection
			if len(baseTag) == 2 {
				meta.Collection = baseTag[1]
			}

			continue
		}

		// parse individual tags
		coalTags := strings.Split(coalStructTag, ",")
		if len(coalStructTag) == 0 {
			coalTags = nil
		}

		// get field type
		fieldKind := structField.Type.Kind()
		if fieldKind == reflect.Ptr {
			fieldKind = structField.Type.Elem().Kind()
		}

		// prepare field
		field := Field{
			Name:     structField.Name,
			Type:     structField.Type,
			Kind:     fieldKind,
			JSONName: getJSONFieldName(&structField),
			BSONName: getBSONFieldName(&structField),
			Optional: structField.Type.Kind() == reflect.Ptr,
			index:    i,
		}

		// check if field is a valid to-one relationship
		if structField.Type == toOneType || structField.Type == optionalToOneType {
			if len(coalTags) > 0 && strings.Count(coalTags[0], ":") > 0 {
				// check tag
				if strings.Count(coalTags[0], ":") > 1 {
					panic(`coal: expected to find a tag of the form coal:"name:type" on to-one relationship`)
				}

				// parse special to-one relationship tag
				toOneTag := strings.Split(coalTags[0], ":")

				// set relationship data
				field.ToOne = true
				field.RelName = toOneTag[0]
				field.RelType = toOneTag[1]

				// remove tag
				coalTags = coalTags[1:]
			}
		}

		// check if field is a valid to-many relationship
		if structField.Type == toManyType {
			if len(coalTags) > 0 && strings.Count(coalTags[0], ":") > 0 {
				// check tag
				if strings.Count(coalTags[0], ":") > 1 {
					panic(`coal: expected to find a tag of the form coal:"name:type" on to-many relationship`)
				}

				// parse special to-many relationship tag
				toManyTag := strings.Split(coalTags[0], ":")

				// set relationship data
				field.ToMany = true
				field.RelName = toManyTag[0]
				field.RelType = toManyTag[1]

				// remove tag
				coalTags = coalTags[1:]
			}
		}

		// check if field is a valid has-one relationship
		if structField.Type == hasOneType {
			// check tag
			if len(coalTags) != 1 || strings.Count(coalTags[0], ":") != 2 {
				panic(`coal: expected to find a tag of the form coal:"name:type:inverse" on has-one relationship`)
			}

			// parse special has-one relationship tag
			hasOneTag := strings.Split(coalTags[0], ":")

			// set relationship data
			field.HasOne = true
			field.RelName = hasOneTag[0]
			field.RelType = hasOneTag[1]
			field.RelInverse = hasOneTag[2]

			// remove tag
			coalTags = coalTags[1:]
		}

		// check if field is a valid has-many relationship
		if structField.Type == hasManyType {
			// check tag
			if len(coalTags) != 1 || strings.Count(coalTags[0], ":") != 2 {
				panic(`coal: expected to find a tag of the form coal:"name:type:inverse" on has-many relationship`)
			}

			// parse special has-many relationship tag
			hasManyTag := strings.Split(coalTags[0], ":")

			// set relationship data
			field.HasMany = true
			field.RelName = hasManyTag[0]
			field.RelType = hasManyTag[1]
			field.RelInverse = hasManyTag[2]

			// remove tag
			coalTags = coalTags[1:]
		}

		// panic on any additional tags
		for _, tag := range coalTags {
			panic("coal: unexpected tag " + tag)
		}

		// add field
		meta.Fields = append(meta.Fields, field)
	}

	// cache meta
	metaCache[modelName] = meta

	return meta
}

// FindField returns the first field that has a matching Name, JSONName, or
// BSONName. FindField will return nil if no field has been found.
func (m *Meta) FindField(name string) *Field {
	for _, field := range m.Fields {
		if field.JSONName == name || field.BSONName == name || field.Name == name {
			return &field
		}
	}

	return nil
}

// MustFindField returns the first field that has a matching Name, JSONName, or
// BSONName. MustFindField will panic if no field has been found.
func (m *Meta) MustFindField(name string) *Field {
	field := m.FindField(name)
	if field == nil {
		panic(`coal: field "` + name + `" not found on "` + m.Name + `"`)
	}

	return field
}

// Make returns a pointer to a new zero initialized model e.g. *Post.
//
// Note: Other libraries like mgo might replace the pointer content with a new
// structure, therefore the model eventually needs to be initialized again
// using Init().
func (m *Meta) Make() Model {
	pointer := reflect.New(reflect.TypeOf(m.model).Elem()).Interface()
	return Init(pointer.(Model))
}

// MakeSlice returns a pointer to a zero length slice of the model e.g. *[]*Post.
//
// Note: Don't forget to initialize the slice using InitSlice() after adding
// elements with libraries like mgo.
func (m *Meta) MakeSlice() interface{} {
	slice := reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(m.model)), 0, 0)
	pointer := reflect.New(slice.Type())
	pointer.Elem().Set(slice)
	return pointer.Interface()
}

func getJSONFieldName(field *reflect.StructField) string {
	tag := field.Tag.Get("json")
	values := strings.Split(tag, ",")

	// check for "-"
	if tag == "-" {
		return ""
	}

	// check first value
	if len(tag) > 0 || len(values[0]) > 0 {
		return values[0]
	}

	return field.Name
}

func getBSONFieldName(field *reflect.StructField) string {
	tag := field.Tag.Get("bson")
	values := strings.Split(tag, ",")

	// check for "-"
	if tag == "-" {
		return ""
	}

	// check first value
	if len(tag) > 0 || len(values[0]) > 0 {
		return values[0]
	}

	return strings.ToLower(field.Name)
}

{'fraction_non_alphanumeric': 0.12450724725574626, 'fraction_numerical': 0.006549821092849779, 'mean_word_length': 3.1142714570858283, 'pattern_counts': {'":': 22, '<': 5, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6832694', 'n_tokens_mistral': 5884, 'n_tokens_neox': 5157, 'n_words': 1698}