3
0
corteza/server/pkg/dal/alteration.go
2023-10-26 17:09:59 +02:00

369 lines
7.4 KiB
Go

package dal
import (
"encoding/json"
"fmt"
)
type (
Alteration struct {
ID uint64
BatchID uint64
DependsOn uint64
Resource string
ResourceType string
ConnectionID uint64
AttributeAdd *AttributeAdd
AttributeDelete *AttributeDelete
AttributeReType *AttributeReType
AttributeReEncode *AttributeReEncode
ModelAdd *ModelAdd
ModelDelete *ModelDelete
}
AlterationSet []*Alteration
AttributeAdd struct {
Attr *Attribute `json:"attr"`
}
AttributeDelete struct {
Attr *Attribute `json:"attr"`
}
AttributeReType struct {
Attr *Attribute `json:"attr"`
To Type `json:"to"`
}
AttributeReEncode struct {
Attr *Attribute `json:"attr"`
To Codec `json:"to"`
}
ModelAdd struct {
Model *Model `json:"model"`
}
ModelDelete struct {
Model *Model `json:"model"`
}
// auxAttributeType is a helper struct used for marshaling/unmarshaling
//
// This is required since the Type inside the Attribute is an interface and we
// need to help the encoding/json a bit.
auxAttributeReType struct {
Attr *Attribute `json:"attr"`
To *auxAttributeType `json:"to"`
}
// auxAttributeReEncode is a helper struct used for marshaling/unmarshaling
//
// This is required since the Codec inside the Attribute is an interface and we
// need to help the encoding/json a bit.
auxAttributeReEncode struct {
Attr *Attribute `json:"attr"`
To *auxStoreCodec `json:"to"`
}
)
// Merge merges the two alteration slices
func (aa AlterationSet) Merge(bb AlterationSet) (cc AlterationSet) {
// @todo the Merge function currently just merges the two slices together
// and removes any duplicates that would occur due to the merge.
// We should also handle overlapping/transitive alterations to reduce
// the amount of needles processing.
//
// A quick list of overlapping alterations:
// * attribute A added and then renamed from A to A'
// * attribute A renamed to A' and then renamed to A''
// * attribute A deleted and then created
aux := append(aa, bb...)
seen := make(map[int]bool, len(aux)/2)
for i, a := range aux {
if seen[i] {
continue
}
found := false
for j := i + 1; j < len(aux); j++ {
if seen[j] {
continue
}
if a.compare(*aux[j]) {
seen[j] = true
found = true
cc = append(cc, aux[j])
break
}
}
if !found {
cc = append(cc, a)
}
}
return
}
func (a AttributeReType) MarshalJSON() ([]byte, error) {
aux := auxAttributeReType{
Attr: a.Attr,
To: &auxAttributeType{},
}
switch t := a.To.(type) {
case *TypeID:
aux.To.Type = "ID"
aux.To.ID = t
case *TypeRef:
aux.To.Type = "Ref"
aux.To.Ref = t
case *TypeTimestamp:
aux.To.Type = "Timestamp"
aux.To.Timestamp = t
case *TypeTime:
aux.To.Type = "Time"
aux.To.Time = t
case *TypeDate:
aux.To.Type = "Date"
aux.To.Date = t
case *TypeNumber:
aux.To.Type = "Number"
aux.To.Number = t
case *TypeText:
aux.To.Type = "Text"
aux.To.Text = t
case *TypeBoolean:
aux.To.Type = "Boolean"
aux.To.Boolean = t
case *TypeEnum:
aux.To.Type = "Enum"
aux.To.Enum = t
case *TypeGeometry:
aux.To.Type = "Geometry"
aux.To.Geometry = t
case *TypeJSON:
aux.To.Type = "JSON"
aux.To.JSON = t
case *TypeBlob:
aux.To.Type = "Blob"
aux.To.Blob = t
case *TypeUUID:
aux.To.Type = "UUID"
aux.To.UUID = t
}
return json.Marshal(aux)
}
func (a *AttributeReType) UnmarshalJSON(data []byte) (err error) {
aux := &auxAttributeReType{}
err = json.Unmarshal(data, &aux)
if err != nil {
return err
}
if a == nil {
*a = AttributeReType{}
}
a.Attr = aux.Attr
switch aux.To.Type {
case "ID":
a.To = aux.To.ID
case "Ref":
a.To = aux.To.Ref
case "Timestamp":
a.To = aux.To.Timestamp
case "Time":
a.To = aux.To.Time
case "Date":
a.To = aux.To.Date
case "Number":
a.To = aux.To.Number
case "Text":
a.To = aux.To.Text
case "Boolean":
a.To = aux.To.Boolean
case "Enum":
a.To = aux.To.Enum
case "Geometry":
a.To = aux.To.Geometry
case "JSON":
a.To = aux.To.JSON
case "Blob":
a.To = aux.To.Blob
case "UUID":
a.To = aux.To.UUID
}
return
}
func (a AttributeReEncode) MarshalJSON() ([]byte, error) {
aux := auxAttributeReEncode{
Attr: a.Attr,
To: &auxStoreCodec{},
}
switch t := a.To.(type) {
case *CodecPlain:
aux.To.Type = "CodecPlain"
aux.To.CodecPlain = t
case *CodecRecordValueSetJSON:
aux.To.Type = "CodecRecordValueSetJSON"
aux.To.CodecRecordValueSetJSON = t
case *CodecAlias:
aux.To.Type = "CodecAlias"
aux.To.CodecAlias = t
}
return json.Marshal(aux)
}
func (a *AttributeReEncode) UnmarshalJSON(data []byte) (err error) {
aux := &auxAttributeReEncode{}
err = json.Unmarshal(data, &aux)
if err != nil {
return err
}
if a == nil {
*a = AttributeReEncode{}
}
a.Attr = aux.Attr
switch aux.To.Type {
case "CodecPlain":
a.To = aux.To.CodecPlain
case "CodecRecordValueSetJSON":
a.To = aux.To.CodecRecordValueSetJSON
case "CodecAlias":
a.To = aux.To.CodecAlias
}
return
}
func (a Alteration) compare(b Alteration) (cmp bool) {
if a.AttributeAdd == nil && b.AttributeAdd != nil {
return false
}
if a.AttributeDelete == nil && b.AttributeDelete != nil {
return false
}
if a.AttributeReType == nil && b.AttributeReType != nil {
return false
}
if a.AttributeReEncode == nil && b.AttributeReEncode != nil {
return false
}
if a.ModelAdd == nil && b.ModelAdd != nil {
return false
}
if a.ModelDelete == nil && b.ModelDelete != nil {
return false
}
switch {
case a.AttributeAdd != nil:
return a.compareAttributeAdd(b)
case a.AttributeDelete != nil:
return a.compareAttributeDelete(b)
case a.AttributeReType != nil:
return a.compareAttributeReType(b)
case a.AttributeReEncode != nil:
return a.compareAttributeReEncode(b)
case a.ModelAdd != nil:
return a.compareModelAdd(b)
case a.ModelDelete != nil:
return a.compareModelDelete(b)
}
panic(fmt.Sprintf("unsupported alteration type %v", a))
}
func (a Alteration) compareAttributeAdd(b Alteration) bool {
if a.AttributeAdd == nil || b.AttributeAdd == nil {
return a.AttributeAdd == b.AttributeAdd
}
return a.AttributeAdd.Attr.Compare(b.AttributeAdd.Attr)
}
func (a Alteration) compareAttributeDelete(b Alteration) bool {
if a.AttributeDelete == nil || b.AttributeDelete == nil {
return a.AttributeDelete == b.AttributeDelete
}
return a.AttributeDelete.Attr.Compare(b.AttributeDelete.Attr)
}
func (a Alteration) compareAttributeReType(b Alteration) bool {
if !a.AttributeReType.Attr.Compare(b.AttributeReType.Attr) {
return false
}
if a.AttributeReType == nil || b.AttributeReType == nil {
return a.AttributeReType == b.AttributeReType
}
return a.AttributeReType.To.Type() == b.AttributeReType.To.Type()
}
func (a Alteration) compareAttributeReEncode(b Alteration) bool {
if !a.AttributeReEncode.Attr.Compare(b.AttributeReEncode.Attr) {
return false
}
if a.AttributeReEncode == nil || b.AttributeReEncode == nil {
return a.AttributeReEncode == b.AttributeReEncode
}
return a.AttributeReEncode.To.Type() == b.AttributeReEncode.To.Type()
}
func (a Alteration) compareModelAdd(b Alteration) bool {
if a.ModelAdd == nil || b.ModelAdd == nil {
return a.ModelAdd == b.ModelAdd
}
return a.ModelAdd.Model.Compare(*b.ModelAdd.Model)
}
func (a Alteration) compareModelDelete(b Alteration) bool {
if a.ModelDelete == nil || b.ModelDelete == nil {
return a.ModelDelete == b.ModelDelete
}
return a.ModelDelete.Model.Compare(*b.ModelDelete.Model)
}