56 lines
1.6 KiB
Go
56 lines
1.6 KiB
Go
package state
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// ParseIntentOutput parses `ananke intent` CLI output from local/remote commands.
|
|
// Signature: ParseIntentOutput(raw string) (Intent, error)
|
|
// Why: Startup/shutdown coordination depends on intent state being interpreted
|
|
// consistently from command output so remote peers and local orchestration can
|
|
// share one durable control-plane signal.
|
|
func ParseIntentOutput(raw string) (Intent, error) {
|
|
for _, line := range strings.Split(raw, "\n") {
|
|
line = strings.TrimSpace(line)
|
|
if line == "" {
|
|
continue
|
|
}
|
|
idx := strings.Index(line, "intent=")
|
|
if idx < 0 {
|
|
continue
|
|
}
|
|
payload := strings.TrimSpace(line[idx:])
|
|
fields := strings.Fields(payload)
|
|
stateValue := strings.TrimSpace(strings.TrimPrefix(fields[0], "intent="))
|
|
if stateValue == "" || stateValue == "none" {
|
|
return Intent{}, nil
|
|
}
|
|
in := Intent{State: stateValue}
|
|
if strings.Contains(payload, `reason="`) {
|
|
parts := strings.SplitN(payload, `reason="`, 2)
|
|
if end := strings.Index(parts[1], `"`); end >= 0 {
|
|
in.Reason = parts[1][:end]
|
|
}
|
|
}
|
|
for _, field := range fields[1:] {
|
|
if strings.HasPrefix(field, "source=") {
|
|
in.Source = strings.TrimSpace(strings.TrimPrefix(field, "source="))
|
|
}
|
|
if strings.HasPrefix(field, "updated_at=") {
|
|
ts := strings.TrimSpace(strings.TrimPrefix(field, "updated_at="))
|
|
if ts != "" {
|
|
parsed, err := time.Parse(time.RFC3339, ts)
|
|
if err != nil {
|
|
return Intent{}, fmt.Errorf("parse updated_at %q: %w", ts, err)
|
|
}
|
|
in.UpdatedAt = parsed
|
|
}
|
|
}
|
|
}
|
|
return in, nil
|
|
}
|
|
return Intent{}, fmt.Errorf("intent output missing intent= token")
|
|
}
|