Files

158 lines
2.7 KiB
Go

package problems
import (
"strconv"
"aego/core/ids"
"aego/scene"
)
const ServiceID = "engine.problems"
type Severity uint8
const (
Info Severity = iota
Warning
Error
)
type SubjectKind uint8
const (
SubjectNone SubjectKind = iota
SubjectNode
SubjectField
SubjectAsset
SubjectSetting
SubjectFile
)
type Subject struct {
Kind SubjectKind
Scene *scene.Scene
Node ids.NodeID
Field string
Asset ids.AssetID
Setting string
File string
Line int
Col int
}
type Diagnostic struct {
Owner string
Code string
Severity Severity
Message string
Help string
Subject Subject
BlocksPlay bool
}
func (d *Diagnostic) Key() string {
s := &d.Subject
return d.Owner + "|" + d.Code + "|" + strconv.Itoa(int(s.Kind)) + "|" + s.Node.String() + "|" + s.Field + "|" +
s.Asset.String() + "|" + s.Setting + "|" + s.File + ":" + strconv.Itoa(s.Line) + ":" + strconv.Itoa(s.Col)
}
type Source struct {
Revision func() uint64
Append func(dst []Diagnostic) []Diagnostic
}
type entry struct {
id uint64
src Source
}
type Service struct {
entries []entry
next uint64
gen uint64
rev uint64
valid bool
list []Diagnostic
seen map[string]bool
}
func New() *Service {
return &Service{seen: map[string]bool{}}
}
func (s *Service) Add(src Source) func() {
s.next++
id := s.next
s.entries = append(s.entries, entry{id: id, src: src})
s.gen++
return func() {
for i := range s.entries {
if s.entries[i].id == id {
s.entries = append(s.entries[:i], s.entries[i+1:]...)
s.gen++
return
}
}
}
}
func (s *Service) revision() (uint64, bool) {
h := (s.gen ^ 14695981039346656037) * 1099511628211
force := false
for _, e := range s.entries {
if e.src.Revision == nil {
force = true
continue
}
h = (h ^ e.src.Revision()) * 1099511628211
}
return h, force
}
func (s *Service) Collect() ([]Diagnostic, uint64) {
if s == nil {
return nil, 0
}
rev, force := s.revision()
if s.valid && rev == s.rev && !force {
return s.list, rev
}
s.list = s.list[:0]
clear(s.seen)
for _, e := range s.entries {
if e.src.Append == nil {
continue
}
start := len(s.list)
s.list = e.src.Append(s.list)
n := start
for i := start; i < len(s.list); i++ {
k := s.list[i].Key()
if s.seen[k] {
continue
}
s.seen[k] = true
s.list[n] = s.list[i]
n++
}
s.list = s.list[:n]
}
s.rev, s.valid = rev, true
return s.list, rev
}
func (s *Service) Blocking() (Diagnostic, int) {
list, _ := s.Collect()
var first Diagnostic
n := 0
for i := range list {
if list[i].BlocksPlay && list[i].Severity == Error {
if n == 0 {
first = list[i]
}
n++
}
}
return first, n
}