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
// Copyright (c) Mike Grier.
//! The seam: what every entry has in common, as a trait.
//!
//! Each entry is already a value whose `perform` is the single point where
//! Win32 is touched. This module adds the trait over that, so a consumer's code
//! can be written against "a request that produces `T`" rather than against a
//! concrete entry -- and can therefore be exercised in that consumer's own
//! tests without a filesystem, a network path, or a device that may not be
//! present.
//!
//! # Why two traits rather than one
//!
//! The distinction is real, not cosmetic. An open is a **parameter set**: it
//! may be performed repeatedly, producing an independent handle each time, so
//! it takes `&self`. A close is **one-shot**: performing it consumes the
//! request, which is what makes closing twice through this crate impossible.
//!
//! Collapsing them into one trait would have to pick a side, and both choices
//! lie. A `&self` trait would make a close look repeatable; a `self` trait would
//! make every open look single-use and force a caller to rebuild a request it
//! could simply have performed again.
//!
//! # Why the error type is an associated type
//!
//! Most entries fail only as Windows failed, so their error is a
//! [`Win32Error`](crate::Win32Error). Two do not:
//! [`crate::final_path::QueryFinalPath`] and
//! [`crate::full_path::ResolveFullPath`] each retry a growing buffer, and "the
//! required size kept changing" is a failure Win32 has no code for. They report
//! it as [`FinalPathError::Unstable`](crate::FinalPathError::Unstable) and
//! [`FullPathError::Unstable`](crate::FullPathError::Unstable) respectively.
//!
//! Fixing the trait's error to `Win32Error` would have left those entries
//! outside the seam, which would make the seam not level -- a consumer could
//! substitute a fake for some entries and not the rest. An associated `Error`
//! keeps every entry reachable through one trait without any of them having to
//! invent a code it does not have.
//!
//! That last clause is load-bearing rather than decorative. `ResolveFullPath`
//! did invent one for a while, returning a synthesized
//! `ERROR_INSUFFICIENT_BUFFER` that Win32 can also produce by itself, so a
//! caller could not tell the crate's own retry giving up from a genuine Windows
//! failure. The rule stated here is what the entry now follows.
//!
//! # This is a seam, not an abstraction layer
//!
//! The traits exist so a *consumer* can substitute a fake. They are not a
//! plug-in point for alternative implementations of Windows, and nothing in
//! this crate dispatches through them: the entries keep their inherent
//! `perform` methods, which is what an ordinary caller uses.
/// A request that may be performed more than once.
///
/// Implemented by the entries that carry parameters and produce something new
/// each time: [`crate::open::OpenFile`],
/// [`crate::open_by_id::OpenFileByIdentifier`], and
/// [`crate::watch::WatchDirectory`].
///
/// # Example
///
/// A consumer writes its own code against the trait, then tests it against a
/// fake that never touches the filesystem:
///
/// ```
/// use windows_namespace_request_sys::outcome::Outcome;
/// use windows_namespace_request_sys::request::Request;
/// use windows_namespace_request_sys::Win32Error;
/// use windows_sys::Win32::Foundation::ERROR_FILE_NOT_FOUND;
///
/// // The consumer's code: generic over the request, so it can be exercised
/// // without opening anything.
/// fn count_successes<R: Request>(requests: &[R], attempts: usize) -> usize {
/// requests
/// .iter()
/// .flat_map(|request| (0..attempts).map(move |_| request.perform()))
/// .filter(Result::is_ok)
/// .count()
/// }
///
/// // The consumer's fake: a canned outcome, no Win32 anywhere.
/// struct AlwaysMissing;
///
/// impl Request for AlwaysMissing {
/// type Error = Win32Error;
/// type Output = ();
///
/// fn perform(&self) -> Outcome<()> {
/// Err(Win32Error::from_code(ERROR_FILE_NOT_FOUND))
/// }
/// }
///
/// struct AlwaysOpens;
///
/// impl Request for AlwaysOpens {
/// type Error = Win32Error;
/// type Output = u32;
///
/// fn perform(&self) -> Outcome<u32> {
/// Ok(7)
/// }
/// }
///
/// assert_eq!(count_successes(&[AlwaysMissing, AlwaysMissing], 3), 0);
/// assert_eq!(count_successes(&[AlwaysOpens], 3), 3, "a request may be performed repeatedly");
/// ```
/// A request that is consumed by performing it.
///
/// Implemented by [`crate::close::CloseRequest`], where performing twice would
/// mean closing a handle twice. The trait carries that property rather than
/// leaving it to a comment.
///
/// # Example
///
/// ```
/// use windows_namespace_request_sys::outcome::Outcome;
/// use windows_namespace_request_sys::request::ConsumingRequest;
/// use windows_namespace_request_sys::Win32Error;
///
/// // A consumer's cleanup step, written against the trait.
/// fn perform_all<R: ConsumingRequest>(requests: Vec<R>) -> usize {
/// requests
/// .into_iter()
/// .filter(|_| true)
/// .map(ConsumingRequest::perform)
/// .filter(Result::is_ok)
/// .count()
/// }
///
/// struct FakeClose;
///
/// impl ConsumingRequest for FakeClose {
/// type Error = Win32Error;
/// type Output = ();
///
/// fn perform(self) -> Outcome<()> {
/// Ok(())
/// }
/// }
///
/// assert_eq!(perform_all(vec![FakeClose, FakeClose]), 2);
/// ```