Maintain local ⭤ remote in sync with automatic AT Protocol parity for Laravel (alpha & unstable)
1<?php
2
3namespace SocialDept\AtpParity\Import;
4
5/**
6 * Immutable value object representing the result of an import operation.
7 */
8readonly class ImportResult
9{
10 public function __construct(
11 public string $did,
12 public string $collection,
13 public int $recordsSynced,
14 public int $recordsSkipped,
15 public int $recordsFailed,
16 public bool $completed,
17 public ?string $cursor = null,
18 public ?string $error = null,
19 ) {}
20
21 /**
22 * Check if the import completed successfully.
23 */
24 public function isSuccess(): bool
25 {
26 return $this->completed && $this->error === null;
27 }
28
29 /**
30 * Check if the import was partially completed.
31 */
32 public function isPartial(): bool
33 {
34 return ! $this->completed && $this->recordsSynced > 0;
35 }
36
37 /**
38 * Check if the import failed.
39 */
40 public function isFailed(): bool
41 {
42 return $this->error !== null;
43 }
44
45 /**
46 * Get total records processed.
47 */
48 public function totalProcessed(): int
49 {
50 return $this->recordsSynced + $this->recordsSkipped + $this->recordsFailed;
51 }
52
53 /**
54 * Create a successful result.
55 */
56 public static function success(string $did, string $collection, int $synced, int $skipped = 0, int $failed = 0): self
57 {
58 return new self(
59 did: $did,
60 collection: $collection,
61 recordsSynced: $synced,
62 recordsSkipped: $skipped,
63 recordsFailed: $failed,
64 completed: true,
65 );
66 }
67
68 /**
69 * Create a partial result (incomplete).
70 */
71 public static function partial(string $did, string $collection, int $synced, string $cursor, int $skipped = 0, int $failed = 0): self
72 {
73 return new self(
74 did: $did,
75 collection: $collection,
76 recordsSynced: $synced,
77 recordsSkipped: $skipped,
78 recordsFailed: $failed,
79 completed: false,
80 cursor: $cursor,
81 );
82 }
83
84 /**
85 * Create a failed result.
86 */
87 public static function failed(string $did, string $collection, string $error, int $synced = 0, int $skipped = 0, int $failed = 0, ?string $cursor = null): self
88 {
89 return new self(
90 did: $did,
91 collection: $collection,
92 recordsSynced: $synced,
93 recordsSkipped: $skipped,
94 recordsFailed: $failed,
95 completed: false,
96 cursor: $cursor,
97 error: $error,
98 );
99 }
100
101 /**
102 * Merge multiple results for the same DID into one aggregate result.
103 *
104 * @param ImportResult[] $results
105 */
106 public static function aggregate(string $did, array $results): self
107 {
108 $synced = 0;
109 $skipped = 0;
110 $failed = 0;
111 $errors = [];
112 $allCompleted = true;
113
114 foreach ($results as $result) {
115 $synced += $result->recordsSynced;
116 $skipped += $result->recordsSkipped;
117 $failed += $result->recordsFailed;
118
119 if (! $result->completed) {
120 $allCompleted = false;
121 }
122
123 if ($result->error) {
124 $errors[] = "{$result->collection}: {$result->error}";
125 }
126 }
127
128 return new self(
129 did: $did,
130 collection: '*',
131 recordsSynced: $synced,
132 recordsSkipped: $skipped,
133 recordsFailed: $failed,
134 completed: $allCompleted,
135 error: $errors ? implode('; ', $errors) : null,
136 );
137 }
138}