@@ -141,6 +141,39 @@ public func withTemporaryFile<Result>(
141141 }
142142}
143143
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+
144177/// Creates a temporary file and evaluates a closure with the temporary file as an argument.
145178/// The temporary file will live on disk while the closure is evaluated and will be deleted afterwards.
146179///
@@ -167,6 +200,40 @@ public func withTemporaryFile<Result>(
167200 }
168201}
169202
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+
170237// FIXME: This isn't right place to declare this, probably POSIX or merge with FileSystemError?
171238//
172239/// Contains the error which can be thrown while creating a directory using POSIX's mkdir.
@@ -252,6 +319,44 @@ public func withTemporaryDirectory<Result>(
252319 }
253320}
254321
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+
255360/// Creates a temporary directory and evaluates a closure with the directory path as an argument.
256361/// The temporary directory will live on disk while the closure is evaluated and will be deleted afterwards.
257362///
@@ -277,3 +382,35 @@ public func withTemporaryDirectory<Result>(
277382 }
278383}
279384
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