diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9c82ed8..1c5f599 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,6 +12,10 @@ jobs: steps: - uses: actions/checkout@v3 - uses: actions/setup-dotnet@v3 + - uses: actions/setup-dotnet@v3 + name: Install .NET SDK 6.x + with: + version: 6.x - name: Restore dependencies run: dotnet restore /p:TreatWarningsAsErrors=true - name: Build @@ -22,8 +26,8 @@ jobs: name: Generate NuGet Packages runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-dotnet@v1 + - uses: actions/checkout@v3 + - uses: actions/setup-dotnet@v3 name: Install Current .NET SDK - name: Generate NuGet Packages run: dotnet pack --configuration Release --output nupkg /p:TreatWarningsAsErrors=true diff --git a/TempDirectory.Test/TempDirectory.Test.csproj b/TempDirectory.Test/TempDirectory.Test.csproj index 2b3d3ab..6e60dcf 100644 --- a/TempDirectory.Test/TempDirectory.Test.csproj +++ b/TempDirectory.Test/TempDirectory.Test.csproj @@ -1,7 +1,7 @@ - net7.0 + net7.0;net6.0 false Messerli.TempDirectory.Test Messerli.TempDirectory.Test diff --git a/TempDirectory.Test/TempDirectoryBuilderTest.cs b/TempDirectory.Test/TempDirectoryBuilderTest.cs deleted file mode 100644 index c6110aa..0000000 --- a/TempDirectory.Test/TempDirectoryBuilderTest.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Xunit; - -namespace Messerli.TempDirectory.Test; - -public sealed class TempDirectoryBuilderTest -{ - private const string Prefix = "prefix"; - private const string Suffix = "suffix"; - private const string PrefixSeparator = "@"; - private const string SuffixSeparator = "."; - - [Theory] - [MemberData(nameof(GetTempDirectoryBuilderConfigurations))] - public void CreatesTempDirectory(ITempDirectoryBuilder tempDirectoryBuilder) - { - using var tempDirectory = tempDirectoryBuilder.Create(); - Assert.True(Directory.Exists(tempDirectory.FullName)); - } - - [Theory] - [MemberData(nameof(GetTempDirectoryBuilderConfigurations))] - [SuppressMessage("IDisposableAnalyzers.Correctness", "IDISP017:Prefer using", Justification = "Intentional for testing purposes.")] - public void DeletesTempDirectory(ITempDirectoryBuilder tempDirectoryBuilder) - { - var tempDirectory = tempDirectoryBuilder.Create(); - var fullName = tempDirectory.FullName; - Assert.True(Directory.Exists(tempDirectory.FullName)); - tempDirectory.Dispose(); - - Assert.False(Directory.Exists(fullName)); - } - - [Theory] - [MemberData(nameof(GetTempDirectoryBuilderConfigurations))] - public void FullNameContainsName(ITempDirectoryBuilder tempDirectoryBuilder) - { - using var tempDirectory = tempDirectoryBuilder.Create(); - Assert.Contains(tempDirectory.Name, tempDirectory.FullName); - } - - [Theory] - [MemberData(nameof(GetTempDirectoryBuilderConfigurations))] - public void FullNameIsNotName(ITempDirectoryBuilder tempDirectoryBuilder) - { - using var tempDirectory = tempDirectoryBuilder.Create(); - Assert.NotEqual(tempDirectory.FullName, tempDirectory.Name); - } - - [Fact] - public void PrefixIsInName() - { - using var tempDirectory = new TempDirectoryBuilder().Prefix(Prefix).Create(); - Assert.StartsWith(Prefix, tempDirectory.Name); - } - - [Fact] - public void SuffixIsInName() - { - using var tempDirectory = new TempDirectoryBuilder().Suffix(Suffix).Create(); - Assert.EndsWith(Suffix, tempDirectory.Name); - } - - [Fact] - public void SeparatorsAreInName() - { - using var tempDirectory = new TempDirectoryBuilder() - .Prefix(Prefix) - .PrefixSeparator(PrefixSeparator) - .Suffix(Prefix) - .SuffixSeparator(SuffixSeparator) - .Create(); - Assert.Contains(PrefixSeparator, tempDirectory.Name); - Assert.Contains(SuffixSeparator, tempDirectory.Name); - } - - [Fact] - public void SeparatorsAreNotInNameWithoutPrefixOrSuffix() - { - using var tempDirectory = new TempDirectoryBuilder() - .PrefixSeparator(PrefixSeparator) - .SuffixSeparator(SuffixSeparator) - .Create(); - Assert.DoesNotContain(PrefixSeparator, tempDirectory.Name); - Assert.DoesNotContain(SuffixSeparator, tempDirectory.Name); - } - - public static TheoryData GetTempDirectoryBuilderConfigurations() - => new() - { - new TempDirectoryBuilder(), - new TempDirectoryBuilder().Prefix(Prefix), - new TempDirectoryBuilder().Suffix(Suffix), - new TempDirectoryBuilder().Prefix(Prefix).Suffix(Suffix), - new TempDirectoryBuilder().Prefix(Prefix).PrefixSeparator(PrefixSeparator), - new TempDirectoryBuilder().Suffix(Prefix).SuffixSeparator(SuffixSeparator), - new TempDirectoryBuilder().Prefix(Prefix).PrefixSeparator(PrefixSeparator).Suffix(Prefix).SuffixSeparator(SuffixSeparator), - }; -} diff --git a/TempDirectory.Test/TempSubdirectoryTest.cs b/TempDirectory.Test/TempSubdirectoryTest.cs new file mode 100644 index 0000000..409dca1 --- /dev/null +++ b/TempDirectory.Test/TempSubdirectoryTest.cs @@ -0,0 +1,47 @@ +using Xunit; + +namespace Messerli.TempDirectory.Test; + +public sealed class TempSubdirectoryTest +{ + private const string Prefix = "prefix"; + + [Fact] + public void CreatesTempDirectory() + { + using var tempDirectory = TempSubdirectory.Create(); + Assert.True(Directory.Exists(tempDirectory.FullName)); + } + + [Fact] + public void CreatesTempDirectoryWithPrefix() + { + using var tempDirectory = TempSubdirectory.Create(Prefix); + Assert.True(Directory.Exists(tempDirectory.FullName)); + Assert.StartsWith(Prefix, Path.GetFileName(tempDirectory.FullName)); + } + + [Fact] + public void DeletesTempDirectoryOnDispose() + { + var tempDirectory = TempSubdirectory.Create(); + + using (tempDirectory) + { + Assert.True(Directory.Exists(tempDirectory.FullName)); + } + + Assert.False(Directory.Exists(tempDirectory.FullName)); + } + + [Fact] + public void AllowsTempDirectoryToBeDisposedMoreThanOnce() + { + var tempDirectory = TempSubdirectory.Create(); + + using (tempDirectory) + using (tempDirectory) + { + } + } +} diff --git a/TempDirectory/ITempDirectoryBuilder.cs b/TempDirectory/ITempDirectoryBuilder.cs deleted file mode 100644 index b89f0d1..0000000 --- a/TempDirectory/ITempDirectoryBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Messerli.TempDirectory; - -public interface ITempDirectoryBuilder -{ - ITempDirectoryBuilder Prefix(string prefix); - - ITempDirectoryBuilder PrefixSeparator(string prefixSeparator); - - ITempDirectoryBuilder Suffix(string suffix); - - ITempDirectoryBuilder SuffixSeparator(string suffixSeparator); - - TempDirectory Create(); -} diff --git a/TempDirectory/TempDirectory.cs b/TempDirectory/TempDirectory.cs deleted file mode 100644 index b509a1d..0000000 --- a/TempDirectory/TempDirectory.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Messerli.TempDirectory; - -public delegate void OnDispose(); - -public sealed class TempDirectory : IDisposable -{ - private readonly OnDispose _onDispose; - - public TempDirectory(string name, string fullName, OnDispose onDispose) - { - Name = name; - FullName = fullName; - _onDispose = onDispose; - } - - public string Name { get; } - - public string FullName { get; } - - public void Dispose() => _onDispose(); -} diff --git a/TempDirectory/TempDirectory.csproj b/TempDirectory/TempDirectory.csproj index 6fd8e2e..fee301d 100644 --- a/TempDirectory/TempDirectory.csproj +++ b/TempDirectory/TempDirectory.csproj @@ -1,8 +1,8 @@ - 0.2.1 - netstandard2.0 + 0.3.0 + net7.0;netstandard2.0 Messerli.TempDirectory Messerli.TempDirectory diff --git a/TempDirectory/TempDirectoryBuilder.cs b/TempDirectory/TempDirectoryBuilder.cs deleted file mode 100644 index 7eee888..0000000 --- a/TempDirectory/TempDirectoryBuilder.cs +++ /dev/null @@ -1,68 +0,0 @@ -namespace Messerli.TempDirectory; - -public sealed class TempDirectoryBuilder : ITempDirectoryBuilder -{ - private readonly string _prefix = string.Empty; - private readonly string _suffix = string.Empty; - private readonly string _prefixSeparator = "-"; - private readonly string _suffixSeparator = "-"; - - public TempDirectoryBuilder() - { - } - - private TempDirectoryBuilder(string prefix, string suffix, string prefixSeparator, string suffixSeparator) - { - _prefix = prefix; - _suffix = suffix; - _prefixSeparator = prefixSeparator; - _suffixSeparator = suffixSeparator; - } - - public ITempDirectoryBuilder Prefix(string prefix) - => DeepClone(prefix: prefix); - - public ITempDirectoryBuilder PrefixSeparator(string prefixSeparator) - => DeepClone(prefixSeparator: prefixSeparator); - - public ITempDirectoryBuilder Suffix(string suffix) - => DeepClone(suffix: suffix); - - public ITempDirectoryBuilder SuffixSeparator(string suffixSeparator) - => DeepClone(suffixSeparator: suffixSeparator); - - public TempDirectory Create() - { - var tempPath = Path.GetTempPath(); - var directoryName = GenerateDirectoryName(); - var path = Path.Combine(tempPath, directoryName); - - Directory.CreateDirectory(path); - var onDispose = CreateDirectoryDeletionFunction(path); - - return new TempDirectory(directoryName, path, onDispose); - } - - private ITempDirectoryBuilder DeepClone( - string? prefix = null, - string? suffix = null, - string? prefixSeparator = null, - string? suffixSeparator = null) - => new TempDirectoryBuilder( - prefix ?? _prefix, - suffix ?? _suffix, - prefixSeparator ?? _prefixSeparator, - suffixSeparator ?? _suffixSeparator); - - private string GenerateDirectoryName() - { - var guid = Guid.NewGuid().ToString(); - var prefixSubstring = string.IsNullOrEmpty(_prefix) ? string.Empty : _prefix + _prefixSeparator; - var suffixSubstring = string.IsNullOrEmpty(_suffix) ? string.Empty : _suffixSeparator + _suffix; - - return $"{prefixSubstring}{guid}{suffixSubstring}"; - } - - private static OnDispose CreateDirectoryDeletionFunction(string directoryPath) - => () => Directory.Delete(directoryPath, true); -} diff --git a/TempDirectory/TempSubdirectory.cs b/TempDirectory/TempSubdirectory.cs new file mode 100644 index 0000000..5d48108 --- /dev/null +++ b/TempDirectory/TempSubdirectory.cs @@ -0,0 +1,40 @@ +namespace Messerli.TempDirectory; + +/// A temporary subdirectory in the temporary folder of the current user. +/// Disposing will remove the temporary subdirectory. +public sealed class TempSubdirectory : IDisposable +{ + private bool _isDisposed; + + private TempSubdirectory(string fullName) + { + FullName = fullName; + } + + /// Gets the full path of the directory. + public string FullName { get; } + + /// Creates a temporary subdirectory in the temporary folder of the current user. + public static TempSubdirectory Create(string? prefix = null) + { +#if NET7_0_OR_GREATER + var tempSubdirectory = Directory.CreateTempSubdirectory(prefix); + return new TempSubdirectory(tempSubdirectory.FullName); +#else + var fullName = Path.Combine(Path.GetTempPath(), $"{prefix}{Guid.NewGuid()}"); + Directory.CreateDirectory(fullName); + return new TempSubdirectory(fullName); +#endif + } + + public void Dispose() + { + if (!_isDisposed) + { + _isDisposed = true; + Directory.Delete(FullName, recursive: true); + } + } + + public override string ToString() => $"{nameof(TempSubdirectory)}({FullName})"; +} diff --git a/changelog.md b/changelog.md index 525288d..a9b273d 100644 --- a/changelog.md +++ b/changelog.md @@ -1,10 +1,12 @@ # Changelog +## 0.3.0 +- Simplify API surface -## 0.1.0 -- Initial release +## 0.2.1 +- Update metadata of NuGet package. ## 0.2.0 - Target framework is now .NET Standard 2.0 -## 0.2.1 -- Update metadata of NuGet package. +## 0.1.0 +- Initial release diff --git a/readme.md b/readme.md index 13f95e0..aeedb31 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,12 @@ -# TempDirectoryProvider +# TempDirectory [![Build](https://github.com/messerli-informatik-ag/temp-directory/actions/workflows/build.yml/badge.svg)](https://github.com/messerli-informatik-ag/temp-directory/actions/workflows/build.yml) [![NuGet](https://img.shields.io/nuget/v/Messerli.TempDirectory.svg)](https://www.nuget.org/packages/Messerli.TempDirectory/) -A `TempDirectoryBuilder` that provides unique temporary directories and automatically cleans them up for you. +This library provides a `TempSubdirectory` class that provides a unique subdirectory in the user's temp directory +that is automatically cleaned up when the class is disposed. + +### Usage +```cs +using var directory = TempSubdirectory.Create("prefix"); +File.WriteAllText(Path.Combine(directory.FullName, "example.txt"), contents: "..."); +```