pi_async_fs 0.1.0

Runtime-agnostic asynchronous filesystem contracts for local and remote storage
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
// 单个 namespace 目标创建失败时的副作用证据。
//
// [`CreateTargetEvidence`] 只回答“本次创建操作是否建立过目标”这一问题。
// 它不把一次易受并发和远端完成不确定性影响的创建失败,误报成目标当前
// 存在或不存在的实时断言。后续创建失败载体会拥有一个本类型值,使调用方
// 能把诊断原因与本次操作的可证明副作用一起处理。

use core::cmp::Ordering;
use core::fmt;
use core::hash::{Hash, Hasher};

use pi_result::Error;

// 创建操作失败时,本次操作对目标条目的可证明影响。
//
// # 作用与使用位置
//
// 本类型用于一次操作只创建一个最终 namespace 目标的普通错误结果,包括
// 原子排他创建普通文件、跨进程协调式创建普通文件和单层目录创建。创建成功
// 时直接返回该接口自己的成功值,不附带本类型;查询目标是否存在、读取元
// 信息、覆盖已有目标和创建临时文件也不使用本类型。
//
// 递归建目录可能创建多个中间目标,不能用一个三态值完整表达已经发生的
// 副作用,因此明确不使用本类型。复制、改名、替换、截断和覆盖同样具有
// 不同的提交点或恢复资产,必须使用各自单独冻结的结果合同。
//
// 它描述的是“本次操作是否创建过目标”,不是读取错误结果时 namespace 的
// 实时状态。其它线程、进程或远端参与者可能在创建操作之后立即修改、改名
// 或删除目标;因此任何变体都不能代替新的存在性查询、稳定身份核验或协调
// 租约。
//
// # 失败副作用与重试边界
//
// 本库不会在创建成功而后续稳定身份查询、协调登记或资源交付失败时,自动
// 删除已经创建的目标。自动补偿删除会与已经观察或使用该文件、目录或远端
// 等价目标的其它参与者竞争,并可能删除不再属于本次操作的 namespace 条目。
//
// 本类型刻意不提供 `is_retry_safe`。即使证据是
// [`Self::NotCreatedByOperation`],调用方仍须结合错误种类、外部并发和所需
// 幂等语义判断能否重试;[`Self::CreatedByOperation`] 与 [`Self::Unknown`]
// 更不能被解释为可以再次执行原创建方法。
//
// 丢弃尚未完成的创建 Future 没有返回本证据的通道。若原生创建已经提交,
// 后台完成所有者仍须完成资源清理,但新建目标可能保留。创建方法必须
// 在自己的取消合同中再次公开这一边界。
//
// # 基本能力与演进
//
// 本类型刻意不实现 `Clone`、`Copy` 或 `Default`。它实现调试、显示、判等、
// 全序和哈希,以支持诊断、确定性索引和问题归档;排序只比较证据标签,不
// 表示可靠程度、时间先后或重试优先级。`Send` 与 `Sync` 由无字段枚举自动
// 获得,不使用手写 `unsafe impl`。
//
// 本类型不承诺稳定 ABI、整数判别值、序列化格式或可反向解析的文本协议。
// `#[non_exhaustive]` 要求库外调用方保留保守分支,以便以后增加更精确而不
// 削弱现有语义的可移植证据。
#[non_exhaustive]
/// 创建操作失败时,对目标条目是否由本次操作创建的可证明事实。
///
/// 证据只描述本次调用的历史,不是目标当前位置的实时查询,也不单独表示
/// 可以安全重试。它不提供持久化保证。
pub enum CreateTargetEvidence {
    // 能证明本次操作没有创建目标条目。
    //
    // 本变体不证明目标当前不存在。常见情况包括目标原本已经存在、创建前
    // 参数或角色校验失败,以及在原生创建提交前被协调状态明确拒绝。
    /// 能证明本次操作没有创建目标条目。
    ///
    /// 这不证明目标当前位置为空;其它参与者可能已经创建对象。
    NotCreatedByOperation,

    // 能证明本次操作已经创建过目标条目。
    //
    // 后续错误可能来自稳定身份查询、进程内协调登记、跨进程协议步骤或已
    // 打开资源的交付阶段。本变体不证明目标仍位于原 locator,不证明它仍是
    // 同一个稳定对象,也不证明空内容、目录项或元信息已经持久化。
    /// 能证明本次操作已经创建过目标条目。
    ///
    /// 这不证明目标当前仍存在、仍位于原位置或已经持久化。
    CreatedByOperation,

    // 无法证明本次操作是否创建过目标条目。
    //
    // 常见来源包括远端提交响应丢失、取消与后台完成竞态,以及无法给出
    // 可移植提交证据的第三方 adapter。调用方必须按可能已经创建处理。
    /// 无法证明本次操作是否创建过目标条目。
    ///
    /// 调用方必须按“可能已经创建”处理并重新查询。
    Unknown,
}

impl fmt::Debug for CreateTargetEvidence {
    // 以 Rust 变体名显示证据,不查询 namespace 或目标状态。
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str(match self {
            Self::NotCreatedByOperation => "NotCreatedByOperation",
            Self::CreatedByOperation => "CreatedByOperation",
            Self::Unknown => "Unknown",
        })
    }
}

impl fmt::Display for CreateTargetEvidence {
    // 输出面向日志的人类可读摘要,不承诺可反向解析。
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str(match self {
            Self::NotCreatedByOperation => "target not created by operation",
            Self::CreatedByOperation => "target created by operation",
            Self::Unknown => "target creation outcome unknown",
        })
    }
}

impl PartialEq for CreateTargetEvidence {
    // 仅在两个值表示相同证据标签时判等。
    fn eq(&self, other: &Self) -> bool {
        matches!(
            (self, other),
            (
                Self::NotCreatedByOperation,
                Self::NotCreatedByOperation
            ) | (
                Self::CreatedByOperation,
                Self::CreatedByOperation
            ) | (Self::Unknown, Self::Unknown)
        )
    }
}

impl Eq for CreateTargetEvidence {}

impl PartialOrd for CreateTargetEvidence {
    // 返回与 [`Ord`] 一致的证据标签全序。
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for CreateTargetEvidence {
    // 按变体声明顺序比较,不表达证据强度或恢复优先级。
    fn cmp(&self, other: &Self) -> Ordering {
        fn rank(evidence: &CreateTargetEvidence) -> u8 {
            match evidence {
                CreateTargetEvidence::NotCreatedByOperation => 0,
                CreateTargetEvidence::CreatedByOperation => 1,
                CreateTargetEvidence::Unknown => 2,
            }
        }

        rank(self).cmp(&rank(other))
    }
}

impl Hash for CreateTargetEvidence {
    // 按与判等一致的证据标签向调用方哈希器写入状态。
    fn hash<H>(&self, state: &mut H)
    where
        H: Hasher,
    {
        let discriminant = match self {
            Self::NotCreatedByOperation => 0_u8,
            Self::CreatedByOperation => 1_u8,
            Self::Unknown => 2_u8,
        };
        discriminant.hash(state);
    }
}

// 跨进程协调式创建成功后共同拥有授权与首个文件资源的载体。
//
// # 作用与使用位置
//
// 以后单独冻结的跨进程协调式新建接口必须在一个受控流程中建立目标、稳定
// 文件身份、协调协议、公开授权和首个文件资源。成功时使用本类型同时交付
// 后两项,避免调用方收到授权后再执行一次可能失败且浪费底层打开的操作。
//
// `A` 对应 [`crate::FileNamespace::CrossProcessAuthority`],`F` 对应
// [`crate::FileNamespace::File`]。类型定义不重复这些关联类型约束,使本
// helper 也能服务第三方 adapter 的具体类型;真正的 namespace 方法签名
// 负责把两者约束到同一个实现。
//
// 本类型只是成功结果载体,不实现 [`crate::FileIo`],也不通过 `Deref`、
// `AsRef<F>` 或隐式转换伪装成普通文件。调用方必须显式访问或拆解文件字段,
// 从而持续看见与它配对的跨进程授权。
//
// # 所有权、释放顺序与基本能力
//
// 两个字段均为拥有型且对库外私有。内部声明顺序刻意先放置 `file`,再放置
// `authority`:调用方直接 Drop 整个载体时,Rust 的字段析构顺序会先释放
// 文件资源,再释放公开授权。文件资源按照 [`crate::FileIo`] 合同已经拥有
// 自己所需的私有协调核心,因此调用方以后整体拆解后仍可以自行选择两个值
// 的独立生命周期。
//
// 本类型不实现 `Clone`、`Copy`、`Default`、判等、排序或哈希。它不应复制
// 文件句柄或把活动资源当作稳定值键。`Send` 与 `Sync` 只由 `A` 和 `F` 的
// 字段能力条件性自动获得,不使用手写 `unsafe impl`。
//
// `Debug` 与 `Display` 不要求泛型参数实现格式化 trait,只显示载体角色和
// 两项资源均存在的脱敏摘要;它们不打印 locator、稳定身份、原生句柄、锁
// 文件、认证信息或后端内部状态。本类型不声明稳定 ABI 或序列化格式。
/// 跨进程协调式创建成功后返回的授权与首个文件资源。
///
/// 两个值绑定同一目标实例。载体按所有权保存它们,不隐式克隆;调用方可以
/// 分别借用,或一次性拆解并独立管理生命周期。
pub struct CrossProcessCreateSuccess<A, F> {
    file: F,
    authority: A,
}

impl<A, F> CrossProcessCreateSuccess<A, F> {
    // 组合正确配对的跨进程授权与本次创建得到的首个文件资源。
    //
    // # 参数顺序与所有权
    //
    // 参数顺序固定为“授权、文件资源”,与类型参数 `<A, F>` 及以后单独冻结
    // 的拆解返回顺序一致。两个参数都按值移入载体,不克隆授权或文件,也不
    // 增加任何格式化、线程、生命周期或文件接口泛型约束。内部仍把 `file`
    // 存放在 `authority` 之前,以保持整体 Drop 时先释放文件的顺序。
    //
    // 本方法保持公开,使 crate 外部的 [`crate::FileNamespace`] adapter 能够
    // 生产统一的协调式创建成功结果。模块刻意不实现 `From<(A, F)>`,避免
    // 无语义的元组转换掩盖“授权与文件必须正确配对”的生产者责任。
    //
    // # 配对不变量与安全边界
    //
    // 生产者必须保证 `authority` 与 `file` 来自同一次成功的协调式创建,
    // 并且绑定相同 namespace/backend 实例、稳定文件身份、协调根目录和协议
    // 版本。本泛型构造方法不接收 locator 或后端验证器,不能自行证明这些
    // 条件。
    //
    // 错配属于严重的 adapter 合同错误,会误导资源管理和诊断。本载体不会
    // 被其它安全接口当作新的授权证明消费;真实能力仍由不可随意构造的 `A`
    // 自身承担。因此本构造方法保持安全,不以 `unsafe` 把一个普通字段组合
    // 动作伪装成内存安全前置条件。
    //
    // # 副作用、panic 与性能
    //
    // 本方法只移动两个字段,不访问文件、网络、全局协调器或锁文件,不分配、
    // 不加锁、不记录日志。计划成本为 `O(1)`;合法输入不应 panic。消费同一
    // 拥有型授权和资源的动作无法重复执行,因此不描述为幂等操作,但构造本身
    // 没有载体之外的外部副作用。
    #[must_use]
    /// 按“授权、文件资源”的顺序构造成功值。
    pub fn new(authority: A, file: F) -> Self {
        Self { file, authority }
    }

    // 共享借用与首个文件资源配对的跨进程授权。
    //
    // # 返回值与所有权
    //
    // 返回引用的生命周期严格绑定到 `self` 的本次共享借用。本方法不克隆、
    // 移动或重建授权;调用方可以把该引用传给接受 `&A` 的后续协调式打开
    // 接口,并在借用结束后继续访问或整体拆解成功载体。
    //
    // 当具体 `A: Sync` 时,普通 Rust 共享借用允许多个线程并发读取授权。
    // 这只复用授权绑定的协调核心,不克隆公开令牌,也不授予绕过同文件操作
    // 冲突矩阵的能力。
    //
    // # 接口选择
    //
    // 模块不提供 `authority_mut`:授权绑定 namespace/backend、稳定身份、
    // 协调根目录和协议版本,不能让调用方在仍配对原文件资源时原地替换或
    // 修改。它也不实现 `AsRef<A>`、`Deref` 或只消费授权的 `into_authority`,
    // 以免调用方无意间隐藏或丢弃首个文件资源。
    //
    // # 副作用、幂等性与性能
    //
    // 本方法不重新核验身份,不取得文件锁,不执行文件或网络 I/O,不修改
    // 协调状态,不分配、不记录日志。借用规则允许时,重复调用返回同一授权
    // 的共享引用;计划成本为 `O(1)`,合法调用不应 panic。
    #[must_use]
    /// 返回跨进程授权的共享引用。
    pub fn authority(&self) -> &A {
        &self.authority
    }

    // 共享借用协调式创建得到的首个文件资源。
    //
    // # 返回值、角色与所有权
    //
    // 返回引用的生命周期严格绑定到 `self` 的本次共享借用。本方法不克隆、
    // 移动、重新打开或复制文件资源及其底层句柄。调用方可以使用具体 `F`
    // 通过共享借用提供的查询、随机读取或其它接口;真正获准的操作仍由创建
    // 时选择的 [`crate::FileAccessMode`] 和 [`crate::FileIo`] 合同决定。
    //
    // 当具体 `F: Sync` 时,可以按 Rust 共享借用规则跨线程使用该引用。这只
    // 表示内存访问安全,不绕过稳定文件身份协调器、同文件操作冲突矩阵或
    // 需要 `&mut F`/`F` 的独占方法接收者。
    //
    // # 接口选择
    //
    // 模块不实现 `AsRef<F>` 或 `Deref`,避免把同时携带授权的成功载体透明
    // 伪装成普通文件资源。需要独占可变借用的操作由以后单独冻结的
    // `file_mut` 提供;需要所有权时则必须整体拆解,不能通过单独的
    // `into_file` 静默丢弃配对授权。
    //
    // # 副作用、幂等性与性能
    //
    // 本方法不重新核验稳定身份,不取得操作租约,不执行文件或网络 I/O,
    // 不分配、不加锁、不记录日志。借用规则允许时,重复调用返回同一文件
    // 资源的共享引用;计划成本为 `O(1)`,合法调用不应 panic。
    #[must_use]
    /// 返回首个文件资源的共享引用。
    pub fn file(&self) -> &F {
        &self.file
    }

    // 独占可变地借用协调式创建得到的首个文件资源。
    //
    // # 返回值、角色与借用边界
    //
    // 返回引用的生命周期严格绑定到 `self` 的本次独占借用。本方法不克隆、
    // 移动、重新打开或替换文件资源及其底层句柄。它允许调用方使用具体 `F`
    // 中要求 `&mut self` 的严格追加、顺序读取、刷新或其它独占接口。
    //
    // 获得 `&mut F` 不会扩大创建时冻结的 [`crate::FileAccessMode`]。每个实际
    // 文件操作仍须在产生副作用前核验角色并经稳定文件身份协调器准入;不允许
    // 的方法必须返回明确错误,不能因为 Rust 借用独占就绕过文件语义。
    //
    // 返回借用存活期间,调用方不能通过同一载体再次借用授权或文件。该规则
    // 由 Rust 的 `&mut self` 借用保证,不需要内部锁。接口只允许修改 `file`
    // 自己的合法状态,不暴露 `authority` 的可变引用,也不允许替换两者的配对。
    //
    // # 接口选择
    //
    // 模块不实现 `DerefMut` 或 `AsMut<F>`,避免把成功载体伪装成普通可变文件
    // 包装器。需要分别长期拥有授权和文件的调用方必须使用以后单独冻结的
    // 整体拆解接口,不能延长本方法返回的借用。
    //
    // # 副作用、幂等性与性能
    //
    // 取得可变引用本身不申请操作租约,不执行文件或网络 I/O,不访问锁文件
    // 或协调状态,不分配、不加锁、不记录日志。计划成本为 `O(1)`;合法调用
    // 不应 panic。在前一次可变借用结束后可以再次调用,但返回资源当时的当前
    // 状态,因此本访问动作本身没有额外外部副作用。
    #[must_use]
    /// 返回首个文件资源的独占引用。
    pub fn file_mut(&mut self) -> &mut F {
        &mut self.file
    }

    // 消费成功载体并一次性取回跨进程授权与首个文件资源。
    //
    // # 返回顺序与所有权
    //
    // 返回顺序固定为“授权、文件资源”,即 `(A, F)`;它与类型参数 `<A, F>`
    // 以及 [`Self::new(authority, file)`](Self::new) 的参数顺序完全对称。方法
    // 消费 `self`,把两个字段按值移出,不克隆授权、不复制或重新打开文件
    // 句柄,也不为任一字段增加新的 trait 或生命周期约束。
    //
    // 元组只用于拆解一个已经由命名类型表达正确配对关系的成功结果,不作为
    // 协调式创建接口的直接成功类型。模块刻意不提供只消费其中一项的
    // `into_authority` 或 `into_file`:调用方必须显式接收两项资产,避免在
    // 不知情时静默丢弃仍有用途的授权或首个已打开文件资源。
    //
    // # 拆解后的生命周期与协调语义
    //
    // 拆解不会解除授权与文件资源在创建时建立的逻辑配对,也不会重新核验
    // namespace/backend、稳定文件身份、协调根目录或协议版本。调用方取得
    // 所有权后可以分别决定两项资源的生命周期,但必须继续遵守各自接口的
    // 释放和跨进程协调合同。
    //
    // 如果调用方直接 Drop 未拆解的载体,字段声明顺序保证先释放文件、再
    // 释放授权;一旦调用本方法,该自动顺序不再适用,元组解构后的局部变量
    // 析构顺序由调用方代码决定。文件资源已经拥有维持其进程内安全所需的
    // 私有协调核心,因此合法地先释放授权不会把仍存活的文件资源变成悬垂
    // 引用;但失去公开授权后,调用方将不能再用它建立新的协调式资源。
    //
    // # 副作用、线程、panic 与性能
    //
    // 本方法不取得文件锁或操作租约,不执行文件、网络或 sidecar I/O,不
    // 查询全局协调器,不分配、不记录日志,也不修改目标。它只移动两个字段,
    // 计划时间和额外空间成本均为 `O(1)`;合法调用不应 panic。`Send`/`Sync`
    // 能力仍由返回的 `A` 与 `F` 各自决定。因为方法消费一次性所有权,它不能
    // 在同一个值上重复调用,不具有幂等性。
    #[must_use]
    /// 消费成功值并按构造顺序返回授权和文件资源。
    pub fn into_parts(self) -> (A, F) {
        (self.authority, self.file)
    }
}

impl<A, F> fmt::Debug for CrossProcessCreateSuccess<A, F> {
    // 显示不依赖底层资源格式化能力的脱敏结构摘要。
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str(
            "CrossProcessCreateSuccess { authority: <present>, file: <present> }",
        )
    }
}

impl<A, F> fmt::Display for CrossProcessCreateSuccess<A, F> {
    // 输出面向日志的脱敏成功载体摘要,不承诺稳定文本协议。
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str(
            "cross-process authority and file resource are present",
        )
    }
}

// 单个 namespace 目标创建普通失败时的结构化结果载体。
//
// # 作用与使用位置
//
// 本类型将统一的 [`pi_result::Error`] 诊断报告与本次创建操作的
// [`CreateTargetEvidence`] 绑定在同一个错误分支中。以后单独冻结的
// `FileNamespace::create_new`、其跨进程协调式对应方法和单层目录创建方法
// 会把本类型作为 `pi_result::RawResult` 的错误值;各接口的成功值仍按其
// 自身作用返回,不附带本载体。
//
// 本类型只适合一个最终目标和一个明确创建提交点。递归建目录、复制、改名、
// 替换、覆盖及截断不能仅靠一个 [`CreateTargetEvidence`] 表达全部部分副作用,
// 因而不得为了复用类型而丢失这些操作自己的恢复信息。
//
// 目标证据不是日志附件。调用方必须能够通过普通类型接口读取它,并与错误
// 原因一起决定是否重新查询、人工恢复或拒绝盲目重试。把两个维度放入一个
// 载体也确保 adapter 不能只返回诊断而遗漏已经发生的创建副作用。
//
// # 与错误类型的关系
//
// 本类型不是新的错误分类,也不实现 [`std::error::Error`]。`error` 字段已经
// 是完整的 `pi_result` report;创建过程中产生的本地叶子错误仍须使用
// `thiserror` 定义、分类并在公开接口处收敛为该 report。本类型与
// `crate::BufferFailure`、`crate::OverwriteFailure` 一样,只负责携带普通
// `Result` 错误分支必须整体处理的恢复信息,避免再把 report 包装成第二条
// 丢失原有 frame 或 attachment 的错误链。
//
// # 所有权与基本能力
//
// 两个字段对库外保持私有,后续只通过逐项冻结的借用访问器和一次性拆解
// 接口提供访问。本类型不实现 `Clone`、`Copy`、`Default`、判等、排序或
// 哈希:report 是一次性诊断资产,不具有适合本接口的稳定值比较语义。
//
// `Debug` 与 `Display` 不保存或显示 locator、目标内容、认证信息或其它额外
// 敏感数据,只组合已经进入 report 的诊断和目标证据摘要。`Send` 与 `Sync`
// 由字段条件性自动获得,不使用手写 `unsafe impl`;本类型没有借用字段,
// 因而可以作为拥有型 `'static` 创建 Future 的失败结果。
/// 单目标创建失败时的统一诊断和目标副作用证据。
///
/// 本类型不保存定位符或文件资源,也不会在释放时重试、删除或补偿目标。
pub struct CreateFailure {
    error: Error,
    target_evidence: CreateTargetEvidence,
}

impl CreateFailure {
    // 组合一个创建操作的统一诊断报告与目标副作用证据。
    //
    // # 参数顺序与所有权
    //
    // 参数顺序固定为“诊断报告、目标副作用证据”,与
    // [`crate::BufferFailure::new`] 和 [`crate::OverwriteFailure::new`] 先放置
    // 诊断的心智模型一致。两个参数都按值移入载体;本方法不克隆 report 或
    // 证据,也不接受可能在接口边界隐式丢失 frame、attachment 或错误分类的
    // `impl Into<Error>`。
    //
    // 本构造方法保持公开,因为 crate 外部的 [`crate::FileNamespace`]
    // adapter 也必须能产生完整的创建失败结果。adapter 在调用前必须已经把
    // 叶子错误归一化为 [`pi_result::Error`],并保证 `target_evidence` 真实反映
    // 本次操作是否跨过创建提交点。
    //
    // # 验证边界
    //
    // 构造方法不接收 locator、后端句柄或提交令牌,因此不会查询 namespace,
    // 也无法独立验证证据。虚假证据属于严重的 adapter 合同错误,可能误导
    // 恢复与重试;由于本方法不依据证据执行内存访问,它本身不会因此造成
    // Rust 内存安全意义上的未定义行为。
    //
    // # 副作用、panic 与性能
    //
    // 本方法只移动两个字段,不执行本地或远端 I/O,不访问全局协调状态,
    // 不分配、不加锁、不记录日志,也不修改目标。计划成本为 `O(1)`;合法
    // 输入不应 panic。消费相同拥有型参数的动作无法重复执行,因此不把构造
    // 描述为幂等操作,但它没有载体之外的可观察副作用。
    #[must_use]
    /// 按“错误、目标证据”的顺序构造失败值。
    pub fn new(error: Error, target_evidence: CreateTargetEvidence) -> Self {
        Self {
            error,
            target_evidence,
        }
    }

    // 共享借用本次创建失败的统一 `pi_result` 诊断报告。
    //
    // # 返回值与所有权
    //
    // 返回引用的生命周期严格绑定到 `self` 的本次共享借用。本方法不消费
    // [`CreateFailure`],不克隆、移动或重建 report;调用方可以在借用结束后
    // 继续读取目标证据,或使用以后单独冻结的一次性拆解接口同时取得两个
    // 字段的所有权。
    //
    // 本接口不提供可变引用。诊断报告与 [`CreateTargetEvidence`] 共同描述同一
    // 次失败,不能通过本接口单独替换或移出报告,使载体留下彼此失配的两个
    // 维度。模块也刻意不提供只消费报告的 `into_error`,避免恢复代码静默
    // 丢弃目标副作用证据。
    //
    // # 接口选择
    //
    // 本类型不实现 `Deref<Target = Error>` 或 `AsRef<Error>`。显式的 `error`
    // 方法提醒调用方:这份 report 只是结构化失败载体的一部分,不应把整个
    // [`CreateFailure`] 当作普通 report 透明传递并遗漏目标证据。
    //
    // # 副作用、幂等性与性能
    //
    // 本方法不修改载体,不执行 I/O,不查询目标或协调状态,不分配、不加锁、
    // 不记录日志。借用规则允许时,重复调用返回同一 report 的共享引用;计划
    // 成本为 `O(1)`,合法调用不应 panic。
    #[must_use]
    /// 返回失败诊断的共享引用。
    pub fn error(&self) -> &Error {
        &self.error
    }

    // 共享借用本次创建操作的目标副作用证据。
    //
    // # 返回值与语义
    //
    // 返回引用的生命周期严格绑定到 `self` 的本次共享借用。它指向创建完成
    // 路径记录在载体中的同一份 [`CreateTargetEvidence`],不会克隆、移动或
    // 重新推断证据。
    //
    // 本方法不查询 locator、文件元信息或远端对象状态,因此返回值只回答
    // “本次操作是否创建过目标”,不能证明读取引用这一时刻目标是否存在、
    // 是否仍具有同一稳定身份或是否已经持久化。调用方必须把它与 [`Self::error`]
    // 及外部协调状态一起解释。
    //
    // # 接口选择
    //
    // 本接口不提供可变引用、`AsRef<CreateTargetEvidence>` 或将三态压缩为布尔值
    // 的 `was_created`。可变访问会破坏报告与证据的对应关系;布尔值无法保留
    // [`CreateTargetEvidence::Unknown`],也无法兼容该非穷举枚举以后增加的更
    // 精确证据。需要所有权时必须使用以后单独冻结的整体拆解接口。
    //
    // # 副作用、幂等性与性能
    //
    // 本方法不修改载体,不执行 I/O,不访问协调器,不分配、不加锁、不记录
    // 日志。借用规则允许时,重复调用返回同一证据的共享引用;计划成本为
    // `O(1)`,合法调用不应 panic。
    #[must_use]
    /// 返回目标创建证据的共享引用。
    pub fn target_evidence(&self) -> &CreateTargetEvidence {
        &self.target_evidence
    }

    // 消费失败载体并一次性取回诊断报告与目标副作用证据。
    //
    // # 返回顺序与所有权
    //
    // 返回顺序固定为“诊断报告、目标副作用证据”,与
    // [`Self::new(error, target_evidence)`](Self::new) 的参数顺序完全对称。
    // 本方法消费 `self`,把两个字段按值移出;不克隆 report 或证据,也不
    // 重新分类错误、查询目标或生成新的恢复结论。
    //
    // 元组只用于拆解一个已经通过命名类型建立的不变量,不作为创建接口的
    // 错误类型直接暴露。调用方取得所有权后必须同时处理两个元素;模块刻意
    // 不提供只取走其中一个字段的消费方法,避免另一个必要维度被静默丢弃。
    //
    // # 副作用、幂等性与性能
    //
    // 本方法只移动字段,不执行本地或远端 I/O,不修改目标,不访问协调器,
    // 不分配、不加锁、不记录日志。计划成本为 `O(1)`,合法调用不应 panic。
    // 因为方法消费一次性所有权,它不能在同一个值上重复执行,不具有幂等性。
    #[must_use]
    /// 消费失败值并按构造顺序返回全部字段。
    pub fn into_parts(self) -> (Error, CreateTargetEvidence) {
        (self.error, self.target_evidence)
    }
}

impl fmt::Debug for CreateFailure {
    // 显示统一诊断和目标证据,不查询或修改 namespace 目标。
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter
            .debug_struct("CreateFailure")
            .field("error", &self.error)
            .field("target_evidence", &self.target_evidence)
            .finish()
    }
}

impl fmt::Display for CreateFailure {
    // 输出面向日志的失败摘要,不承诺稳定文本协议。
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            formatter,
            "{}; target: {}",
            self.error, self.target_evidence
        )
    }
}

// 递归创建目录层级失败时的诊断与多目标副作用载体。
//
// # 作用与使用位置
//
// 本类型专门作为以后单独冻结的 `FileNamespace::create_dir_all` 普通错误值。
// 递归创建可能先建立若干缺失祖先,随后在更深目录失败;单目标
// [`CreateFailure`] 的三态证据无法回答哪些中间目录已经由本次操作建立。
// 本载体把统一诊断与两组拥有型 locator 放在同一个错误分支中,使调用方
// 可以明确区分“已证明创建”和“已提交但结果不确定”。
//
// 本类型不用于严格单层目录创建、普通文件创建、复制、改名、替换、覆盖或
// 截断。这些操作具有不同的目标数量、提交点和恢复资产,不能仅因都可能修改
// namespace 就共用一个模糊错误类型。
//
// # 两组目标证据
//
// `confirmed_created` 按祖先到后代的顺序保存能够证明由本次操作创建成功的
// 目录定位符;`uncertain_targets` 按相同顺序保存已经提交创建、但 backend
// 无法证明是否成功的定位符。两个列表必须各自无重复并彼此不相交,且都不
// 包含调用前已经存在的目录或从未实际提交创建的剩余路径。
//
// 本地逐层创建通常至多产生一个不确定目标。远端 backend 可以使用一次请求
// 批量建立多层目录,因此不确定集合使用 `Vec<L>` 而不是 `Option<L>`。批量
// 能力不允许 adapter 把未经尝试的全部后代填入该列表;每个项目都必须对应
// 一次真实提交。
//
// 两组 locator 都只是本次失败路径的历史副作用证据。其它线程、进程或远端
// 参与者可能随后删除、改名或替换这些位置,因此列表不证明读取失败值时目标
// 仍存在、仍是目录或仍指向本次创建的对象。调用方需要当前状态时必须重新
// 查询,并处理查询与后续动作之间的新竞态。
//
// # 泛型、所有权与基本能力
//
// `L` 是产生该失败值的 `FileNamespace::Locator`。类型定义本身不增加
// `Clone`、格式化、线程或生命周期约束;字段按所有权保存 locator,不借用
// 创建 Future、namespace 或调用方输入。具体 `Send` 与 `Sync` 只由
// `Error`、`Vec<L>` 和 `L` 的字段能力条件性自动获得,不使用手写
// `unsafe impl`。
//
// 本类型不实现 `Clone`、`Copy`、`Default`、判等、排序或哈希。诊断 report
// 和并发 namespace 中的历史位置集合都不具有适合这些 trait 的稳定值语义,
// 克隆还会隐式复制可能较长的路径列表。
//
// `Debug` 与 `Display` 不要求 `L: Debug` 或 `L: Display`,只显示统一错误与
// 两个列表的数量,不打印可能包含用户名称、凭据或敏感路径的 locator。
// 本类型不实现 `std::error::Error`;它与 [`CreateFailure`] 一样,是携带恢复
// 资产的普通 `RawResult` 错误分支,而不是包裹 `pi_result::Error` 的第二条
// 错误链。
//
// # 不变量与验证责任
//
// locator 是否来自本次操作、排列顺序、去重和两个集合互斥均由生产本值的
// adapter 保证。泛型载体没有 namespace、目标句柄或提交令牌,不能自行执行
// I/O 核验。虚假证据属于严重 adapter 合同错误,会误导恢复逻辑;但本类型
// 不依据这些值执行裸内存访问,因此该错误本身不是 Rust 内存安全意义上的
// 未定义行为。
/// 递归目录创建失败时的统一诊断和多目标副作用证据。
///
/// `confirmed_created` 保存已确认由本次调用创建的目录,`uncertain_targets`
/// 保存已经提交创建但结果无法证明的目录。两个列表均按祖先到后代排列、
/// 各自无重复且彼此不相交;调用前已存在或从未尝试的目录不得进入列表。
/// 本类型不会在释放时回滚或删除任何目录。
pub struct CreateDirectoriesFailure<L> {
    error: Error,
    confirmed_created: Vec<L>,
    uncertain_targets: Vec<L>,
}

impl<L> CreateDirectoriesFailure<L> {
    // 组合递归目录创建的统一诊断和两类多目标副作用证据。
    //
    // # 参数顺序与所有权
    //
    // 参数顺序固定为“诊断报告、已确认创建目标、不确定目标”,与字段的语义
    // 顺序以及以后单独冻结的整体拆解返回顺序一致。三个参数都按所有权移入
    // 载体;本方法不克隆 locator、错误报告或列表,也不接受可能在接口边界
    // 隐式丢失 `pi_result` frame、attachment 或错误分类的 `impl Into<Error>`。
    //
    // 本构造器保持公开,使 crate 外部的 [`crate::FileNamespace`] adapter 也
    // 能产生统一失败值。它不对 `L` 增加 `Clone`、判等、排序、哈希、格式化、
    // 线程或生命周期约束。
    //
    // # 生产者不变量与安全边界
    //
    // adapter 在调用前必须保证 `confirmed_created` 和 `uncertain_targets`
    // 分别按祖先到后代排列、各自无重复、彼此不相交,并且只包含本次操作
    // 实际提交过创建的目标。前者只包含已证明创建成功的目录,后者只包含
    // 已提交但无法证明结果的目录;调用前已经存在或从未尝试的目标不能进入
    // 任一列表。
    //
    // 本方法不自动排序、去重或检查集合相交。自动排序会破坏生产顺序证据,
    // 通用 `Ord` 也不能证明 locator 的祖先关系;隐式去重则会掩盖 adapter
    // 状态机错误。泛型载体没有 namespace、目标句柄或提交令牌,无法执行
    // 真实 I/O 核验。
    //
    // 构造器保持安全函数。虚假证据可能误导调用方的恢复决策,属于严重的
    // adapter 合同错误,但构造本值本身不依据 locator 执行裸内存访问,也不
    // 建立后续内存安全操作的证明,因此不能用 `unsafe fn` 错误表示其风险。
    //
    // # 副作用、panic 与性能
    //
    // 本方法只移动三个字段,计划成本为 `O(1)`;它不新增分配、不执行本地
    // 或远端 I/O、不修改 namespace、不加锁、不记录日志。满足生产者合同的
    // 调用不应 panic。由于参数所有权被消费,同一个值不能重复构造,但本
    // 动作没有载体之外的可观察副作用。
    #[must_use]
    /// 按“错误、已确认创建、不确定目标”的顺序构造失败值。
    ///
    /// 调用方负责满足类型文档声明的列表不变量;本构造器不改变列表顺序。
    pub fn new(
        error: Error,
        confirmed_created: Vec<L>,
        uncertain_targets: Vec<L>,
    ) -> Self {
        Self {
            error,
            confirmed_created,
            uncertain_targets,
        }
    }

    // 共享借用递归目录创建失败的统一诊断报告。
    //
    // # 返回值与所有权
    //
    // 返回引用的生命周期严格绑定到 `self` 的本次共享借用。本方法不消费
    // 失败载体,不克隆、移动或重新包装 [`pi_result::Error`];调用方可以在
    // 借用结束后继续访问两组目标副作用证据,或使用以后单独冻结的整体拆解
    // 接口一次性取得全部字段。
    //
    // 本接口不提供可变引用。诊断与已确认创建、不确定目标共同描述同一次
    // 递归创建失败,不能让调用方单独替换报告并留下彼此失配的恢复信息。
    // 模块也不提供只消费报告的 `into_error`,避免错误传播代码静默丢弃两组
    // namespace 副作用证据。
    //
    // # 接口选择
    //
    // 本类型不实现 `Deref<Target = Error>` 或 `AsRef<Error>`。显式的 `error`
    // 方法提醒调用方:report 只是多维失败结果的一部分,不能把整个
    // [`CreateDirectoriesFailure`] 当作普通错误透明传递。
    //
    // # 副作用、幂等性与性能
    //
    // 本方法计划为 `O(1)` 共享借用,无分配、无锁、无 I/O,不读取或修改
    // namespace,也不记录日志。借用规则允许时重复调用返回同一 report 的
    // 共享引用,合法调用不应 panic。
    #[must_use]
    /// 返回失败诊断的共享引用。
    pub fn error(&self) -> &Error {
        &self.error
    }

    // 按祖先到后代顺序共享借用已确认由本次操作创建的目录定位符。
    //
    // # 返回值与证据语义
    //
    // 返回切片只包含能够证明由本次递归操作创建成功的目录,不包含调用前
    // 已经存在的目录、不确定目标或从未提交创建的剩余路径。空切片表示失败
    // 前没有任何目录被证明创建成功,不表示请求根当前不存在。
    //
    // 列表是历史副作用证据,不是实时 namespace 快照。项目可能在创建后被
    // 其它参与者删除、改名或替换;调用方不能仅凭本切片执行无条件清理,也
    // 不能把 locator 当作稳定对象身份。需要恢复时必须重新查询并核验当前
    // 状态。
    //
    // # 借用与接口选择
    //
    // 返回引用的生命周期严格绑定到 `self` 的共享借用。本方法不复制 `Vec`
    // 或任何 `L`;确实需要拥有副本的调用方可以在具体 `L: Clone` 时显式复制。
    // 切片本身已经支持长度查询、正向与反向迭代和索引,因此不增加重复的
    // iterator 或计数访问器。
    //
    // 本接口不提供可变切片,避免调用方破坏祖先顺序、去重以及与不确定集合
    // 互斥的不变量。类型也不实现 `AsRef<[L]>`,因为载体中存在两组语义不同
    // 的 `L` 列表,隐式转换无法清楚表达调用方选择的是哪一组。
    //
    // # 副作用、幂等性与性能
    //
    // 本方法计划为 `O(1)` 共享借用,无分配、无锁、无 I/O,不查询或修改
    // namespace。借用规则允许时重复调用返回同一列表的共享切片,合法调用
    // 不应 panic。
    #[must_use]
    /// 返回已确认由本次操作创建的目录列表。
    pub fn confirmed_created(&self) -> &[L] {
        &self.confirmed_created
    }

    // 按祖先到后代顺序共享借用创建结果不确定的目录定位符。
    //
    // # 返回值与证据语义
    //
    // 返回切片只包含已经真实提交创建、但 backend 无法证明是否成功的目标。
    // 它不包含已确认创建、已确认未创建、调用前已经存在或失败后尚未尝试的
    // 目录。空切片表示本次失败没有不确定创建结果,不表示没有其它已确认的
    // namespace 副作用。
    //
    // 本地逐层创建通常产生零个或一个项目;远端批量操作可能产生多个项目。
    // 每个 locator 都必须按“可能已经创建”处理,但不能直接当作当前存在、
    // 仍是目录或可以安全删除的证明。恢复时应重新查询并核验并发参与者可能
    // 已经创建、删除、改名或替换目标的事实。
    //
    // # 借用与接口选择
    //
    // 返回引用的生命周期严格绑定到 `self` 的共享借用。本方法不复制 `Vec`
    // 或任何 locator;确实需要拥有副本的调用方可以在具体 `L: Clone` 时显式
    // 复制。切片已经提供长度查询、正向与反向迭代和索引,不再增加单项或
    // iterator 便利接口。
    //
    // 本接口不提供可变切片,避免调用方破坏祖先顺序、去重以及与已确认集合
    // 互斥的不变量。载体也不实现含义不明的 `AsRef<[L]>`。
    //
    // # 副作用、幂等性与性能
    //
    // 本方法计划为 `O(1)` 共享借用,无分配、无锁、无 I/O,不查询或修改
    // namespace。借用规则允许时重复调用返回同一列表的共享切片,合法调用
    // 不应 panic。
    #[must_use]
    /// 返回已经提交创建但结果无法证明的目录列表。
    pub fn uncertain_targets(&self) -> &[L] {
        &self.uncertain_targets
    }

    // 消费失败载体并一次性取回诊断与两组目标副作用证据。
    //
    // # 返回顺序与所有权
    //
    // 返回顺序固定为“诊断报告、已确认创建目标、不确定目标”,即
    // `(Error, Vec<L>, Vec<L>)`;它与
    // [`Self::new(error, confirmed_created, uncertain_targets)`](Self::new)
    // 的参数顺序完全对称。本方法消费 `self` 并按值移出三个字段,不克隆
    // report、列表或 locator。
    //
    // 拆解不会重新排序、去重、合并两组 locator,也不会查询 namespace 或
    // 重新推断证据。调用方取得的列表保持生产者记录的祖先到后代顺序与原有
    // 不变量,但仍只是失败发生时的历史副作用证据。
    //
    // # 接口选择
    //
    // 模块不提供 `into_error`、`into_confirmed_created` 或
    // `into_uncertain_targets` 等部分消费接口,避免调用方静默丢弃其余恢复
    // 信息。类型也不实现与三元组之间的 `From`/`Into`:构造时应使用有语义
    // 名称的 [`Self::new`],拆解时应显式使用本方法,使字段顺序与两组证据
    // 含义持续可见。
    //
    // # 副作用、幂等性与性能
    //
    // 本方法只移动字段,计划成本为 `O(1)`;不新增分配、不执行 I/O、不访问
    // namespace、不加锁、不记录日志。满足类型不变量的调用不应 panic。
    // 因为方法消费一次性所有权,同一个失败值只能拆解一次,不具有幂等性。
    #[must_use]
    /// 消费失败值并按构造顺序返回全部字段。
    pub fn into_parts(self) -> (Error, Vec<L>, Vec<L>) {
        (self.error, self.confirmed_created, self.uncertain_targets)
    }
}

impl<L> fmt::Debug for CreateDirectoriesFailure<L> {
    // 显示统一诊断和两类目标数量,不格式化或查询 locator。
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter
            .debug_struct("CreateDirectoriesFailure")
            .field("error", &self.error)
            .field("confirmed_created_count", &self.confirmed_created.len())
            .field("uncertain_targets_count", &self.uncertain_targets.len())
            .finish()
    }
}

impl<L> fmt::Display for CreateDirectoriesFailure<L> {
    // 输出脱敏失败摘要及目标数量,不承诺稳定文本协议。
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            formatter,
            "{}; confirmed created: {}; uncertain targets: {}",
            self.error,
            self.confirmed_created.len(),
            self.uncertain_targets.len()
        )
    }
}