141 lines
3.8 KiB
Go
141 lines
3.8 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
|
|
"gitea.maximumdirect.net/eric/narratio/internal/adapters/storage"
|
|
"gitea.maximumdirect.net/eric/narratio/internal/config"
|
|
"gitea.maximumdirect.net/eric/narratio/internal/manifest"
|
|
)
|
|
|
|
type finding struct {
|
|
Severity string
|
|
Category string
|
|
Message string
|
|
}
|
|
|
|
type findingError struct {
|
|
count int
|
|
}
|
|
|
|
func (e findingError) Error() string {
|
|
return fmt.Sprintf("%d validation error(s)", e.count)
|
|
}
|
|
|
|
func renderFindings(out io.Writer, campaign, sessionID string, findings []finding) error {
|
|
if campaign != "" || sessionID != "" {
|
|
fmt.Fprintf(out, "Campaign: %s\n", campaign)
|
|
fmt.Fprintf(out, "Session: %s\n\n", sessionID)
|
|
}
|
|
errorsCount := 0
|
|
for _, f := range findings {
|
|
if f.Severity == "ERROR" {
|
|
errorsCount++
|
|
}
|
|
fmt.Fprintf(out, "%-5s %-10s %s\n", f.Severity, f.Category, f.Message)
|
|
}
|
|
if errorsCount > 0 {
|
|
return findingError{count: errorsCount}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func okFinding(category, msg string) finding { return finding{"OK", category, msg} }
|
|
func infoFinding(category, msg string) finding { return finding{"INFO", category, msg} }
|
|
func warnFinding(category, msg string) finding { return finding{"WARN", category, msg} }
|
|
func errorFinding(category, msg string) finding { return finding{"ERROR", category, msg} }
|
|
|
|
func sessionSourceSummary(cfg *config.Config) string {
|
|
source := cfg.SessionSource.Source
|
|
if source == "" {
|
|
source = "session_config"
|
|
}
|
|
if cfg.SessionSource.S3Key != "" {
|
|
return source + " " + cfg.SessionSource.S3Key
|
|
}
|
|
return source + " " + cfg.SessionPath
|
|
}
|
|
|
|
func validateStableInputFindings(cfg *config.Config) []finding {
|
|
checks := inspectStableInputs(cfg)
|
|
out := make([]finding, 0, len(checks))
|
|
for _, check := range checks {
|
|
if check.Err != nil {
|
|
msg := check.Name + ": " + check.Err.Error()
|
|
if strings.TrimSpace(check.Path) != "" {
|
|
msg = fmt.Sprintf("%s missing: %v", check.Name, check.Err)
|
|
}
|
|
out = append(out, errorFinding("inputs", msg))
|
|
continue
|
|
}
|
|
out = append(out, okFinding("inputs", check.Name+": "+check.Path))
|
|
}
|
|
return out
|
|
}
|
|
|
|
func resolveHelperConfigRelativePath(input config.ResolvedInputFile) (string, error) {
|
|
if strings.TrimSpace(input.ConfigPath) == "" {
|
|
return "", fmt.Errorf("source config path is required")
|
|
}
|
|
path := strings.TrimSpace(input.Path)
|
|
if path == "" {
|
|
return "", fmt.Errorf("path is required")
|
|
}
|
|
if filepath.IsAbs(path) {
|
|
return filepath.Clean(path), nil
|
|
}
|
|
return filepath.Clean(filepath.Join(filepath.Dir(input.ConfigPath), path)), nil
|
|
}
|
|
|
|
func validateLocalAudioFindings(cfg *config.Config) []finding {
|
|
check := inspectLocalAudioPresence(cfg)
|
|
if !check.Checked {
|
|
return nil
|
|
}
|
|
if check.Err != nil {
|
|
return []finding{errorFinding("audio", check.Err.Error())}
|
|
}
|
|
return []finding{okFinding("audio", fmt.Sprintf("%d local audio file(s)", len(check.Paths)))}
|
|
}
|
|
|
|
func validateRemoteAudioFinding(ctx context.Context, cfg *config.Config, store storage.ObjectStore) finding {
|
|
check := inspectRemoteAudioPresence(ctx, cfg, store)
|
|
if check.Err != nil {
|
|
return errorFinding("audio", check.Err.Error())
|
|
}
|
|
return okFinding("audio", fmt.Sprintf("%d remote .flac object(s)", len(check.Keys)))
|
|
}
|
|
|
|
func loadLocalManifest(ctx context.Context, path string) (*manifest.Manifest, error) {
|
|
if _, err := os.Stat(path); err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
store := &manifest.LocalStore{}
|
|
return store.Load(ctx, path)
|
|
}
|
|
|
|
func writeStageStatuses(out io.Writer, m *manifest.Manifest) {
|
|
if m == nil || len(m.Stages) == 0 {
|
|
fmt.Fprintln(out, "stages: no stages recorded")
|
|
return
|
|
}
|
|
fmt.Fprintln(out, "stages:")
|
|
names := make([]string, 0, len(m.Stages))
|
|
for name := range m.Stages {
|
|
names = append(names, name)
|
|
}
|
|
sort.Strings(names)
|
|
for _, name := range names {
|
|
fmt.Fprintf(out, "- %s: %s\n", name, m.Stages[name].Status)
|
|
}
|
|
}
|