aboutsummaryrefslogtreecommitdiff
path: root/source/import/ImportReport.mc
diff options
context:
space:
mode:
authorcirakg <cirakg@cirakg.xyz>2026-10-20 01:51:05 +0200
committercirakg <cirakg@cirakg.xyz>2026-10-20 01:51:05 +0200
commit40d9a1d5c3e85486aea964c1446402a3be6ec05a (patch)
tree7af82c46a459afc99156e5a6022946b43e35a3de /source/import/ImportReport.mc
parentbc228753bfb4a2faea540cddb20fc39ebd265db5 (diff)
feat(import): parse otpauth URIs and unencrypted Aegis JSON in resumable steps
Streaming JSON tokenizer with depth and length limits and native skipping of long strings; Aegis reader for database versions 1-3 with groups and encrypted-export detection; otpauth parser with ';' separators and Steam; import job with Replace mode, duplicate and replacement checks, a sealed multi-part buffer and a report. Google Authenticator exports deferred. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'source/import/ImportReport.mc')
-rw-r--r--source/import/ImportReport.mc71
1 files changed, 71 insertions, 0 deletions
diff --git a/source/import/ImportReport.mc b/source/import/ImportReport.mc
new file mode 100644
index 0000000..b2a986a
--- /dev/null
+++ b/source/import/ImportReport.mc
@@ -0,0 +1,71 @@
+// Author: cirakg & claude ai
+// Summary: The outcome of one import: counts, per-item errors and skips, notes, and the texts for the status field and results screen.
+
+import Toybox.Lang;
+
+class ImportReport {
+
+ // At most this many detail lines are kept (the results screen scrolls).
+ static const MAX_DETAILS = 40;
+
+ var imported as Number = 0;
+ var pendingLocked as Number = 0;
+ var duplicates as Number = 0;
+ var errors as Number = 0;
+ var skipped as Number = 0;
+ var removed as Number = 0;
+ // Set when the whole import failed (not JSON, encrypted, storage full
+ // before anything was stored...).
+ var failure as String? = null;
+ // Set while a multi-part import waits for its next part.
+ var waitingChars as Number = -1;
+ var details as Array<String> = [] as Array<String>;
+
+ function initialize() {
+ }
+
+ function addDetail(line as String) as Void {
+ if (details.size() < MAX_DETAILS) {
+ details.add(line);
+ } else if (details.size() == MAX_DETAILS) {
+ details.add("…");
+ }
+ }
+
+ // One line for the phone's Import status field, for example
+ // "Imported 12 · 2 duplicates skipped · 1 error · 1 skipped".
+ function summary() as String {
+ var f = failure;
+ if (f != null) {
+ return "Not imported: " + f;
+ }
+ if (waitingChars >= 0) {
+ return "Waiting for more: received " + waitingChars
+ + " characters. Paste the next part into the import fields and turn 'Import now' on again.";
+ }
+ var text = "Imported " + imported;
+ if (pendingLocked > 0) {
+ text += " (" + pendingLocked + " locked, waiting for your PIN)";
+ }
+ if (removed > 0) {
+ text += " · " + removed + " old imported removed";
+ }
+ if (duplicates > 0) {
+ text += " · " + duplicates + (duplicates == 1 ? " duplicate skipped" : " duplicates skipped");
+ }
+ if (errors > 0) {
+ text += " · " + errors + (errors == 1 ? " error" : " errors");
+ }
+ if (skipped > 0) {
+ text += " · " + skipped + " skipped";
+ }
+ return text;
+ }
+
+ // The summary followed by the details, for the results screen.
+ function lines() as Array<String> {
+ var out = [summary()] as Array<String>;
+ out.addAll(details);
+ return out;
+ }
+}