package intent import ( "bytes" "encoding/json" "fmt" "io/fs" "path" "sort" ) // overlayDoc 是 JSON 配置文件的顶层结构。 // // { // "intents": [ { ...IntentSpec... } ], // "disable": [ "demo.run.status_query" ] // } // // intents 内同 id 条目覆盖内置或先前定义(version 必须递增);disable 按id禁用 // (保护域内的 id 会整批失败)。目录 root 内按文件名字典序加载,roots 按给定顺序应用。 type overlayDoc struct { Intents []IntentSpec `json:"intents"` Disable []string `json:"disable"` } // overlayEntry 单条 overlay 指令,source 定位到文件与下标,供错误与诊断使用。 type overlayEntry struct { disable bool id string spec IntentSpec source string } // loadOverlays 从 fsys 读取全部 roots。root 可以是目录(加载其中 *.json,字典序) // 或单个文件路径。任何读取、解析或未知字段错误都会带来源返回。 func loadOverlays(fsys fs.FS, roots []string) ([]overlayEntry, error) { var entries []overlayEntry for _, root := range roots { root = path.Clean(root) files, err := overlayFiles(fsys, root) if err != nil { return nil, newError(LoadFailed, "读取配置 root 失败", "root", root, "error", err.Error()) } for _, f := range files { data, err := fs.ReadFile(fsys, f) if err != nil { return nil, newError(LoadFailed, "读取配置文件失败", "file", f, "error", err.Error()) } var doc overlayDoc dec := json.NewDecoder(bytes.NewReader(data)) dec.DisallowUnknownFields() if err := dec.Decode(&doc); err != nil { return nil, newError(InvalidSpec, "配置解析失败(含未知字段)", "file", f, "error", err.Error()) } for i := range doc.Intents { entries = append(entries, overlayEntry{spec: doc.Intents[i], source: fmt.Sprintf("%s#intents[%d]", f, i)}) } for i, id := range doc.Disable { entries = append(entries, overlayEntry{disable: true, id: id, source: fmt.Sprintf("%s#disable[%d]", f, i)}) } } } return entries, nil } func overlayFiles(fsys fs.FS, root string) ([]string, error) { st, err := fs.Stat(fsys, root) if err != nil { return nil, err } if !st.IsDir() { return []string{root}, nil } matches, err := fs.Glob(fsys, path.Join(root, "*.json")) if err != nil { return nil, err } sort.Strings(matches) return matches, nil }