| 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "errors" |
| 6 | "unicode/utf8" |
| 7 | |
| 8 | "reasonix/desktop/internal/workspacestate" |
| 9 | "reasonix/internal/agent" |
| 10 | "reasonix/internal/session" |
| 11 | ) |
| 12 | |
| 13 | const historicalFailureDetailLimit = 2000 |
| 14 | |
| 15 | // historicalImportFailureCode projects the identity the producer attached to a |
| 16 | // preparation failure. It reads sentinels and typed errors only; a failure no |
| 17 | // producer classified stays import_failed and still carries its detail. |
| 18 | func historicalImportFailureCode(err error) string { |
| 19 | var operationErr *SessionOperationError |
| 20 | var transcriptErr *session.TranscriptInitializationError |
| 21 | switch { |
| 22 | case err == nil: |
| 23 | return "" |
| 24 | case historicalSourceBusyError(err): |
| 25 | return "source_busy" |
| 26 | case errors.Is(err, context.Canceled): |
| 27 | return "cancelled" |
| 28 | case errors.Is(err, errSessionWorkspaceConflict): |
| 29 | return "workspace_conflict" |
| 30 | case errors.Is(err, agent.ErrSessionReplayLimitExceeded): |
| 31 | return "history_too_large" |
| 32 | case errors.Is(err, agent.ErrSessionHistoryDamaged), errors.Is(err, session.ErrDamagedStore), errors.As(err, &transcriptErr): |
| 33 | return "history_damaged" |
| 34 | case errors.Is(err, session.ErrUnsupportedVersion): |
| 35 | return "unsupported_version" |
| 36 | case errors.Is(err, workspacestate.ErrMutationConflict), errors.Is(err, session.ErrImportConflict): |
| 37 | return "state_conflict" |
| 38 | case errors.As(err, &operationErr) && operationErr.Code != "": |
| 39 | return operationErr.Code |
| 40 | default: |
| 41 | return "import_failed" |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | // historicalImportFailureDetail is display text for the local user, bounded so |
| 46 | // a failure that quotes imported content cannot flood the surface. |
| 47 | func historicalImportFailureDetail(err error) string { |
| 48 | if err == nil || errors.Is(err, context.Canceled) { |
| 49 | return "" |
| 50 | } |
| 51 | detail := err.Error() |
| 52 | if len(detail) <= historicalFailureDetailLimit { |
| 53 | return detail |
| 54 | } |
| 55 | cut := historicalFailureDetailLimit |
| 56 | for cut > 0 && !utf8.RuneStart(detail[cut]) { |
| 57 | cut-- |
| 58 | } |
| 59 | return detail[:cut] + "…" |
| 60 | } |
| 61 |