115 lines
2.5 KiB
Go
115 lines
2.5 KiB
Go
package dnd
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"gitea.maximumdirect.net/eric/notarius/internal/core/source"
|
|
)
|
|
|
|
type UnitRef struct {
|
|
value int
|
|
fromNumber bool
|
|
}
|
|
|
|
type SourceRefResponse struct {
|
|
SourceID string `json:"source_id"`
|
|
StartUnitID UnitRef `json:"start_unit_id"`
|
|
EndUnitID UnitRef `json:"end_unit_id"`
|
|
}
|
|
|
|
func UnitRefFromString(value string) UnitRef {
|
|
parsed, _ := parseUnitRefNumber(value)
|
|
return UnitRef{value: parsed}
|
|
}
|
|
|
|
func UnitRefFromInt(value int) UnitRef {
|
|
return UnitRef{
|
|
value: value,
|
|
fromNumber: true,
|
|
}
|
|
}
|
|
|
|
func (ref UnitRef) String() string {
|
|
if ref.value == 0 {
|
|
return ""
|
|
}
|
|
return strconv.Itoa(ref.value)
|
|
}
|
|
|
|
func (ref *UnitRef) UnmarshalJSON(raw []byte) error {
|
|
raw = bytes.TrimSpace(raw)
|
|
if len(raw) == 0 {
|
|
return fmt.Errorf("unit ref must be a string or integer")
|
|
}
|
|
if raw[0] == '"' {
|
|
var value string
|
|
if err := json.Unmarshal(raw, &value); err != nil {
|
|
return err
|
|
}
|
|
number, err := parseUnitRefNumber(value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*ref = UnitRef{value: number}
|
|
return nil
|
|
}
|
|
|
|
number, err := parseUnitRefNumber(string(raw))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*ref = UnitRefFromInt(number)
|
|
return nil
|
|
}
|
|
|
|
func (ref UnitRef) MarshalJSON() ([]byte, error) {
|
|
if ref.fromNumber {
|
|
return []byte(strconv.Itoa(ref.value)), nil
|
|
}
|
|
return json.Marshal(ref.String())
|
|
}
|
|
|
|
func ResolveUnitID(doc *source.SourceDocument, field string, ref UnitRef) (int, error) {
|
|
if ref.value <= 0 {
|
|
return 0, fmt.Errorf("%s must be positive", field)
|
|
}
|
|
if _, ok := source.UnitIndex(doc, ref.value); !ok {
|
|
return 0, fmt.Errorf("%s %d was not found", field, ref.value)
|
|
}
|
|
return ref.value, nil
|
|
}
|
|
|
|
func SourceRefCandidate(doc *source.SourceDocument, ref SourceRefResponse) source.SourceRef {
|
|
return source.SourceRef{
|
|
SourceID: strings.TrimSpace(ref.SourceID),
|
|
StartUnitID: unitIDCandidate(ref.StartUnitID),
|
|
EndUnitID: unitIDCandidate(ref.EndUnitID),
|
|
}
|
|
}
|
|
|
|
func unitIDCandidate(ref UnitRef) int {
|
|
return ref.value
|
|
}
|
|
|
|
func parseUnitRefNumber(value string) (int, error) {
|
|
trimmed := strings.TrimSpace(value)
|
|
if trimmed == "" {
|
|
return 0, fmt.Errorf("unit ref must not be empty")
|
|
}
|
|
if trimmed != value {
|
|
return 0, fmt.Errorf("unit ref must not contain leading or trailing whitespace")
|
|
}
|
|
number, err := strconv.Atoi(value)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("unit ref must be an integer")
|
|
}
|
|
if number <= 0 {
|
|
return 0, fmt.Errorf("unit ref must be positive")
|
|
}
|
|
return number, nil
|
|
}
|