ExecuteWithStream parses the flat JSON args, converts to engine argv form, and runs engine.handleSearch via a fresh Engine instance. Output streams line-by-line through onOutput.
(ctx context.Context, args []string, onOutput func(string))
| 91 | // argv form, and runs engine.handleSearch via a fresh Engine instance. |
| 92 | // Output streams line-by-line through onOutput. |
| 93 | func (p *BuiltinSearchPlugin) ExecuteWithStream(ctx context.Context, args []string, onOutput func(string)) (string, error) { |
| 94 | parsed, err := parseSearchArgs(args) |
| 95 | if err != nil { |
| 96 | return "", err |
| 97 | } |
| 98 | if parsed.Term == "" { |
| 99 | return "", fmt.Errorf("term required (usage: @search {\"term\":\"regex\"})") |
| 100 | } |
| 101 | |
| 102 | argv := buildSearchArgv(parsed) |
| 103 | |
| 104 | var fullOutput strings.Builder |
| 105 | var mu sync.Mutex |
| 106 | emit := func(line string, isErr bool) { |
| 107 | mu.Lock() |
| 108 | defer mu.Unlock() |
| 109 | if onOutput != nil { |
| 110 | prefix := "" |
| 111 | if isErr { |
| 112 | prefix = "ERR: " |
| 113 | } |
| 114 | onOutput(prefix + line) |
| 115 | } |
| 116 | fullOutput.WriteString(line) |
| 117 | fullOutput.WriteString("\n") |
| 118 | } |
| 119 | outWriter := engine.NewStreamWriter(func(line string) { emit(line, false) }) |
| 120 | errWriter := engine.NewStreamWriter(func(line string) { emit(line, true) }) |
| 121 | |
| 122 | eng := engine.NewEngine(outWriter, errWriter, "") |
| 123 | execErr := eng.Execute(ctx, "search", argv) |
| 124 | |
| 125 | outWriter.Flush() |
| 126 | errWriter.Flush() |
| 127 | |
| 128 | if execErr != nil { |
| 129 | return fullOutput.String(), fmt.Errorf("@search failed: %w", execErr) |
| 130 | } |
| 131 | return fullOutput.String(), nil |
| 132 | } |
| 133 | |
| 134 | // searchArgs is the typed view of @search's JSON input. |
| 135 | type searchArgs struct { |
no test coverage detected