@@ -141,6 +141,39 @@ public func withTemporaryFile<Result>(
141
141
}
142
142
}
143
143
144
+ /// Creates a temporary file and evaluates a closure with the temporary file as an argument.
145
+ /// The temporary file will live on disk while the closure is evaluated and will be deleted when
146
+ /// the cleanup block is called.
147
+ ///
148
+ /// This function is basically a wrapper over posix's mkstemps() function to create disposable files.
149
+ ///
150
+ /// - Parameters:
151
+ /// - dir: If specified the temporary file will be created in this directory otherwise environment variables
152
+ /// TMPDIR, TEMP and TMP will be checked for a value (in that order). If none of the env variables are
153
+ /// set, dir will be set to `/tmp/`.
154
+ /// - prefix: The prefix to the temporary file name.
155
+ /// - suffix: The suffix to the temporary file name.
156
+ /// - body: A closure to execute that receives the TemporaryFile as an argument.
157
+ /// If `body` has a return value, that value is also used as the
158
+ /// return value for the `withTemporaryFile` function.
159
+ /// The cleanup block should be called when the temporary file is no longer needed.
160
+ ///
161
+ /// - Throws: TempFileError and rethrows all errors from `body`.
162
+ @available ( macOS 10 . 15 , iOS 13 . 0 , tvOS 13 . 0 , watchOS 6 . 0 , * )
163
+ public func withTemporaryFile< Result> (
164
+ dir: AbsolutePath ? = nil , prefix: String = " TemporaryFile " , suffix: String = " " , _ body: ( TemporaryFile , @escaping ( TemporaryFile ) async -> Void ) async throws -> Result
165
+ ) async throws -> Result {
166
+ return try await body ( TemporaryFile ( dir: dir, prefix: prefix, suffix: suffix) ) { tempFile in
167
+ #if os(Windows)
168
+ _ = tempFile. path. pathString. withCString ( encodedAs: UTF16 . self) {
169
+ _wunlink ( $0)
170
+ }
171
+ #else
172
+ unlink ( tempFile. path. pathString)
173
+ #endif
174
+ }
175
+ }
176
+
144
177
/// Creates a temporary file and evaluates a closure with the temporary file as an argument.
145
178
/// The temporary file will live on disk while the closure is evaluated and will be deleted afterwards.
146
179
///
@@ -167,6 +200,40 @@ public func withTemporaryFile<Result>(
167
200
}
168
201
}
169
202
203
+ /// Creates a temporary file and evaluates a closure with the temporary file as an argument.
204
+ /// The temporary file will live on disk while the closure is evaluated and will be deleted afterwards.
205
+ ///
206
+ /// This function is basically a wrapper over posix's mkstemps() function to create disposable files.
207
+ ///
208
+ /// - Parameters:
209
+ /// - dir: If specified the temporary file will be created in this directory otherwise environment variables
210
+ /// TMPDIR, TEMP and TMP will be checked for a value (in that order). If none of the env variables are
211
+ /// set, dir will be set to `/tmp/`.
212
+ /// - prefix: The prefix to the temporary file name.
213
+ /// - suffix: The suffix to the temporary file name.
214
+ /// - deleteOnClose: Whether the file should get deleted after the call of `body`
215
+ /// - body: A closure to execute that receives the TemporaryFile as an argument.
216
+ /// If `body` has a return value, that value is also used as the
217
+ /// return value for the `withTemporaryFile` function.
218
+ ///
219
+ /// - Throws: TempFileError and rethrows all errors from `body`.
220
+ @available ( macOS 10 . 15 , iOS 13 . 0 , tvOS 13 . 0 , watchOS 6 . 0 , * )
221
+ public func withTemporaryFile< Result> (
222
+ dir: AbsolutePath ? = nil , prefix: String = " TemporaryFile " , suffix: String = " " , deleteOnClose: Bool = true , _ body: ( TemporaryFile ) async throws -> Result
223
+ ) async throws -> Result {
224
+ try await withTemporaryFile ( dir: dir, prefix: prefix, suffix: suffix) { tempFile, cleanup in
225
+ let result : Result
226
+ do {
227
+ result = try await body ( tempFile)
228
+ if ( deleteOnClose) { await cleanup ( tempFile) }
229
+ } catch {
230
+ if ( deleteOnClose) { await cleanup ( tempFile) }
231
+ throw error
232
+ }
233
+ return result
234
+ }
235
+ }
236
+
170
237
// FIXME: This isn't right place to declare this, probably POSIX or merge with FileSystemError?
171
238
//
172
239
/// Contains the error which can be thrown while creating a directory using POSIX's mkdir.
@@ -252,6 +319,44 @@ public func withTemporaryDirectory<Result>(
252
319
}
253
320
}
254
321
322
+ /// Creates a temporary directory and evaluates a closure with the directory path as an argument.
323
+ /// The temporary directory will live on disk while the closure is evaluated and will be deleted when
324
+ /// the cleanup closure is called. This allows the temporary directory to have an arbitrary lifetime.
325
+ ///
326
+ /// This function is basically a wrapper over posix's mkdtemp() function.
327
+ ///
328
+ /// - Parameters:
329
+ /// - dir: If specified the temporary directory will be created in this directory otherwise environment
330
+ /// variables TMPDIR, TEMP and TMP will be checked for a value (in that order). If none of the env
331
+ /// variables are set, dir will be set to `/tmp/`.
332
+ /// - prefix: The prefix to the temporary file name.
333
+ /// - body: A closure to execute that receives the absolute path of the directory as an argument.
334
+ /// If `body` has a return value, that value is also used as the
335
+ /// return value for the `withTemporaryDirectory` function.
336
+ /// The cleanup block should be called when the temporary directory is no longer needed.
337
+ ///
338
+ /// - Throws: MakeDirectoryError and rethrows all errors from `body`.
339
+ @available ( macOS 10 . 15 , iOS 13 . 0 , tvOS 13 . 0 , watchOS 6 . 0 , * )
340
+ public func withTemporaryDirectory< Result> (
341
+ dir: AbsolutePath ? = nil , prefix: String = " TemporaryDirectory " , _ body: ( AbsolutePath , @escaping ( AbsolutePath ) async -> Void ) async throws -> Result
342
+ ) async throws -> Result {
343
+ // Construct path to the temporary directory.
344
+ let templatePath = try AbsolutePath ( validating: prefix + " .XXXXXX " , relativeTo: determineTempDirectory ( dir) )
345
+
346
+ // Convert templatePath to a C style string terminating with null char to be an valid input
347
+ // to mkdtemp method. The XXXXXX in this string will be replaced by a random string
348
+ // which will be the actual path to the temporary directory.
349
+ var template = [ UInt8] ( templatePath. pathString. utf8) . map ( { Int8 ( $0) } ) + [ Int8 ( 0 ) ]
350
+
351
+ if TSCLibc . mkdtemp ( & template) == nil {
352
+ throw MakeDirectoryError ( errno: errno)
353
+ }
354
+
355
+ return try await body ( AbsolutePath ( validating: String ( cString: template) ) ) { path in
356
+ _ = try ? FileManager . default. removeItem ( atPath: path. pathString)
357
+ }
358
+ }
359
+
255
360
/// Creates a temporary directory and evaluates a closure with the directory path as an argument.
256
361
/// The temporary directory will live on disk while the closure is evaluated and will be deleted afterwards.
257
362
///
@@ -277,3 +382,35 @@ public func withTemporaryDirectory<Result>(
277
382
}
278
383
}
279
384
385
+ /// Creates a temporary directory and evaluates a closure with the directory path as an argument.
386
+ /// The temporary directory will live on disk while the closure is evaluated and will be deleted afterwards.
387
+ ///
388
+ /// This function is basically a wrapper over posix's mkdtemp() function.
389
+ ///
390
+ /// - Parameters:
391
+ /// - dir: If specified the temporary directory will be created in this directory otherwise environment
392
+ /// variables TMPDIR, TEMP and TMP will be checked for a value (in that order). If none of the env
393
+ /// variables are set, dir will be set to `/tmp/`.
394
+ /// - prefix: The prefix to the temporary file name.
395
+ /// - removeTreeOnDeinit: If enabled try to delete the whole directory tree otherwise remove only if its empty.
396
+ /// - body: A closure to execute that receives the absolute path of the directory as an argument.
397
+ /// If `body` has a return value, that value is also used as the
398
+ /// return value for the `withTemporaryDirectory` function.
399
+ ///
400
+ /// - Throws: MakeDirectoryError and rethrows all errors from `body`.
401
+ @available ( macOS 10 . 15 , iOS 13 . 0 , tvOS 13 . 0 , watchOS 6 . 0 , * )
402
+ public func withTemporaryDirectory< Result> (
403
+ dir: AbsolutePath ? = nil , prefix: String = " TemporaryDirectory " , removeTreeOnDeinit: Bool = false , _ body: ( AbsolutePath ) async throws -> Result
404
+ ) async throws -> Result {
405
+ try await withTemporaryDirectory ( dir: dir, prefix: prefix) { path, cleanup in
406
+ let result : Result
407
+ do {
408
+ result = try await body ( path)
409
+ if removeTreeOnDeinit { await cleanup ( path) }
410
+ } catch {
411
+ if removeTreeOnDeinit { await cleanup ( path) }
412
+ throw error
413
+ }
414
+ return result
415
+ }
416
+ }
0 commit comments