(ctx context.Context, r *Repo, args []string, callback func(T), failureErr error, opts ...GenericOption)
| 270 | } |
| 271 | |
| 272 | func runCommandWithProgress[T ProgressEntryValidator](ctx context.Context, r *Repo, args []string, callback func(T), failureErr error, opts ...GenericOption) (T, error) { |
| 273 | logger := LoggerFromContext(ctx) |
| 274 | cmdCtx, cancel := context.WithCancel(ctx) |
| 275 | defer cancel() |
| 276 | cmdCtx = ContextWithLogger(cmdCtx, nil) // ensure no logger is used |
| 277 | cmd := r.commandWithContext(cmdCtx, args, opts...) |
| 278 | |
| 279 | // Ensure the command is logged since we're overriding the logger |
| 280 | if logger != nil { |
| 281 | fmt.Fprintf(logger, "command: %q\n", cmd) |
| 282 | } else { |
| 283 | logger = io.Discard |
| 284 | } |
| 285 | |
| 286 | buf := buffer.New(8 * 1024) // 8KB IO buffer for the realtime event parsing |
| 287 | reader, writer := nio.Pipe(buf) |
| 288 | r.handleOutput(cmd, withAllTo(writer)) |
| 289 | |
| 290 | var readErr error |
| 291 | var summary T |
| 292 | var wg sync.WaitGroup |
| 293 | |
| 294 | wg.Add(1) |
| 295 | go func() { |
| 296 | defer wg.Done() |
| 297 | result, err := processProgressOutput[T](reader, logger, callback) |
| 298 | summary = result |
| 299 | if err != nil { |
| 300 | readErr = fmt.Errorf("output processing: %w", err) |
| 301 | } |
| 302 | }() |
| 303 | |
| 304 | cmdErr := cmd.Run() |
| 305 | writer.Close() |
| 306 | wg.Wait() |
| 307 | |
| 308 | if cmdErr != nil || readErr != nil { |
| 309 | if cmdErr != nil { |
| 310 | cmdErr = handleResticExitError(cmdErr, failureErr) |
| 311 | } |
| 312 | return summary, newCmdError(cmd, errors.Join(cmdErr, readErr)) |
| 313 | } |
| 314 | |
| 315 | return summary, nil |
| 316 | } |
| 317 | |
| 318 | func (r *Repo) Backup(ctx context.Context, paths []string, progressCallback func(*BackupProgressEntry), opts ...GenericOption) (*BackupProgressEntry, error) { |
| 319 | for _, p := range paths { |
no test coverage detected