100 lines
3.5 KiB
Go
100 lines
3.5 KiB
Go
package gsp
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
const GSPMessageVersion = "0.1"
|
|
|
|
var allowedMessageIntents = map[string]bool{
|
|
"design": true,
|
|
"implement": true,
|
|
"review": true,
|
|
"test": true,
|
|
"acceptance": true,
|
|
"handoff": true,
|
|
"inspect": true,
|
|
}
|
|
|
|
type Message struct {
|
|
MessageVersion string `json:"gspMessageVersion,omitempty" yaml:"gspMessageVersion"`
|
|
ID string `json:"id,omitempty" yaml:"id"`
|
|
From string `json:"from,omitempty" yaml:"from"`
|
|
To string `json:"to,omitempty" yaml:"to"`
|
|
Intent string `json:"intent,omitempty" yaml:"intent"`
|
|
Entry string `json:"entry,omitempty" yaml:"entry"`
|
|
Stage string `json:"stage,omitempty" yaml:"stage"`
|
|
Requires []string `json:"requires,omitempty" yaml:"requires"`
|
|
ContextPack ContextPack `json:"contextPack,omitempty" yaml:"contextPack"`
|
|
File string `json:"file,omitempty" yaml:"-"`
|
|
}
|
|
|
|
type ContextPack struct {
|
|
Mode string `json:"mode,omitempty" yaml:"mode"`
|
|
Depth int `json:"depth,omitempty" yaml:"depth"`
|
|
Budget int `json:"budget,omitempty" yaml:"budget"`
|
|
}
|
|
|
|
func ReadMessage(root, file string) (Message, error) {
|
|
path := file
|
|
if !filepath.IsAbs(path) {
|
|
path = filepath.Join(root, file)
|
|
}
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return Message{}, err
|
|
}
|
|
var message Message
|
|
if err := yaml.Unmarshal(data, &message); err != nil {
|
|
return Message{}, err
|
|
}
|
|
message.File = relPath(root, path)
|
|
return message, nil
|
|
}
|
|
|
|
func (p *Project) ValidateMessage(message Message) Report {
|
|
report := Report{OK: true}
|
|
if message.MessageVersion == "" {
|
|
report.addError("missing_message_version", message.ID, message.File, "message requires gspMessageVersion")
|
|
} else if message.MessageVersion != GSPMessageVersion {
|
|
report.addError("unsupported_message_version", message.ID, message.File, fmt.Sprintf("GSP message version %q is not supported", message.MessageVersion))
|
|
}
|
|
if message.ID == "" {
|
|
report.addError("missing_id", "", message.File, "message requires id")
|
|
}
|
|
if message.From == "" {
|
|
report.addError("missing_from", message.ID, message.File, "message requires from")
|
|
}
|
|
if message.To == "" {
|
|
report.addError("missing_to", message.ID, message.File, "message requires to")
|
|
}
|
|
if message.Intent == "" {
|
|
report.addError("missing_intent", message.ID, message.File, "message requires intent")
|
|
} else if !allowedMessageIntents[message.Intent] {
|
|
report.addError("invalid_intent", message.ID, message.File, fmt.Sprintf("intent %q is not allowed", message.Intent))
|
|
}
|
|
if message.Entry == "" {
|
|
report.addError("missing_entry", message.ID, message.File, "message requires entry")
|
|
} else if _, ok := p.ByID[message.Entry]; !ok {
|
|
report.addError("missing_entry_gsp", message.Entry, message.File, fmt.Sprintf("entry references missing GSP %q", message.Entry))
|
|
}
|
|
if message.Stage != "" {
|
|
if _, ok := p.Manifest.minResolution(message.Stage); !ok {
|
|
report.addError("unknown_stage", message.ID, message.File, fmt.Sprintf("unknown stage %q", message.Stage))
|
|
}
|
|
}
|
|
for _, id := range message.Requires {
|
|
if _, ok := p.ByID[id]; !ok {
|
|
report.addError("missing_required_gsp", id, message.File, fmt.Sprintf("requires references missing GSP %q", id))
|
|
}
|
|
}
|
|
if message.ContextPack.Mode != "" && !allowedMessageIntents[message.ContextPack.Mode] {
|
|
report.addError("invalid_context_pack_mode", message.ID, message.File, fmt.Sprintf("contextPack mode %q is not allowed", message.ContextPack.Mode))
|
|
}
|
|
return report
|
|
}
|