package models import ( "fmt" "reflect" "strings" "time" ) // Change represents a change in the rpdata history through the API. type Change struct { ID string `bson:"_id"` Model ChangeModel `bson:"model"` Op string `bson:"op"` Author string `bson:"author"` Listed bool `bson:"listed"` Keys []ChangeKey `bson:"keys"` Date time.Time `bson:"date"` ChangeObjectSet } type ChangeObjectSet struct { Logs []*Log `bson:"logs" json:"logs,omitempty"` Characters []*Character `bson:"characters" json:"characters,omitempty"` Channels []*Channel `bson:"channels" json:"channels,omitempty"` Posts []*Post `bson:"posts" json:"posts,omitempty"` Stories []*Story `bson:"stories" json:"stories,omitempty"` Tags []*Tag `bson:"tags" json:"tags,omitempty"` Chapters []*Chapter `bson:"chapters" json:"chapters,omitempty"` Comments []*Comment `bson:"comments" json:"comments,omitempty"` Files []*File `bson:"files" json:"files,omitempty"` } // AddObject adds the model into the appropriate array. func (change *ChangeObjectSet) AddObject(object interface{}) bool { if v := reflect.ValueOf(object); v.Kind() == reflect.Struct { ptr := reflect.PtrTo(v.Type()) ptrValue := reflect.New(ptr.Elem()) ptrValue.Elem().Set(v) object = ptrValue.Interface() } switch object := object.(type) { case *Log: change.Logs = append(change.Logs, object) case []*Log: change.Logs = append(change.Logs, object...) case *Character: change.Characters = append(change.Characters, object) case []*Character: change.Characters = append(change.Characters, object...) case *Channel: change.Channels = append(change.Channels, object) case []*Channel: change.Channels = append(change.Channels, object...) case *Post: change.Posts = append(change.Posts, object) case []*Post: change.Posts = append(change.Posts, object...) case *Story: change.Stories = append(change.Stories, object) case []*Story: change.Stories = append(change.Stories, object...) case *Tag: change.Tags = append(change.Tags, object) case []*Tag: change.Tags = append(change.Tags, object...) case *Chapter: change.Chapters = append(change.Chapters, object) case []*Chapter: change.Chapters = append(change.Chapters, object...) case *Comment: change.Comments = append(change.Comments, object) case []*Comment: change.Comments = append(change.Comments, object...) case *File: change.Files = append(change.Files, object) case []*File: change.Files = append(change.Files, object...) default: return false } return true } // Objects makes a combined, mixed array of all the models stored in this change. func (change *Change) Objects() []interface{} { data := make([]interface{}, 0, 8) for _, log := range change.Logs { data = append(data, log) } for _, channel := range change.Channels { data = append(data, channel) } for _, character := range change.Characters { data = append(data, character) } for _, post := range change.Posts { data = append(data, post) } for _, story := range change.Stories { data = append(data, story) } for _, tag := range change.Tags { data = append(data, tag) } for _, chapter := range change.Chapters { data = append(data, chapter) } for _, comment := range change.Comments { data = append(data, comment) } for _, file := range change.Files { data = append(data, file) } return data } func (change *Change) PassesFilter(filter ChangeFilter) bool { if filter.Author != nil && change.Author != *filter.Author { return false } // For unlisted changes, pass it only if the filter refers to the specific index. if !change.Listed { hasSpecificKey := false KeyFindLoop: for _, key := range filter.Keys { if key.ID == "*" { continue } for _, changeKey := range change.Keys { if changeKey.Model == key.Model && changeKey.ID == key.ID { hasSpecificKey = true break KeyFindLoop } } } if !hasSpecificKey { return false } } if filter.EarliestDate != nil && filter.EarliestDate.Before(change.Date) { return false } if len(filter.Keys) > 0 { foundKey := false KeyFindLoop2: for _, key := range filter.Keys { for _, changeKey := range change.Keys { if changeKey == key { foundKey = true break KeyFindLoop2 } } } if !foundKey { return false } } return true } // ChangeKey is a key for a change that can be used when subscribing to them. type ChangeKey struct { Model ChangeModel `bson:"model"` ID string `bson:"id"` } func (ck *ChangeKey) String() string { return ck.Model.String() + ":" + ck.ID } func (ck *ChangeKey) Decode(str string) error { split := strings.Split(str, ":") if len(split) != 2 { return fmt.Errorf("invalid change key: %s", str) } model := ChangeModel(split[0]) if !model.IsValid() { return fmt.Errorf("invalid change key model: %s", model) } ck.Model = model ck.ID = split[1] return nil } // ChangeFilter is a filter for listing changes. type ChangeFilter struct { Keys []ChangeKey EarliestDate *time.Time LatestDate *time.Time Author *string Limit *int }