public void GenerateForIDistributedReaderWriterLock() { var files = CodeGenHelpers.EnumerateSolutionFiles() .Where(f => f.IndexOf("DistributedLock.Core", StringComparison.OrdinalIgnoreCase) < 0) .Where(f => Regex.IsMatch(Path.GetFileName(f), @"Distributed.*?ReaderWriterLock\.cs$", RegexOptions.IgnoreCase)); var errors = new List <string>(); foreach (var file in files) { var lockCode = File.ReadAllText(file); if (lockCode.Contains("AUTO-GENERATED") || !CodeGenHelpers.HasPublicType(lockCode, out _)) { continue; } bool isUpgradeable; if (lockCode.Contains(": IInternalDistributedUpgradeableReaderWriterLock<")) { isUpgradeable = true; } else if (lockCode.Contains(": IInternalDistributedReaderWriterLock<")) { isUpgradeable = false; } else { errors.Add($"{file} does not implement the expected interface"); continue; } var lockType = Path.GetFileNameWithoutExtension(file); var explicitImplementations = new StringBuilder(); var publicMethods = new StringBuilder(); foreach (var methodLockType in new[] { LockType.Read, LockType.Upgrade, LockType.Write }.Where(t => isUpgradeable || t != LockType.Upgrade)) { foreach (var isAsync in new[] { false, true }) { foreach (var isTry in new[] { true, false }) { var upgradeableText = methodLockType == LockType.Upgrade ? "Upgradeable" : ""; var handleType = lockType + upgradeableText + "Handle"; var methodName = $"{(isTry ? "Try" : "")}Acquire{upgradeableText}{(methodLockType == LockType.Write ? "Write" : "Read")}Lock{(isAsync ? "Async" : "")}"; AppendExplicitInterfaceMethod( explicitImplementations, $"IDistributed{upgradeableText}ReaderWriterLock", methodName, $"IDistributed{(methodLockType == LockType.Upgrade ? "LockUpgradeable" : "Synchronization")}Handle" ); var simplifiedMethodName = methodLockType == LockType.Upgrade ? methodName : methodName.Replace("ReadLock", "").Replace("WriteLock", ""); publicMethods.AppendLine() .Append(' ', 8).Append("public ") .Append(isAsync ? "ValueTask<" : "").Append(handleType).Append(isTry ? "?" : "").Append(isAsync ? ">" : "").Append(' ') .Append(methodName) .Append("(").Append("TimeSpan").Append(isTry ? "" : "?").AppendLine($" timeout = {(isTry ? "default" : "null")}, CancellationToken cancellationToken = default) =>") .Append(' ', 12) .Append( isTry && isAsync ? $"this.As<IInternalDistributed{upgradeableText}ReaderWriterLock<{(methodLockType == LockType.Upgrade ? lockType + "Handle, " : "")}{handleType}>>()" + $".Internal{simplifiedMethodName}(timeout, cancellationToken" : $"DistributedLockHelpers.{simplifiedMethodName}(this, timeout, cancellationToken" ) .Append(methodLockType == LockType.Read ? ", isWrite: false" : methodLockType == LockType.Write ? ", isWrite: true" : "") .AppendLine(");"); } } } var @namespace = Regex.Match(lockCode, @"\nnamespace (?<namespace>\S+)").Groups["namespace"].Value; var code = $@"using System; using System.Threading; using System.Threading.Tasks; using Medallion.Threading.Internal; namespace {@namespace} {{ public partial class {lockType} {{ // AUTO-GENERATED {explicitImplementations}{publicMethods} }} }}"; code = DocCommentGenerator.AddDocComments(code); var outputPath = Path.Combine(Path.GetDirectoryName(file) !, $"{Path.GetFileNameWithoutExtension(file)}.IDistributed{(isUpgradeable ? "Upgradeable" : "")}ReaderWriterLock.cs"); if (!File.Exists(outputPath) || CodeGenHelpers.NormalizeCodeWhitespace(File.ReadAllText(outputPath)) != CodeGenHelpers.NormalizeCodeWhitespace(code)) { File.WriteAllText(outputPath, code); errors.Add($"updated {file}"); } } Assert.IsEmpty(errors); }
public void GenerateForIDistributedLockAndSemaphore([Values("Lock", "Semaphore")] string name) { var files = CodeGenHelpers.EnumerateSolutionFiles() .Where(f => f.IndexOf($"Distributed{name}.Core", StringComparison.OrdinalIgnoreCase) < 0) .Where(f => f.EndsWith($"Distributed{name}.cs", StringComparison.OrdinalIgnoreCase) && Path.GetFileName(f)[0] != 'I'); var errors = new List <string>(); foreach (var file in files) { var lockCode = File.ReadAllText(file); if (lockCode.Contains("AUTO-GENERATED") || !CodeGenHelpers.HasPublicType(lockCode, out _)) { continue; } if (!lockCode.Contains($": IInternalDistributed{name}<")) { errors.Add($"{file} does not implement the expected interface"); continue; } var lockType = Path.GetFileNameWithoutExtension(file); var handleType = lockType + "Handle"; var explicitImplementations = new StringBuilder(); var @interface = $"IDistributed{name}"; foreach (var method in new[] { "TryAcquire", "Acquire", "TryAcquireAsync", "AcquireAsync" }) { AppendExplicitInterfaceMethod(explicitImplementations, @interface, method, "IDistributedSynchronizationHandle"); } var @namespace = Regex.Match(lockCode, @"\nnamespace (?<namespace>\S+)").Groups["namespace"].Value; var code = $@"using System; using System.Threading; using System.Threading.Tasks; using Medallion.Threading.Internal; namespace {@namespace} {{ public partial class {lockType} {{ // AUTO-GENERATED {explicitImplementations} public {handleType}? TryAcquire(TimeSpan timeout = default, CancellationToken cancellationToken = default) => DistributedLockHelpers.TryAcquire(this, timeout, cancellationToken); public {handleType} Acquire(TimeSpan? timeout = null, CancellationToken cancellationToken = default) => DistributedLockHelpers.Acquire(this, timeout, cancellationToken); public ValueTask<{handleType}?> TryAcquireAsync(TimeSpan timeout = default, CancellationToken cancellationToken = default) => this.As<IInternalDistributed{name}<{handleType}>>().InternalTryAcquireAsync(timeout, cancellationToken); public ValueTask<{handleType}> AcquireAsync(TimeSpan? timeout = null, CancellationToken cancellationToken = default) => DistributedLockHelpers.AcquireAsync(this, timeout, cancellationToken); }} }}"; code = DocCommentGenerator.AddDocComments(code); var outputPath = Path.Combine(Path.GetDirectoryName(file) !, Path.GetFileNameWithoutExtension(file) + $".IDistributed{name}.cs"); if (!File.Exists(outputPath) || CodeGenHelpers.NormalizeCodeWhitespace(File.ReadAllText(outputPath)) != CodeGenHelpers.NormalizeCodeWhitespace(code)) { File.WriteAllText(outputPath, code); errors.Add($"updated {file}"); } } Assert.IsEmpty(errors); }