ExecuteWithStream parses the flat JSON args into the engine's argv form and dispatches to engine.handleRead via a fresh Engine instance. Output is streamed line-by-line through onOutput when present.
(ctx context.Context, args []string, onOutput func(string))
| 100 | // form and dispatches to engine.handleRead via a fresh Engine instance. |
| 101 | // Output is streamed line-by-line through onOutput when present. |
| 102 | func (p *BuiltinReadPlugin) ExecuteWithStream(ctx context.Context, args []string, onOutput func(string)) (string, error) { |
| 103 | parsed, err := parseReadArgs(args) |
| 104 | if err != nil { |
| 105 | return "", err |
| 106 | } |
| 107 | if parsed.File == "" { |
| 108 | return "", fmt.Errorf("file required (usage: @read {\"file\":\"path\"})") |
| 109 | } |
| 110 | |
| 111 | argv := buildReadArgv(parsed) |
| 112 | |
| 113 | var fullOutput strings.Builder |
| 114 | var mu sync.Mutex |
| 115 | emit := func(line string, isErr bool) { |
| 116 | mu.Lock() |
| 117 | defer mu.Unlock() |
| 118 | if onOutput != nil { |
| 119 | prefix := "" |
| 120 | if isErr { |
| 121 | prefix = "ERR: " |
| 122 | } |
| 123 | onOutput(prefix + line) |
| 124 | } |
| 125 | fullOutput.WriteString(line) |
| 126 | fullOutput.WriteString("\n") |
| 127 | } |
| 128 | outWriter := engine.NewStreamWriter(func(line string) { emit(line, false) }) |
| 129 | errWriter := engine.NewStreamWriter(func(line string) { emit(line, true) }) |
| 130 | |
| 131 | eng := engine.NewEngine(outWriter, errWriter, "") |
| 132 | execErr := eng.Execute(ctx, "read", argv) |
| 133 | |
| 134 | outWriter.Flush() |
| 135 | errWriter.Flush() |
| 136 | |
| 137 | if execErr != nil { |
| 138 | return fullOutput.String(), fmt.Errorf("@read failed: %w", execErr) |
| 139 | } |
| 140 | return fullOutput.String(), nil |
| 141 | } |
| 142 | |
| 143 | // readArgs is the typed view of @read's JSON input. Keeping this |
| 144 | // separate from the JSON unmarshal call makes the dispatch logic |
no test coverage detected