aster_forge_cloud_files_windows/
connection.rs

1//! Owned callback snapshots and active CFAPI connection lifecycle fencing.
2
3use std::{
4    ffi::OsString,
5    fmt,
6    path::PathBuf,
7    sync::{
8        Arc, Mutex, MutexGuard,
9        atomic::{AtomicU8, AtomicU64, Ordering},
10    },
11};
12
13use aster_forge_cloud_files_core::HydrationCoordinator;
14use aster_forge_cloud_files_core::{
15    Alignment, ByteRange, CloudBackendErrorKind, ContentReadRange, ContentReadResponse,
16    ContentRevision, HydrationError, HydrationRequest, SessionGeneration, SessionState,
17};
18use bytes::Bytes;
19
20use crate::{
21    Result, WindowsCloudFilesError, WindowsFetchDataWaiterRegistry, WindowsFileIdentity,
22    WindowsSyncRootIdentity,
23};
24
25/// Maximum priority value documented by CFAPI.
26pub const CFAPI_MAX_PRIORITY_HINT: u8 = 15;
27/// Physical range alignment required by CFAPI transfer operations.
28pub const CFAPI_TRANSFER_ALIGNMENT_BYTES: u64 = 4096;
29
30/// Additional callback metadata and implicit-hydration behavior requested at connect time.
31#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
32pub struct WindowsSyncRootConnectOptions {
33    /// Ask CFAPI to populate owned process metadata in callback snapshots.
34    pub require_process_info: bool,
35    /// Ask CFAPI to provide a full normalized placeholder path.
36    pub require_full_file_path: bool,
37    /// Block provider-originated implicit hydration before a callback is generated.
38    pub block_self_implicit_hydration: bool,
39}
40
41/// Opaque native connection key returned by `CfConnectSyncRoot`.
42///
43/// This value is deliberately distinct from [`SessionGeneration`]. The key addresses one native
44/// communication channel; the generation is the durable monotonic fence used by Forge work.
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
46pub struct WindowsConnectionKey(i64);
47
48impl WindowsConnectionKey {
49    /// Wraps the exact opaque scalar returned by CFAPI.
50    #[must_use]
51    pub const fn new(value: i64) -> Self {
52        Self(value)
53    }
54
55    /// Returns the exact native scalar.
56    #[must_use]
57    pub const fn get(self) -> i64 {
58        self.0
59    }
60}
61
62/// Opaque transfer key used by CFAPI to correlate hydration operations.
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
64pub struct WindowsTransferKey(i64);
65
66impl WindowsTransferKey {
67    /// Wraps the exact callback scalar.
68    #[must_use]
69    pub const fn new(value: i64) -> Self {
70        Self(value)
71    }
72
73    /// Returns the exact callback scalar.
74    #[must_use]
75    pub const fn get(self) -> i64 {
76        self.0
77    }
78}
79
80/// Opaque request key used by newer CFAPI operation correlation.
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
82pub struct WindowsRequestKey(i64);
83
84impl WindowsRequestKey {
85    /// Wraps the exact callback scalar.
86    #[must_use]
87    pub const fn new(value: i64) -> Self {
88        Self(value)
89    }
90
91    /// Returns the exact callback scalar.
92    #[must_use]
93    pub const fn get(self) -> i64 {
94        self.0
95    }
96}
97
98/// CFAPI callback range length.
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
100pub enum WindowsCallbackRangeLength {
101    /// One exact non-zero byte count.
102    Exact(u64),
103    /// Native `CF_EOF` (`-1`), meaning from the offset through end of file.
104    ToEnd,
105}
106
107/// Owned CFAPI callback range preserving the native `CF_EOF` distinction.
108#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
109pub struct WindowsCallbackRange {
110    offset: u64,
111    length: WindowsCallbackRangeLength,
112}
113
114impl WindowsCallbackRange {
115    /// Creates one exact non-empty range and rejects end overflow.
116    /// # Errors
117    ///
118    /// Returns an error when validation fails or an underlying backend, store, or platform
119    /// operation fails.
120    pub fn exact(offset: u64, length: u64) -> Result<Self> {
121        if offset > i64::MAX as u64 {
122            return Err(invalid_callback(
123                "callback range offset exceeds signed CFAPI boundary",
124            ));
125        }
126        if length == 0 {
127            return Err(invalid_callback("callback range length must be non-zero"));
128        }
129        if length > i64::MAX as u64 {
130            return Err(invalid_callback(
131                "callback range length exceeds signed CFAPI boundary",
132            ));
133        }
134        if offset
135            .checked_add(length)
136            .is_none_or(|end| end > i64::MAX as u64)
137        {
138            return Err(invalid_callback(
139                "callback range end exceeds signed CFAPI boundary",
140            ));
141        }
142        Ok(Self {
143            offset,
144            length: WindowsCallbackRangeLength::Exact(length),
145        })
146    }
147
148    /// Creates a range extending from `offset` through the current end of file.
149    /// # Errors
150    ///
151    /// Returns an error when validation fails or an underlying backend, store, or platform
152    /// operation fails.
153    pub fn to_end(offset: u64) -> Result<Self> {
154        if offset > i64::MAX as u64 {
155            return Err(invalid_callback(
156                "callback range offset exceeds signed CFAPI boundary",
157            ));
158        }
159        Ok(Self {
160            offset,
161            length: WindowsCallbackRangeLength::ToEnd,
162        })
163    }
164
165    /// Converts the signed CFAPI offset/length pair into an owned range.
166    /// # Errors
167    ///
168    /// Returns an error when validation fails or an underlying backend, store, or platform
169    /// operation fails.
170    pub fn from_cfapi(offset: i64, length: i64) -> Result<Self> {
171        let offset = u64::try_from(offset)
172            .map_err(|_| invalid_callback("callback range offset must be non-negative"))?;
173        match length {
174            -1 => Self::to_end(offset),
175            1.. => Self::exact(
176                offset,
177                u64::try_from(length)
178                    .map_err(|_| invalid_callback("callback range length exceeds u64"))?,
179            ),
180            _ => Err(invalid_callback(
181                "callback range length must be positive or CF_EOF",
182            )),
183        }
184    }
185
186    /// Returns the first requested byte offset.
187    #[must_use]
188    pub const fn offset(self) -> u64 {
189        self.offset
190    }
191
192    /// Returns the exact native length semantics.
193    #[must_use]
194    pub const fn length(self) -> WindowsCallbackRangeLength {
195        self.length
196    }
197
198    /// Returns the exclusive end for an exact range, or `None` for `ToEnd`.
199    #[must_use]
200    pub const fn end_exclusive(self) -> Option<u64> {
201        match self.length {
202            WindowsCallbackRangeLength::Exact(length) => Some(self.offset + length),
203            WindowsCallbackRangeLength::ToEnd => None,
204        }
205    }
206}
207
208/// Owned process information requested through `CF_CONNECT_FLAG_REQUIRE_PROCESS_INFO`.
209#[derive(Debug, Clone, PartialEq, Eq)]
210pub struct WindowsProcessInfoSnapshot {
211    /// Native process identifier.
212    pub process_id: u32,
213    /// Executable image path, preserving Windows wide-string data.
214    pub image_path: Option<PathBuf>,
215    /// Optional package name.
216    pub package_name: Option<OsString>,
217    /// Optional application identifier.
218    pub application_id: Option<OsString>,
219    /// Optional command line.
220    pub command_line: Option<OsString>,
221    /// Native logon session identifier.
222    pub session_id: u32,
223}
224
225/// Owned common data copied from `CF_CALLBACK_INFO` before the callback returns.
226#[derive(Clone, PartialEq, Eq)]
227pub struct WindowsCallbackInfoSnapshot {
228    generation: SessionGeneration,
229    connection_key: WindowsConnectionKey,
230    transfer_key: WindowsTransferKey,
231    request_key: WindowsRequestKey,
232    volume_guid_name: Option<OsString>,
233    volume_dos_name: Option<OsString>,
234    volume_serial_number: u32,
235    sync_root_file_id: i64,
236    sync_root_identity: WindowsSyncRootIdentity,
237    file_id: i64,
238    file_size: u64,
239    file_identity: WindowsFileIdentity,
240    normalized_path: Option<PathBuf>,
241    priority_hint: u8,
242    process: Option<WindowsProcessInfoSnapshot>,
243}
244
245impl WindowsCallbackInfoSnapshot {
246    /// Creates and validates a fully owned callback-info snapshot.
247    #[expect(
248        clippy::too_many_arguments,
249        reason = "the constructor mirrors the flat, versioned CF_CALLBACK_INFO ownership boundary"
250    )]
251    /// # Errors
252    ///
253    /// Returns an error when validation fails or an underlying backend, store, or platform
254    /// operation fails.
255    pub fn new(
256        generation: SessionGeneration,
257        connection_key: WindowsConnectionKey,
258        transfer_key: WindowsTransferKey,
259        request_key: WindowsRequestKey,
260        volume_guid_name: Option<OsString>,
261        volume_dos_name: Option<OsString>,
262        volume_serial_number: u32,
263        sync_root_file_id: i64,
264        sync_root_identity: WindowsSyncRootIdentity,
265        file_id: i64,
266        file_size: u64,
267        file_identity: WindowsFileIdentity,
268        normalized_path: Option<PathBuf>,
269        priority_hint: u8,
270        process: Option<WindowsProcessInfoSnapshot>,
271    ) -> Result<Self> {
272        if priority_hint > CFAPI_MAX_PRIORITY_HINT {
273            return Err(invalid_callback("priority hint exceeds CFAPI maximum"));
274        }
275        if file_size > i64::MAX as u64 {
276            return Err(invalid_callback(
277                "callback file size exceeds signed CFAPI boundary",
278            ));
279        }
280        let scope = sync_root_identity.decode()?;
281        let item_key = file_identity.decode()?;
282        if item_key.scope() != &scope {
283            return Err(invalid_callback(
284                "file identity belongs to another sync-root scope",
285            ));
286        }
287        Ok(Self {
288            generation,
289            connection_key,
290            transfer_key,
291            request_key,
292            volume_guid_name,
293            volume_dos_name,
294            volume_serial_number,
295            sync_root_file_id,
296            sync_root_identity,
297            file_id,
298            file_size,
299            file_identity,
300            normalized_path,
301            priority_hint,
302            process,
303        })
304    }
305
306    /// Returns the durable platform session fence captured at callback ingress.
307    #[must_use]
308    pub fn generation(&self) -> SessionGeneration {
309        self.generation
310    }
311
312    /// Returns the native connection key used by later CFAPI operations.
313    #[must_use]
314    pub const fn connection_key(&self) -> WindowsConnectionKey {
315        self.connection_key
316    }
317
318    /// Returns the native transfer key.
319    #[must_use]
320    pub const fn transfer_key(&self) -> WindowsTransferKey {
321        self.transfer_key
322    }
323
324    /// Returns the native request key.
325    #[must_use]
326    pub const fn request_key(&self) -> WindowsRequestKey {
327        self.request_key
328    }
329
330    /// Returns the optional volume GUID name.
331    #[must_use]
332    pub fn volume_guid_name(&self) -> Option<&std::ffi::OsStr> {
333        self.volume_guid_name.as_deref()
334    }
335
336    /// Returns the optional DOS volume name.
337    #[must_use]
338    pub fn volume_dos_name(&self) -> Option<&std::ffi::OsStr> {
339        self.volume_dos_name.as_deref()
340    }
341
342    /// Returns the native volume serial number.
343    #[must_use]
344    pub const fn volume_serial_number(&self) -> u32 {
345        self.volume_serial_number
346    }
347
348    /// Returns the sync-root file identifier.
349    #[must_use]
350    pub const fn sync_root_file_id(&self) -> i64 {
351        self.sync_root_file_id
352    }
353
354    /// Returns the owned sync-root identity bytes.
355    #[must_use]
356    pub const fn sync_root_identity(&self) -> &WindowsSyncRootIdentity {
357        &self.sync_root_identity
358    }
359
360    /// Returns the native placeholder file identifier.
361    #[must_use]
362    pub const fn file_id(&self) -> i64 {
363        self.file_id
364    }
365
366    /// Returns the non-negative file size copied from CFAPI.
367    #[must_use]
368    pub const fn file_size(&self) -> u64 {
369        self.file_size
370    }
371
372    /// Returns the owned stable item identity.
373    #[must_use]
374    pub const fn file_identity(&self) -> &WindowsFileIdentity {
375        &self.file_identity
376    }
377
378    /// Returns the optional owned normalized path.
379    #[must_use]
380    pub fn normalized_path(&self) -> Option<&std::path::Path> {
381        self.normalized_path.as_deref()
382    }
383
384    /// Returns the native scheduling priority hint.
385    #[must_use]
386    pub const fn priority_hint(&self) -> u8 {
387        self.priority_hint
388    }
389
390    /// Returns optional owned process metadata.
391    #[must_use]
392    pub const fn process(&self) -> Option<&WindowsProcessInfoSnapshot> {
393        self.process.as_ref()
394    }
395}
396
397impl fmt::Debug for WindowsCallbackInfoSnapshot {
398    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
399        formatter
400            .debug_struct("WindowsCallbackInfoSnapshot")
401            .field("generation", &self.generation)
402            .field("connection_key", &self.connection_key)
403            .field("transfer_key", &self.transfer_key)
404            .field("request_key", &self.request_key)
405            .field("volume_serial_number", &self.volume_serial_number)
406            .field("sync_root_file_id", &self.sync_root_file_id)
407            .field("sync_root_identity", &self.sync_root_identity)
408            .field("file_id", &self.file_id)
409            .field("file_size", &self.file_size)
410            .field("file_identity", &self.file_identity)
411            .field("priority_hint", &self.priority_hint)
412            .field("has_process_info", &self.process.is_some())
413            .finish_non_exhaustive()
414    }
415}
416
417/// Bitset copied from `CF_CALLBACK_FETCH_DATA_FLAGS`.
418#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
419pub struct WindowsFetchDataFlags(u32);
420
421impl WindowsFetchDataFlags {
422    /// Recovery of a hydration interrupted by an unclean provider or system shutdown.
423    pub const RECOVERY: Self = Self(0x1);
424    /// Hydration was explicitly initiated rather than caused by ordinary file I/O.
425    pub const EXPLICIT_HYDRATION: Self = Self(0x2);
426
427    /// Preserves the exact native bitset, including future bits.
428    #[must_use]
429    pub const fn from_bits_retain(bits: u32) -> Self {
430        Self(bits)
431    }
432
433    /// Returns the exact native bitset.
434    #[must_use]
435    pub const fn bits(self) -> u32 {
436        self.0
437    }
438
439    /// Returns whether all bits in `other` are present.
440    #[must_use]
441    pub const fn contains(self, other: Self) -> bool {
442        self.0 & other.0 == other.0
443    }
444}
445
446/// Owned `FETCH_DATA` callback snapshot.
447#[derive(Debug, Clone, PartialEq, Eq)]
448pub struct WindowsFetchDataSnapshot {
449    info: WindowsCallbackInfoSnapshot,
450    flags: WindowsFetchDataFlags,
451    required_range: WindowsCallbackRange,
452    optional_range: Option<WindowsCallbackRange>,
453    last_dehydration_time: i64,
454    last_dehydration_reason: u32,
455}
456
457impl WindowsFetchDataSnapshot {
458    /// Creates an owned fetch snapshot after native pointers have been copied.
459    #[must_use]
460    pub const fn new(
461        info: WindowsCallbackInfoSnapshot,
462        flags: WindowsFetchDataFlags,
463        required_range: WindowsCallbackRange,
464        optional_range: Option<WindowsCallbackRange>,
465        last_dehydration_time: i64,
466        last_dehydration_reason: u32,
467    ) -> Self {
468        Self {
469            info,
470            flags,
471            required_range,
472            optional_range,
473            last_dehydration_time,
474            last_dehydration_reason,
475        }
476    }
477
478    /// Returns common owned callback information.
479    #[must_use]
480    pub const fn info(&self) -> &WindowsCallbackInfoSnapshot {
481        &self.info
482    }
483
484    /// Returns native fetch flags.
485    #[must_use]
486    pub const fn flags(&self) -> WindowsFetchDataFlags {
487        self.flags
488    }
489
490    /// Returns whether CFAPI is recovering hydration interrupted by an unclean provider or system
491    /// shutdown. Products should reconcile their durable content/cache state before selecting the
492    /// revision passed to `hydrate` or `restart_hydration`.
493    #[must_use]
494    pub const fn is_recovery(&self) -> bool {
495        self.flags.contains(WindowsFetchDataFlags::RECOVERY)
496    }
497
498    /// Returns the range required to satisfy outstanding I/O.
499    #[must_use]
500    pub const fn required_range(&self) -> WindowsCallbackRange {
501        self.required_range
502    }
503
504    /// Returns the optional broader hydration hint.
505    #[must_use]
506    pub const fn optional_range(&self) -> Option<WindowsCallbackRange> {
507        self.optional_range
508    }
509
510    /// Returns the native last-dehydration timestamp.
511    #[must_use]
512    pub const fn last_dehydration_time(&self) -> i64 {
513        self.last_dehydration_time
514    }
515
516    /// Returns the exact native last-dehydration reason bits.
517    #[must_use]
518    pub const fn last_dehydration_reason(&self) -> u32 {
519        self.last_dehydration_reason
520    }
521}
522
523/// Bitset copied from `CF_CALLBACK_CANCEL_FLAGS`.
524#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
525pub struct WindowsCancelFetchDataFlags(u32);
526
527impl WindowsCancelFetchDataFlags {
528    /// The platform's fixed callback timeout expired.
529    pub const IO_TIMEOUT: Self = Self(0x1);
530    /// The requesting application or user aborted hydration.
531    pub const IO_ABORTED: Self = Self(0x2);
532
533    /// Preserves the exact native bitset, including future bits.
534    #[must_use]
535    pub const fn from_bits_retain(bits: u32) -> Self {
536        Self(bits)
537    }
538
539    /// Returns the exact native bitset.
540    #[must_use]
541    pub const fn bits(self) -> u32 {
542        self.0
543    }
544
545    /// Returns whether all bits in `other` are present.
546    #[must_use]
547    pub const fn contains(self, other: Self) -> bool {
548        self.0 & other.0 == other.0
549    }
550}
551
552/// Owned `CANCEL_FETCH_DATA` callback snapshot.
553#[derive(Debug, Clone, PartialEq, Eq)]
554pub struct WindowsCancelFetchDataSnapshot {
555    info: WindowsCallbackInfoSnapshot,
556    flags: WindowsCancelFetchDataFlags,
557    range: WindowsCallbackRange,
558}
559
560impl WindowsCancelFetchDataSnapshot {
561    /// Creates an owned cancellation snapshot.
562    #[must_use]
563    pub const fn new(
564        info: WindowsCallbackInfoSnapshot,
565        flags: WindowsCancelFetchDataFlags,
566        range: WindowsCallbackRange,
567    ) -> Self {
568        Self { info, flags, range }
569    }
570
571    /// Returns common owned callback information.
572    #[must_use]
573    pub const fn info(&self) -> &WindowsCallbackInfoSnapshot {
574        &self.info
575    }
576
577    /// Returns the native cancellation reason flags.
578    #[must_use]
579    pub const fn flags(&self) -> WindowsCancelFetchDataFlags {
580        self.flags
581    }
582
583    /// Returns the original hydration subrange that is no longer needed.
584    #[must_use]
585    pub const fn range(&self) -> WindowsCallbackRange {
586        self.range
587    }
588}
589
590/// Accepted hydration request plus its non-cloneable active-session lease.
591#[derive(Debug)]
592pub struct WindowsFetchDataRequest {
593    snapshot: WindowsFetchDataSnapshot,
594    lease: WindowsCallbackLease,
595    terminal: FetchTerminalGate,
596    #[cfg(windows)]
597    completion_authority: NativeCompletionAuthority,
598}
599
600/// Product-neutral metadata replacement for one CFAPI `RESTART_HYDRATION` operation.
601///
602/// The optional identity must still decode to the same stable `CloudItemKey` as the callback.
603/// Revision resolution remains in the product backend adapter; this value only carries the native
604/// placeholder state that CFAPI may update while restarting pending I/O.
605#[derive(Debug, Clone, Default, PartialEq, Eq)]
606pub struct WindowsRestartHydration {
607    metadata: Option<crate::WindowsPlaceholderMetadata>,
608    identity: Option<WindowsFileIdentity>,
609    mark_in_sync: bool,
610}
611
612impl WindowsRestartHydration {
613    /// Starts with no metadata or identity replacement.
614    #[must_use]
615    pub const fn new() -> Self {
616        Self {
617            metadata: None,
618            identity: None,
619            mark_in_sync: false,
620        }
621    }
622
623    /// Replaces the native filesystem metadata, including the logical file size.
624    #[must_use]
625    pub const fn with_metadata(mut self, metadata: crate::WindowsPlaceholderMetadata) -> Self {
626        self.metadata = Some(metadata);
627        self
628    }
629
630    /// Replaces the persisted native identity after same-item validation.
631    #[must_use]
632    pub fn with_identity(mut self, identity: WindowsFileIdentity) -> Self {
633        self.identity = Some(identity);
634        self
635    }
636
637    /// Marks the restarted placeholder in-sync after successful native completion.
638    #[must_use]
639    pub const fn mark_in_sync(mut self) -> Self {
640        self.mark_in_sync = true;
641        self
642    }
643
644    /// Returns the optional replacement metadata.
645    #[must_use]
646    pub const fn metadata(&self) -> Option<crate::WindowsPlaceholderMetadata> {
647        self.metadata
648    }
649
650    /// Returns the optional replacement identity.
651    #[must_use]
652    pub const fn identity(&self) -> Option<&WindowsFileIdentity> {
653        self.identity.as_ref()
654    }
655
656    /// Returns whether the native restart should set CFAPI in-sync state.
657    #[must_use]
658    pub const fn should_mark_in_sync(&self) -> bool {
659        self.mark_in_sync
660    }
661
662    #[cfg(windows)]
663    fn validate_for(&self, info: &WindowsCallbackInfoSnapshot) -> Result<()> {
664        if let Some(identity) = &self.identity
665            && identity.decode()? != info.file_identity().decode()?
666        {
667            return Err(WindowsCloudFilesError::IdentityItemMismatch);
668        }
669        Ok(())
670    }
671}
672
673/// Cloneable correlation handle for reporting progress while the request itself is awaiting
674/// hydration. It owns no terminal completion authority.
675#[derive(Debug, Clone)]
676pub struct WindowsFetchDataProgressReporter {
677    correlation: WindowsFetchDataCorrelation,
678    #[cfg(windows)]
679    completion_authority: NativeCompletionAuthority,
680}
681
682impl WindowsFetchDataProgressReporter {
683    /// Returns the owned callback correlation used for progress reporting.
684    #[must_use]
685    pub const fn correlation(&self) -> WindowsFetchDataCorrelation {
686        self.correlation
687    }
688
689    /// Reports one monotonic progress sample and refreshes the matching local watchdog.
690    ///
691    /// # Errors
692    ///
693    /// Returns an error when this reporter has no native completion authority, the progress sample
694    /// is invalid, or CFAPI rejects the native progress report.
695    #[cfg(windows)]
696    pub fn report(
697        &self,
698        registry: &WindowsFetchDataWaiterRegistry,
699        progress: crate::WindowsFetchDataProgress,
700        now: std::time::Instant,
701    ) -> Result<usize> {
702        self.completion_authority.require_native()?;
703        let updated = registry.report_progress_correlation(self.correlation, progress, now)?;
704        if updated != 0 {
705            crate::native_connection::report_fetch_progress(self.correlation, progress)?;
706        }
707        Ok(updated)
708    }
709}
710
711/// Compact scalar correlation needed by waiter lookup and `CfReportProviderProgress`.
712///
713/// Unlike [`WindowsCallbackInfoSnapshot`], this value owns no paths, identities, or process text,
714/// so cloning a progress reporter does not duplicate callback payloads.
715#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
716pub struct WindowsFetchDataCorrelation {
717    generation: SessionGeneration,
718    connection_key: WindowsConnectionKey,
719    transfer_key: WindowsTransferKey,
720    request_key: WindowsRequestKey,
721    file_id: i64,
722}
723
724impl WindowsFetchDataCorrelation {
725    pub(crate) fn from_info(info: &WindowsCallbackInfoSnapshot) -> Self {
726        Self {
727            generation: info.generation(),
728            connection_key: info.connection_key(),
729            transfer_key: info.transfer_key(),
730            request_key: info.request_key(),
731            file_id: info.file_id(),
732        }
733    }
734
735    /// Returns the accepting session generation.
736    #[must_use]
737    pub const fn generation(self) -> SessionGeneration {
738        self.generation
739    }
740
741    /// Returns the native connection key.
742    #[must_use]
743    pub const fn connection_key(self) -> WindowsConnectionKey {
744        self.connection_key
745    }
746
747    /// Returns the native transfer key.
748    #[must_use]
749    pub const fn transfer_key(self) -> WindowsTransferKey {
750        self.transfer_key
751    }
752
753    /// Returns the native request key.
754    #[must_use]
755    pub const fn request_key(self) -> WindowsRequestKey {
756        self.request_key
757    }
758
759    /// Returns the native placeholder file identifier.
760    #[must_use]
761    pub const fn file_id(self) -> i64 {
762        self.file_id
763    }
764}
765
766#[derive(Debug, Clone)]
767pub(crate) struct FetchTerminalGate {
768    state: Arc<AtomicU8>,
769}
770
771impl Default for FetchTerminalGate {
772    fn default() -> Self {
773        Self {
774            state: Arc::new(AtomicU8::new(FetchTerminalState::Pending as u8)),
775        }
776    }
777}
778
779#[repr(u8)]
780#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
781enum FetchTerminalState {
782    #[default]
783    Pending,
784    NativeAttempted,
785    PlatformCancelled,
786    WatchdogTimedOut,
787}
788
789#[cfg(windows)]
790#[derive(Debug, Clone, Copy, PartialEq, Eq)]
791enum NativeCompletionAuthority {
792    Detached,
793    Native,
794}
795
796#[cfg(windows)]
797impl NativeCompletionAuthority {
798    fn require_native(self) -> Result<()> {
799        match self {
800            Self::Native => Ok(()),
801            Self::Detached => Err(WindowsCloudFilesError::MissingNativeCompletionAuthority),
802        }
803    }
804}
805
806impl FetchTerminalGate {
807    fn state(&self) -> FetchTerminalState {
808        match self.state.load(Ordering::Acquire) {
809            value if value == FetchTerminalState::NativeAttempted as u8 => {
810                FetchTerminalState::NativeAttempted
811            }
812            value if value == FetchTerminalState::PlatformCancelled as u8 => {
813                FetchTerminalState::PlatformCancelled
814            }
815            value if value == FetchTerminalState::WatchdogTimedOut as u8 => {
816                FetchTerminalState::WatchdogTimedOut
817            }
818            _ => FetchTerminalState::Pending,
819        }
820    }
821
822    fn attempted(&self) -> bool {
823        self.state() == FetchTerminalState::NativeAttempted
824    }
825
826    pub(crate) fn platform_cancelled(&self) -> bool {
827        self.state() == FetchTerminalState::PlatformCancelled
828    }
829
830    pub(crate) fn watchdog_timed_out(&self) -> bool {
831        self.state() == FetchTerminalState::WatchdogTimedOut
832    }
833
834    #[cfg(any(windows, test))]
835    pub(crate) fn begin_native(&self) -> bool {
836        self.transition(
837            FetchTerminalState::Pending,
838            FetchTerminalState::NativeAttempted,
839        ) || self.transition(
840            FetchTerminalState::WatchdogTimedOut,
841            FetchTerminalState::NativeAttempted,
842        )
843    }
844
845    pub(crate) fn cancel_by_platform(&self) -> bool {
846        self.transition(
847            FetchTerminalState::Pending,
848            FetchTerminalState::PlatformCancelled,
849        ) || self.transition(
850            FetchTerminalState::WatchdogTimedOut,
851            FetchTerminalState::PlatformCancelled,
852        )
853    }
854
855    pub(crate) fn cancel_by_watchdog(&self) -> bool {
856        self.transition(
857            FetchTerminalState::Pending,
858            FetchTerminalState::WatchdogTimedOut,
859        )
860    }
861
862    fn transition(&self, from: FetchTerminalState, to: FetchTerminalState) -> bool {
863        self.state
864            .compare_exchange(from as u8, to as u8, Ordering::AcqRel, Ordering::Acquire)
865            .is_ok()
866    }
867}
868
869impl WindowsFetchDataRequest {
870    /// Returns the immutable callback snapshot.
871    #[must_use]
872    pub const fn snapshot(&self) -> &WindowsFetchDataSnapshot {
873        &self.snapshot
874    }
875
876    /// Returns the accepted generation lease.
877    #[must_use]
878    pub const fn lease(&self) -> &WindowsCallbackLease {
879        &self.lease
880    }
881
882    /// Returns whether this request already made its one terminal native completion attempt.
883    #[must_use]
884    pub fn terminal_attempted(&self) -> bool {
885        self.terminal.attempted()
886    }
887
888    /// Creates an owned progress reporter that can be moved to another worker while this request
889    /// awaits hydration.
890    #[must_use]
891    pub fn progress_reporter(&self) -> WindowsFetchDataProgressReporter {
892        WindowsFetchDataProgressReporter {
893            correlation: WindowsFetchDataCorrelation::from_info(self.snapshot.info()),
894            #[cfg(windows)]
895            completion_authority: self.completion_authority,
896        }
897    }
898
899    /// Restarts pending CFAPI hydration after the backend has established that the original
900    /// placeholder bytes or revision are no longer valid. This consumes the terminal native
901    /// attempt for the old callback; CFAPI will issue a subsequent callback for the restart.
902    ///
903    /// # Errors
904    ///
905    /// Returns an error when this request has no native completion authority, the replacement
906    /// identity does not match the callback item, or CFAPI rejects the restart operation.
907    #[cfg(windows)]
908    pub fn restart_hydration(self, replacement: &WindowsRestartHydration) -> Result<()> {
909        self.completion_authority.require_native()?;
910        replacement.validate_for(self.snapshot.info())?;
911        if !self.terminal.begin_native() {
912            return Ok(());
913        }
914        crate::native_connection::restart_fetch_hydration(&self.snapshot, replacement)
915    }
916
917    /// Builds the exact revision-bound core hydration request for this callback.
918    ///
919    /// The caller resolves `revision` from product-owned metadata using the stable item identity
920    /// in the snapshot. Windows callback paths and filenames are never used as revision sources.
921    /// # Errors
922    ///
923    /// Returns an error when validation fails or an underlying backend, store, or platform
924    /// operation fails.
925    pub fn hydration_request(
926        &self,
927        revision: ContentRevision,
928        alignment: Alignment,
929    ) -> Result<HydrationRequest> {
930        let info = self.snapshot.info();
931        let key = info.file_identity().decode()?;
932        let range = hydration_byte_range(self.snapshot.required_range(), info.file_size())?;
933        let platform_alignment = Alignment::new(CFAPI_TRANSFER_ALIGNMENT_BYTES)
934            .ok_or_else(|| invalid_transfer("CFAPI transfer alignment must be non-zero"))?;
935        let alignment = platform_alignment.intersection(alignment)?;
936        Ok(HydrationRequest::range(
937            key,
938            revision,
939            info.file_size(),
940            range,
941            alignment,
942            info.generation(),
943        ))
944    }
945
946    /// Waits for product-neutral hydration work and returns an owned, callback-bound transfer.
947    ///
948    /// This method performs no native call and does not consume terminal completion ownership.
949    /// Platform workers may use it for explicit orchestration; ordinary Windows workers should
950    /// prefer `WindowsFetchDataRequest::hydrate` so every error is terminally reported to CFAPI.
951    /// # Errors
952    ///
953    /// Returns an error when validation fails or an underlying backend, store, or platform
954    /// operation fails.
955    pub async fn prepare_transfer(
956        &self,
957        coordinator: &HydrationCoordinator,
958        revision: ContentRevision,
959        alignment: Alignment,
960    ) -> Result<WindowsFetchDataTransfer> {
961        let hydration = self.hydration_request(revision.clone(), alignment)?;
962        let waiter = coordinator.request(hydration)?;
963        let response = waiter.wait().await?;
964        WindowsFetchDataTransfer::from_response(&self.snapshot, &revision, response)
965    }
966
967    /// Registers this callback's core waiter for native cancellation while hydration is pending.
968    ///
969    /// A platform cancellation that covers the complete waiter range returns `Cancelled` without
970    /// manufacturing a core/backend failure. Partial cancellation retains the waiter because bytes
971    /// outside that subrange remain required by CFAPI.
972    /// # Errors
973    ///
974    /// Returns an error when validation fails or an underlying backend, store, or platform
975    /// operation fails.
976    pub async fn prepare_registered_transfer(
977        &mut self,
978        coordinator: &HydrationCoordinator,
979        revision: ContentRevision,
980        alignment: Alignment,
981        registry: &WindowsFetchDataWaiterRegistry,
982    ) -> Result<WindowsFetchDataPreparation> {
983        if self.terminal.platform_cancelled() {
984            return Ok(WindowsFetchDataPreparation::Cancelled);
985        }
986        if self.terminal.watchdog_timed_out() {
987            return Ok(WindowsFetchDataPreparation::TimedOut);
988        }
989        let hydration = self.hydration_request(revision.clone(), alignment)?;
990        let range = match hydration.read().read_range() {
991            ContentReadRange::Range(range) => range,
992            ContentReadRange::Whole => {
993                return Err(invalid_transfer(
994                    "CFAPI fetch hydration must use one logical range",
995                ));
996            }
997        };
998        let waiter = coordinator.request(hydration)?;
999        let registration = registry.register(
1000            &self.snapshot,
1001            range,
1002            waiter.cancellation_handle(),
1003            self.terminal.clone(),
1004        )?;
1005        let response = waiter.wait().await;
1006        let platform_cancelled = registration.platform_cancelled();
1007        let watchdog_timed_out = registration.watchdog_timed_out();
1008        drop(registration);
1009        if platform_cancelled {
1010            return Ok(WindowsFetchDataPreparation::Cancelled);
1011        }
1012        if watchdog_timed_out {
1013            return Ok(WindowsFetchDataPreparation::TimedOut);
1014        }
1015        match response {
1016            Ok(response) => {
1017                WindowsFetchDataTransfer::from_response(&self.snapshot, &revision, response)
1018                    .map(WindowsFetchDataPreparation::Transfer)
1019            }
1020            Err(error) => Err(error.into()),
1021        }
1022    }
1023
1024    /// Resolves this request through the product-neutral hydration coordinator and transfers the
1025    /// exact logical bytes to CFAPI.
1026    ///
1027    /// Backend lookup, authentication, retries, and revision selection remain in the backend and
1028    /// product adapter. This method owns the terminal success/failure mapping once work starts.
1029    ///
1030    /// # Errors
1031    ///
1032    /// Returns an error when hydration preparation fails or CFAPI rejects the terminal transfer or
1033    /// failure completion.
1034    #[cfg(windows)]
1035    pub async fn hydrate(
1036        mut self,
1037        coordinator: &HydrationCoordinator,
1038        revision: ContentRevision,
1039        alignment: Alignment,
1040        registry: &WindowsFetchDataWaiterRegistry,
1041    ) -> Result<()> {
1042        match self
1043            .prepare_registered_transfer(coordinator, revision, alignment, registry)
1044            .await
1045        {
1046            Ok(WindowsFetchDataPreparation::Transfer(transfer)) => {
1047                self.complete_transfer_inner(&transfer)
1048            }
1049            Ok(WindowsFetchDataPreparation::Cancelled) => Ok(()),
1050            Ok(WindowsFetchDataPreparation::TimedOut) => {
1051                self.finish_request_error(WindowsCloudFilesError::FetchDataWatchdogTimeout)
1052            }
1053            Err(error) => self.finish_request_error(error),
1054        }
1055    }
1056
1057    /// Completes this request with one already resolved core content response.
1058    ///
1059    /// # Errors
1060    ///
1061    /// Returns an error when the response does not match this callback or CFAPI rejects the
1062    /// terminal transfer or failure completion.
1063    #[cfg(windows)]
1064    pub fn complete(
1065        mut self,
1066        revision: &ContentRevision,
1067        response: ContentReadResponse,
1068    ) -> Result<()> {
1069        self.complete_inner(revision, response)
1070    }
1071
1072    /// Completes this request with a structured CFAPI failure at most once.
1073    ///
1074    /// # Errors
1075    ///
1076    /// Returns an error when this request has no native completion authority or CFAPI rejects the
1077    /// failure completion.
1078    #[cfg(windows)]
1079    pub fn fail(mut self, failure: WindowsFetchDataFailure) -> Result<()> {
1080        self.fail_inner(failure)
1081    }
1082
1083    #[cfg(windows)]
1084    pub(crate) fn fail_inner(&mut self, failure: WindowsFetchDataFailure) -> Result<()> {
1085        self.completion_authority.require_native()?;
1086        if !self.terminal.begin_native() {
1087            return Ok(());
1088        }
1089        crate::native_connection::complete_fetch_failure(&self.snapshot, failure)
1090    }
1091
1092    #[cfg(windows)]
1093    fn complete_inner(
1094        &mut self,
1095        revision: &ContentRevision,
1096        response: ContentReadResponse,
1097    ) -> Result<()> {
1098        if self.terminal.attempted() {
1099            return Ok(());
1100        }
1101        let transfer =
1102            match WindowsFetchDataTransfer::from_response(&self.snapshot, revision, response) {
1103                Ok(transfer) => transfer,
1104                Err(error) => return self.finish_request_error(error),
1105            };
1106        self.complete_transfer_inner(&transfer)
1107    }
1108
1109    #[cfg(windows)]
1110    fn complete_transfer_inner(&mut self, transfer: &WindowsFetchDataTransfer) -> Result<()> {
1111        self.completion_authority.require_native()?;
1112        if !self.terminal.begin_native() {
1113            return Ok(());
1114        }
1115        crate::native_connection::complete_fetch_success(&self.snapshot, transfer)
1116    }
1117
1118    #[cfg(windows)]
1119    fn finish_request_error(&mut self, error: WindowsCloudFilesError) -> Result<()> {
1120        let failure = match &error {
1121            WindowsCloudFilesError::Hydration(error) => {
1122                WindowsFetchDataFailure::from_hydration_error(error)
1123            }
1124            _ => WindowsFetchDataFailure::InvalidRequest,
1125        };
1126        match self.fail_inner(failure) {
1127            Ok(()) => Err(error),
1128            Err(completion_error) => Err(completion_error),
1129        }
1130    }
1131}
1132
1133/// Portable outcome of waiting for one cancellation-registered CFAPI hydration request.
1134#[derive(Debug, Clone, PartialEq, Eq)]
1135pub enum WindowsFetchDataPreparation {
1136    /// Owned logical bytes are ready for a synchronous native transfer.
1137    Transfer(WindowsFetchDataTransfer),
1138    /// CFAPI cancelled the complete waiter range before hydration completed.
1139    Cancelled,
1140    /// The host watchdog cancelled the waiter before the platform callback deadline.
1141    TimedOut,
1142}
1143
1144/// Owned logical bytes ready for one synchronous CFAPI transfer call.
1145#[derive(Debug, Clone, PartialEq, Eq)]
1146pub struct WindowsFetchDataTransfer {
1147    offset: i64,
1148    length: i64,
1149    bytes: Bytes,
1150}
1151
1152impl WindowsFetchDataTransfer {
1153    /// Validates that a core response exactly satisfies the native required range.
1154    /// # Errors
1155    ///
1156    /// Returns an error when validation fails or an underlying backend, store, or platform
1157    /// operation fails.
1158    pub fn from_response(
1159        snapshot: &WindowsFetchDataSnapshot,
1160        revision: &ContentRevision,
1161        response: ContentReadResponse,
1162    ) -> Result<Self> {
1163        let info = snapshot.info();
1164        let expected = hydration_byte_range(snapshot.required_range(), info.file_size())?;
1165        let request = aster_forge_cloud_files_core::ContentReadRequest::range(
1166            info.file_identity().decode()?,
1167            revision.clone(),
1168            info.file_size(),
1169            expected,
1170        );
1171        request.validate_response(&response)?;
1172        let (_, offset, bytes, total_size) = response.into_parts();
1173        if total_size != info.file_size() {
1174            return Err(invalid_transfer(
1175                "hydrated response size does not match callback file size",
1176            ));
1177        }
1178        let offset = i64::try_from(offset)
1179            .map_err(|_| invalid_transfer("transfer offset exceeds signed CFAPI boundary"))?;
1180        if bytes.is_empty() {
1181            return Err(invalid_transfer("successful transfer must contain bytes"));
1182        }
1183        let length = i64::try_from(bytes.len())
1184            .map_err(|_| invalid_transfer("transfer length exceeds signed CFAPI boundary"))?;
1185        Ok(Self {
1186            offset,
1187            length,
1188            bytes,
1189        })
1190    }
1191
1192    /// Returns the first logical byte offset.
1193    pub const fn offset(&self) -> i64 {
1194        self.offset
1195    }
1196
1197    /// Returns the owned bytes kept alive for the synchronous native call.
1198    pub fn bytes(&self) -> &[u8] {
1199        &self.bytes
1200    }
1201
1202    /// Returns the signed CFAPI transfer length.
1203    pub const fn length(&self) -> i64 {
1204        self.length
1205    }
1206}
1207
1208impl Drop for WindowsFetchDataRequest {
1209    fn drop(&mut self) {
1210        #[cfg(windows)]
1211        {
1212            if self.completion_authority == NativeCompletionAuthority::Native {
1213                let _ = self.fail_inner(WindowsFetchDataFailure::ProviderTerminated);
1214            }
1215        }
1216    }
1217}
1218
1219/// Structured failure classifications accepted by the minimal fetch terminal path.
1220#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1221pub enum WindowsFetchDataFailure {
1222    /// The callback snapshot or request contract was malformed.
1223    InvalidRequest,
1224    /// The bounded callback queue or provider resources were exhausted.
1225    InsufficientResources,
1226    /// The provider connection is closing or its worker is gone.
1227    ProviderTerminated,
1228    /// Product credentials are missing, expired, or rejected.
1229    AuthenticationFailed,
1230    /// The requesting principal cannot read this item.
1231    AccessDenied,
1232    /// The callback metadata no longer matches current backend content state.
1233    NotInSync,
1234    /// A transient backend or transport outage prevented hydration.
1235    NetworkUnavailable,
1236    /// The backend does not support the requested content operation.
1237    NotSupported,
1238    /// The platform or user cancelled the hydration request.
1239    Cancelled,
1240    /// The provider failed without a narrower portable classification.
1241    Unsuccessful,
1242}
1243
1244/// Owned notification that CFAPI has already completed a local filesystem effect.
1245#[derive(Debug, Clone, PartialEq, Eq)]
1246pub enum WindowsObservedNotification {
1247    /// CFAPI completed a dehydration operation.
1248    DehydrateCompleted {
1249        /// Common owned callback correlation and stable identity.
1250        info: WindowsCallbackInfoSnapshot,
1251        /// Native completion flag bits, including future values.
1252        flags: u32,
1253        /// Native dehydration reason bits, including future values.
1254        reason: u32,
1255    },
1256    /// CFAPI completed a deletion operation.
1257    DeleteCompleted {
1258        /// Common owned callback correlation and stable identity.
1259        info: WindowsCallbackInfoSnapshot,
1260        /// Native completion flag bits, including future values.
1261        flags: u32,
1262    },
1263    /// CFAPI completed a rename or move operation.
1264    RenameCompleted {
1265        /// Common owned callback correlation and stable identity.
1266        info: WindowsCallbackInfoSnapshot,
1267        /// Native completion flag bits, including future values.
1268        flags: u32,
1269        /// Source path copied before the native callback returned.
1270        source_path: Option<PathBuf>,
1271    },
1272}
1273
1274impl WindowsObservedNotification {
1275    /// Returns the common callback information.
1276    #[must_use]
1277    pub const fn info(&self) -> &WindowsCallbackInfoSnapshot {
1278        match self {
1279            Self::DehydrateCompleted { info, .. }
1280            | Self::DeleteCompleted { info, .. }
1281            | Self::RenameCompleted { info, .. } => info,
1282        }
1283    }
1284}
1285
1286/// Owned CFAPI operation that blocks a local filesystem effect until the provider acknowledges it.
1287#[derive(Debug, Clone, PartialEq, Eq)]
1288pub enum WindowsPreflightSnapshot {
1289    /// Previously transferred data must be validated before CFAPI may serve it.
1290    ValidateData {
1291        /// Common owned callback correlation and stable identity.
1292        info: WindowsCallbackInfoSnapshot,
1293        /// Native validation flag bits, including future values.
1294        flags: u32,
1295        /// Exact logical range awaiting validation.
1296        range: WindowsCallbackRange,
1297    },
1298    /// A placeholder is about to dehydrate.
1299    Dehydrate {
1300        /// Common owned callback correlation and stable identity.
1301        info: WindowsCallbackInfoSnapshot,
1302        /// Native dehydration flag bits, including future values.
1303        flags: u32,
1304        /// Native dehydration reason bits, including future values.
1305        reason: u32,
1306    },
1307    /// A placeholder is about to be deleted.
1308    Delete {
1309        /// Common owned callback correlation and stable identity.
1310        info: WindowsCallbackInfoSnapshot,
1311        /// Native delete flag bits, including future values.
1312        flags: u32,
1313    },
1314    /// A placeholder is about to be renamed or moved.
1315    Rename {
1316        /// Common owned callback correlation and stable identity.
1317        info: WindowsCallbackInfoSnapshot,
1318        /// Native rename flag bits, including future values.
1319        flags: u32,
1320        /// Target path copied before the native callback returned.
1321        target_path: Option<PathBuf>,
1322    },
1323}
1324
1325impl WindowsPreflightSnapshot {
1326    /// Returns common callback information.
1327    #[must_use]
1328    pub const fn info(&self) -> &WindowsCallbackInfoSnapshot {
1329        match self {
1330            Self::ValidateData { info, .. }
1331            | Self::Dehydrate { info, .. }
1332            | Self::Delete { info, .. }
1333            | Self::Rename { info, .. } => info,
1334        }
1335    }
1336}
1337
1338/// Accepted preflight request with exactly one native acknowledgement responsibility.
1339#[derive(Debug)]
1340pub struct WindowsPreflightRequest {
1341    snapshot: WindowsPreflightSnapshot,
1342    lease: WindowsCallbackLease,
1343    acknowledged: bool,
1344    #[cfg(windows)]
1345    completion_authority: NativeCompletionAuthority,
1346}
1347
1348impl WindowsPreflightRequest {
1349    /// Returns the immutable preflight snapshot.
1350    #[must_use]
1351    pub const fn snapshot(&self) -> &WindowsPreflightSnapshot {
1352        &self.snapshot
1353    }
1354
1355    /// Returns the accepted generation lease.
1356    #[must_use]
1357    pub const fn lease(&self) -> &WindowsCallbackLease {
1358        &self.lease
1359    }
1360
1361    /// Returns whether this request has already attempted its native acknowledgement.
1362    #[must_use]
1363    pub const fn acknowledged(&self) -> bool {
1364        self.acknowledged
1365    }
1366
1367    /// Acknowledges that the product has durably accepted this local platform effect.
1368    ///
1369    /// # Errors
1370    ///
1371    /// Returns an error when this request has no native completion authority or CFAPI rejects the
1372    /// acknowledgement.
1373    #[cfg(windows)]
1374    pub fn approve(mut self) -> Result<()> {
1375        self.acknowledge_inner(WindowsFetchDataFailure::Unsuccessful, true)
1376    }
1377
1378    /// Denies the local platform effect with a structured CFAPI failure classification.
1379    ///
1380    /// # Errors
1381    ///
1382    /// Returns an error when this request has no native completion authority or CFAPI rejects the
1383    /// acknowledgement.
1384    #[cfg(windows)]
1385    pub fn deny(mut self, failure: WindowsFetchDataFailure) -> Result<()> {
1386        self.acknowledge_inner(failure, false)
1387    }
1388
1389    #[cfg(windows)]
1390    pub(crate) fn acknowledge_inner(
1391        &mut self,
1392        failure: WindowsFetchDataFailure,
1393        approved: bool,
1394    ) -> Result<()> {
1395        self.completion_authority.require_native()?;
1396        if self.acknowledged {
1397            return Ok(());
1398        }
1399        self.acknowledged = true;
1400        crate::native_connection::complete_preflight(&self.snapshot, failure, approved)
1401    }
1402}
1403
1404impl Drop for WindowsPreflightRequest {
1405    fn drop(&mut self) {
1406        #[cfg(windows)]
1407        {
1408            if self.completion_authority == NativeCompletionAuthority::Native {
1409                let _ = self.acknowledge_inner(WindowsFetchDataFailure::ProviderTerminated, false);
1410            }
1411        }
1412    }
1413}
1414
1415/// Bounded callback-ingress counters for one native connection generation.
1416#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
1417pub struct WindowsCallbackQueueMetrics {
1418    accepted_callbacks: u64,
1419    queued_fetch_data: u64,
1420    queued_cancel_observations: u64,
1421    queued_preflights: u64,
1422    queued_observations: u64,
1423    fetch_queue_full: u64,
1424    cancel_observation_queue_full: u64,
1425    preflight_queue_full: u64,
1426    observation_queue_full: u64,
1427    receiver_disconnected: u64,
1428    closing_rejections: u64,
1429    invalid_snapshot_rejections: u64,
1430    panic_failures: u64,
1431}
1432
1433impl WindowsCallbackQueueMetrics {
1434    #[must_use]
1435    pub const fn accepted_callbacks(self) -> u64 {
1436        self.accepted_callbacks
1437    }
1438    #[must_use]
1439    pub const fn queued_fetch_data(self) -> u64 {
1440        self.queued_fetch_data
1441    }
1442    #[must_use]
1443    pub const fn queued_cancel_observations(self) -> u64 {
1444        self.queued_cancel_observations
1445    }
1446    /// Returns blocking native preflight requests queued for durable product decisions.
1447    #[must_use]
1448    pub const fn queued_preflights(self) -> u64 {
1449        self.queued_preflights
1450    }
1451    /// Returns completed native filesystem observations queued for reconciliation.
1452    #[must_use]
1453    pub const fn queued_observations(self) -> u64 {
1454        self.queued_observations
1455    }
1456    #[must_use]
1457    pub const fn fetch_queue_full(self) -> u64 {
1458        self.fetch_queue_full
1459    }
1460    #[must_use]
1461    pub const fn cancel_observation_queue_full(self) -> u64 {
1462        self.cancel_observation_queue_full
1463    }
1464    /// Returns blocking preflight requests failed because the queue was full.
1465    #[must_use]
1466    pub const fn preflight_queue_full(self) -> u64 {
1467        self.preflight_queue_full
1468    }
1469    /// Returns completed observations dropped because the queue was full.
1470    #[must_use]
1471    pub const fn observation_queue_full(self) -> u64 {
1472        self.observation_queue_full
1473    }
1474    #[must_use]
1475    pub const fn receiver_disconnected(self) -> u64 {
1476        self.receiver_disconnected
1477    }
1478    #[must_use]
1479    pub const fn closing_rejections(self) -> u64 {
1480        self.closing_rejections
1481    }
1482    #[must_use]
1483    pub const fn invalid_snapshot_rejections(self) -> u64 {
1484        self.invalid_snapshot_rejections
1485    }
1486    #[must_use]
1487    pub const fn panic_failures(self) -> u64 {
1488        self.panic_failures
1489    }
1490}
1491
1492impl WindowsFetchDataFailure {
1493    /// Maps a product-neutral hydration failure to the closest CFAPI terminal classification.
1494    #[must_use]
1495    pub fn from_hydration_error(error: &HydrationError) -> Self {
1496        match error {
1497            HydrationError::Cancelled => Self::Cancelled,
1498            HydrationError::Contract(_) => Self::InvalidRequest,
1499            HydrationError::InFlightLimitExceeded { .. } => Self::InsufficientResources,
1500            HydrationError::Backend(error) => match error.kind() {
1501                CloudBackendErrorKind::AuthenticationRequired => Self::AuthenticationFailed,
1502                CloudBackendErrorKind::PermissionDenied => Self::AccessDenied,
1503                CloudBackendErrorKind::NotFound
1504                | CloudBackendErrorKind::Conflict
1505                | CloudBackendErrorKind::PreconditionFailed => Self::NotInSync,
1506                CloudBackendErrorKind::InvalidRequest => Self::InvalidRequest,
1507                CloudBackendErrorKind::RateLimited
1508                | CloudBackendErrorKind::TemporarilyUnavailable => Self::NetworkUnavailable,
1509                CloudBackendErrorKind::Unsupported => Self::NotSupported,
1510                CloudBackendErrorKind::InvalidResponse | CloudBackendErrorKind::Internal => {
1511                    Self::Unsuccessful
1512                }
1513            },
1514        }
1515    }
1516}
1517
1518/// Accepted cancellation notification plus its non-cloneable active-session lease.
1519#[derive(Debug)]
1520pub struct WindowsCancelFetchDataRequest {
1521    snapshot: WindowsCancelFetchDataSnapshot,
1522    lease: WindowsCallbackLease,
1523}
1524
1525impl WindowsCancelFetchDataRequest {
1526    /// Returns the immutable callback snapshot.
1527    #[must_use]
1528    pub const fn snapshot(&self) -> &WindowsCancelFetchDataSnapshot {
1529        &self.snapshot
1530    }
1531
1532    /// Returns the accepted generation lease.
1533    #[must_use]
1534    pub const fn lease(&self) -> &WindowsCallbackLease {
1535        &self.lease
1536    }
1537}
1538
1539/// One immutable callback snapshot accepted by a specific connection generation.
1540#[derive(Debug)]
1541pub enum WindowsCallbackRequest {
1542    /// A hydration request that requires a later terminal `CfExecute` operation.
1543    FetchData(WindowsFetchDataRequest),
1544    /// A cancellation notification for an existing hydration request or subrange.
1545    CancelFetchData(WindowsCancelFetchDataRequest),
1546    /// A blocking native filesystem operation awaiting one explicit acknowledgement.
1547    Preflight(WindowsPreflightRequest),
1548    /// A completed native filesystem effect delivered for durable observation/reconciliation.
1549    Observation {
1550        /// Owned completion snapshot.
1551        notification: WindowsObservedNotification,
1552        /// Lease fencing this observation to the accepting session generation.
1553        lease: WindowsCallbackLease,
1554    },
1555}
1556
1557impl WindowsCallbackRequest {
1558    /// Returns common owned callback information.
1559    #[must_use]
1560    pub const fn info(&self) -> &WindowsCallbackInfoSnapshot {
1561        match self {
1562            Self::FetchData(request) => request.snapshot.info(),
1563            Self::CancelFetchData(request) => request.snapshot.info(),
1564            Self::Preflight(request) => request.snapshot.info(),
1565            Self::Observation { notification, .. } => notification.info(),
1566        }
1567    }
1568
1569    /// Returns the generation that accepted this request.
1570    #[must_use]
1571    pub fn generation(&self) -> SessionGeneration {
1572        self.info().generation()
1573    }
1574
1575    /// Binds a detached fetch snapshot to an exact generation lease for portable orchestration
1576    /// and contract tests. Detached requests never call CFAPI, including from `Drop`.
1577    /// # Errors
1578    ///
1579    /// Returns an error when validation fails or an underlying backend, store, or platform
1580    /// operation fails.
1581    pub fn detached_fetch_data(
1582        snapshot: WindowsFetchDataSnapshot,
1583        lease: WindowsCallbackLease,
1584    ) -> Result<Self> {
1585        validate_request_generation(snapshot.info().generation(), lease.generation())?;
1586        Ok(Self::FetchData(WindowsFetchDataRequest {
1587            snapshot,
1588            lease,
1589            terminal: FetchTerminalGate::default(),
1590            #[cfg(windows)]
1591            completion_authority: NativeCompletionAuthority::Detached,
1592        }))
1593    }
1594
1595    #[cfg(windows)]
1596    pub(crate) fn native_fetch_data(
1597        snapshot: WindowsFetchDataSnapshot,
1598        lease: WindowsCallbackLease,
1599    ) -> Result<Self> {
1600        validate_request_generation(snapshot.info().generation(), lease.generation())?;
1601        Ok(Self::FetchData(WindowsFetchDataRequest {
1602            snapshot,
1603            lease,
1604            terminal: FetchTerminalGate::default(),
1605            completion_authority: NativeCompletionAuthority::Native,
1606        }))
1607    }
1608
1609    /// Binds a cancellation snapshot to the exact generation lease that accepted it.
1610    /// # Errors
1611    ///
1612    /// Returns an error when validation fails or an underlying backend, store, or platform
1613    /// operation fails.
1614    pub fn cancel_fetch_data(
1615        snapshot: WindowsCancelFetchDataSnapshot,
1616        lease: WindowsCallbackLease,
1617    ) -> Result<Self> {
1618        validate_request_generation(snapshot.info().generation(), lease.generation())?;
1619        Ok(Self::CancelFetchData(WindowsCancelFetchDataRequest {
1620            snapshot,
1621            lease,
1622        }))
1623    }
1624
1625    /// Binds a detached preflight snapshot to the generation that accepted it for portable
1626    /// orchestration and contract tests. Detached requests never acknowledge through CFAPI.
1627    /// # Errors
1628    ///
1629    /// Returns an error when validation fails or an underlying backend, store, or platform
1630    /// operation fails.
1631    pub fn detached_preflight(
1632        snapshot: WindowsPreflightSnapshot,
1633        lease: WindowsCallbackLease,
1634    ) -> Result<Self> {
1635        validate_request_generation(snapshot.info().generation(), lease.generation())?;
1636        Ok(Self::Preflight(WindowsPreflightRequest {
1637            snapshot,
1638            lease,
1639            acknowledged: false,
1640            #[cfg(windows)]
1641            completion_authority: NativeCompletionAuthority::Detached,
1642        }))
1643    }
1644
1645    #[cfg(windows)]
1646    pub(crate) fn native_preflight(
1647        snapshot: WindowsPreflightSnapshot,
1648        lease: WindowsCallbackLease,
1649    ) -> Result<Self> {
1650        validate_request_generation(snapshot.info().generation(), lease.generation())?;
1651        Ok(Self::Preflight(WindowsPreflightRequest {
1652            snapshot,
1653            lease,
1654            acknowledged: false,
1655            completion_authority: NativeCompletionAuthority::Native,
1656        }))
1657    }
1658
1659    /// Binds a completed notification to the generation that accepted it.
1660    /// # Errors
1661    ///
1662    /// Returns an error when validation fails or an underlying backend, store, or platform
1663    /// operation fails.
1664    pub fn observation(
1665        notification: WindowsObservedNotification,
1666        lease: WindowsCallbackLease,
1667    ) -> Result<Self> {
1668        validate_request_generation(notification.info().generation(), lease.generation())?;
1669        Ok(Self::Observation {
1670            notification,
1671            lease,
1672        })
1673    }
1674}
1675
1676#[derive(Debug)]
1677struct ConnectionLifecycle {
1678    state: SessionState,
1679    active_callbacks: usize,
1680}
1681
1682#[derive(Debug, Default)]
1683struct AtomicWindowsCallbackQueueMetrics {
1684    accepted_callbacks: AtomicU64,
1685    queued_fetch_data: AtomicU64,
1686    queued_cancel_observations: AtomicU64,
1687    queued_preflights: AtomicU64,
1688    queued_observations: AtomicU64,
1689    fetch_queue_full: AtomicU64,
1690    cancel_observation_queue_full: AtomicU64,
1691    preflight_queue_full: AtomicU64,
1692    observation_queue_full: AtomicU64,
1693    receiver_disconnected: AtomicU64,
1694    closing_rejections: AtomicU64,
1695    invalid_snapshot_rejections: AtomicU64,
1696    panic_failures: AtomicU64,
1697}
1698
1699impl AtomicWindowsCallbackQueueMetrics {
1700    fn snapshot(&self) -> WindowsCallbackQueueMetrics {
1701        WindowsCallbackQueueMetrics {
1702            accepted_callbacks: self.accepted_callbacks.load(Ordering::Relaxed),
1703            queued_fetch_data: self.queued_fetch_data.load(Ordering::Relaxed),
1704            queued_cancel_observations: self.queued_cancel_observations.load(Ordering::Relaxed),
1705            queued_preflights: self.queued_preflights.load(Ordering::Relaxed),
1706            queued_observations: self.queued_observations.load(Ordering::Relaxed),
1707            fetch_queue_full: self.fetch_queue_full.load(Ordering::Relaxed),
1708            cancel_observation_queue_full: self
1709                .cancel_observation_queue_full
1710                .load(Ordering::Relaxed),
1711            preflight_queue_full: self.preflight_queue_full.load(Ordering::Relaxed),
1712            observation_queue_full: self.observation_queue_full.load(Ordering::Relaxed),
1713            receiver_disconnected: self.receiver_disconnected.load(Ordering::Relaxed),
1714            closing_rejections: self.closing_rejections.load(Ordering::Relaxed),
1715            invalid_snapshot_rejections: self.invalid_snapshot_rejections.load(Ordering::Relaxed),
1716            panic_failures: self.panic_failures.load(Ordering::Relaxed),
1717        }
1718    }
1719
1720    #[cfg(any(windows, test))]
1721    fn increment(&self, event: QueueMetricEvent) {
1722        let counter = match event {
1723            QueueMetricEvent::Accepted => &self.accepted_callbacks,
1724            QueueMetricEvent::QueuedFetch => &self.queued_fetch_data,
1725            QueueMetricEvent::QueuedCancel => &self.queued_cancel_observations,
1726            QueueMetricEvent::QueuedPreflight => &self.queued_preflights,
1727            QueueMetricEvent::QueuedObservation => &self.queued_observations,
1728            QueueMetricEvent::FetchFull => &self.fetch_queue_full,
1729            QueueMetricEvent::CancelFull => &self.cancel_observation_queue_full,
1730            QueueMetricEvent::PreflightFull => &self.preflight_queue_full,
1731            QueueMetricEvent::ObservationFull => &self.observation_queue_full,
1732            QueueMetricEvent::Disconnected => &self.receiver_disconnected,
1733            QueueMetricEvent::Closing => &self.closing_rejections,
1734            QueueMetricEvent::Invalid => &self.invalid_snapshot_rejections,
1735            QueueMetricEvent::Panic => &self.panic_failures,
1736        };
1737        let _ = counter.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |value| {
1738            Some(value.saturating_add(1))
1739        });
1740    }
1741}
1742
1743#[derive(Debug)]
1744struct ConnectionSessionInner {
1745    generation: SessionGeneration,
1746    lifecycle: Mutex<ConnectionLifecycle>,
1747    queue_metrics: AtomicWindowsCallbackQueueMetrics,
1748}
1749
1750/// Shareable lifecycle fence for one active native connection generation.
1751#[derive(Debug, Clone)]
1752pub struct WindowsConnectionSession {
1753    inner: Arc<ConnectionSessionInner>,
1754}
1755
1756impl WindowsConnectionSession {
1757    /// Starts one accepting connection generation.
1758    #[must_use]
1759    pub fn new(generation: SessionGeneration) -> Self {
1760        Self {
1761            inner: Arc::new(ConnectionSessionInner {
1762                generation,
1763                lifecycle: Mutex::new(ConnectionLifecycle {
1764                    state: SessionState::Accepting,
1765                    active_callbacks: 0,
1766                }),
1767                queue_metrics: AtomicWindowsCallbackQueueMetrics::default(),
1768            }),
1769        }
1770    }
1771
1772    /// Returns the durable generation fence owned by this connection.
1773    #[must_use]
1774    pub fn generation(&self) -> SessionGeneration {
1775        self.inner.generation
1776    }
1777
1778    /// Returns the current lifecycle state.
1779    #[must_use]
1780    pub fn state(&self) -> SessionState {
1781        lock_lifecycle(&self.inner).state
1782    }
1783
1784    /// Returns the number of accepted callback requests still owned by downstream work.
1785    #[must_use]
1786    pub fn active_callbacks(&self) -> usize {
1787        lock_lifecycle(&self.inner).active_callbacks
1788    }
1789
1790    /// Returns a point-in-time bounded callback ingress snapshot.
1791    #[must_use]
1792    pub fn queue_metrics(&self) -> WindowsCallbackQueueMetrics {
1793        self.inner.queue_metrics.snapshot()
1794    }
1795
1796    #[cfg(any(windows, test))]
1797    fn record_queue_event(&self, event: QueueMetricEvent) {
1798        self.inner.queue_metrics.increment(event);
1799    }
1800
1801    #[cfg(any(windows, test))]
1802    pub(crate) fn record_accepted_callback(&self) {
1803        self.record_queue_event(QueueMetricEvent::Accepted);
1804    }
1805    #[cfg(any(windows, test))]
1806    pub(crate) fn record_queued_fetch(&self) {
1807        self.record_queue_event(QueueMetricEvent::QueuedFetch);
1808    }
1809    #[cfg(any(windows, test))]
1810    pub(crate) fn record_queued_cancel(&self) {
1811        self.record_queue_event(QueueMetricEvent::QueuedCancel);
1812    }
1813    #[cfg(any(windows, test))]
1814    pub(crate) fn record_fetch_queue_full(&self) {
1815        self.record_queue_event(QueueMetricEvent::FetchFull);
1816    }
1817    #[cfg(any(windows, test))]
1818    pub(crate) fn record_cancel_queue_full(&self) {
1819        self.record_queue_event(QueueMetricEvent::CancelFull);
1820    }
1821    #[cfg(any(windows, test))]
1822    pub(crate) fn record_queued_preflight(&self) {
1823        self.record_queue_event(QueueMetricEvent::QueuedPreflight);
1824    }
1825    #[cfg(any(windows, test))]
1826    pub(crate) fn record_queued_observation(&self) {
1827        self.record_queue_event(QueueMetricEvent::QueuedObservation);
1828    }
1829    #[cfg(any(windows, test))]
1830    pub(crate) fn record_preflight_queue_full(&self) {
1831        self.record_queue_event(QueueMetricEvent::PreflightFull);
1832    }
1833    #[cfg(any(windows, test))]
1834    pub(crate) fn record_observation_queue_full(&self) {
1835        self.record_queue_event(QueueMetricEvent::ObservationFull);
1836    }
1837    #[cfg(any(windows, test))]
1838    pub(crate) fn record_receiver_disconnected(&self) {
1839        self.record_queue_event(QueueMetricEvent::Disconnected);
1840    }
1841    #[cfg(any(windows, test))]
1842    pub(crate) fn record_closing_rejection(&self) {
1843        self.record_queue_event(QueueMetricEvent::Closing);
1844    }
1845    #[cfg(any(windows, test))]
1846    pub(crate) fn record_invalid_snapshot(&self) {
1847        self.record_queue_event(QueueMetricEvent::Invalid);
1848    }
1849    #[cfg(any(windows, test))]
1850    pub(crate) fn record_panic_failure(&self) {
1851        self.record_queue_event(QueueMetricEvent::Panic);
1852    }
1853
1854    /// Accepts one callback only while this exact generation is accepting new work.
1855    /// # Errors
1856    ///
1857    /// Returns an error when validation fails or an underlying backend, store, or platform
1858    /// operation fails.
1859    pub fn begin_callback(
1860        &self,
1861        callback_generation: SessionGeneration,
1862    ) -> Result<WindowsCallbackLease> {
1863        if callback_generation != self.inner.generation {
1864            return Err(WindowsCloudFilesError::StaleConnectionGeneration {
1865                expected: self.inner.generation.get(),
1866                actual: callback_generation.get(),
1867            });
1868        }
1869        let mut lifecycle = lock_lifecycle(&self.inner);
1870        if lifecycle.state != SessionState::Accepting {
1871            return Err(WindowsCloudFilesError::ConnectionNotAccepting {
1872                state: lifecycle.state,
1873            });
1874        }
1875        lifecycle.active_callbacks = lifecycle
1876            .active_callbacks
1877            .checked_add(1)
1878            .ok_or(WindowsCloudFilesError::ActiveCallbackCountOverflow)?;
1879        drop(lifecycle);
1880        Ok(WindowsCallbackLease {
1881            session: self.inner.clone(),
1882            released: false,
1883        })
1884    }
1885
1886    /// Moves `Accepting -> Closing`, rejecting all later callback ingress.
1887    ///
1888    /// Returns `true` only for the first close request. Repeated calls are idempotent.
1889    #[must_use]
1890    pub fn begin_closing(&self) -> bool {
1891        let mut lifecycle = lock_lifecycle(&self.inner);
1892        if lifecycle.state == SessionState::Accepting {
1893            lifecycle.state = SessionState::Closing;
1894            true
1895        } else {
1896            false
1897        }
1898    }
1899
1900    /// Records successful native disconnect and begins draining accepted work.
1901    ///
1902    /// The session closes immediately when no callback leases remain; otherwise the final lease
1903    /// release performs `Draining -> Closed`.
1904    /// # Errors
1905    ///
1906    /// Returns an error when validation fails or an underlying backend, store, or platform
1907    /// operation fails.
1908    pub fn mark_disconnected(&self) -> Result<()> {
1909        let mut lifecycle = lock_lifecycle(&self.inner);
1910        match lifecycle.state {
1911            SessionState::Closing => {
1912                lifecycle.state = if lifecycle.active_callbacks == 0 {
1913                    SessionState::Closed
1914                } else {
1915                    SessionState::Draining
1916                };
1917                Ok(())
1918            }
1919            SessionState::Draining | SessionState::Closed => Ok(()),
1920            SessionState::Accepting => Err(WindowsCloudFilesError::InvalidConnectionTransition {
1921                from: SessionState::Accepting,
1922                to: SessionState::Draining,
1923            }),
1924        }
1925    }
1926}
1927
1928#[cfg(any(windows, test))]
1929#[derive(Debug, Clone, Copy)]
1930enum QueueMetricEvent {
1931    Accepted,
1932    QueuedFetch,
1933    QueuedCancel,
1934    QueuedPreflight,
1935    QueuedObservation,
1936    FetchFull,
1937    CancelFull,
1938    PreflightFull,
1939    ObservationFull,
1940    Disconnected,
1941    Closing,
1942    Invalid,
1943    Panic,
1944}
1945
1946/// Non-cloneable ownership proof for one callback accepted before the closing fence.
1947pub struct WindowsCallbackLease {
1948    session: Arc<ConnectionSessionInner>,
1949    released: bool,
1950}
1951
1952impl WindowsCallbackLease {
1953    /// Returns the generation that accepted the callback.
1954    #[must_use]
1955    pub fn generation(&self) -> SessionGeneration {
1956        self.session.generation
1957    }
1958
1959    /// Releases the accepted callback count explicitly.
1960    pub fn release(mut self) {
1961        self.release_inner();
1962    }
1963
1964    /// Checks whether this exact accepted callback may complete against `session`.
1965    #[must_use]
1966    pub fn accepts_completion(&self, session: &WindowsConnectionSession) -> bool {
1967        !self.released
1968            && Arc::ptr_eq(&self.session, &session.inner)
1969            && lock_lifecycle(&self.session).state != SessionState::Closed
1970    }
1971
1972    fn release_inner(&mut self) {
1973        if self.released {
1974            return;
1975        }
1976        let mut lifecycle = lock_lifecycle(&self.session);
1977        lifecycle.active_callbacks = lifecycle.active_callbacks.saturating_sub(1);
1978        if lifecycle.state == SessionState::Draining && lifecycle.active_callbacks == 0 {
1979            lifecycle.state = SessionState::Closed;
1980        }
1981        self.released = true;
1982    }
1983}
1984
1985impl fmt::Debug for WindowsCallbackLease {
1986    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1987        formatter
1988            .debug_struct("WindowsCallbackLease")
1989            .field("generation", &self.session.generation)
1990            .field("released", &self.released)
1991            .finish()
1992    }
1993}
1994
1995impl Drop for WindowsCallbackLease {
1996    fn drop(&mut self) {
1997        self.release_inner();
1998    }
1999}
2000
2001fn lock_lifecycle(inner: &ConnectionSessionInner) -> MutexGuard<'_, ConnectionLifecycle> {
2002    match inner.lifecycle.lock() {
2003        Ok(guard) => guard,
2004        Err(poisoned) => poisoned.into_inner(),
2005    }
2006}
2007
2008fn invalid_callback(reason: &'static str) -> WindowsCloudFilesError {
2009    WindowsCloudFilesError::InvalidCallbackSnapshot { reason }
2010}
2011
2012fn invalid_transfer(reason: &'static str) -> WindowsCloudFilesError {
2013    WindowsCloudFilesError::InvalidFetchTransfer { reason }
2014}
2015
2016fn hydration_byte_range(range: WindowsCallbackRange, file_size: u64) -> Result<ByteRange> {
2017    if !range
2018        .offset()
2019        .is_multiple_of(CFAPI_TRANSFER_ALIGNMENT_BYTES)
2020    {
2021        return Err(invalid_transfer(
2022            "CFAPI transfer offset must be aligned to 4096 bytes",
2023        ));
2024    }
2025    let length = match range.length() {
2026        WindowsCallbackRangeLength::Exact(length) => length,
2027        WindowsCallbackRangeLength::ToEnd => file_size
2028            .checked_sub(range.offset())
2029            .ok_or_else(|| invalid_transfer("CF_EOF transfer offset exceeds callback file size"))?,
2030    };
2031    if length == 0 {
2032        return Err(invalid_transfer(
2033            "CFAPI fetch range resolves to an empty successful transfer",
2034        ));
2035    }
2036    let Some(end) = range.offset().checked_add(length) else {
2037        return Err(invalid_transfer("CFAPI fetch range end exceeds u64"));
2038    };
2039    if end > file_size {
2040        return Err(invalid_transfer(
2041            "CFAPI fetch range exceeds callback file size",
2042        ));
2043    }
2044    if end != file_size && !length.is_multiple_of(CFAPI_TRANSFER_ALIGNMENT_BYTES) {
2045        return Err(invalid_transfer(
2046            "CFAPI transfer length must be aligned to 4096 bytes unless it reaches end-of-file",
2047        ));
2048    }
2049    ByteRange::new(range.offset(), length).map_err(Into::into)
2050}
2051
2052fn validate_request_generation(
2053    snapshot: SessionGeneration,
2054    lease: SessionGeneration,
2055) -> Result<()> {
2056    if snapshot == lease {
2057        return Ok(());
2058    }
2059    Err(WindowsCloudFilesError::StaleConnectionGeneration {
2060        expected: lease.get(),
2061        actual: snapshot.get(),
2062    })
2063}
2064
2065#[cfg(test)]
2066mod tests {
2067    use std::sync::atomic::Ordering;
2068
2069    use super::{FetchTerminalGate, SessionGeneration, WindowsConnectionSession};
2070
2071    #[test]
2072    fn fetch_terminal_gate_allows_exactly_one_attempt() {
2073        let gate = FetchTerminalGate::default();
2074        assert!(!gate.attempted());
2075        assert!(gate.begin_native());
2076        assert!(gate.attempted());
2077        assert!(!gate.begin_native());
2078        assert!(gate.attempted());
2079        assert!(!gate.cancel_by_platform());
2080    }
2081
2082    #[test]
2083    fn platform_cancellation_suppresses_every_native_attempt() {
2084        let gate = FetchTerminalGate::default();
2085        assert!(gate.cancel_by_platform());
2086        assert!(!gate.attempted());
2087        assert!(!gate.cancel_by_platform());
2088        assert!(!gate.begin_native());
2089        assert!(!gate.attempted());
2090    }
2091
2092    #[test]
2093    fn watchdog_timeout_preserves_one_native_failure_attempt() {
2094        let gate = FetchTerminalGate::default();
2095        assert!(gate.cancel_by_watchdog());
2096        assert!(gate.watchdog_timed_out());
2097        assert!(!gate.attempted());
2098        assert!(gate.begin_native());
2099        assert!(gate.attempted());
2100        assert!(!gate.begin_native());
2101    }
2102
2103    #[test]
2104    fn callback_queue_metrics_count_each_bounded_ingress_outcome() {
2105        let generation = SessionGeneration::new(1).expect("generation fixture should be valid");
2106        let session = WindowsConnectionSession::new(generation);
2107        session.record_accepted_callback();
2108        session.record_queued_fetch();
2109        session.record_queued_cancel();
2110        session.record_queued_preflight();
2111        session.record_queued_observation();
2112        session.record_fetch_queue_full();
2113        session.record_cancel_queue_full();
2114        session.record_preflight_queue_full();
2115        session.record_observation_queue_full();
2116        session.record_receiver_disconnected();
2117        session.record_closing_rejection();
2118        session.record_invalid_snapshot();
2119        session.record_panic_failure();
2120        let metrics = session.queue_metrics();
2121        assert_eq!(metrics.accepted_callbacks(), 1);
2122        assert_eq!(metrics.queued_fetch_data(), 1);
2123        assert_eq!(metrics.queued_cancel_observations(), 1);
2124        assert_eq!(metrics.queued_preflights(), 1);
2125        assert_eq!(metrics.queued_observations(), 1);
2126        assert_eq!(metrics.fetch_queue_full(), 1);
2127        assert_eq!(metrics.cancel_observation_queue_full(), 1);
2128        assert_eq!(metrics.preflight_queue_full(), 1);
2129        assert_eq!(metrics.observation_queue_full(), 1);
2130        assert_eq!(metrics.receiver_disconnected(), 1);
2131        assert_eq!(metrics.closing_rejections(), 1);
2132        assert_eq!(metrics.invalid_snapshot_rejections(), 1);
2133        assert_eq!(metrics.panic_failures(), 1);
2134
2135        session
2136            .inner
2137            .queue_metrics
2138            .accepted_callbacks
2139            .store(u64::MAX, Ordering::Relaxed);
2140        session.record_accepted_callback();
2141        assert_eq!(session.queue_metrics().accepted_callbacks(), u64::MAX);
2142    }
2143
2144    #[test]
2145    fn callback_queue_metrics_do_not_lose_concurrent_updates() {
2146        let generation = SessionGeneration::new(1).expect("generation fixture should be valid");
2147        let session = WindowsConnectionSession::new(generation);
2148        std::thread::scope(|scope| {
2149            for _ in 0..8 {
2150                let session = session.clone();
2151                scope.spawn(move || {
2152                    for _ in 0..1_000 {
2153                        session.record_accepted_callback();
2154                    }
2155                });
2156            }
2157        });
2158        assert_eq!(session.queue_metrics().accepted_callbacks(), 8_000);
2159    }
2160}