Skip to content

Commit

Permalink
Use file scoped namespaces (#31)
Browse files Browse the repository at this point in the history
  • Loading branch information
cklutz authored Oct 24, 2024
1 parent 6942080 commit 9e6dd10
Show file tree
Hide file tree
Showing 34 changed files with 3,513 additions and 3,548 deletions.
49 changes: 24 additions & 25 deletions src/LockCheck/CodeAnalysisShim.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,33 @@
// Some attribute definitions that are not available on .NET Framework.
// Add additional ones as required.

namespace System.Diagnostics.CodeAnalysis
namespace System.Diagnostics.CodeAnalysis;

/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter will not be null even if the corresponding type allows it.</summary>
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
internal sealed class NotNullWhenAttribute : Attribute
{
/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter will not be null even if the corresponding type allows it.</summary>
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
internal sealed class NotNullWhenAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified return value condition.</summary>
/// <param name="returnValue">
/// The return value condition. If the method returns this value, the associated parameter will not be null.
/// </param>
public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
/// <summary>Initializes the attribute with the specified return value condition.</summary>
/// <param name="returnValue">
/// The return value condition. If the method returns this value, the associated parameter will not be null.
/// </param>
public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;

/// <summary>Gets the return value condition.</summary>
public bool ReturnValue { get; }
}
/// <summary>Gets the return value condition.</summary>
public bool ReturnValue { get; }
}

/// <summary>Specifies that the output will be non-null if the named parameter is non-null.</summary>
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)]
internal sealed class NotNullIfNotNullAttribute : Attribute
{
/// <summary>Initializes the attribute with the associated parameter name.</summary>
/// <param name="parameterName">
/// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null.
/// </param>
public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName;
/// <summary>Specifies that the output will be non-null if the named parameter is non-null.</summary>
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)]
internal sealed class NotNullIfNotNullAttribute : Attribute
{
/// <summary>Initializes the attribute with the associated parameter name.</summary>
/// <param name="parameterName">
/// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null.
/// </param>
public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName;

/// <summary>Gets the associated parameter name.</summary>
public string ParameterName { get; }
}
/// <summary>Gets the associated parameter name.</summary>
public string ParameterName { get; }
}
#endif
235 changes: 117 additions & 118 deletions src/LockCheck/ExceptionUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,146 +8,145 @@
using System.Reflection;
#endif

namespace LockCheck
namespace LockCheck;

/// <summary>
/// Extension methods.
/// </summary>
public static class ExceptionUtils
{
/// <summary>
/// Extension methods.
/// </summary>
public static class ExceptionUtils
{
#if NETFRAMEWORK
// In .NET Framework the Exception.HResult property has no setter.
private static readonly Lazy<MethodInfo> s_setErrorCodeMethod = new(
() => typeof(Exception).GetMethod("SetErrorCode",
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod,
null, [typeof(int)], null));
// In .NET Framework the Exception.HResult property has no setter.
private static readonly Lazy<MethodInfo> s_setErrorCodeMethod = new(
() => typeof(Exception).GetMethod("SetErrorCode",
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod,
null, [typeof(int)], null));
#endif

/// <summary>
/// Determines if the current <see cref="IOException"/> is likely due to a locked file condition.
/// </summary>
/// <param name="exception">The exception to check.</param>
/// <returns><c>true</c> if the <paramref name="exception"/> is due to a locked file, <c>false</c> otherwise.</returns>
public static bool IsFileLocked(this IOException exception)
/// <summary>
/// Determines if the current <see cref="IOException"/> is likely due to a locked file condition.
/// </summary>
/// <param name="exception">The exception to check.</param>
/// <returns><c>true</c> if the <paramref name="exception"/> is due to a locked file, <c>false</c> otherwise.</returns>
public static bool IsFileLocked(this IOException exception)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return Windows.Extensions.IsFileLocked(exception);
}
return Windows.Extensions.IsFileLocked(exception);
}
#if NET
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return Linux.Extensions.IsFileLocked(exception);
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return Linux.Extensions.IsFileLocked(exception);
}
#endif

return false;
}
return false;
}

/// <summary>
/// Throws a new <see cref="IOException"/> that contains the given exception as an inner exception,
/// if the <paramref name="ex"/> is due to a file being locked. Otherwise does not throw and exception.
/// </summary>
/// <param name="ex">The exception to check and rethrow</param>
/// <param name="fileName">The file name to check for being locked.</param>
/// <param name="features">Optional features.</param>
/// <example>
/// <![CDATA[
/// string fileName = "C:\\temp\\foo.txt";
/// try
/// {
/// var file2 = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite);
/// }
/// catch (Exception ex)
/// {
/// if (!ex.RethrowWithLockingInformation(fileName))
/// {
/// // RethrowWithLockingInformation() has not rethrown the exception, because it
/// // didn't appear that "fileName" is locked. Rethrow ourselves - or do something
/// // else..
/// throw;
/// }
/// }
/// ]]>
/// </example>
/// <returns>
/// <c>false</c> when <paramref name="ex"/> has not been rethrown.
/// </returns>
public static bool RethrowWithLockingInformation(this Exception ex, string fileName, LockManagerFeatures features = default)
{
if (fileName == null)
throw new ArgumentNullException(nameof(fileName));
/// <summary>
/// Throws a new <see cref="IOException"/> that contains the given exception as an inner exception,
/// if the <paramref name="ex"/> is due to a file being locked. Otherwise does not throw and exception.
/// </summary>
/// <param name="ex">The exception to check and rethrow</param>
/// <param name="fileName">The file name to check for being locked.</param>
/// <param name="features">Optional features.</param>
/// <example>
/// <![CDATA[
/// string fileName = "C:\\temp\\foo.txt";
/// try
/// {
/// var file2 = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite);
/// }
/// catch (Exception ex)
/// {
/// if (!ex.RethrowWithLockingInformation(fileName))
/// {
/// // RethrowWithLockingInformation() has not rethrown the exception, because it
/// // didn't appear that "fileName" is locked. Rethrow ourselves - or do something
/// // else..
/// throw;
/// }
/// }
/// ]]>
/// </example>
/// <returns>
/// <c>false</c> when <paramref name="ex"/> has not been rethrown.
/// </returns>
public static bool RethrowWithLockingInformation(this Exception ex, string fileName, LockManagerFeatures features = default)
{
if (fileName == null)
throw new ArgumentNullException(nameof(fileName));

return RethrowWithLockingInformation(ex, [fileName], features);
}
return RethrowWithLockingInformation(ex, [fileName], features);
}

/// <summary>
/// Throws a new <see cref="IOException"/> that contains the given exception as an inner exception,
/// if the <paramref name="ex"/> is due to a file being locked. Otherwise does not throw and exception.
/// </summary>
/// <param name="ex">The exception to check and rethrow</param>
/// <param name="fileNames">The file names to check for being locked.</param>
/// <param name="features">Optional features.</param>
/// <param name="maxProcesses">Maximum number of processes to include in the output.
/// If this value is <c>null</c> an internal default will be applied.
/// If this value is <c>-1</c> all found processes will be output.</param>
/// <example>
/// <![CDATA[
/// string fileName = "C:\\temp\\foo.txt";
/// try
/// {
/// var file2 = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite);
/// }
/// catch (Exception ex)
/// {
/// if (!ex.RethrowWithLockingInformation(fileName))
/// {
/// // RethrowWithLockingInformation() has not rethrown the exception, because it
/// // didn't appear that "fileName" is locked. Rethrow ourselves - or do something
/// // else..
/// throw;
/// }
/// }
/// ]]>
/// </example>
/// <returns>
/// <c>false</c> when <paramref name="ex"/> has not been rethrown.
/// </returns>
public static bool RethrowWithLockingInformation(this Exception ex, string[] fileNames, LockManagerFeatures features = default, int? maxProcesses = null)
{
if (fileNames == null)
throw new ArgumentNullException(nameof(fileNames));
/// <summary>
/// Throws a new <see cref="IOException"/> that contains the given exception as an inner exception,
/// if the <paramref name="ex"/> is due to a file being locked. Otherwise does not throw and exception.
/// </summary>
/// <param name="ex">The exception to check and rethrow</param>
/// <param name="fileNames">The file names to check for being locked.</param>
/// <param name="features">Optional features.</param>
/// <param name="maxProcesses">Maximum number of processes to include in the output.
/// If this value is <c>null</c> an internal default will be applied.
/// If this value is <c>-1</c> all found processes will be output.</param>
/// <example>
/// <![CDATA[
/// string fileName = "C:\\temp\\foo.txt";
/// try
/// {
/// var file2 = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite);
/// }
/// catch (Exception ex)
/// {
/// if (!ex.RethrowWithLockingInformation(fileName))
/// {
/// // RethrowWithLockingInformation() has not rethrown the exception, because it
/// // didn't appear that "fileName" is locked. Rethrow ourselves - or do something
/// // else..
/// throw;
/// }
/// }
/// ]]>
/// </example>
/// <returns>
/// <c>false</c> when <paramref name="ex"/> has not been rethrown.
/// </returns>
public static bool RethrowWithLockingInformation(this Exception ex, string[] fileNames, LockManagerFeatures features = default, int? maxProcesses = null)
{
if (fileNames == null)
throw new ArgumentNullException(nameof(fileNames));

if (fileNames.Length > 0)
if (fileNames.Length > 0)
{
if (ex is IOException ioEx && ioEx.IsFileLocked())
{
if (ex is IOException ioEx && ioEx.IsFileLocked())
{
// It is a race to get the lockers, while they are still there. So do this as early as possible.
var lockers = LockManager.GetLockingProcessInfos(fileNames, features);
// It is a race to get the lockers, while they are still there. So do this as early as possible.
var lockers = LockManager.GetLockingProcessInfos(fileNames, features);

if (lockers.Any())
{
// Alter behavior of Format(). It would return int.MaxValue on null also.
// Here we want to return a baked in default in this case.
int max = maxProcesses == -1 ? int.MaxValue : maxProcesses.GetValueOrDefault(10);
var sb = new StringBuilder();
sb.Append(ex.Message);
sb.Append(' ');
ProcessInfo.Format(sb, lockers, fileNames, max);
if (lockers.Any())
{
// Alter behavior of Format(). It would return int.MaxValue on null also.
// Here we want to return a baked in default in this case.
int max = maxProcesses == -1 ? int.MaxValue : maxProcesses.GetValueOrDefault(10);
var sb = new StringBuilder();
sb.Append(ex.Message);
sb.Append(' ');
ProcessInfo.Format(sb, lockers, fileNames, max);

var exception = new IOException(sb.ToString(), ex);
var exception = new IOException(sb.ToString(), ex);
#if NETFRAMEWORK
s_setErrorCodeMethod.Value?.Invoke(exception, [ex.HResult]);
s_setErrorCodeMethod.Value?.Invoke(exception, [ex.HResult]);
#endif
#if NET
exception.HResult = ex.HResult;
exception.HResult = ex.HResult;
#endif

throw exception;
}
throw exception;
}
}
return false;
}
return false;
}
}
37 changes: 18 additions & 19 deletions src/LockCheck/IHasErrorState.cs
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
using System;

namespace LockCheck
namespace LockCheck;

/// <summary>
/// An entity that can have error state.
/// </summary>
public interface IHasErrorState
{
/// <summary>
/// An entity that can have error state.
/// Get a value that indicates if the entity is erroneous.
/// </summary>
public interface IHasErrorState
{
/// <summary>
/// Get a value that indicates if the entity is erroneous.
/// </summary>
bool HasError { get; }
bool HasError { get; }

/// <summary>
/// Set the error state of the entity.
/// </summary>
/// <remarks>
/// Implementations shall only consider the first call to this method, setting <see cref="HasError"/>
/// to <c>true</c>. Further calls to this function shall be ignored.
/// </remarks>
/// <param name="ex">An optional exception that caused the error.</param>
/// <param name="errorCode">An option error code that describes the error.</param>
void SetError(Exception? ex = null, int errorCode = 0);
}
/// <summary>
/// Set the error state of the entity.
/// </summary>
/// <remarks>
/// Implementations shall only consider the first call to this method, setting <see cref="HasError"/>
/// to <c>true</c>. Further calls to this function shall be ignored.
/// </remarks>
/// <param name="ex">An optional exception that caused the error.</param>
/// <param name="errorCode">An option error code that describes the error.</param>
void SetError(Exception? ex = null, int errorCode = 0);
}
Loading

0 comments on commit 9e6dd10

Please sign in to comment.