1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//! # Storage Layer
//!
//! This module defines the storage abstraction for padz. The [`DataStore`] trait
//! allows the application to work with different storage backends.
//!
//! ## Hybrid Store Architecture
//!
//! Padz maintains a split-brain model:
//! 1. **Truth**: Text files on disk.
//! 2. **Cache**: A JSON metadata "database" (`data.json`).
//!
//! The system assumes the cache is *always potentially dirty* and self-heals lazily.
//!
//! ### Philosophy
//! - **Files are Truth**: If a file exists in `.padz/`, it is a valid pad. If deleted, the pad is gone.
//! - **Lazy Reconciliation**: The database is updated whenever a "read" operation occurs.
//! - **Robustness**: Operations are robust against process termination. The editor saves directly to disk.
//!
//! ## Reconciliation Logic
//!
//! The `sync` process runs automatically before listing pads:
//!
//! 1. **Orphan Adoption**: `pad-X.txt` exists but `X` not in DB → Parse and add to DB.
//! 2. **Zombie Cleanup**: `X` in DB but `pad-X.txt` missing → Remove from DB.
//! 3. **Staleness Check**: File `mtime` > DB `updated_at` → Re-parse to update cached title.
//! 4. **Garbage Collection**: Empty/whitespace-only file → Delete file and DB entry.
//!
//! ## Deletion Lifecycle
//!
//! - **Soft Delete**: Moves the pad from the Active bucket to the Deleted bucket.
//! - **Purge**: Permanently removes both file and metadata entry from the Deleted bucket.
//!
//! ## File Extension Handling
//!
//! - New pads use the configured `file-ext` (default `.txt`)
//! - When reading, tries configured extension first, falls back to `.txt`
//! - Mixed extensions are supported gracefully
//!
//! ## Metadata Fields
//!
//! The `data.json` stores `HashMap<Uuid, Metadata>` with:
//! - `id`, `created_at`, `updated_at`: Identity and timestamps
//! - `is_pinned`, `pinned_at`: Pin state
//! - `delete_protected`: Protection flag
//! - `title`: Cached title for fast listing
//!
//! ## Architecture
//!
//! The store layer is split into two tiers:
//!
//! 1. **[`backend::StorageBackend`]**: Low-level I/O trait (pure read/write operations)
//! - [`fs_backend::FsBackend`]: Filesystem backend with atomic writes
//! - [`mem_backend::MemBackend`]: In-memory backend for testing
//!
//! 2. **[`pad_store::PadStore<B>`]**: Business logic layer (sync, doctor, CRUD)
//! - Implements [`DataStore`] trait
//! - Generic over any `StorageBackend`
//!
//! For convenience, type aliases are provided:
//! - [`fs::FileStore`]: `PadStore<FsBackend>` - production use
//! - [`memory::InMemoryStore`]: `PadStore<MemBackend>` - testing
//!
//! ## Storage Layout
//!
//! ```text
//! .padz/
//! ├── data.json # Metadata Cache
//! ├── config.json # Scope configuration
//! └── pad-{uuid}.{ext} # Pad content files
//! ```
use crateResult;
use crate;
use crateTagEntry;
use ;
use PathBuf;
use Uuid;
/// Which lifecycle bucket a pad lives in.
///
/// Bucket membership IS lifecycle state — there is no separate `is_deleted` flag.
/// Moving a pad between buckets is the mechanism for delete/restore/archive/unarchive.
/// Report from the `doctor` operation.
/// Abstract interface for pad storage.
///
/// All methods are bucket-aware: pads live in Active, Archived, or Deleted buckets.
/// Tags are scope-level (shared across buckets).