diff --git a/.agents/prompts/refactor-source-generator-to-codewriter.prompt.md b/.agents/prompts/refactor-source-generator-to-codewriter.prompt.md
index d1bbb1f2..e230d224 100644
--- a/.agents/prompts/refactor-source-generator-to-codewriter.prompt.md
+++ b/.agents/prompts/refactor-source-generator-to-codewriter.prompt.md
@@ -19,11 +19,11 @@ Refactor the selected legacy emitter implementation from manual `string` / `Stri
### Requirements
1. Use structured declaration APIs where applicable:
- - `WriteClass/WriteStruct/WriteRecordClass/WriteInterface/WriteEnum`
- - `WriteMethod`, `WriteProperty`, `WriteField`, `WriteConstructor`
+ - `Class/Struct/RecordClass/Interface/Enum`
+ - `Method`, `Property`, `Field`, `Constructor`
2. Use XML helper extensions instead of raw `///` composition:
- `XmlSummary`, `XmlParam`, `XmlReturn`, `XmlRemarks`, `XmlCode` or `XmlCodeBlock`
-3. Use `TypeReferenceOptions` when type text becomes complex (nullability, generics, arrays).
+3. Use `TypeReference` when type text becomes complex (nullability, generics, arrays).
4. Ensure writer lifetime is output-scoped (`generationContext.CreateCodeWriter()` inside callback).
5. Preserve behavior, diagnostics, and generated names.
6. Keep changes minimal and focused; do not reformat unrelated logic.
diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
index e1c5ba67..c96c9270 100644
--- a/.github/workflows/pr.yml
+++ b/.github/workflows/pr.yml
@@ -11,21 +11,10 @@ concurrency:
jobs:
build:
name: Build and test
- runs-on: ubuntu-latest
- timeout-minutes: 30
- steps:
- - uses: actions/checkout@v7
- with:
- fetch-depth: 0
- fetch-tags: true
-
- - name: Setup .NET
- uses: actions/setup-dotnet@v6
- with:
- dotnet-version: "10.0.x"
-
- - name: Run PR pipeline
- run: dotnet run --project build/PipelineCLI/PipelineCLI.csproj --configuration Release
+ uses: purview-dev/build/.github/workflows/purview-build.yml@main
+ with:
+ build-version: "0.2.1"
+ secrets: inherit
samples:
name: Build and test samples
@@ -42,13 +31,8 @@ jobs:
with:
dotnet-version: "10.0.x"
- # This is so the analyzer projects can be used in the sample app projects.
- # The analyzer projects are not referenced by the sample app projects, the assemblies are, so they need to be built first.
- - name: Run pipeline build
- run: dotnet run --project build/PipelineCLI/PipelineCLI.csproj --configuration Release -- --Build:RunTests=false --Release:Mode=None
-
- name: Build SampleApp
run: dotnet build samples/SampleApp/SampleApp.slnx --configuration Release
- name: Test SampleApp
- run: dotnet test samples/SampleApp/SampleApp.slnx --configuration Release --no-build
+ run: dotnet test samples/SampleApp/SampleApp.slnx --configuration Release --no-build
\ No newline at end of file
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 1396ecfe..e48c62c0 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -6,48 +6,14 @@ on:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
- cancel-in-progress: true
+ cancel-in-progress: false
jobs:
release:
name: Release packages
- runs-on: ubuntu-latest
- timeout-minutes: 30
- permissions:
- contents: write
- steps:
- - uses: actions/checkout@v7
- with:
- fetch-depth: 0
- fetch-tags: true
-
- - name: Setup .NET
- uses: actions/setup-dotnet@v6
- with:
- dotnet-version: "10.0.x"
-
- - name: Check for version bump
- id: version
- shell: bash
- run: |
- VERSION=$(node -p "require('./package.json').version")
- TAG="v$VERSION"
- if git rev-parse "$TAG" >/dev/null 2>&1; then
- echo "Version $VERSION is already tagged as $TAG. Skipping release."
- echo "should_publish=false" >> "$GITHUB_OUTPUT"
- else
- echo "New version $VERSION detected. Releasing $TAG."
- echo "should_publish=true" >> "$GITHUB_OUTPUT"
- echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- echo "tag=$TAG" >> "$GITHUB_OUTPUT"
- fi
-
- - name: Run release pipeline
- if: steps.version.outputs.should_publish == 'true'
- env:
- Release__ShouldPublish: true
- Release__Mode: NuGet
- Build__RunPack: true
- NuGet__ApiKey: ${{ secrets.NUGET__APIKEY }}
- GITHUB_TOKEN: ${{ github.token }}
- run: dotnet run --project build/PipelineCLI/PipelineCLI.csproj --configuration Release
+ uses: purview-dev/build/.github/workflows/purview-release.yml@main
+ with:
+ build-version: "0.2.1"
+ release-mode: NuGet
+ release-branch: main
+ secrets: inherit
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index a2b88be1..a9370176 100644
--- a/.gitignore
+++ b/.gitignore
@@ -197,4 +197,5 @@ node_modules/
# BenchmarkDotNet output
BenchmarkDotNet.Artifacts/!scripts/*
-!build/
\ No newline at end of file
+!build/
+.tools/
diff --git a/Directory.Packages.props b/Directory.Packages.props
index ece5b75c..c6d17db5 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -10,16 +10,9 @@
Consumers on net48 or net8+ built with VS 2022 17.14+ or .NET 10+ SDK are fully supported. -->
4.14.0
1.65.51
- 1.0.0-prerelease.30
- 3.2.8
+ 1.0.0-prerelease.33
-
-
-
-
-
-
diff --git a/Justfile b/Justfile
index cb8b8d78..977113cf 100644
--- a/Justfile
+++ b/Justfile
@@ -5,8 +5,9 @@ solution_file := root_folder + "Telemetry.SourceGenerator.slnx"
test_solution := solution_file
build_configuration := "Release"
-pipeline_solution := "build/Pipeline.slnx"
-pipeline_project := "build/PipelineCLI/PipelineCLI.csproj"
+pipeline_version := "0.2.1"
+pipeline_feed := "https://api.nuget.org/v3/index.json"
+pipeline_tool := ".tools/purview-build/purview-build"
sample_solution_file := "./samples/SampleApp/SampleApp.slnx"
artifact_folder := "./artifacts/"
@@ -18,38 +19,50 @@ benchmark_solution := "./benchmarks/Purview.Telemetry.Benchmarks/Purview.Telemet
default:
just --list
+# Install the shared Purview.Build tool (authenticated to the Purview-Dev feed) if not present
+[private]
+ensure-pipeline-tool:
+ if [ ! -x "{{ pipeline_tool }}" ]; then \
+ dotnet tool install Purview.Build --tool-path .tools/purview-build --add-source "{{ pipeline_feed }}" --version "{{ pipeline_version }}"; \
+ fi
+
# Run the PR pipeline (restore, build, lint, tests)
[group('Pipeline')]
pipeline-pr *args:
+ just ensure-pipeline-tool
echo "Running PR pipeline..."
- dotnet run --project {{ pipeline_project }} --configuration {{ build_configuration }} {{ args }}
+ "{{ pipeline_tool }}" {{ args }}
# Run the build pipeline (restore, build, lint)
[group('Pipeline')]
pipeline-build *args:
+ just ensure-pipeline-tool
echo "Running build pipeline..."
- dotnet run --project {{ pipeline_project }} --configuration {{ build_configuration }} -- --Build:RunTests=false --Release:Mode=None {{ args }}
+ "{{ pipeline_tool }}" --Build:RunTests=false --Release:Mode=None {{ args }}
# Run the release pipeline (restore, build, lint, tests, pack, publish, GitHub release)
[group('Pipeline')]
pipeline-release *args:
+ just ensure-pipeline-tool
echo "Running release pipeline..."
- dotnet run --project {{ pipeline_project }} --configuration {{ build_configuration }} -- --Release:Mode=NuGet {{ args }}
+ "{{ pipeline_tool }}" --Release:Mode=NuGet {{ args }}
# Run the release pipeline (restore, build, lint, tests, pack, local nuget publish)
# Note: `just` runs recipes through the shell, which strips backslashes from unquoted arguments.
-# Always use forward slashes for the feed path, e.g.
+# Use the LOCAL_NUGET_FEED_PATH environment variable or forward slashes, e.g.
# just pipeline-local-release --PublishLocalNuGet:LocalFeedPath=p:/_sync-projects/.local-nuget/
[group('Pipeline')]
pipeline-local-release *args:
+ just ensure-pipeline-tool
echo "Running local release pipeline..."
- dotnet run --project {{ pipeline_project }} --configuration {{ build_configuration }} -- --Release:Mode=LocalNuGet {{ args }}
+ "{{ pipeline_tool }}" --Release:Mode=LocalNuGet {{ args }}
# Run the pipeline with tests enabled
[group('Pipeline')]
pipeline-tests *args:
+ just ensure-pipeline-tool
echo "Running tests pipeline..."
- dotnet run --project {{ pipeline_project }} --configuration {{ build_configuration }} -- --Build:RunTests=true --Release:Mode=None {{ args }}
+ "{{ pipeline_tool }}" --Build:RunTests=true --Release:Mode=None {{ args }}
# -----------------------------------------------------------------------------
# Build and Test
@@ -156,11 +169,6 @@ vs:
echo "Opening {{ BLUE }}{{ solution_file }}{{ NORMAL }}..."
open "{{ solution_file }}"
-# Open the solution in Visual Studio/ Registered application
-[group('Utilities')]
-vs-pipeline:
- open {{ pipeline_solution }}
-
# Opens the root folder in Visual Studio Code
[group('System/ Shell')]
diff --git a/README.md b/README.md
index 337da4cb..0fbfdaad 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
Generates [`ActivitySource`](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.activitysource), [`ILogger`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.ilogger), and [`Metrics`](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.metrics) based telemetry from methods you define on an interface.
-[](https://github.com/purview-dev/telemetry-sourcegenerator/actions/workflows/ci.yml)
+[](https://github.com/purview-dev/telemetry-sourcegenerator/actions/workflows/release.yml)
## Features
diff --git a/build/Directory.Build.props b/build/Directory.Build.props
deleted file mode 100644
index f89239e0..00000000
--- a/build/Directory.Build.props
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
- Purview.Telemetry.SourceGenerator
- true
-
-
-
-
-
- $(NoWarn);CA1062;CA1515;CA2007;CA1873;
-
-
diff --git a/build/Directory.Build.targets b/build/Directory.Build.targets
deleted file mode 100644
index a3bbd31a..00000000
--- a/build/Directory.Build.targets
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/build/Pipeline.slnx b/build/Pipeline.slnx
deleted file mode 100644
index 410fbba4..00000000
--- a/build/Pipeline.slnx
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/build/PipelineCLI/GlobalUsings.cs b/build/PipelineCLI/GlobalUsings.cs
deleted file mode 100644
index 992ea97f..00000000
--- a/build/PipelineCLI/GlobalUsings.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-global using Microsoft.Extensions.Configuration;
-global using Microsoft.Extensions.DependencyInjection;
-global using Microsoft.Extensions.Logging;
-global using Microsoft.Extensions.Options;
-global using ModularPipelines;
-global using ModularPipelines.Extensions;
-global using Octokit;
-global using Octokit.Internal;
-global using Purview.Telemetry.SourceGenerator.PipelineCLI.Helpers;
-global using Purview.Telemetry.SourceGenerator.PipelineCLI.Modules;
-global using Purview.Telemetry.SourceGenerator.PipelineCLI.Settings;
diff --git a/build/PipelineCLI/Helpers/DotNetCLIOptions.cs b/build/PipelineCLI/Helpers/DotNetCLIOptions.cs
deleted file mode 100644
index 0fbf697c..00000000
--- a/build/PipelineCLI/Helpers/DotNetCLIOptions.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using ModularPipelines.Options;
-
-namespace Purview.Telemetry.SourceGenerator.PipelineCLI.Helpers;
-
-public sealed record DotNetCLIOptions : CommandLineToolOptions
-{
- public static DotNetCLIOptions Create(params string[] commandParts) =>
- new() { Tool = "dotnet", CommandParts = commandParts };
-}
diff --git a/build/PipelineCLI/Helpers/PathHelpers.cs b/build/PipelineCLI/Helpers/PathHelpers.cs
deleted file mode 100644
index be5485e5..00000000
--- a/build/PipelineCLI/Helpers/PathHelpers.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-namespace Purview.Telemetry.SourceGenerator.PipelineCLI.Helpers;
-
-static class PathHelpers
-{
- public static string FindRepositoryRoot(string? startDirectory = null)
- {
- if (string.IsNullOrEmpty(startDirectory))
- startDirectory = PipelineProjectDirectory.Find();
-
- DirectoryInfo? directory = new(startDirectory);
- while (directory is not null)
- {
- if (File.Exists(Path.Combine(directory.FullName, "package.json")))
- return directory.FullName;
-
- directory = directory.Parent;
- }
-
- throw new InvalidOperationException("Could not locate the repository root (no package.json found).");
- }
-}
diff --git a/build/PipelineCLI/Helpers/TestHelpers.cs b/build/PipelineCLI/Helpers/TestHelpers.cs
deleted file mode 100644
index 0ae4f0d6..00000000
--- a/build/PipelineCLI/Helpers/TestHelpers.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-namespace Purview.Telemetry.SourceGenerator.PipelineCLI.Helpers;
-
-static class TestHelpers
-{
- public static string BuildTUnitTreeNodeFilter(
- string? assembly = null,
- string? @namespace = null,
- string? className = null,
- string? testNameQuery = null
- )
- {
- var filter = "/";
- filter += assembly switch
- {
- null => "*",
- _ => assembly,
- };
-
- filter += @namespace switch
- {
- null => "*",
- _ => @namespace,
- };
-
- filter += className switch
- {
- null => "*",
- _ => className,
- };
-
- filter += testNameQuery switch
- {
- null => "*",
- _ => testNameQuery,
- };
-
- return filter;
- }
-}
diff --git a/build/PipelineCLI/Modules/BuildModule.cs b/build/PipelineCLI/Modules/BuildModule.cs
deleted file mode 100644
index ecca6c5d..00000000
--- a/build/PipelineCLI/Modules/BuildModule.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using ModularPipelines.Attributes;
-using ModularPipelines.Context;
-using ModularPipelines.DotNet.Extensions;
-using ModularPipelines.Models;
-using ModularPipelines.Modules;
-
-namespace Purview.Telemetry.SourceGenerator.PipelineCLI.Modules;
-
-[ModuleCategory("Build")]
-[DependsOn]
-public class BuildModule(IOptions settings) : Module
-{
- protected override async Task ExecuteAsync(
- IModuleContext context,
- CancellationToken cancellationToken
- )
- {
- return await context
- .DotNet()
- .Build(
- new()
- {
- ProjectSolution = settings.Value.Solution,
- Configuration = settings.Value.Configuration,
- NoRestore = true,
- },
- cancellationToken: cancellationToken
- );
- }
-}
diff --git a/build/PipelineCLI/Modules/CreateGitHubReleaseModule.cs b/build/PipelineCLI/Modules/CreateGitHubReleaseModule.cs
deleted file mode 100644
index 97aa0502..00000000
--- a/build/PipelineCLI/Modules/CreateGitHubReleaseModule.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using ModularPipelines.Attributes;
-using ModularPipelines.Configuration;
-using ModularPipelines.Context;
-using ModularPipelines.GitHub.Extensions;
-using ModularPipelines.Models;
-using ModularPipelines.Modules;
-
-namespace Purview.Telemetry.SourceGenerator.PipelineCLI.Modules;
-
-[ModuleCategory("Release")]
-[DependsOn]
-[DependsOn]
-public class CreateGitHubReleaseModule(IOptions releaseSettings, IOptions gitSettings)
- : Module
-{
- protected override ModuleConfiguration Configure() =>
- ModuleConfiguration
- .Create()
- .WithSkipWhen(_ =>
- releaseSettings.Value.Mode is not (ReleaseMode.NuGet or ReleaseMode.GitHubRelease)
- || string.IsNullOrWhiteSpace(gitSettings.Value.GetGitHubToken())
- ? SkipDecision.Skip(
- "GitHub release creation is disabled. Set Release__Mode=NuGet (or GitHubRelease) and GITHUB_TOKEN to create a GitHub release."
- )
- : SkipDecision.DoNotSkip
- )
- .Build();
-
- protected override async Task ExecuteAsync(IModuleContext context, CancellationToken cancellationToken)
- {
- var versionResult = await context.GetModule();
- var version =
- versionResult.ValueOrDefault
- ?? throw new InvalidOperationException("The version was not produced by the version module.");
-
- var tag = $"v{version}";
-
- var repositoryIdString = context.GitHub().EnvironmentVariables.RepositoryId;
- if (!long.TryParse(repositoryIdString, out var repositoryId))
- {
- throw new InvalidOperationException(
- $"Failed to parse RepositoryId '{repositoryIdString}' as a valid long integer."
- );
- }
-
- // Create a new release on GitHub with the specified tag and generate release notes
- return await context
- .GitHub()
- .Client.Repository.Release.Create(
- repositoryId,
- new NewRelease(tag) { Name = tag, GenerateReleaseNotes = true }
- );
- }
-}
diff --git a/build/PipelineCLI/Modules/LintModule.cs b/build/PipelineCLI/Modules/LintModule.cs
deleted file mode 100644
index 8d18f15d..00000000
--- a/build/PipelineCLI/Modules/LintModule.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using ModularPipelines.Attributes;
-using ModularPipelines.Configuration;
-using ModularPipelines.Context;
-using ModularPipelines.DotNet.Extensions;
-using ModularPipelines.Models;
-using ModularPipelines.Modules;
-
-namespace Purview.Telemetry.SourceGenerator.PipelineCLI.Modules;
-
-[ModuleCategory("Build")]
-public sealed class LintModule(IOptions settings) : Module
-{
- protected override ModuleConfiguration Configure() =>
- ModuleConfiguration
- .Create()
- .WithSkipWhen(_ =>
- settings.Value.RunLint
- ? SkipDecision.DoNotSkip
- : SkipDecision.Skip("Linting is disabled. Set Build__RunLint=true to enable it.")
- )
- .Build();
-
- protected override async Task ExecuteAsync(
- IModuleContext context,
- CancellationToken cancellationToken
- )
- {
- var repositoryRoot = PathHelpers.FindRepositoryRoot();
- var dotnet = context.DotNet();
- var restoreResult = await dotnet.Tool.Restore(
- new() { Interactive = false, ToolManifest = Path.Combine(repositoryRoot, ".config", "dotnet-tools.json") },
- new() { WorkingDirectory = repositoryRoot },
- cancellationToken
- );
- if (restoreResult.ExitCode != 0)
- return restoreResult;
-
- // Restore worked, now run the linter
- return await context.Shell.Command.ExecuteCommandLineTool(
- DotNetCLIOptions.Create("tool", "run", "csharpier", "check", repositoryRoot),
- new() { WorkingDirectory = repositoryRoot },
- cancellationToken: cancellationToken
- );
- }
-}
diff --git a/build/PipelineCLI/Modules/PackModule.cs b/build/PipelineCLI/Modules/PackModule.cs
deleted file mode 100644
index 779ad130..00000000
--- a/build/PipelineCLI/Modules/PackModule.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-using ModularPipelines.Attributes;
-using ModularPipelines.Configuration;
-using ModularPipelines.Context;
-using ModularPipelines.DotNet.Extensions;
-using ModularPipelines.DotNet.Options;
-using ModularPipelines.Models;
-using ModularPipelines.Modules;
-
-namespace Purview.Telemetry.SourceGenerator.PipelineCLI.Modules;
-
-[ModuleCategory("Build")]
-[DependsOn]
-[DependsOn]
-public sealed class PackModule(IOptions settings) : Module
-{
- protected override ModuleConfiguration Configure() =>
- ModuleConfiguration
- .Create()
- .WithSkipWhen(_ =>
- !settings.Value.RunPack
- ? SkipDecision.Skip("Packing is disabled. Set Build__RunPack=true to enable it.")
- : SkipDecision.DoNotSkip
- )
- .Build();
-
- protected override async Task ExecuteAsync(
- IModuleContext context,
- CancellationToken cancellationToken
- )
- {
- var versionResult = await context.GetModule();
- var nugetVersion =
- versionResult.ValueOrDefault
- ?? throw new InvalidOperationException("The version was not produced by the version module.");
-
- Directory.CreateDirectory(settings.Value.ArtifactsFolder);
-
- var version = nugetVersion.ToString();
- return await context
- .DotNet()
- .Pack(
- new DotNetPackOptions
- {
- ProjectSolution = settings.Value.Solution,
- Configuration = settings.Value.Configuration,
- Output = settings.Value.ArtifactsFolder,
- Properties = [("PackageVersion", version), ("Version", version)],
- },
- cancellationToken: cancellationToken
- );
- }
-}
diff --git a/build/PipelineCLI/Modules/PublishLocalNuGetModule.cs b/build/PipelineCLI/Modules/PublishLocalNuGetModule.cs
deleted file mode 100644
index e25949a4..00000000
--- a/build/PipelineCLI/Modules/PublishLocalNuGetModule.cs
+++ /dev/null
@@ -1,195 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-using ModularPipelines.Attributes;
-using ModularPipelines.Configuration;
-using ModularPipelines.Context;
-using ModularPipelines.Models;
-using ModularPipelines.Modules;
-using NuGet.Versioning;
-
-namespace Purview.Telemetry.SourceGenerator.PipelineCLI.Modules;
-
-[ModuleCategory("Build")]
-[DependsOn]
-public class PublishLocalNuGetModule(
- IOptions localNuGetFeedSettings,
- IOptions releaseSettings,
- IOptions buildSettings
-) : Module
-{
- protected override ModuleConfiguration Configure() =>
- ModuleConfiguration
- .Create()
- .WithSkipWhen(ctx =>
- !ctx.IsRunningLocally() || releaseSettings.Value.Mode != ReleaseMode.LocalNuGet
- ? SkipDecision.Skip(
- "Local NuGet Feed publishing is disabled. Run the pipeline locally with Release__Mode=LocalNuGet to enable it."
- )
- : SkipDecision.DoNotSkip
- )
- .Build();
-
- protected override async Task ExecuteAsync(
- IModuleContext context,
- CancellationToken cancellationToken
- )
- {
- var localFeedPath = localNuGetFeedSettings.Value.LocalFeedPath;
-
- var validationResults = new List();
- var validationContext = new ValidationContext(localNuGetFeedSettings.Value);
- if (
- !Validator.TryValidateObject(
- localNuGetFeedSettings.Value,
- validationContext,
- validationResults,
- validateAllProperties: true
- )
- )
- {
- foreach (var validationResult in validationResults)
- context.Logger.LogError("{Message}", validationResult.ErrorMessage);
-
- throw new InvalidOperationException(
- $"Invalid {nameof(PublishLocalNuGetSettings)} configuration for {nameof(PublishLocalNuGetSettings.LocalFeedPath)}. "
- + "Windows paths with backslashes may have been stripped by the shell; "
- + "use forward slashes, e.g. --PublishLocalNuGet:LocalFeedPath=p:/_sync-projects/.local-nuget/."
- );
- }
-
- var fullLocalFeedPath = Path.GetFullPath(localFeedPath);
- context.Logger.LogInformation("Publishing local NuGet packages to {LocalFeedPath}.", fullLocalFeedPath);
-
- if (!Directory.Exists(fullLocalFeedPath))
- Directory.CreateDirectory(fullLocalFeedPath);
-
- var packages = Directory
- .GetFiles(buildSettings.Value.ArtifactsFolder, "*.nupkg")
- .Concat(Directory.GetFiles(buildSettings.Value.ArtifactsFolder, "*.snupkg"))
- .ToArray();
- if (packages.Length == 0)
- {
- throw new InvalidOperationException(
- $"No packages found in {buildSettings.Value.ArtifactsFolder}. The local feed was not populated."
- );
- }
-
- List nupkgPackages = [];
- foreach (var package in packages)
- {
- var fileName = Path.GetFileName(package);
- var destinationPath = Path.Combine(fullLocalFeedPath, fileName);
-
- if (Path.GetExtension(fileName) == ".nupkg")
- nupkgPackages.Add(await ParsePackageDetailsAsync(package, cancellationToken));
-
- if (!localNuGetFeedSettings.Value.OverwriteExistingPackages && File.Exists(destinationPath))
- {
- context.Logger.LogInformation("Package {Package} already exists in local feed. Skipping.", fileName);
- File.Delete(package);
-
- continue;
- }
-
- File.Move(package, destinationPath, true);
- context.Logger.LogInformation("Copied package {Package} to local feed.", fileName);
- }
-
- if (localNuGetFeedSettings.Value.ClearPackageCache)
- {
- context.Logger.LogInformation("Clearing local NuGet package cache...");
-
- var globalPackagesResult = await context.Shell.Command.ExecuteCommandLineTool(
- DotNetCLIOptions.Create("nuget", "locals", "global-packages", "--list"),
- cancellationToken: cancellationToken
- );
- if (globalPackagesResult.ExitCode != 0)
- return globalPackagesResult;
-
- var httpCacheResult = await context.Shell.Command.ExecuteCommandLineTool(
- DotNetCLIOptions.Create("nuget", "locals", "http-cache", "--list"),
- cancellationToken: cancellationToken
- );
- if (httpCacheResult.ExitCode != 0)
- return httpCacheResult;
-
- var globalPackagePaths = globalPackagesResult
- .StandardOutput.Replace("global-packages: ", "", StringComparison.Ordinal)
- .Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries)
- .Where(Directory.Exists);
-
- var httpCachePaths = httpCacheResult
- .StandardOutput.Replace("http-cache: ", "", StringComparison.Ordinal)
- .Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries)
- .Where(Directory.Exists);
-
- foreach (var artifact in nupkgPackages)
- {
-#pragma warning disable CA1308 // Normalize strings to uppercase
- var loweredPackageId = artifact.PackageId.ToLowerInvariant();
- var loweredVersion = artifact.Version.ToFullString().ToLowerInvariant();
-#pragma warning restore CA1308 // Normalize strings to uppercase
-
- foreach (var globalPath in globalPackagePaths)
- {
- var packagePath = Path.Combine(globalPath, loweredPackageId, artifact.Version.ToFullString());
- if (Directory.Exists(packagePath))
- {
- Directory.Delete(packagePath, true);
- context.Logger.LogInformation(
- "Deleted package {Package} version {Version} from global packages cache.",
- artifact.PackageId,
- artifact.Version
- );
- }
- }
- foreach (var httpCachePath in httpCachePaths)
- {
- string[] packagePaths =
- [
- Path.Combine(httpCachePath, "list_" + loweredPackageId + ".dat"),
- Path.Combine(httpCachePath, "list_" + loweredPackageId + "_index.dat"),
- Path.Combine(httpCachePath, "list_" + loweredPackageId + "_range_*.dat"),
- Path.Combine(httpCachePath, "nupkg_" + loweredPackageId + "." + loweredVersion + ".dat"),
- ];
-
- foreach (var path in packagePaths)
- {
- var directory = Path.GetDirectoryName(path);
- var pattern = Path.GetFileName(path);
- foreach (var file in Directory.EnumerateFiles(directory!, pattern, SearchOption.AllDirectories))
- {
- File.Delete(file);
- context.Logger.LogInformation(
- "Deleted package {Package} version {Version} from HTTP cache.",
- artifact.PackageId,
- artifact.Version
- );
- }
- }
- }
- }
- }
-
- if (localNuGetFeedSettings.Value.ShutdownDotnetBuilderServer)
- {
- context.Logger.LogInformation("Shutting down dotnet builder server...");
-
- return await context.Shell.Command.ExecuteCommandLineTool(
- DotNetCLIOptions.Create("build-server", "shutdown"),
- cancellationToken: cancellationToken
- );
- }
-
- return null;
- }
-
- static async Task ParsePackageDetailsAsync(string artifact, CancellationToken cancellationToken)
- {
- using var packageReader = new NuGet.Packaging.PackageArchiveReader(artifact);
- var packaging = await packageReader.GetNuspecReaderAsync(cancellationToken);
-
- return new(packaging.GetId(), packaging.GetVersion());
- }
-}
-
-record struct PackageDetails(string PackageId, NuGetVersion Version);
diff --git a/build/PipelineCLI/Modules/PublishNuGetModule.cs b/build/PipelineCLI/Modules/PublishNuGetModule.cs
deleted file mode 100644
index 43253275..00000000
--- a/build/PipelineCLI/Modules/PublishNuGetModule.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-using ModularPipelines.Attributes;
-using ModularPipelines.Configuration;
-using ModularPipelines.Context;
-using ModularPipelines.DotNet.Extensions;
-using ModularPipelines.Models;
-using ModularPipelines.Modules;
-
-namespace Purview.Telemetry.SourceGenerator.PipelineCLI.Modules;
-
-[ModuleCategory("Release")]
-[DependsOn]
-[DependsOn]
-public class PublishNuGetModule(
- IOptions buildSettings,
- IOptions nugetSettings,
- IOptions releaseSettings
-) : Module
-{
- protected override ModuleConfiguration Configure() =>
- ModuleConfiguration
- .Create()
- .WithSkipWhen(_ =>
- releaseSettings.Value.Mode != ReleaseMode.NuGet
- || string.IsNullOrWhiteSpace(nugetSettings.Value.GetNuGetAPIKey())
- ? SkipDecision.Skip(
- "NuGet publishing is disabled. Set Release__Mode=NuGet and NuGet__ApiKey (or NUGET_APIKEY) to publish packages to nuget.org."
- )
- : SkipDecision.DoNotSkip
- )
- .Build();
-
- protected override async Task ExecuteAsync(
- IModuleContext context,
- CancellationToken cancellationToken
- )
- {
- var artifactsFolder = buildSettings.Value.ArtifactsFolder;
- if (!Directory.Exists(artifactsFolder))
- {
- throw new InvalidOperationException(
- $"The artifacts folder '{artifactsFolder}' does not exist. "
- + "Ensure the pack step ran (Release__Mode must not be None) before publishing."
- );
- }
-
- var packages = Directory.EnumerateFiles(artifactsFolder, "*.nupkg", SearchOption.TopDirectoryOnly).ToList();
-
- if (packages.Count == 0)
- {
- throw new InvalidOperationException($"No NuGet packages found in {buildSettings.Value.ArtifactsFolder}.");
- }
-
- var tasks = packages.Select(package =>
- context
- .DotNet()
- .Nuget.Push(
- new()
- {
- Path = package,
- Source = nugetSettings.Value.FeedUrl,
- ApiKey = nugetSettings.Value.GetNuGetAPIKey(),
- SkipDuplicate = true,
- },
- cancellationToken: cancellationToken
- )
- );
-
- return await Task.WhenAll(tasks);
- }
-}
diff --git a/build/PipelineCLI/Modules/RestoreModule.cs b/build/PipelineCLI/Modules/RestoreModule.cs
deleted file mode 100644
index 6af1a9f2..00000000
--- a/build/PipelineCLI/Modules/RestoreModule.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using ModularPipelines.Attributes;
-using ModularPipelines.Context;
-using ModularPipelines.DotNet.Extensions;
-using ModularPipelines.DotNet.Options;
-using ModularPipelines.Models;
-using ModularPipelines.Modules;
-
-namespace Purview.Telemetry.SourceGenerator.PipelineCLI.Modules;
-
-[ModuleCategory("Build")]
-public class RestoreModule(IOptions settings) : Module
-{
- protected override async Task ExecuteAsync(
- IModuleContext context,
- CancellationToken cancellationToken
- )
- {
- return await context
- .DotNet()
- .Restore(
- new DotNetRestoreOptions { ProjectSolution = settings.Value.Solution },
- cancellationToken: cancellationToken
- );
- }
-}
diff --git a/build/PipelineCLI/Modules/RunTestsModule.cs b/build/PipelineCLI/Modules/RunTestsModule.cs
deleted file mode 100644
index ca507df2..00000000
--- a/build/PipelineCLI/Modules/RunTestsModule.cs
+++ /dev/null
@@ -1,117 +0,0 @@
-using System.Diagnostics;
-using System.Text.RegularExpressions;
-using ModularPipelines.Attributes;
-using ModularPipelines.Configuration;
-using ModularPipelines.Context;
-using ModularPipelines.DotNet.Extensions;
-using ModularPipelines.DotNet.Options;
-using ModularPipelines.Models;
-using ModularPipelines.Modules;
-
-namespace Purview.Telemetry.SourceGenerator.PipelineCLI.Modules;
-
-[ModuleCategory("Build")]
-[DependsOn]
-public class RunTestsModule(IOptions settings) : Module
-{
- protected override ModuleConfiguration Configure() =>
- ModuleConfiguration
- .Create()
- .WithSkipWhen(_ =>
- settings.Value.RunTests
- ? SkipDecision.DoNotSkip
- : SkipDecision.Skip("Tests are disabled. Set Build__RunTests=true to run them.")
- )
- .Build();
-
- protected override async Task ExecuteAsync(
- IModuleContext context,
- CancellationToken cancellationToken
- )
- {
- var testProjects = FilterTestProjects(
- Directory.EnumerateFiles("src/tests", "*Tests.csproj", SearchOption.AllDirectories).ToList(),
- settings.Value.TestProjects
- );
- if (testProjects.Count == 0)
- {
- context.Logger.LogWarning(
- "No test projects matched 'src/tests' (filter: {TestProjects}), despite tests being enabled. Skipping test execution.",
- settings.Value.TestProjects
- );
-
- return [];
- }
-
- var timings = new List<(string Project, TimeSpan Elapsed, int ExitCode)>();
-
- var tasks = testProjects.Select(async project =>
- {
- var stopwatch = Stopwatch.StartNew();
- var result = await context
- .DotNet()
- .Test(
- new DotNetTestOptions
- {
- Project = project,
- Configuration = settings.Value.Configuration,
- NoBuild = true,
- NoRestore = true,
- Arguments = ["--ignore-exit-code", "8", "--treenode-filter", settings.Value.TestFilter],
- },
- cancellationToken: cancellationToken
- );
- stopwatch.Stop();
-
- lock (timings)
- timings.Add((project, stopwatch.Elapsed, result.ExitCode));
-
- return result;
- });
-
- var results = await Task.WhenAll(tasks);
-
- context.Logger.LogInformation(
- "Test run timings:{NewLine}{Timings}",
- Environment.NewLine,
- string.Join(
- Environment.NewLine,
- timings
- .OrderByDescending(t => t.Elapsed)
- .Select(t => $" {Path.GetFileName(t.Project)}: {t.Elapsed.TotalSeconds:F1}s (exit {t.ExitCode})")
- )
- );
-
- return results;
- }
-
- static IReadOnlyList FilterTestProjects(IReadOnlyList projects, string filter)
- {
- if (string.IsNullOrWhiteSpace(filter) || filter.Trim() == "*")
- return projects;
-
- var patterns = filter
- .Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
- .Select(ToRegexPattern)
- .ToArray();
-
- return projects
- .Where(project =>
- {
- var fileName = Path.GetFileName(project);
- return patterns.Any(pattern => Regex.IsMatch(fileName, pattern, RegexOptions.IgnoreCase));
- })
- .ToList();
- }
-
- static string ToRegexPattern(string entry)
- {
- if (entry.Contains('*', StringComparison.Ordinal))
- {
- var escaped = Regex.Escape(entry);
- return "^" + escaped.Replace("\\*", ".*", StringComparison.Ordinal) + "$";
- }
-
- return "^" + Regex.Escape(entry) + "$";
- }
-}
diff --git a/build/PipelineCLI/Modules/VersionModule.cs b/build/PipelineCLI/Modules/VersionModule.cs
deleted file mode 100644
index 6aa3af2e..00000000
--- a/build/PipelineCLI/Modules/VersionModule.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.Text.Json;
-using ModularPipelines.Attributes;
-using ModularPipelines.Context;
-using ModularPipelines.Modules;
-using NuGet.Versioning;
-
-namespace Purview.Telemetry.SourceGenerator.PipelineCLI.Modules;
-
-[ModuleCategory("Build")]
-public class VersionModule : Module
-{
- protected override async Task ExecuteAsync(
- IModuleContext context,
- CancellationToken cancellationToken
- )
- {
- var packageJsonPath = Path.Combine(Environment.CurrentDirectory, "package.json");
-
- if (!File.Exists(packageJsonPath))
- throw new FileNotFoundException($"Could not find package.json at {packageJsonPath}");
-
- var packageJson = await File.ReadAllTextAsync(packageJsonPath, cancellationToken);
-
- using var document = JsonDocument.Parse(packageJson);
- var version = document.RootElement.GetProperty("version").GetString();
-
- if (string.IsNullOrWhiteSpace(version))
- throw new InvalidOperationException("The version field in package.json is missing or empty.");
-
- if (!NuGetVersion.TryParse(version, out var nugetVersion))
- throw new InvalidOperationException($"The version '{version}' in package.json is not a valid SemVer.");
-
- context.Summary.KeyValue("Version", "Package version", version);
- return nugetVersion;
- }
-}
diff --git a/build/PipelineCLI/PipelineCLI.csproj b/build/PipelineCLI/PipelineCLI.csproj
deleted file mode 100644
index 0264da81..00000000
--- a/build/PipelineCLI/PipelineCLI.csproj
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
- PreserveNewest
-
-
-
diff --git a/build/PipelineCLI/PipelineProjectDirectory.cs b/build/PipelineCLI/PipelineProjectDirectory.cs
deleted file mode 100644
index 78229629..00000000
--- a/build/PipelineCLI/PipelineProjectDirectory.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using System.Runtime.CompilerServices;
-
-namespace Purview.Telemetry.SourceGenerator.PipelineCLI;
-
-static class PipelineProjectDirectory
-{
- const string DirectoryVariable = "MODULAR_PIPELINES_DIRECTORY";
-
- public static string Find([CallerFilePath] string sourceFilePath = "")
- {
- var configuredDirectory = Environment.GetEnvironmentVariable(DirectoryVariable);
- if (!string.IsNullOrWhiteSpace(configuredDirectory))
- {
- return ValidateConfiguredDirectory(configuredDirectory);
- }
-
- var sourceDirectory = Path.GetDirectoryName(sourceFilePath);
- return IsPipelineDirectory(sourceDirectory) ? sourceDirectory! : FindFromBuildOutput();
- }
-
- static string ValidateConfiguredDirectory(string configuredDirectory)
- {
- var fullPath = Path.GetFullPath(configuredDirectory);
- return IsPipelineDirectory(fullPath)
- ? fullPath
- : throw new InvalidOperationException(
- $"{DirectoryVariable} must point to a directory containing appsettings.json and a project file."
- );
- }
-
- static string FindFromBuildOutput()
- {
- for (
- var directory = new DirectoryInfo(AppContext.BaseDirectory);
- directory is not null;
- directory = directory.Parent
- )
- {
- if (IsPipelineDirectory(directory.FullName))
- {
- return directory.FullName;
- }
- }
-
- throw new InvalidOperationException(
- $"Could not locate the pipeline project directory. Set {DirectoryVariable} to its path."
- );
- }
-
- static bool IsPipelineDirectory(string? directory) =>
- directory is not null
- && Directory.Exists(directory)
- && File.Exists(Path.Combine(directory, "appsettings.json"))
- && Directory.EnumerateFiles(directory, "*.csproj").Any();
-}
diff --git a/build/PipelineCLI/Program.cs b/build/PipelineCLI/Program.cs
deleted file mode 100644
index 5d38362a..00000000
--- a/build/PipelineCLI/Program.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-var pipelineDirectory = PipelineProjectDirectory.Find();
-var repositoryRoot = PathHelpers.FindRepositoryRoot(pipelineDirectory);
-
-var builder = Pipeline.CreateBuilder(args);
-
-builder
- .Configuration.AddJsonFile(Path.Combine(pipelineDirectory, "appsettings.json"), optional: false)
- .AddEnvironmentVariables()
- .AddCommandLine(args);
-
-builder.Services.Configure(builder.Configuration.GetSection(BuildSettings.SectionName));
-builder.Services.Configure(builder.Configuration.GetSection(NuGetSettings.SectionName));
-builder.Services.Configure(
- builder.Configuration.GetSection(PublishLocalNuGetSettings.SectionName)
-);
-builder.Services.Configure(builder.Configuration.GetSection(GitHubSettings.SectionName));
-builder.Services.Configure(builder.Configuration.GetSection(ReleaseSettings.SectionName));
-
-builder.Services.AddSingleton(serviceProvider =>
-{
- var settings = serviceProvider.GetRequiredService>();
- var accessToken = settings.Value.GetGitHubToken();
-
- return new GitHubClient(new(settings.Value.ProductHeader), new InMemoryCredentialStore(new(accessToken)));
-});
-
-Environment.CurrentDirectory = repositoryRoot;
-
-builder
- .AddModule()
- .AddModule()
- .AddModule()
- .AddModule()
- .AddModule()
- .AddModule()
- .AddModule()
- .AddModule()
- .AddModule();
-
-await using var pipeline = await builder.BuildAsync();
-
-await pipeline.RunAsync();
diff --git a/build/PipelineCLI/Properties/launchSettings.json b/build/PipelineCLI/Properties/launchSettings.json
deleted file mode 100644
index d5bbebe5..00000000
--- a/build/PipelineCLI/Properties/launchSettings.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "profiles": {
- "Run": {
- "commandName": "Project"
- },
- "Local-NuGet": {
- "commandName": "Project",
- "commandLineArgs": "--Release:Mode=LocalNuGet\r\n--PublishLocalNuGet:LocalFeedPath=p:\\_sync-projects\\.local-nuget\\"
- }
- }
-}
diff --git a/build/PipelineCLI/Settings/BuildSettings.cs b/build/PipelineCLI/Settings/BuildSettings.cs
deleted file mode 100644
index ca5beced..00000000
--- a/build/PipelineCLI/Settings/BuildSettings.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-
-namespace Purview.Telemetry.SourceGenerator.PipelineCLI.Settings;
-
-public sealed class BuildSettings
-{
- public const string SectionName = "Build";
-
- public LogLevel LogLevel { get; init; } = LogLevel.Warning;
-
- [Required(AllowEmptyStrings = false)]
- public string Solution { get; init; } = "src/Telemetry.SourceGenerator.slnx";
-
- [Required(AllowEmptyStrings = false)]
- public string Configuration { get; init; } = "Release";
-
- [Required(AllowEmptyStrings = false)]
- public string ArtifactsFolder { get; init; } = "artifacts";
-
- public bool RunTests { get; init; } = true;
-
- [Required(AllowEmptyStrings = false)]
- public string TestFilter { get; init; } = "/*/*/*/*/";
-
- ///
- /// Comma-separated list of test project file names (or glob patterns) to run.
- /// Empty or "*" runs every test project under src/tests.
- ///
- public string TestProjects { get; init; } = "*";
-
- public bool RunLint { get; init; } = true;
-
- public bool RunPack { get; init; } = true;
-}
diff --git a/build/PipelineCLI/Settings/GitHubSettings.cs b/build/PipelineCLI/Settings/GitHubSettings.cs
deleted file mode 100644
index c8cdfe55..00000000
--- a/build/PipelineCLI/Settings/GitHubSettings.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using ModularPipelines.Attributes;
-
-namespace Purview.Telemetry.SourceGenerator.PipelineCLI.Settings;
-
-public sealed record GitHubSettings
-{
- public const string SectionName = "GitHub";
-
- [SecretValue]
- public string? AccessToken { get; init; }
-
- [SecretValue]
- [ConfigurationKeyName("GITHUB_TOKEN")]
- public string? EnvAccessToken { get; init; }
-
- public string ProductHeader { get; init; } = "Purview.Telemetry.SourceGenerator.Pipeline";
-
- public string? GetGitHubToken()
- {
- if (!string.IsNullOrWhiteSpace(AccessToken))
- return AccessToken;
-
- if (!string.IsNullOrWhiteSpace(EnvAccessToken))
- return EnvAccessToken;
-
- // GitHub Actions provisions the automatic GITHUB_TOKEN as a plain environment variable.
- // The config binder keys it under the "GitHub" section (GitHub:GITHUB_TOKEN), which the
- // standard GITHUB_TOKEN env var does not map to, so read it directly as a fallback.
- var processToken = Environment.GetEnvironmentVariable("GITHUB_TOKEN");
- return string.IsNullOrWhiteSpace(processToken) ? null : processToken;
- }
-}
diff --git a/build/PipelineCLI/Settings/NuGetSettings.cs b/build/PipelineCLI/Settings/NuGetSettings.cs
deleted file mode 100644
index 3f4b475e..00000000
--- a/build/PipelineCLI/Settings/NuGetSettings.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using ModularPipelines.Attributes;
-
-namespace Purview.Telemetry.SourceGenerator.PipelineCLI.Settings;
-
-public sealed record NuGetSettings
-{
- public const string SectionName = "NuGet";
-
- [SecretValue]
- public string? APIKey { get; set; }
-
- [SecretValue]
- [ConfigurationKeyName("NUGET_APIKEY")]
- public string? EnvAPIKey { get; set; }
-
- public string FeedUrl { get; init; } = "https://api.nuget.org/v3/index.json";
-
- public string? GetNuGetAPIKey() =>
- !string.IsNullOrWhiteSpace(APIKey) ? APIKey
- : !string.IsNullOrWhiteSpace(EnvAPIKey) ? EnvAPIKey
- : null;
-}
diff --git a/build/PipelineCLI/Settings/PublishLocalNuGetSettings.cs b/build/PipelineCLI/Settings/PublishLocalNuGetSettings.cs
deleted file mode 100644
index 67cf8df5..00000000
--- a/build/PipelineCLI/Settings/PublishLocalNuGetSettings.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-
-namespace Purview.Telemetry.SourceGenerator.PipelineCLI.Settings;
-
-public sealed record PublishLocalNuGetSettings : IValidatableObject
-{
- public const string SectionName = "PublishLocalNuGet";
-
- [Required(AllowEmptyStrings = false)]
- public string LocalFeedPath { get; init; } = string.Empty;
-
- public bool OverwriteExistingPackages { get; init; } = true;
-
- public bool ShutdownDotnetBuilderServer { get; init; } = true;
-
- public bool ClearPackageCache { get; init; } = true;
-
- public IEnumerable Validate(ValidationContext validationContext)
- {
- if (string.IsNullOrWhiteSpace(LocalFeedPath))
- {
- yield return new ValidationResult("LocalFeedPath is required.", [nameof(LocalFeedPath)]);
- yield break;
- }
-
- // Path.IsPathRooted("p:foo") returns true, but a drive-relative path like "p:foo" is NOT an
- // absolute path: Path.GetFullPath resolves it against the current directory and can silently
- // copy packages to an unintended location. This is the classic signature of a Windows path whose
- // backslashes were stripped by a sh-style shell, e.g. 'p:\_sync-projects\.local-nuget\'.
- if (LocalFeedPath.Length >= 2 && LocalFeedPath[1] == ':')
- {
- var hasSeparatorAfterDrive =
- LocalFeedPath.Length >= 3
- && (
- LocalFeedPath[2] == Path.DirectorySeparatorChar
- || LocalFeedPath[2] == Path.AltDirectorySeparatorChar
- );
- if (!hasSeparatorAfterDrive)
- {
- yield return new ValidationResult(
- $"LocalFeedPath '{LocalFeedPath}' is drive-relative, not an absolute path. "
- + "This is usually caused by the shell stripping backslashes from a Windows path such as "
- + $"'p:\\_sync-projects\\.local-nuget\\'. Use forward slashes instead, e.g. "
- + "'p:/_sync-projects/.local-nuget/'.",
- [nameof(LocalFeedPath)]
- );
- yield break;
- }
- }
-
- if (!Path.IsPathRooted(LocalFeedPath))
- {
- yield return new ValidationResult(
- $"LocalFeedPath must be an absolute path. Received: '{LocalFeedPath}'.",
- [nameof(LocalFeedPath)]
- );
- yield break;
- }
-
- var root = Path.GetPathRoot(LocalFeedPath);
- if (string.IsNullOrEmpty(root))
- {
- yield return new ValidationResult(
- $"LocalFeedPath could not be parsed. Received: '{LocalFeedPath}'.",
- [nameof(LocalFeedPath)]
- );
- yield break;
- }
-
- var lastChar = root[^1];
- if (lastChar == Path.DirectorySeparatorChar || lastChar == Path.AltDirectorySeparatorChar)
- yield break;
-
- if (root.StartsWith(@"\\", StringComparison.Ordinal) || root.StartsWith("//", StringComparison.Ordinal))
- yield break;
-
- yield return new ValidationResult(
- $"LocalFeedPath must be an absolute path (e.g. 'C:\\folder' or '\\\\server\\share'). Received: '{LocalFeedPath}'.",
- [nameof(LocalFeedPath)]
- );
- }
-}
diff --git a/build/PipelineCLI/Settings/ReleaseSettings.cs b/build/PipelineCLI/Settings/ReleaseSettings.cs
deleted file mode 100644
index 77d10f6d..00000000
--- a/build/PipelineCLI/Settings/ReleaseSettings.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace Purview.Telemetry.SourceGenerator.PipelineCLI.Settings;
-
-public enum ReleaseMode
-{
- None,
-
- NuGet,
-
- GitHubRelease,
-
- LocalNuGet,
-}
-
-public sealed record ReleaseSettings
-{
- public const string SectionName = "Release";
-
- public ReleaseMode Mode { get; set; } = ReleaseMode.None;
-}
diff --git a/build/PipelineCLI/appsettings.json b/build/PipelineCLI/appsettings.json
deleted file mode 100644
index b57ed0e0..00000000
--- a/build/PipelineCLI/appsettings.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "NuGet": {
- "FeedUrl": "https://api.nuget.org/v3/index.json"
- },
- "GitHub": {
- "AccessToken": null,
- "ProductHeader": "Purview.Telemetry.SourceGenerator.Pipeline"
- },
- "Release": {
- "Mode": "None"
- }
-}
diff --git a/package.json b/package.json
index 8116fefc..67176199 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "purview-telemetry-sourcegenerator",
- "version": "5.0.0-prerelease.3",
+ "version": "5.0.0-prerelease.4",
"description": "Generates [`ActivitySource`](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.activitysource), [`ILogger`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.ilogger), and [`Metrics`](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.metrics) based on interface methods.",
"readme": "README.md",
"repository": {
diff --git a/purview-build.json b/purview-build.json
new file mode 100644
index 00000000..1f37658e
--- /dev/null
+++ b/purview-build.json
@@ -0,0 +1,11 @@
+{
+ "Build": {
+ "Solution": "src/Telemetry.SourceGenerator.slnx",
+ "TestRoot": "src/tests",
+ "TestPatterns": "*Tests.csproj",
+ "TestFilter": "/*/*/*/*/"
+ },
+ "Release": {
+ "Mode": "None"
+ }
+}
\ No newline at end of file
diff --git a/samples/SampleApp.Net48/SampleApp.Net48.ConsoleApp/SampleApp.Net48.ConsoleApp.csproj b/samples/SampleApp.Net48/SampleApp.Net48.ConsoleApp/SampleApp.Net48.ConsoleApp.csproj
index ff5bb01e..94cde4d3 100644
--- a/samples/SampleApp.Net48/SampleApp.Net48.ConsoleApp/SampleApp.Net48.ConsoleApp.csproj
+++ b/samples/SampleApp.Net48/SampleApp.Net48.ConsoleApp/SampleApp.Net48.ConsoleApp.csproj
@@ -5,20 +5,15 @@
-
+
-
-
-
-
diff --git a/samples/SampleApp.Net48/SampleApp.Net48.slnx b/samples/SampleApp.Net48/SampleApp.Net48.slnx
index 2af29d4e..bb946ae0 100644
--- a/samples/SampleApp.Net48/SampleApp.Net48.slnx
+++ b/samples/SampleApp.Net48/SampleApp.Net48.slnx
@@ -1,9 +1,17 @@
-
+
+
+
+
+
+
diff --git a/samples/SampleApp/README.md b/samples/SampleApp/README.md
index 35345f2b..13f861c0 100644
--- a/samples/SampleApp/README.md
+++ b/samples/SampleApp/README.md
@@ -117,7 +117,7 @@ public interface IWeatherAPIClientTelemetry
[Error]
[AutoCounter]
void FailedToGetForecast(Activity? activity, Exception ex,
- [ExcludeTargets(TargetsEnum.Activities)] int? count);
+ [ExcludeTargets(Targets.Activities)] int? count);
// SINGLE-TARGET: adds ActivityEvent with HTTP status details
[Event]
@@ -137,7 +137,7 @@ public interface IWeatherAPIClientTelemetry
[Event(ActivityStatusCode.Ok)]
[Debug]
void ForecastsRecieved(Activity? activity, int forecastCount,
- [ExpandEnumerable(100), ExcludeTargets(TargetsEnum.Activities)] WeatherForecast[] weatherForecasts);
+ [ExpandEnumerable(100), ExcludeTargets(Targets.Activities)] WeatherForecast[] weatherForecasts);
}
```
diff --git a/samples/SampleApp/SampleApp.APIService/SampleApp.APIService.csproj b/samples/SampleApp/SampleApp.APIService/SampleApp.APIService.csproj
index e8bd0b6a..2682e3c1 100644
--- a/samples/SampleApp/SampleApp.APIService/SampleApp.APIService.csproj
+++ b/samples/SampleApp/SampleApp.APIService/SampleApp.APIService.csproj
@@ -3,21 +3,14 @@
true
-
+
-
-
-
-
diff --git a/samples/SampleApp/SampleApp.Web/Clients/IWeatherAPIClientTelemetry.cs b/samples/SampleApp/SampleApp.Web/Clients/IWeatherAPIClientTelemetry.cs
index beca8b5d..4020b585 100644
--- a/samples/SampleApp/SampleApp.Web/Clients/IWeatherAPIClientTelemetry.cs
+++ b/samples/SampleApp/SampleApp.Web/Clients/IWeatherAPIClientTelemetry.cs
@@ -1,4 +1,4 @@
-using System.Diagnostics;
+using System.Diagnostics;
using System.Net;
using Purview.Telemetry;
@@ -17,7 +17,7 @@ public interface IWeatherAPIClientTelemetry
[Event]
[Error]
[AutoCounter]
- void FailedToGetForecast(Activity? activity, Exception ex, [ExcludeTargets(TargetsEnum.Activities)] int? count);
+ void FailedToGetForecast(Activity? activity, Exception ex, [ExcludeTargets(Targets.Activities)] int? count);
[Event]
void RequestComplete(Activity? activity, HttpStatusCode statusCode, bool isSuccessStatusCode);
@@ -34,7 +34,7 @@ public interface IWeatherAPIClientTelemetry
void ForecastsRecieved(
Activity? activity,
int forecastCount,
- [ExpandEnumerable(100), ExcludeTargets(TargetsEnum.Activities)]
+ [ExpandEnumerable(100), ExcludeTargets(Targets.Activities)]
#pragma warning disable TSG2008 // Unbounded enumeration possible
WeatherForecast[] weatherForecasts
);
diff --git a/samples/SampleApp/SampleApp.Web/SampleApp.Web.csproj b/samples/SampleApp/SampleApp.Web/SampleApp.Web.csproj
index c8d7f42f..dab79320 100644
--- a/samples/SampleApp/SampleApp.Web/SampleApp.Web.csproj
+++ b/samples/SampleApp/SampleApp.Web/SampleApp.Web.csproj
@@ -5,21 +5,14 @@
-
+
-
-
-
-
diff --git a/samples/SampleApp/SampleApp.slnx b/samples/SampleApp/SampleApp.slnx
index 331d4cd5..f752d7f9 100644
--- a/samples/SampleApp/SampleApp.slnx
+++ b/samples/SampleApp/SampleApp.slnx
@@ -4,6 +4,14 @@
+
+
+
+
+
diff --git a/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.ActivityMethods.cs b/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.ActivityMethods.cs
index f2e7c4fd..6dbb1ca6 100644
--- a/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.ActivityMethods.cs
+++ b/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.ActivityMethods.cs
@@ -49,7 +49,7 @@ out var _
var activityVariableName = "activity" + methodTarget.MethodName;
- writer.WriteAssignment(
+ writer.Assignment(
TypeLibrary.Activities.SystemDiagnostics.Activity.MakeNullable(writer),
activityVariableName,
writeValue: assignmentWriter =>
@@ -106,20 +106,23 @@ out var _
if (methodTarget.Tags.Count > 0 || methodTarget.Baggage.Count > 0)
{
- writer.NewLine().Write("if (").Write(activityVariableName).WriteLine(" != null)");
-
- using (writer.OpenBlockScope())
- {
- EmitTagsOrBaggageParameters(writer, activityVariableName, true, methodTarget, false, output);
- EmitTagsOrBaggageParameters(writer, activityVariableName, false, methodTarget, false, output);
- }
+ writer
+ .NewLine()
+ .IfBlock(
+ activityVariableName + " != null",
+ body =>
+ {
+ EmitTagsOrBaggageParameters(writer, activityVariableName, true, methodTarget, false, output);
+ EmitTagsOrBaggageParameters(writer, activityVariableName, false, methodTarget, false, output);
+ }
+ );
}
context.CancellationToken.ThrowIfCancellationRequested();
if (methodTarget.ReturnType.Similar(TypeLibrary.Activities.SystemDiagnostics.Activity))
{
- writer.WriteReturn(returnWriter =>
+ writer.Return(returnWriter =>
returnWriter.Write(activityVariableName).Write(methodTarget.ReturnType.IsNullable ? null : "!")
);
}
@@ -188,20 +191,16 @@ int kind
}
}
- writer.WriteLine(")");
+ writer.Line(")");
}
static void EmitHasListenersTest(CodeWriter writer, ActivityBasedGenerationTarget methodTarget)
{
var returnsVoid = methodTarget.ReturnType.Identity.SpecialType == SpecialType.System_Void;
- writer.Write("if (!").Write(PropertyLibrary.Activities.ActivitySourceFieldName).WriteLine(".HasListeners())");
-
- using (writer.OpenBlockScope())
- {
- writer.WriteLine(
- "return" + (returnsVoid ? null : " null" + (methodTarget.ReturnType.IsNullable ? null : "!")) + ";"
- );
- }
+ writer.IfBlock(
+ "!" + PropertyLibrary.Activities.ActivitySourceFieldName + ".HasListeners()",
+ body => body.Return(returnsVoid ? null : "null" + (methodTarget.ReturnType.IsNullable ? null : "!"))
+ );
writer.NewLine();
}
diff --git a/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.ContextMethods.cs b/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.ContextMethods.cs
index e11c1f95..0be9f6d8 100644
--- a/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.ContextMethods.cs
+++ b/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.ContextMethods.cs
@@ -52,13 +52,14 @@ out var _
EmitHasListenersTest(writer, methodTarget);
- writer.Write("if (").Write(activityVariableName).WriteLine(" != null)");
-
- using (writer.OpenBlockScope())
- {
- EmitTagsOrBaggageParameters(writer, activityVariableName, true, methodTarget, false, output);
- EmitTagsOrBaggageParameters(writer, activityVariableName, false, methodTarget, false, output);
- }
+ writer.IfBlock(
+ activityVariableName + " != null",
+ body =>
+ {
+ EmitTagsOrBaggageParameters(writer, activityVariableName, true, methodTarget, false, output);
+ EmitTagsOrBaggageParameters(writer, activityVariableName, false, methodTarget, false, output);
+ }
+ );
context.CancellationToken.ThrowIfCancellationRequested();
diff --git a/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.EventMethods.cs b/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.EventMethods.cs
index a6e33165..6752ecb7 100644
--- a/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.EventMethods.cs
+++ b/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.EventMethods.cs
@@ -58,46 +58,53 @@ out var statusDescriptionParam
EmitHasListenersTest(writer, methodTarget);
- writer.Write("if (").Write(activityVariableName).WriteLine(" != null)");
-
- using (writer.OpenBlockScope())
- {
- var exceptionParam =
- methodTarget.Parameters.FirstOrDefault(m => m.IsException)
- ?? methodTarget.Tags.FirstOrDefault(m => m.IsException);
- var tagsParameterName = EmitEventTags(writer, methodTarget, activityVariableName, tagsParam, escapeParam);
-
- var eventVariableName = "activityEvent" + methodTarget.MethodName;
-
- writer
- .NewLine()
- .Write(TypeLibrary.Activities.SystemDiagnostics.ActivityEvent)
- .Write(' ')
- .Write(eventVariableName)
- .Write(" = new ")
- // Use explicit type for C# 7.3 compatibility (target-typed new() requires C# 9+)
- .Write(TypeLibrary.Activities.SystemDiagnostics.ActivityEvent)
- .Write("(name: ")
- .Write(methodTarget.ActivityOrEventName.Wrap())
- // timestamp:
- .Write(", timestamp: ")
- .Write(timestampParam?.ParameterName ?? "default")
- // tags:
- .Write(", tags: ")
- .Write(tagsParameterName)
- .WriteLine(");");
-
- writer.NewLine().Write(activityVariableName).Write(".AddEvent(").Write(eventVariableName).WriteLine(");");
-
- if (methodTarget.Baggage.Count > 0)
+ writer.IfBlock(
+ activityVariableName + " != null",
+ body =>
{
- writer.NewLine();
+ var exceptionParam =
+ methodTarget.Parameters.FirstOrDefault(m => m.IsException)
+ ?? methodTarget.Tags.FirstOrDefault(m => m.IsException);
+ var tagsParameterName = EmitEventTags(
+ writer,
+ methodTarget,
+ activityVariableName,
+ tagsParam,
+ escapeParam
+ );
+
+ var eventVariableName = "activityEvent" + methodTarget.MethodName;
+
+ writer
+ .NewLine()
+ .Write(TypeLibrary.Activities.SystemDiagnostics.ActivityEvent)
+ .Write(' ')
+ .Write(eventVariableName)
+ .Write(" = new ")
+ // Use explicit type for C# 7.3 compatibility (target-typed new() requires C# 9+)
+ .Write(TypeLibrary.Activities.SystemDiagnostics.ActivityEvent)
+ .Write("(name: ")
+ .Write(methodTarget.ActivityOrEventName.Wrap())
+ // timestamp:
+ .Write(", timestamp: ")
+ .Write(timestampParam?.ParameterName ?? "default")
+ // tags:
+ .Write(", tags: ")
+ .Write(tagsParameterName)
+ .Line(");");
+
+ writer.NewLine().Write(activityVariableName).Write(".AddEvent(").Write(eventVariableName).Line(");");
+
+ if (methodTarget.Baggage.Count > 0)
+ {
+ writer.NewLine();
- EmitTagsOrBaggageParameters(writer, activityVariableName, false, methodTarget, false, output);
- }
+ EmitTagsOrBaggageParameters(writer, activityVariableName, false, methodTarget, false, output);
+ }
- EmitSetStatus(writer, methodTarget, activityVariableName, statusDescriptionParam, exceptionParam);
- }
+ EmitSetStatus(writer, methodTarget, activityVariableName, statusDescriptionParam, exceptionParam);
+ }
+ );
context.CancellationToken.ThrowIfCancellationRequested();
@@ -131,7 +138,7 @@ static string EmitEventTags(
if (tagsParam != null)
writer.Write(tagsParam.ParameterName);
- writer.WriteLine(");");
+ writer.Line(");");
var useRecordedExceptionRules =
methodTarget.EventAttribute?.UseRecordExceptionRules
@@ -151,12 +158,12 @@ void EmitTag()
{
if (methodTarget.ActivityOrEventName == PropertyLibrary.Activities.Tag_ExceptionEventName)
{
- writer.Write("if (").Write(tagParam.ParameterName).WriteLine(" != null)");
- using (writer.OpenBlockScope())
- {
- // We want the details inside of the current event.
- EmitExceptionParam(writer, tagsListVariableName, escapeValue, tagParam.ParameterName);
- }
+ writer.IfBlock(
+ tagParam.ParameterName + " != null",
+ body =>
+ // We want the details inside of the current event.
+ EmitExceptionParam(writer, tagsListVariableName, escapeValue, tagParam.ParameterName)
+ );
}
else
{
@@ -171,17 +178,16 @@ void EmitTag()
.Write(tagParam.ParameterName)
.Write(", escape: ")
.Write(escapeValue)
- .WriteLine(");");
+ .Line(");");
}
else
{
- writer
- .Write(tagsListVariableName)
- .Write(".Add(")
- .Write(tagParam.GeneratedName.Wrap())
- .Write(", ")
- .Write(tagParam.ParameterName)
- .WriteLine(".ToString());");
+ writer.MethodCallOn(
+ tagsListVariableName,
+ "Add",
+ tagParam.GeneratedName.Wrap(),
+ tagParam.ParameterName + ".ToString()"
+ );
}
}
}
@@ -193,15 +199,13 @@ void EmitTag()
.Write(tagParam.GeneratedName.Wrap())
.Write(", ")
.Write(tagParam.ParameterName)
- .WriteLine(");");
+ .Line(");");
}
}
if (tagParam.SkipOnNullOrEmpty)
{
- writer.Write("if (").Write(tagParam.ParameterName).WriteLine(" != default)");
- using (writer.OpenBlockScope())
- EmitTag();
+ writer.IfBlock(tagParam.ParameterName + " != default", _ => EmitTag());
}
else
{
@@ -247,6 +251,6 @@ static void EmitSetStatus(
}
}
- writer.WriteLine(");");
+ writer.Line(");");
}
}
diff --git a/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.Fields.cs b/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.Fields.cs
index e0b97dc8..a33399a7 100644
--- a/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.Fields.cs
+++ b/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.Fields.cs
@@ -20,15 +20,19 @@ static void EmitFields(ActivityOutputContext output, CodeWriter writer, SourcePr
}
writer
- .Write("readonly static ")
- .Write(TypeLibrary.Activities.SystemDiagnostics.ActivitySource)
- .Write(' ')
- .Write(PropertyLibrary.Activities.ActivitySourceFieldName)
- .Write(" = new ")
- .Write(TypeLibrary.Activities.SystemDiagnostics.ActivitySource)
- .Write('(')
- .Write(activitySourceName!.Wrap())
- .WriteLine(");")
+ .Field(
+ new FieldDeclarationOptions(
+ PropertyLibrary.Activities.ActivitySourceFieldName,
+ TypeLibrary.Activities.SystemDiagnostics.ActivitySource.AsTypeReference()
+ )
+ {
+ IsStatic = true,
+ IsReadOnly = true,
+ Initializer =
+ $"new {(string)TypeLibrary.Activities.SystemDiagnostics.ActivitySource}({activitySourceName!.Wrap()})",
+ IncludeGeneratedAttributes = false,
+ }
+ )
.NewLine();
}
}
diff --git a/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.Methods.cs b/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.Methods.cs
index 45134e33..2e83ee00 100644
--- a/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.Methods.cs
+++ b/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.Methods.cs
@@ -45,33 +45,38 @@ SourceProductionContext context
output.Context.Debug($"Generating {PropertyLibrary.Activities.RecordExceptionMethodName}.");
- writer
- .Write("static void ")
- .Write(PropertyLibrary.Activities.RecordExceptionMethodName)
- .Write('(')
- .Write(TypeLibrary.Activities.SystemDiagnostics.Activity.MakeNullable(writer))
- .Write(" activity, ")
- .Write(TypeLibrary.System.Exception.MakeNullable(writer))
- .Write(" exception, ")
- .Write(PurviewTypeLibrary.System.Boolean)
- .WriteLine(" escape)");
-
- using (writer.OpenBlockScope())
+ using (
+ writer.MethodScope(
+ new MethodDeclarationOptions(
+ PropertyLibrary.Activities.RecordExceptionMethodName,
+ PurviewTypeLibrary.System.Void.AsTypeReference()
+ )
+ {
+ IsStatic = true,
+ Parameters =
+ [
+ new ParameterDeclarationOptions(
+ "activity",
+ TypeLibrary.Activities.SystemDiagnostics.Activity.MakeNullable(writer)
+ ),
+ new ParameterDeclarationOptions("exception", TypeLibrary.System.Exception.MakeNullable(writer)),
+ new ParameterDeclarationOptions("escape", PurviewTypeLibrary.System.Boolean.AsTypeReference()),
+ ],
+ IncludeGeneratedAttributes = false,
+ }
+ )
+ )
{
- writer.WriteLine("if (activity == null || exception == null)");
- using (writer.OpenBlockScope())
- writer.WriteLine("return;");
+ writer.IfBlock("activity == null || exception == null", static body => body.Return());
writer.NewLine();
const string tagsListVariableName = "tagsCollection";
- writer
- .Write(TypeLibrary.Activities.SystemDiagnostics.ActivityTagsCollection)
- .Write(' ')
- .Write(tagsListVariableName)
- .Write(" = new ")
- .Write(TypeLibrary.Activities.SystemDiagnostics.ActivityTagsCollection)
- .WriteLine("();");
+ writer.Assignment(
+ TypeLibrary.Activities.SystemDiagnostics.ActivityTagsCollection,
+ tagsListVariableName,
+ "new " + TypeLibrary.Activities.SystemDiagnostics.ActivityTagsCollection + "()"
+ );
EmitExceptionParam(writer, tagsListVariableName, "escape", "exception");
@@ -92,9 +97,9 @@ SourceProductionContext context
// tags:
.Write(", tags: ")
.Write(tagsListVariableName)
- .WriteLine(");");
+ .Line(");");
- writer.NewLine().Write("activity.AddEvent(").Write(eventVariableName).WriteLine(");");
+ writer.NewLine().Write("activity.AddEvent(").Write(eventVariableName).Line(");");
}
writer.NewLine();
@@ -113,7 +118,7 @@ string exceptionParam
.Write(PropertyLibrary.Activities.Tag_ExceptionEscaped.Wrap())
.Write(", ")
.Write(escapeParam)
- .WriteLine(");");
+ .Line(");");
writer
.Write(tagsListVariableName)
@@ -121,15 +126,14 @@ string exceptionParam
.Write(PropertyLibrary.Activities.Tag_ExceptionMessage.Wrap())
.Write(", ")
.Write(exceptionParam)
- .WriteLine(".Message);");
+ .Line(".Message);");
- writer
- .Write(tagsListVariableName)
- .Write(".Add(")
- .Write(PropertyLibrary.Activities.Tag_ExceptionType.Wrap())
- .Write(", ")
- .Write(exceptionParam)
- .WriteLine(".GetType().FullName);");
+ writer.MethodCallOn(
+ tagsListVariableName,
+ "Add",
+ PropertyLibrary.Activities.Tag_ExceptionType.Wrap(),
+ exceptionParam + ".GetType().FullName"
+ );
writer
.Write(tagsListVariableName)
@@ -137,40 +141,39 @@ string exceptionParam
.Write(PropertyLibrary.Activities.Tag_ExceptionStackTrace.Wrap())
.Write(", ")
.Write(exceptionParam)
- .WriteLine(".StackTrace);");
+ .Line(".StackTrace);");
}
static void EmitThrowStub(CodeWriter writer, ActivityBasedGenerationTarget methodTarget)
{
- writer.NewLine().Write("public ").Write(methodTarget.ReturnType);
-
- writer.Write(' ').Write(methodTarget.MethodName);
-
- if (methodTarget.TypeParameters.Count > 0)
- {
- writer.Write('<');
- for (var i = 0; i < methodTarget.TypeParameters.Count; i++)
- {
- if (i > 0)
- writer.Write(", ");
- writer.Write(methodTarget.TypeParameters[i]);
- }
- writer.Write('>');
- }
-
- writer.Write('(');
+ writer.NewLine();
- for (var i = 0; i < methodTarget.Parameters.Count; i++)
+ using (
+ writer.MethodScope(
+ new MethodDeclarationOptions(
+ methodTarget.MethodName,
+ methodTarget.ReturnType,
+ TypeDeclarationAccessibility.Public
+ )
+ {
+ Parameters =
+ [
+ .. methodTarget.Parameters.Select(p => new ParameterDeclarationOptions(
+ p.ParameterName,
+ p.ParameterType
+ )),
+ ],
+ GenericTypes = [.. methodTarget.TypeParameters],
+ ExpressionBody = "throw new global::System.NotSupportedException()",
+ IncludeGeneratedAttributes = false,
+ }
+ )
+ )
{
- if (i > 0)
- writer.Write(", ");
- writer
- .Write(methodTarget.Parameters[i].ParameterType)
- .Write(' ')
- .Write(methodTarget.Parameters[i].ParameterName);
+ //
}
- writer.WriteLine(") => throw new global::System.NotSupportedException();").NewLine();
+ writer.NewLine();
}
static void EmitMethod(
@@ -233,7 +236,7 @@ SourceProductionContext context
context.CancellationToken.ThrowIfCancellationRequested();
using (
- writer.WriteMethodScope(
+ writer.MethodScope(
new MethodDeclarationOptions(
privateMethodName,
methodTarget.ReturnType,
@@ -277,7 +280,7 @@ SourceProductionContext context
writer.NewLine();
using (
- writer.WriteMethodScope(
+ writer.MethodScope(
new MethodDeclarationOptions(
methodTarget.MethodName,
methodTarget.ReturnType,
@@ -325,24 +328,24 @@ SourceProductionContext context
.Write(methodTarget.MethodName)
.Write("_Activity(")
.Write(paramList)
- .WriteLine(");");
+ .Line(");");
}
else
{
- writer.Write(methodTarget.MethodName).Write("_Activity(").Write(paramList).WriteLine(");");
+ writer.Write(methodTarget.MethodName).Write("_Activity(").Write(paramList).Line(");");
}
}
// Call Logging private method
if (methodTargets.HasFlag(GenerationType.Logging))
{
- writer.Write(methodTarget.MethodName).Write("_Logging(").Write(loggingMetricsParamList).WriteLine(");");
+ writer.Write(methodTarget.MethodName).Write("_Logging(").Write(loggingMetricsParamList).Line(");");
}
// Call Metrics private method
if (methodTargets.HasFlag(GenerationType.Metrics))
{
- writer.Write(methodTarget.MethodName).Write("_Metrics(").Write(loggingMetricsParamList).WriteLine(");");
+ writer.Write(methodTarget.MethodName).Write("_Metrics(").Write(loggingMetricsParamList).Line(");");
}
// Return result if applicable
@@ -365,7 +368,7 @@ SourceProductionContext context
)
{
using (
- writer.WriteMethodScope(
+ writer.MethodScope(
new(methodTarget.MethodName, methodTarget.ReturnType, TypeDeclarationAccessibility.Public)
{
Parameters =
diff --git a/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.Parameters.cs b/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.Parameters.cs
index d727b80b..ad937fa0 100644
--- a/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.Parameters.cs
+++ b/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.Parameters.cs
@@ -44,7 +44,7 @@ void EmitParameter(ActivityBasedParameterTarget param)
writer.Write(".ToString()");
}
- writer.WriteLine(");");
+ writer.Line(");");
}
void EmitParameters()
@@ -56,9 +56,7 @@ void EmitParameters()
if (param.SkipOnNullOrEmpty)
{
- writer.Write("if (").Write(param.ParameterName).WriteLine(" != default)");
- using (writer.OpenBlockScope())
- EmitParameter(param);
+ writer.IfBlock(param.ParameterName + " != default", _ => EmitParameter(param));
}
else
{
@@ -69,9 +67,7 @@ void EmitParameters()
if (checkForNullableActivity)
{
- writer.NewLine().Write("if (").Write(activityVariableName).WriteLine(" != null)");
- using (writer.OpenBlockScope())
- EmitParameters();
+ writer.NewLine().IfBlock(activityVariableName + " != null", _ => EmitParameters());
}
else
{
diff --git a/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.cs b/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.cs
index 7d5ebdab..80994982 100644
--- a/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.cs
+++ b/src/src/SourceGenerator/Emitters/ActivitySourceTargetClassEmitter.cs
@@ -13,7 +13,7 @@ public static void GenerateImplementation(ActivityOutputContext output, SourcePr
output.Context.Debug($"Generating activity class for: {target.FullyQualifiedName}");
var writer = output.CreateWriter();
- using (writer.WriteBlockNamespaceScope(target.ClassNamespace))
+ using (writer.BlockNamespaceScope(target.ClassNamespace))
{
List parentScopes = [];
if (target.TelemetryGeneration.TelemetryNamesNamespace == null)
@@ -21,7 +21,7 @@ public static void GenerateImplementation(ActivityOutputContext output, SourcePr
foreach (var parent in target.ParentClasses)
{
parentScopes.Add(
- writer.WriteClassScope(new(parent) { IsSealed = false, IncludeGeneratedAttributes = false })
+ writer.ClassScope(new(parent) { IsSealed = false, IncludeGeneratedAttributes = false })
);
}
}
diff --git a/src/src/SourceGenerator/Emitters/ConstructorEmitter.cs b/src/src/SourceGenerator/Emitters/ConstructorEmitter.cs
index 847b77e5..214f98d8 100644
--- a/src/src/SourceGenerator/Emitters/ConstructorEmitter.cs
+++ b/src/src/SourceGenerator/Emitters/ConstructorEmitter.cs
@@ -30,7 +30,7 @@ GenerationContext generationContext
}
writer.NewLine();
- writer.WriteConstructor(
+ writer.Constructor(
new ConstructorDeclarationOptions(classNameToGenerate, TypeDeclarationAccessibility.Public)
{
Parameters = BuildParameters(generationType, interfaceType, generationContext),
diff --git a/src/src/SourceGenerator/Emitters/DependencyInjectionClassEmitter.cs b/src/src/SourceGenerator/Emitters/DependencyInjectionClassEmitter.cs
index 1f92fa7a..e0e8b41d 100644
--- a/src/src/SourceGenerator/Emitters/DependencyInjectionClassEmitter.cs
+++ b/src/src/SourceGenerator/Emitters/DependencyInjectionClassEmitter.cs
@@ -49,16 +49,16 @@ GenerationContext generationContext
// When the DI class is placed in a custom namespace (TelemetryNamesNamespace), the
// AddSingleton extension method is no longer in scope, so import it explicitly.
if (attribute.TelemetryNamesNamespace != null)
- writer.WriteUsing(PropertyLibrary.DependencyInjection.DependencyInjectionNamespace);
+ writer.Using(PropertyLibrary.DependencyInjection.DependencyInjectionNamespace);
using (
- writer.WriteBlockNamespaceScope(
+ writer.BlockNamespaceScope(
attribute.TelemetryNamesNamespace ?? PropertyLibrary.DependencyInjection.DependencyInjectionNamespace
)
)
{
using (
- writer.WriteClassScope(
+ writer.ClassScope(
new(classNameToGenerate!, classAccessibility)
{
IsStatic = true,
@@ -102,8 +102,14 @@ CancellationToken token
generationContext.Debug($"Emitting DI method for {interfaceName}.");
+ writer.XmlSummary(
+ $"Registers the generated {XmlSee("global::" + interfaceType.RenderFullName)} implementation with the service collection."
+ );
+ writer.XmlParam("services", "The service collection to register the telemetry implementation with.");
+ writer.XmlReturn("The service collection, for chaining.");
+
using (
- writer.WriteMethodScope(
+ writer.MethodScope(
new MethodDeclarationOptions(
"Add" + methodName,
TypeLibrary.DependencyInjection.IServiceCollection,
@@ -123,13 +129,14 @@ CancellationToken token
)
)
{
- writer
- .Write("return services.AddSingleton<")
- .Write(interfaceType.RenderFullName)
- .Write(", ")
- .Write("global::")
- .Write(BuildImplQualifiedName(telemetryNamesNamespace, interfaceType, className))
- .WriteLine(">();");
+ writer.Return(
+ "services.AddSingleton<"
+ + interfaceType.RenderFullName
+ + ", "
+ + "global::"
+ + BuildImplQualifiedName(telemetryNamesNamespace, interfaceType, className)
+ + ">()"
+ );
}
}
diff --git a/src/src/SourceGenerator/Emitters/EmitterHelpers.cs b/src/src/SourceGenerator/Emitters/EmitterHelpers.cs
index 0c9eb888..7eba1d59 100644
--- a/src/src/SourceGenerator/Emitters/EmitterHelpers.cs
+++ b/src/src/SourceGenerator/Emitters/EmitterHelpers.cs
@@ -43,7 +43,7 @@ TypeDeclarationAccessibility accessibility
if (SharedHelpers.ShouldEmitClassAttributes(requestingType, generationType))
attributes = [EditorBrowsableAttribute()];
- return writer.WriteClassScope(
+ return writer.ClassScope(
new TypeDeclarationOptions(className, accessibility)
{
IncludeGeneratedAttributes = false,
diff --git a/src/src/SourceGenerator/Emitters/GeneratedTypesEmitter.cs b/src/src/SourceGenerator/Emitters/GeneratedTypesEmitter.cs
index 3469e521..ee35661d 100644
--- a/src/src/SourceGenerator/Emitters/GeneratedTypesEmitter.cs
+++ b/src/src/SourceGenerator/Emitters/GeneratedTypesEmitter.cs
@@ -11,55 +11,157 @@ namespace Purview.Telemetry.SourceGenerator.Emitters;
///
static class GeneratedTypesEmitter
{
- static readonly Dictionary> Emitters = new()
+ static IEnumerable<(TypeIdentity Type, Action Emitter)> GetEmitters()
{
// Telemetry Shared
- [TypeLibrary.TelemetryShared.TagAttribute] = WriteTagLikeAttribute,
- [TypeLibrary.TelemetryShared.ExcludeAttribute] = (writer, type) =>
- WriteSimpleAttribute(writer, type, AttributeTargets.Method, includeSuppressMessage: false),
- [TypeLibrary.TelemetryShared.TelemetryGenerationAttribute] = WriteTelemetryGenerationAttribute,
- [TypeLibrary.TelemetryShared.Targets] = WriteTargetsEnum,
- [TypeLibrary.TelemetryShared.NamingConvention] = WriteNamingConventionEnum,
- [TypeLibrary.TelemetryShared.ExcludeTargetsAttribute] = WriteExcludeTargetsAttribute,
+ yield return (
+ TypeLibrary.TelemetryShared.TagAttribute,
+ (writer, type) =>
+ WriteTagLikeAttribute(writer, type, "Marks a parameter as a tag for an activity, event or instrument.")
+ );
+
+ yield return (
+ TypeLibrary.TelemetryShared.ExcludeAttribute,
+ (writer, type) =>
+ WriteSimpleAttribute(
+ writer,
+ type,
+ AttributeTargets.Method,
+ includeSuppressMessage: false,
+ summary: "Marks a method to be excluded from telemetry generation."
+ )
+ );
+ yield return (TypeLibrary.TelemetryShared.TelemetryGenerationAttribute, WriteTelemetryGenerationAttribute);
+ yield return (TypeLibrary.TelemetryShared.Targets, WriteTargetsEnum);
+ yield return (TypeLibrary.TelemetryShared.NamingConvention, WriteNamingConventionEnum);
+ yield return (TypeLibrary.TelemetryShared.ExcludeTargetsAttribute, WriteExcludeTargetsAttribute);
// Activities
- [TypeLibrary.Activities.BaggageAttribute] = WriteTagLikeAttribute,
- [TypeLibrary.Activities.ActivitySourceGenerationAttribute] = WriteActivitySourceGenerationAttribute,
- [TypeLibrary.Activities.ActivitySourceAttribute] = WriteActivitySourceAttribute,
- [TypeLibrary.Activities.ActivityAttribute] = WriteActivityAttribute,
- [TypeLibrary.Activities.EventAttribute] = WriteEventAttribute,
- [TypeLibrary.Activities.ContextAttribute] = (writer, type) =>
- WriteSimpleAttribute(writer, type, AttributeTargets.Method, includeSuppressMessage: false),
- [TypeLibrary.Activities.EscapeAttribute] = (writer, type) =>
- WriteSimpleAttribute(writer, type, AttributeTargets.Parameter, includeSuppressMessage: false),
- [TypeLibrary.Activities.StatusDescriptionAttribute] = (writer, type) =>
- WriteSimpleAttribute(writer, type, AttributeTargets.Parameter, includeSuppressMessage: false),
+ yield return (
+ TypeLibrary.Activities.BaggageAttribute,
+ (writer, type) =>
+ WriteTagLikeAttribute(writer, type, "Marks a parameter as baggage to be attached to an activity.")
+ );
+ yield return (TypeLibrary.Activities.ActivitySourceGenerationAttribute, WriteActivitySourceGenerationAttribute);
+ yield return (TypeLibrary.Activities.ActivitySourceAttribute, WriteActivitySourceAttribute);
+ yield return (TypeLibrary.Activities.ActivityAttribute, WriteActivityAttribute);
+ yield return (TypeLibrary.Activities.EventAttribute, WriteEventAttribute);
+ yield return (
+ TypeLibrary.Activities.ContextAttribute,
+ (writer, type) =>
+ WriteSimpleAttribute(
+ writer,
+ type,
+ AttributeTargets.Method,
+ includeSuppressMessage: false,
+ summary: "Marks a parameter as an activity context."
+ )
+ );
+ yield return (
+ TypeLibrary.Activities.EscapeAttribute,
+ (writer, type) =>
+ WriteSimpleAttribute(
+ writer,
+ type,
+ AttributeTargets.Parameter,
+ includeSuppressMessage: false,
+ summary: "Marks a parameter as the escape flag for a recorded exception."
+ )
+ );
+ yield return (
+ TypeLibrary.Activities.StatusDescriptionAttribute,
+ (writer, type) =>
+ WriteSimpleAttribute(
+ writer,
+ type,
+ AttributeTargets.Parameter,
+ includeSuppressMessage: false,
+ summary: "Marks a parameter as the status description of an activity or event."
+ )
+ );
// Logging
- [TypeLibrary.Logging.LoggerGenerationAttribute] = WriteLoggerGenerationAttribute,
- [TypeLibrary.Logging.LoggerAttribute] = WriteLoggerAttribute,
- [TypeLibrary.Logging.LogAttribute] = WriteLogAttribute,
- [TypeLibrary.Logging.LogPrefixType] = WriteLogPrefixTypeEnum,
- [TypeLibrary.Logging.LoggerGenerationMode] = WriteLoggerGenerationModeEnum,
- [TypeLibrary.Logging.ExpandEnumerableAttribute] = WriteExpandEnumerableAttribute,
- [TypeLibrary.Logging.TraceAttribute] = WriteSpecificLogAttribute,
- [TypeLibrary.Logging.DebugAttribute] = WriteSpecificLogAttribute,
- [TypeLibrary.Logging.InfoAttribute] = WriteSpecificLogAttribute,
- [TypeLibrary.Logging.WarningAttribute] = WriteSpecificLogAttribute,
- [TypeLibrary.Logging.ErrorAttribute] = WriteSpecificLogAttribute,
- [TypeLibrary.Logging.CriticalAttribute] = WriteSpecificLogAttribute,
+ yield return (TypeLibrary.Logging.LoggerGenerationAttribute, WriteLoggerGenerationAttribute);
+ yield return (TypeLibrary.Logging.LoggerAttribute, WriteLoggerAttribute);
+ yield return (TypeLibrary.Logging.LogAttribute, WriteLogAttribute);
+ yield return (TypeLibrary.Logging.LogPrefixType, WriteLogPrefixTypeEnum);
+ yield return (TypeLibrary.Logging.LoggerGenerationMode, WriteLoggerGenerationModeEnum);
+ yield return (TypeLibrary.Logging.ExpandEnumerableAttribute, WriteExpandEnumerableAttribute);
+ yield return (
+ TypeLibrary.Logging.TraceAttribute,
+ (writer, type) => WriteSpecificLogAttribute(writer, type, "Marks a method as a trace-level log method.")
+ );
+ yield return (
+ TypeLibrary.Logging.DebugAttribute,
+ (writer, type) => WriteSpecificLogAttribute(writer, type, "Marks a method as a debug-level log method.")
+ );
+ yield return (
+ TypeLibrary.Logging.InfoAttribute,
+ (writer, type) => WriteSpecificLogAttribute(writer, type, "Marks a method as an informational log method.")
+ );
+ yield return (
+ TypeLibrary.Logging.WarningAttribute,
+ (writer, type) => WriteSpecificLogAttribute(writer, type, "Marks a method as a warning-level log method.")
+ );
+ yield return (
+ TypeLibrary.Logging.ErrorAttribute,
+ (writer, type) => WriteSpecificLogAttribute(writer, type, "Marks a method as an error-level log method.")
+ );
+ yield return (
+ TypeLibrary.Logging.CriticalAttribute,
+ (writer, type) => WriteSpecificLogAttribute(writer, type, "Marks a method as a critical-level log method.")
+ );
// Metrics
- [TypeLibrary.Metrics.MeterGenerationAttribute] = WriteMeterGenerationAttribute,
- [TypeLibrary.Metrics.MeterAttribute] = WriteMeterAttribute,
- [TypeLibrary.Metrics.MeterNameGenerationType] = WriteMeterNameGenerationTypeEnum,
- [TypeLibrary.Metrics.InstrumentMeasurementAttribute] = (writer, type) =>
- WriteSimpleAttribute(writer, type, AttributeTargets.Parameter, includeSuppressMessage: false),
- [TypeLibrary.Metrics.AutoCounterAttribute] = WriteAutoCounterAttribute,
- [TypeLibrary.Metrics.CounterAttribute] = WriteCounterLikeAttribute,
- [TypeLibrary.Metrics.UpDownCounterAttribute] = WriteCounterLikeAttribute,
- [TypeLibrary.Metrics.HistogramAttribute] = WriteCounterLikeAttribute,
- [TypeLibrary.Metrics.ObservableCounterAttribute] = WriteObservableCounterLikeAttribute,
- [TypeLibrary.Metrics.ObservableUpDownCounterAttribute] = WriteObservableCounterLikeAttribute,
- [TypeLibrary.Metrics.ObservableGaugeAttribute] = WriteObservableCounterLikeAttribute,
- };
+ yield return (TypeLibrary.Metrics.MeterGenerationAttribute, WriteMeterGenerationAttribute);
+ yield return (TypeLibrary.Metrics.MeterAttribute, WriteMeterAttribute);
+ yield return (TypeLibrary.Metrics.MeterNameGenerationType, WriteMeterNameGenerationTypeEnum);
+ yield return (
+ TypeLibrary.Metrics.InstrumentMeasurementAttribute,
+ (writer, type) =>
+ WriteSimpleAttribute(
+ writer,
+ type,
+ AttributeTargets.Parameter,
+ includeSuppressMessage: false,
+ summary: "Marks a parameter as the measurement value of an instrument."
+ )
+ );
+ yield return (
+ TypeLibrary.Metrics.AutoCounterAttribute,
+ (writer, type) =>
+ WriteAutoCounterAttribute(writer, type, "Marks a method as an auto-incrementing counter instrument.")
+ );
+ yield return (
+ TypeLibrary.Metrics.CounterAttribute,
+ (writer, type) => WriteCounterLikeAttribute(writer, type, "Marks a method as a counter instrument.")
+ );
+ yield return (
+ TypeLibrary.Metrics.UpDownCounterAttribute,
+ (writer, type) =>
+ WriteCounterLikeAttribute(writer, type, "Marks a method as an up-down counter instrument.")
+ );
+ yield return (
+ TypeLibrary.Metrics.HistogramAttribute,
+ (writer, type) => WriteCounterLikeAttribute(writer, type, "Marks a method as a histogram instrument.")
+ );
+ yield return (
+ TypeLibrary.Metrics.ObservableCounterAttribute,
+ (writer, type) =>
+ WriteObservableCounterLikeAttribute(writer, type, "Marks a method as an observable counter instrument.")
+ );
+ yield return (
+ TypeLibrary.Metrics.ObservableUpDownCounterAttribute,
+ (writer, type) =>
+ WriteObservableCounterLikeAttribute(
+ writer,
+ type,
+ "Marks a method as an observable up-down counter instrument."
+ )
+ );
+ yield return (
+ TypeLibrary.Metrics.ObservableGaugeAttribute,
+ (writer, type) =>
+ WriteObservableCounterLikeAttribute(writer, type, "Marks a method as an observable gauge instrument.")
+ );
+ }
public static void EmitAll(IncrementalGeneratorPostInitializationContext context)
{
@@ -69,14 +171,14 @@ public static void EmitAll(IncrementalGeneratorPostInitializationContext context
context.AddEmbeddedAttributeDefinition();
var settings = GenerationSettings.Create();
- foreach (var type in TypeLibrary.GetAllGeneratedTypes())
+ foreach (var emitter in GetEmitters())
{
CodeWriter writer = new(settings);
WriteMarkerFileHeader(writer);
- Emit(writer, type);
+ emitter.Emitter(writer, emitter.Type);
- context.AddSource($"{type.MetadataFullName}.g.cs", writer);
+ context.AddSource($"{emitter.Type.MetadataFullName}.g.cs", writer);
}
}
@@ -88,22 +190,13 @@ public static void EmitAll(IncrementalGeneratorPostInitializationContext context
///
static void WriteMarkerFileHeader(CodeWriter writer)
{
- writer.WriteAutoGeneratedHeader(nullableDirective: NullableDirectiveMode.Disable);
+ writer.AutoGeneratedHeader(nullableDirective: NullableDirectiveMode.Disable);
writer
- .WriteLine("#if !NET48_OR_GREATER && !PURVIEW_TELEMETRY_NON_NULLABLE")
- .WriteLine("#nullable enable")
- .WriteLine("#endif")
- .NewLine()
- .WriteLine("#pragma warning disable CS8625")
- .NewLine();
- }
-
- static void Emit(CodeWriter writer, TypeIdentity type)
- {
- if (!Emitters.TryGetValue(type, out var emit))
- throw new ArgumentOutOfRangeException(nameof(type), type.Name, "Unknown generation type requested.");
-
- emit(writer, type);
+ .HashDefines(
+ "!NET48_OR_GREATER && !PURVIEW_TELEMETRY_NON_NULLABLE",
+ hashWriter => hashWriter.Line("#nullable enable")
+ )
+ .PragmaDisable("CS8625");
}
// -------------------------------------------------------------------------------------------
@@ -117,106 +210,131 @@ static void EmitAttribute(
AttributeTargets targets,
Action body,
bool wrapInExcludeLoggingGuard = false,
- bool includeSuppressMessage = true
+ bool includeSuppressMessage = true,
+ string? summary = null
)
{
- if (wrapInExcludeLoggingGuard)
- writer.WriteLine("#if !EXCLUDE_PURVIEW_TELEMETRY_LOGGING").NewLine();
+ using var scope = wrapInExcludeLoggingGuard
+ ? writer.HashDefinesScope("!EXCLUDE_PURVIEW_TELEMETRY_LOGGING")
+ : writer.EmptyScope();
var attributes = ImmutableArray.Empty;
attributes = attributes.Add(ConditionalAttribute());
if (includeSuppressMessage)
attributes = attributes.Add(SuppressMessageAttribute());
- writer
- .WriteFileScopedNamespace(TypeLibrary.PurviewTelemetryNamespace)
- .WriteAttributeClass(
- new(type.Name, TypeDeclarationAccessibility.Internal) { Attributes = attributes },
- targets,
- body
- );
-
- if (wrapInExcludeLoggingGuard)
- writer.WriteLine("#endif");
+ writer.FileScopedNamespace(TypeLibrary.PurviewTelemetryNamespace);
+ if (summary != null)
+ writer.XmlSummary(summary);
+ writer.AttributeClass(
+ new(type.Name, TypeDeclarationAccessibility.Internal) { Attributes = attributes },
+ targets,
+ body
+ );
}
static void WriteSimpleAttribute(
CodeWriter writer,
TypeIdentity type,
AttributeTargets targets,
- bool includeSuppressMessage
+ bool includeSuppressMessage,
+ string? summary = null
)
{
- EmitAttribute(writer, type, targets, static _ => { }, includeSuppressMessage: includeSuppressMessage);
+ EmitAttribute(
+ writer,
+ type,
+ targets,
+ static _ => { },
+ includeSuppressMessage: includeSuppressMessage,
+ summary: summary
+ );
}
static AttributeDeclarationOptions ConditionalAttribute() =>
new(new TypeIdentity("ConditionalAttribute", "System.Diagnostics"))
{
- Arguments = [new("\"PURVIEW_TELEMETRY_ATTRIBUTES\"")],
+ Arguments = [new("PURVIEW_TELEMETRY_ATTRIBUTES".Surround())],
};
static AttributeDeclarationOptions SuppressMessageAttribute() =>
new(new TypeIdentity("SuppressMessageAttribute", "System.Diagnostics.CodeAnalysis"))
{
- Arguments = [new("\"Design\""), new("\"CA1019:Define accessors for attribute arguments\"")],
+ Arguments = [new("Design".Surround()), new("CA1019:Define accessors for attribute arguments".Surround())],
};
// -------------------------------------------------------------------------------------------
// Members
// -------------------------------------------------------------------------------------------
- static void WriteEmptyConstructor(CodeWriter writer, TypeIdentity type) =>
- writer.WriteConstructor(
- new ConstructorDeclarationOptions(type.Name, TypeDeclarationAccessibility.Public),
- static _ => { }
- );
+ static void WriteEmptyConstructor(CodeWriter writer, TypeIdentity type, string? summary = null)
+ {
+ if (summary != null)
+ writer.XmlSummary(summary);
+ writer.Constructor(new(type.Name, TypeDeclarationAccessibility.Public), static _ => { });
+ }
- static void WriteNameConstructor(CodeWriter writer, TypeIdentity type) =>
- writer.WriteConstructor(
- new ConstructorDeclarationOptions(type.Name, TypeDeclarationAccessibility.Public)
+ static void WriteNameConstructor(CodeWriter writer, TypeIdentity type, string? summary = null)
+ {
+ if (summary != null)
+ {
+ writer.XmlSummary(summary);
+ writer.XmlParam("name", "The name of the telemetry entry.");
+ }
+ writer.Constructor(
+ new(type.Name, TypeDeclarationAccessibility.Public)
{
- Parameters =
- [
- new ParameterDeclarationOptions("name", PurviewTypeLibrary.System.String.AsTypeReference()),
- ],
+ Parameters = [new("name", PurviewTypeLibrary.System.String.AsTypeReference())],
},
- ctor => ctor.WriteAssignment("Name", "name")
+ ctor => ctor.Assignment("Name", "name")
);
+ }
- static void WriteMessageTemplateConstructor(CodeWriter writer, TypeIdentity type) =>
- writer.WriteConstructor(
- new ConstructorDeclarationOptions(type.Name, TypeDeclarationAccessibility.Public)
+ static void WriteMessageTemplateConstructor(CodeWriter writer, TypeIdentity type, string? summary = null)
+ {
+ if (summary != null)
+ {
+ writer.XmlSummary(summary);
+ writer.XmlParam("messageTemplate", "The message template used to generate the log message.");
+ }
+ writer.Constructor(
+ new(type.Name, TypeDeclarationAccessibility.Public)
{
- Parameters =
- [
- new ParameterDeclarationOptions(
- "messageTemplate",
- PurviewTypeLibrary.System.String.AsTypeReference()
- ),
- ],
+ Parameters = [new("messageTemplate", PurviewTypeLibrary.System.String.AsTypeReference())],
},
- ctor => ctor.WriteAssignment("MessageTemplate", "messageTemplate")
+ ctor => ctor.Assignment("MessageTemplate", "messageTemplate")
);
+ }
- static void WriteEventIdConstructor(CodeWriter writer, TypeIdentity type) =>
- writer.WriteConstructor(
- new ConstructorDeclarationOptions(type.Name, TypeDeclarationAccessibility.Public)
+ static void WriteEventIdConstructor(CodeWriter writer, TypeIdentity type, string? summary = null)
+ {
+ if (summary != null)
+ {
+ writer.XmlSummary(summary);
+ writer.XmlParam("eventId", "The event identifier of the log entry.");
+ }
+ writer.Constructor(
+ new(type.Name, TypeDeclarationAccessibility.Public)
{
Parameters = [new("eventId", PurviewTypeLibrary.System.Int32.AsTypeReference())],
},
- ctor => ctor.WriteAssignment("EventId", "eventId")
+ ctor => ctor.Assignment("EventId", "eventId")
);
+ }
/// Writes a public property with generated attributes and an optional initializer.
- static void WritePublicProperty(CodeWriter writer, string name, TypeReference type, string? initializer = null)
+ static void WritePublicProperty(
+ CodeWriter writer,
+ string name,
+ TypeReference type,
+ string? initializer = null,
+ string? summary = null
+ )
{
- writer.WriteProperty(
- new PropertyDeclarationOptions(name, type, TypeDeclarationAccessibility.Public)
- {
- HasSetter = true,
- Initializer = initializer,
- }
+ if (summary != null)
+ writer.XmlSummary(summary);
+ writer.Property(
+ new(name, type, TypeDeclarationAccessibility.Public) { HasSetter = true, Initializer = initializer }
);
}
@@ -224,40 +342,54 @@ static void WritePublicProperty(CodeWriter writer, string name, TypeReference ty
/// Writes a public nullable-capable string property inside the NET48_OR_GREATER/
/// PURVIEW_TELEMETRY_NON_NULLABLE preprocessor guard used by the marker attributes.
///
- static void WriteNullableStringProperty(CodeWriter writer, string name)
+ static void WriteNullableStringProperty(CodeWriter writer, string name, string? summary = null)
{
- writer.WriteLine("#if NET48_OR_GREATER || PURVIEW_TELEMETRY_NON_NULLABLE");
- writer.WriteProperty(
- new PropertyDeclarationOptions(
- name,
- PurviewTypeLibrary.System.String.AsTypeReference(),
- TypeDeclarationAccessibility.Public
- )
- {
- HasSetter = true,
- IncludeGeneratedAttributes = false,
- }
- );
- writer.WriteLine("#else");
- writer.WriteProperty(
- new(name, PurviewTypeLibrary.System.String.MakeNullable(writer), TypeDeclarationAccessibility.Public)
+ writer.HashDefines(
+ "NET48_OR_GREATER || PURVIEW_TELEMETRY_NON_NULLABLE",
+ hashWriter =>
{
- HasSetter = true,
- IncludeGeneratedAttributes = false,
+ if (summary != null)
+ hashWriter.XmlSummary(summary);
+ hashWriter
+ .Property(
+ new(
+ name,
+ PurviewTypeLibrary.System.String.AsTypeReference(),
+ TypeDeclarationAccessibility.Public
+ )
+ {
+ HasSetter = true,
+ IncludeGeneratedAttributes = false,
+ }
+ )
+ .HashElse()
+ .Property(
+ new(
+ name,
+ PurviewTypeLibrary.System.String.MakeNullable(writer),
+ TypeDeclarationAccessibility.Public
+ )
+ {
+ HasSetter = true,
+ IncludeGeneratedAttributes = false,
+ }
+ );
}
);
- writer.WriteLine("#endif");
}
/// Writes a public non-nullable string property (used for defaults that always have a value).
- static void WritePlainStringProperty(CodeWriter writer, string name, string? initializer = null)
+ static void WritePlainStringProperty(
+ CodeWriter writer,
+ string name,
+ string? initializer = null,
+ string? summary = null
+ )
{
- writer.WriteProperty(
- new PropertyDeclarationOptions(
- name,
- PurviewTypeLibrary.System.String.AsTypeReference(),
- TypeDeclarationAccessibility.Public
- )
+ if (summary != null)
+ writer.XmlSummary(summary);
+ writer.Property(
+ new(name, PurviewTypeLibrary.System.String.AsTypeReference(), TypeDeclarationAccessibility.Public)
{
HasSetter = true,
IncludeGeneratedAttributes = false,
@@ -270,7 +402,7 @@ static void WritePlainStringProperty(CodeWriter writer, string name, string? ini
// Shared templates
// -------------------------------------------------------------------------------------------
- static void WriteTagLikeAttribute(CodeWriter writer, TypeIdentity type)
+ static void WriteTagLikeAttribute(CodeWriter writer, TypeIdentity type, string? summary = null)
{
EmitAttribute(
writer,
@@ -278,30 +410,26 @@ static void WriteTagLikeAttribute(CodeWriter writer, TypeIdentity type)
AttributeTargets.Parameter,
body =>
{
- WriteEmptyConstructor(body, type);
- body.WriteConstructor(
- new ConstructorDeclarationOptions(type.Name, TypeDeclarationAccessibility.Public)
+ WriteEmptyConstructor(body, type, $"Constructs a new instance of the {XmlSee(type.Name)}.");
+ body.Constructor(
+ new(type.Name, TypeDeclarationAccessibility.Public)
{
- Parameters =
- [
- new ParameterDeclarationOptions(
- "skipOnNullOrEmpty",
- PurviewTypeLibrary.System.Boolean.AsTypeReference()
- ),
- ],
+ Parameters = [new("skipOnNullOrEmpty", PurviewTypeLibrary.System.Boolean.AsTypeReference())],
},
- ctor => ctor.WriteAssignment("SkipOnNullOrEmpty", "skipOnNullOrEmpty")
+ ctor => ctor.Assignment("SkipOnNullOrEmpty", "skipOnNullOrEmpty")
+ );
+ body.XmlSummary(
+ $"Constructs a new instance specifying the {XmlSee("Name")} and whether empty values are skipped."
);
- body.WriteConstructor(
- new ConstructorDeclarationOptions(type.Name, TypeDeclarationAccessibility.Public)
+ body.XmlParam("name", $"The {XmlSee("Name")}.");
+ body.XmlParam("skipOnNullOrEmpty", "Whether to skip the value when it is null or empty.");
+ body.Constructor(
+ new(type.Name, TypeDeclarationAccessibility.Public)
{
Parameters =
[
- new ParameterDeclarationOptions("name", PurviewTypeLibrary.System.String.AsTypeReference()),
- new ParameterDeclarationOptions(
- "skipOnNullOrEmpty",
- PurviewTypeLibrary.System.Boolean.AsTypeReference()
- )
+ new("name", PurviewTypeLibrary.System.String.AsTypeReference()),
+ new("skipOnNullOrEmpty", PurviewTypeLibrary.System.Boolean.AsTypeReference())
{
DefaultValue = "false",
},
@@ -309,14 +437,20 @@ static void WriteTagLikeAttribute(CodeWriter writer, TypeIdentity type)
},
ctor =>
{
- ctor.WriteAssignment("Name", "name");
- ctor.WriteAssignment("SkipOnNullOrEmpty", "skipOnNullOrEmpty");
+ ctor.Assignment("Name", "name");
+ ctor.Assignment("SkipOnNullOrEmpty", "skipOnNullOrEmpty");
}
);
- WriteNullableStringProperty(body, "Name");
- WritePublicProperty(body, "SkipOnNullOrEmpty", PurviewTypeLibrary.System.Boolean.AsTypeReference());
- }
+ WriteNullableStringProperty(body, "Name", "Optional. Gets the name of the tag or baggage value.");
+ WritePublicProperty(
+ body,
+ "SkipOnNullOrEmpty",
+ PurviewTypeLibrary.System.Boolean.AsTypeReference(),
+ summary: "Determines whether the value is skipped when it is null or empty."
+ );
+ },
+ summary: summary
);
}
@@ -330,27 +464,24 @@ static void WriteTelemetryGenerationAttribute(CodeWriter writer, TypeIdentity ty
AttributeTargets.Assembly | AttributeTargets.Interface,
body =>
{
- WriteEmptyConstructor(body, type);
- body.WriteConstructor(
- new ConstructorDeclarationOptions(type.Name, TypeDeclarationAccessibility.Public)
+ WriteEmptyConstructor(body, type, $"Constructs a new instance of the {XmlSee(type.Name)}.");
+ body.XmlSummary(
+ "Constructs a new instance specifying whether a dependency-injection extension is generated, the generated class name and the dependency-injection class name."
+ );
+ body.XmlParam("generateDependencyExtension", "Whether to generate a dependency-injection extension.");
+ body.XmlParam("className", "The name of the generated telemetry class.");
+ body.XmlParam("dependencyInjectionClassName", "The name of the generated dependency-injection class.");
+ body.Constructor(
+ new(type.Name, TypeDeclarationAccessibility.Public)
{
Parameters =
[
- new ParameterDeclarationOptions(
- "generateDependencyExtension",
- PurviewTypeLibrary.System.Boolean.AsTypeReference()
- ),
- new ParameterDeclarationOptions(
- "className",
- PurviewTypeLibrary.System.String.AsTypeReference()
- )
+ new("generateDependencyExtension", PurviewTypeLibrary.System.Boolean.AsTypeReference()),
+ new("className", PurviewTypeLibrary.System.String.AsTypeReference())
{
DefaultValue = "null",
},
- new ParameterDeclarationOptions(
- "dependencyInjectionClassName",
- PurviewTypeLibrary.System.String.AsTypeReference()
- )
+ new("dependencyInjectionClassName", PurviewTypeLibrary.System.String.AsTypeReference())
{
DefaultValue = "null",
},
@@ -358,24 +489,23 @@ static void WriteTelemetryGenerationAttribute(CodeWriter writer, TypeIdentity ty
},
ctor =>
{
- ctor.WriteAssignment("GenerateDependencyExtension", "generateDependencyExtension");
- ctor.WriteAssignment("ClassName", "className");
- ctor.WriteAssignment("DependencyInjectionClassName", "dependencyInjectionClassName");
+ ctor.Assignment("GenerateDependencyExtension", "generateDependencyExtension");
+ ctor.Assignment("ClassName", "className");
+ ctor.Assignment("DependencyInjectionClassName", "dependencyInjectionClassName");
}
);
- body.WriteConstructor(
- new ConstructorDeclarationOptions(type.Name, TypeDeclarationAccessibility.Public)
+ body.XmlSummary(
+ "Constructs a new instance specifying the generated class name and the dependency-injection class name."
+ );
+ body.XmlParam("className", "The name of the generated telemetry class.");
+ body.XmlParam("dependencyInjectionClassName", "The name of the generated dependency-injection class.");
+ body.Constructor(
+ new(type.Name, TypeDeclarationAccessibility.Public)
{
Parameters =
[
- new ParameterDeclarationOptions(
- "className",
- PurviewTypeLibrary.System.String.AsTypeReference()
- ),
- new ParameterDeclarationOptions(
- "dependencyInjectionClassName",
- PurviewTypeLibrary.System.String.AsTypeReference()
- )
+ new("className", PurviewTypeLibrary.System.String.AsTypeReference()),
+ new("dependencyInjectionClassName", PurviewTypeLibrary.System.String.AsTypeReference())
{
DefaultValue = "null",
},
@@ -383,8 +513,8 @@ static void WriteTelemetryGenerationAttribute(CodeWriter writer, TypeIdentity ty
},
ctor =>
{
- ctor.WriteAssignment("ClassName", "className");
- ctor.WriteAssignment("DependencyInjectionClassName", "dependencyInjectionClassName");
+ ctor.Assignment("ClassName", "className");
+ ctor.Assignment("DependencyInjectionClassName", "dependencyInjectionClassName");
}
);
@@ -392,30 +522,47 @@ static void WriteTelemetryGenerationAttribute(CodeWriter writer, TypeIdentity ty
body,
"GenerateDependencyExtension",
PurviewTypeLibrary.System.Boolean.AsTypeReference(),
- "true"
+ "true",
+ "Determines whether a dependency-injection extension method is generated."
+ );
+ WriteNullableStringProperty(body, "ClassName", "The name of the generated telemetry class.");
+ WriteNullableStringProperty(
+ body,
+ "DependencyInjectionClassName",
+ "The name of the generated dependency-injection class."
);
- WriteNullableStringProperty(body, "ClassName");
- WriteNullableStringProperty(body, "DependencyInjectionClassName");
WritePublicProperty(
body,
"DependencyInjectionClassIsPublic",
- PurviewTypeLibrary.System.Boolean.AsTypeReference()
+ PurviewTypeLibrary.System.Boolean.AsTypeReference(),
+ summary: "Determines whether the dependency-injection class is generated as public."
);
WritePublicProperty(
body,
"NamingConvention",
namingConvention.AsTypeReference(),
- $"{namingConvention.RenderFullName}.OpenTelemetry"
+ $"{namingConvention.RenderFullName}.OpenTelemetry",
+ "Determines the naming convention used for generated telemetry names."
);
WritePublicProperty(
body,
"GenerateTelemetryNamesClass",
PurviewTypeLibrary.System.Boolean.AsTypeReference(),
- "true"
+ "true",
+ "Determines whether a telemetry names class is generated."
);
- WriteNullableStringProperty(body, "TelemetryNamesClassName");
- WriteNullableStringProperty(body, "TelemetryNamesNamespace");
- }
+ WriteNullableStringProperty(
+ body,
+ "TelemetryNamesClassName",
+ "The name of the generated telemetry names class."
+ );
+ WriteNullableStringProperty(
+ body,
+ "TelemetryNamesNamespace",
+ "The namespace of the generated telemetry names class."
+ );
+ },
+ summary: "Specifies the telemetry generation behaviour for an interface or assembly."
);
}
@@ -430,17 +577,19 @@ static void WriteExcludeTargetsAttribute(CodeWriter writer, TypeIdentity type)
body =>
{
body.XmlSummary("Constructs a new instance with the specified targets to exclude.");
- body.WriteConstructor(
- new ConstructorDeclarationOptions(type.Name, TypeDeclarationAccessibility.Public)
+ body.XmlParam("targets", $"The {XmlSee("ExcludedTargets")}.");
+ body.Constructor(
+ new(type.Name, TypeDeclarationAccessibility.Public)
{
- Parameters = [new ParameterDeclarationOptions("targets", targets.AsTypeReference())],
+ Parameters = [new("targets", targets.AsTypeReference())],
},
- ctor => ctor.WriteAssignment("ExcludedTargets", "targets")
+ ctor => ctor.Assignment("ExcludedTargets", "targets")
);
body.XmlSummary("Gets or sets the targets to exclude for this parameter.");
WritePublicProperty(body, "ExcludedTargets", targets.AsTypeReference());
- }
+ },
+ summary: "Marks a parameter as excluded from the specified telemetry targets."
);
}
@@ -456,20 +605,24 @@ static void WriteActivitySourceGenerationAttribute(CodeWriter writer, TypeIdenti
AttributeTargets.Assembly,
body =>
{
- body.WriteConstructor(
- new ConstructorDeclarationOptions(type.Name, TypeDeclarationAccessibility.Public)
+ body.XmlSummary("Constructs a new instance specifying the activity source name and default behaviour.");
+ body.XmlParam("name", "The name of the activity source.");
+ body.XmlParam("defaultToTags", "Whether parameters are inferred as tags by default.");
+ body.XmlParam(
+ "generateDiagnosticsForMissingActivity",
+ "Whether diagnostics are generated for missing activity definitions."
+ );
+ body.Constructor(
+ new(type.Name, TypeDeclarationAccessibility.Public)
{
Parameters =
[
- new ParameterDeclarationOptions("name", PurviewTypeLibrary.System.String.AsTypeReference()),
- new ParameterDeclarationOptions(
- "defaultToTags",
- PurviewTypeLibrary.System.Boolean.AsTypeReference()
- )
+ new("name", PurviewTypeLibrary.System.String.AsTypeReference()),
+ new("defaultToTags", PurviewTypeLibrary.System.Boolean.AsTypeReference())
{
DefaultValue = "true",
},
- new ParameterDeclarationOptions(
+ new(
"generateDiagnosticsForMissingActivity",
PurviewTypeLibrary.System.Boolean.AsTypeReference()
)
@@ -480,35 +633,54 @@ static void WriteActivitySourceGenerationAttribute(CodeWriter writer, TypeIdenti
},
ctor =>
{
- ctor.WriteLine(
- "if (string.IsNullOrWhiteSpace(name)) throw new System.ArgumentNullException(nameof(name));"
+ ctor.IfBlock(
+ "string.IsNullOrWhiteSpace(name)",
+ static body => body.Throw("new System.ArgumentNullException(nameof(name))")
);
- ctor.WriteAssignment("Name", "name");
- ctor.WriteAssignment("DefaultToTags", "defaultToTags");
- ctor.WriteAssignment(
+ ctor.Assignment("Name", "name");
+ ctor.Assignment("DefaultToTags", "defaultToTags");
+ ctor.Assignment(
"GenerateDiagnosticsForMissingActivity",
"generateDiagnosticsForMissingActivity"
);
}
);
- WriteNullableStringProperty(body, "Name");
- WritePublicProperty(body, "DefaultToTags", PurviewTypeLibrary.System.Boolean.AsTypeReference(), "true");
- WriteNullableStringProperty(body, "BaggageAndTagPrefix");
- WritePlainStringProperty(body, "BaggageAndTagSeparator", "\".\"");
+ WriteNullableStringProperty(body, "Name", "The name of the activity source.");
+ WritePublicProperty(
+ body,
+ "DefaultToTags",
+ PurviewTypeLibrary.System.Boolean.AsTypeReference(),
+ "true",
+ "Determines whether parameters are inferred as tags by default."
+ );
+ WriteNullableStringProperty(
+ body,
+ "BaggageAndTagPrefix",
+ "The prefix applied to generated baggage and tag names."
+ );
+ WritePlainStringProperty(
+ body,
+ "BaggageAndTagSeparator",
+ "\".\"",
+ "The separator used between baggage and tag name parts."
+ );
WritePublicProperty(
body,
"LowercaseBaggageAndTagKeys",
PurviewTypeLibrary.System.Boolean.AsTypeReference(),
- "true"
+ "true",
+ "Determines whether baggage and tag keys are lowercased."
);
WritePublicProperty(
body,
"GenerateDiagnosticsForMissingActivity",
PurviewTypeLibrary.System.Boolean.AsTypeReference(),
- "true"
+ "true",
+ "Determines whether diagnostics are generated for missing activity definitions."
);
- }
+ },
+ summary: "Specifies the default activity source generation behaviour for an assembly."
);
}
@@ -527,10 +699,14 @@ static void WriteActivitySourceAttribute(CodeWriter writer, TypeIdentity type)
body.XmlParam("name", $"The {XmlSee("Name")}.");
WriteNameConstructor(body, type);
- WriteNullableStringProperty(body, "Name");
+ WriteNullableStringProperty(body, "Name", "Optional. Gets the name of the activity source.");
body.XmlSummary("Specifies the default when inferring between tag or baggage.");
WritePublicProperty(body, "DefaultToTags", PurviewTypeLibrary.System.Boolean.AsTypeReference(), "true");
- WriteNullableStringProperty(body, "BaggageAndTagPrefix");
+ WriteNullableStringProperty(
+ body,
+ "BaggageAndTagPrefix",
+ "The prefix applied to generated baggage and tag names."
+ );
body.XmlSummary("Determines if the name is used as a prefix.");
WritePublicProperty(
body,
@@ -545,7 +721,8 @@ static void WriteActivitySourceAttribute(CodeWriter writer, TypeIdentity type)
PurviewTypeLibrary.System.Boolean.AsTypeReference(),
"true"
);
- }
+ },
+ summary: "Marks an interface as an activity source."
);
}
@@ -559,29 +736,43 @@ static void WriteActivityAttribute(CodeWriter writer, TypeIdentity type)
AttributeTargets.Method,
body =>
{
+ body.XmlSummary($"Constructs a new instance of the {XmlSee("ActivityAttribute")}.");
WriteEmptyConstructor(body, type);
+
+ body.XmlSummary($"Constructs a new instance specifying the {XmlSee("Name")}.");
+ body.XmlParam("name", $"The {XmlSee("Name")}.");
WriteNameConstructor(body, type);
- body.WriteConstructor(
- new ConstructorDeclarationOptions(type.Name, TypeDeclarationAccessibility.Public)
+
+ body.XmlSummary($"Constructs a new instance specifying the {XmlSee("Kind")}.");
+ body.XmlParam("kind", $"The {XmlSee("Kind")}.");
+ body.Constructor(
+ new(type.Name, TypeDeclarationAccessibility.Public)
{
- Parameters = [new ParameterDeclarationOptions("kind", activityKind.AsTypeReference())],
+ Parameters = [new("kind", activityKind.AsTypeReference())],
},
- ctor => ctor.WriteAssignment("Kind", "kind")
+ ctor => ctor.Assignment("Kind", "kind")
);
- body.WriteConstructor(
- new ConstructorDeclarationOptions(type.Name, TypeDeclarationAccessibility.Public)
+
+ body.XmlSummary(
+ $"Constructs a new instance specifying the {XmlSee("Name")}, {XmlSee("Kind")} and whether the activity is created without starting it."
+ );
+ body.XmlParam("name", $"The {XmlSee("Name")}.");
+ body.XmlParam("kind", $"The {XmlSee("Kind")}.");
+ body.XmlParam(
+ "createOnly",
+ $"Whether the activity is created without starting it ({XmlSee("CreateOnly")})."
+ );
+ body.Constructor(
+ new(type.Name, TypeDeclarationAccessibility.Public)
{
Parameters =
[
- new ParameterDeclarationOptions("name", PurviewTypeLibrary.System.String.AsTypeReference()),
- new ParameterDeclarationOptions("kind", activityKind.AsTypeReference())
+ new("name", PurviewTypeLibrary.System.String.AsTypeReference()),
+ new("kind", activityKind.AsTypeReference())
{
DefaultValue = $"{activityKind.RenderFullName}.Internal",
},
- new ParameterDeclarationOptions(
- "createOnly",
- PurviewTypeLibrary.System.Boolean.AsTypeReference()
- )
+ new("createOnly", PurviewTypeLibrary.System.Boolean.AsTypeReference())
{
DefaultValue = "false",
},
@@ -589,16 +780,27 @@ static void WriteActivityAttribute(CodeWriter writer, TypeIdentity type)
},
ctor =>
{
- ctor.WriteAssignment("Name", "name");
- ctor.WriteAssignment("Kind", "kind");
- ctor.WriteAssignment("CreateOnly", "createOnly");
+ ctor.Assignment("Name", "name");
+ ctor.Assignment("Kind", "kind");
+ ctor.Assignment("CreateOnly", "createOnly");
}
);
- WriteNullableStringProperty(body, "Name");
- WritePublicProperty(body, "Kind", activityKind.AsTypeReference());
- WritePublicProperty(body, "CreateOnly", PurviewTypeLibrary.System.Boolean.AsTypeReference());
- }
+ WriteNullableStringProperty(body, "Name", "Optional. Gets the name of the activity.");
+ WritePublicProperty(
+ body,
+ "Kind",
+ activityKind.AsTypeReference(),
+ summary: "Gets the kind of the activity."
+ );
+ WritePublicProperty(
+ body,
+ "CreateOnly",
+ PurviewTypeLibrary.System.Boolean.AsTypeReference(),
+ summary: "Determines whether the activity is created without starting it."
+ );
+ },
+ summary: "Marks a method as an activity."
);
}
@@ -612,40 +814,49 @@ static void WriteEventAttribute(CodeWriter writer, TypeIdentity type)
AttributeTargets.Method,
body =>
{
- body.WriteConstructor(
- new ConstructorDeclarationOptions(type.Name, TypeDeclarationAccessibility.Public)
+ body.XmlSummary($"Constructs a new instance specifying the {XmlSee("StatusCode")}.");
+ body.XmlParam("statusCode", $"The {XmlSee("StatusCode")}.");
+ body.Constructor(
+ new(type.Name, TypeDeclarationAccessibility.Public)
{
Parameters =
[
- new ParameterDeclarationOptions("statusCode", statusCode.AsTypeReference())
+ new("statusCode", statusCode.AsTypeReference())
{
DefaultValue = $"{statusCode.RenderFullName}.Unset",
},
],
},
- ctor => ctor.WriteAssignment("StatusCode", "statusCode")
+ ctor => ctor.Assignment("StatusCode", "statusCode")
);
- body.WriteConstructor(
- new ConstructorDeclarationOptions(type.Name, TypeDeclarationAccessibility.Public)
+ body.XmlSummary(
+ $"Constructs a new instance specifying the {XmlSee("Name")}, exception handling behaviour and {XmlSee("StatusCode")}."
+ );
+ body.XmlParam("name", $"The {XmlSee("Name")}.");
+ body.XmlParam(
+ "useRecordExceptionRules",
+ $"Whether to use record exception rules ({XmlSee("UseRecordExceptionRules")})."
+ );
+ body.XmlParam(
+ "recordExceptionAsEscaped",
+ $"Whether a recorded exception is escaped ({XmlSee("RecordExceptionAsEscaped")})."
+ );
+ body.XmlParam("statusCode", $"The {XmlSee("StatusCode")}.");
+ body.Constructor(
+ new(type.Name, TypeDeclarationAccessibility.Public)
{
Parameters =
[
- new ParameterDeclarationOptions("name", PurviewTypeLibrary.System.String.AsTypeReference()),
- new ParameterDeclarationOptions(
- "useRecordExceptionRules",
- PurviewTypeLibrary.System.Boolean.AsTypeReference()
- )
+ new("name", PurviewTypeLibrary.System.String.AsTypeReference()),
+ new("useRecordExceptionRules", PurviewTypeLibrary.System.Boolean.AsTypeReference())
{
DefaultValue = "true",
},
- new ParameterDeclarationOptions(
- "recordExceptionAsEscaped",
- PurviewTypeLibrary.System.Boolean.AsTypeReference()
- )
+ new("recordExceptionAsEscaped", PurviewTypeLibrary.System.Boolean.AsTypeReference())
{
DefaultValue = "true",
},
- new ParameterDeclarationOptions("statusCode", statusCode.AsTypeReference())
+ new("statusCode", statusCode.AsTypeReference())
{
DefaultValue = $"{statusCode.RenderFullName}.Unset",
},
@@ -653,29 +864,41 @@ static void WriteEventAttribute(CodeWriter writer, TypeIdentity type)
},
ctor =>
{
- ctor.WriteAssignment("Name", "name");
- ctor.WriteAssignment("UseRecordExceptionRules", "useRecordExceptionRules");
- ctor.WriteAssignment("RecordExceptionAsEscaped", "recordExceptionAsEscaped");
- ctor.WriteAssignment("StatusCode", "statusCode");
+ ctor.Assignment("Name", "name");
+ ctor.Assignment("UseRecordExceptionRules", "useRecordExceptionRules");
+ ctor.Assignment("RecordExceptionAsEscaped", "recordExceptionAsEscaped");
+ ctor.Assignment("StatusCode", "statusCode");
}
);
- WriteNullableStringProperty(body, "Name");
+ WriteNullableStringProperty(body, "Name", "Optional. Gets the name of the event.");
WritePublicProperty(
body,
"UseRecordExceptionRules",
PurviewTypeLibrary.System.Boolean.AsTypeReference(),
- "true"
+ "true",
+ "Determines whether the default exception-handling rules are used."
);
WritePublicProperty(
body,
"RecordExceptionAsEscaped",
PurviewTypeLibrary.System.Boolean.AsTypeReference(),
- "true"
+ "true",
+ "Determines whether a recorded exception is escaped."
);
- WritePublicProperty(body, "StatusCode", statusCode.AsTypeReference());
- WriteNullableStringProperty(body, "StatusDescription");
- }
+ WritePublicProperty(
+ body,
+ "StatusCode",
+ statusCode.AsTypeReference(),
+ summary: "Gets the status code of the event."
+ );
+ WriteNullableStringProperty(
+ body,
+ "StatusDescription",
+ "Optional. Gets the status description of the event."
+ );
+ },
+ summary: "Marks a method as an activity event."
);
}
@@ -693,25 +916,39 @@ static void WriteLoggerGenerationAttribute(CodeWriter writer, TypeIdentity type)
AttributeTargets.Assembly,
body =>
{
- WriteEmptyConstructor(body, type);
- body.WriteConstructor(
- new ConstructorDeclarationOptions(type.Name, TypeDeclarationAccessibility.Public)
+ WriteEmptyConstructor(body, type, $"Constructs a new instance of the {XmlSee(type.Name)}.");
+ body.XmlSummary($"Constructs a new instance specifying the default {XmlSee("DefaultLevel")}.");
+ body.XmlParam("defaultLevel", $"The default {XmlSee("DefaultLevel")}.");
+ body.Constructor(
+ new(type.Name, TypeDeclarationAccessibility.Public)
{
- Parameters = [new ParameterDeclarationOptions("defaultLevel", logLevel.AsTypeReference())],
+ Parameters = [new("defaultLevel", logLevel.AsTypeReference())],
},
- ctor => ctor.WriteAssignment("DefaultLevel", "defaultLevel")
+ ctor => ctor.Assignment("DefaultLevel", "defaultLevel")
);
WritePublicProperty(
body,
"DefaultLevel",
logLevel.AsTypeReference(),
- $"{logLevel.RenderFullName}.Information"
+ $"{logLevel.RenderFullName}.Information",
+ "Gets or sets the default log level used by generated log methods."
+ );
+ WritePublicProperty(
+ body,
+ "GenerationMode",
+ TypeLibrary.Logging.LoggerGenerationMode.AsTypeReference(),
+ summary: "Gets or sets the log generation mode used for generated log methods."
+ );
+ WritePublicProperty(
+ body,
+ "DefaultPrefixType",
+ TypeLibrary.Logging.LogPrefixType.AsTypeReference(),
+ summary: "Gets or sets the default log prefix type used by generated log methods."
);
- WritePublicProperty(body, "GenerationMode", TypeLibrary.Logging.LoggerGenerationMode.AsTypeReference());
- WritePublicProperty(body, "DefaultPrefixType", TypeLibrary.Logging.LogPrefixType.AsTypeReference());
},
- wrapInExcludeLoggingGuard: true
+ wrapInExcludeLoggingGuard: true,
+ summary: "Specifies the default logging generation behaviour for an assembly."
);
}
@@ -726,17 +963,19 @@ static void WriteLoggerAttribute(CodeWriter writer, TypeIdentity type)
AttributeTargets.Interface,
body =>
{
- WriteEmptyConstructor(body, type);
- body.WriteConstructor(
- new ConstructorDeclarationOptions(type.Name, TypeDeclarationAccessibility.Public)
+ WriteEmptyConstructor(body, type, $"Constructs a new instance of the {XmlSee(type.Name)}.");
+ body.XmlSummary(
+ $"Constructs a new instance specifying the default {XmlSee("DefaultLevel")} and an optional custom prefix."
+ );
+ body.XmlParam("defaultLevel", $"The default {XmlSee("DefaultLevel")}.");
+ body.XmlParam("customPrefix", $"The custom log prefix ({XmlSee("CustomPrefix")}).");
+ body.Constructor(
+ new(type.Name, TypeDeclarationAccessibility.Public)
{
Parameters =
[
- new ParameterDeclarationOptions("defaultLevel", logLevel.AsTypeReference()),
- new ParameterDeclarationOptions(
- "customPrefix",
- PurviewTypeLibrary.System.String.AsTypeReference()
- )
+ new("defaultLevel", logLevel.AsTypeReference()),
+ new("customPrefix", PurviewTypeLibrary.System.String.AsTypeReference())
{
DefaultValue = "null",
},
@@ -744,11 +983,11 @@ static void WriteLoggerAttribute(CodeWriter writer, TypeIdentity type)
},
ctor =>
{
- ctor.WriteAssignment("DefaultLevel", "defaultLevel");
- ctor.WriteAssignment("CustomPrefix", "customPrefix");
- ctor.WriteIfBlock(
+ ctor.Assignment("DefaultLevel", "defaultLevel");
+ ctor.Assignment("CustomPrefix", "customPrefix");
+ ctor.IfBlock(
"!string.IsNullOrWhiteSpace(CustomPrefix)",
- block => block.WriteAssignment("PrefixType", $"{logPrefixType.RenderFullName}.Custom")
+ block => block.Assignment("PrefixType", $"{logPrefixType.RenderFullName}.Custom")
);
}
);
@@ -757,13 +996,29 @@ static void WriteLoggerAttribute(CodeWriter writer, TypeIdentity type)
body,
"DefaultLevel",
logLevel.AsTypeReference(),
- $"{logLevel.RenderFullName}.Information"
+ $"{logLevel.RenderFullName}.Information",
+ "Gets or sets the default log level used by generated log methods."
+ );
+ WriteNullableStringProperty(
+ body,
+ "CustomPrefix",
+ "Gets or sets the custom log prefix used by generated log methods."
+ );
+ WritePublicProperty(
+ body,
+ "PrefixType",
+ logPrefixType.AsTypeReference(),
+ summary: "Gets or sets the log prefix type used by generated log methods."
+ );
+ WritePublicProperty(
+ body,
+ "GenerationMode",
+ TypeLibrary.Logging.LoggerGenerationMode.AsTypeReference(),
+ summary: "Gets or sets the log generation mode used for generated log methods."
);
- WriteNullableStringProperty(body, "CustomPrefix");
- WritePublicProperty(body, "PrefixType", logPrefixType.AsTypeReference());
- WritePublicProperty(body, "GenerationMode", TypeLibrary.Logging.LoggerGenerationMode.AsTypeReference());
},
- wrapInExcludeLoggingGuard: true
+ wrapInExcludeLoggingGuard: true,
+ summary: "Marks an interface as a logger."
);
}
@@ -777,64 +1032,66 @@ static void WriteLogAttribute(CodeWriter writer, TypeIdentity type)
AttributeTargets.Method,
body =>
{
- WriteEmptyConstructor(body, type);
- WriteMessageTemplateConstructor(body, type);
- WriteEventIdConstructor(body, type);
- body.WriteConstructor(
- new ConstructorDeclarationOptions(type.Name, TypeDeclarationAccessibility.Public)
+ WriteEmptyConstructor(body, type, $"Constructs a new instance of the {XmlSee(type.Name)}.");
+ WriteMessageTemplateConstructor(
+ body,
+ type,
+ $"Constructs a new instance specifying the {XmlSee("MessageTemplate")}."
+ );
+ WriteEventIdConstructor(body, type, $"Constructs a new instance specifying the {XmlSee("EventId")}.");
+ body.XmlSummary(
+ $"Constructs a new instance specifying the {XmlSee("Level")}, optional {XmlSee("MessageTemplate")} and {XmlSee("Name")}."
+ );
+ body.XmlParam("level", $"The {XmlSee("Level")}.");
+ body.XmlParam("messageTemplate", $"The {XmlSee("MessageTemplate")}.");
+ body.XmlParam("name", $"The {XmlSee("Name")}.");
+ body.Constructor(
+ new(type.Name, TypeDeclarationAccessibility.Public)
{
Parameters =
[
- new ParameterDeclarationOptions("level", logLevel.AsTypeReference()),
- new ParameterDeclarationOptions(
- "messageTemplate",
- PurviewTypeLibrary.System.String.AsTypeReference()
- )
- {
- DefaultValue = "null",
- },
- new ParameterDeclarationOptions("name", PurviewTypeLibrary.System.String.AsTypeReference())
+ new("level", logLevel.AsTypeReference()),
+ new("messageTemplate", PurviewTypeLibrary.System.String.AsTypeReference())
{
DefaultValue = "null",
},
+ new("name", PurviewTypeLibrary.System.String.AsTypeReference()) { DefaultValue = "null" },
],
},
ctor =>
{
- ctor.WriteAssignment("Level", "level");
- ctor.WriteAssignment("MessageTemplate", "messageTemplate");
- ctor.WriteAssignment("Name", "name");
+ ctor.Assignment("Level", "level");
+ ctor.Assignment("MessageTemplate", "messageTemplate");
+ ctor.Assignment("Name", "name");
}
);
- body.WriteConstructor(
- new ConstructorDeclarationOptions(type.Name, TypeDeclarationAccessibility.Public)
+ body.XmlSummary(
+ $"Constructs a new instance specifying the {XmlSee("EventId")}, {XmlSee("Level")}, optional {XmlSee("MessageTemplate")} and {XmlSee("Name")}."
+ );
+ body.XmlParam("eventId", $"The {XmlSee("EventId")}.");
+ body.XmlParam("level", $"The {XmlSee("Level")}.");
+ body.XmlParam("messageTemplate", $"The {XmlSee("MessageTemplate")}.");
+ body.XmlParam("name", $"The {XmlSee("Name")}.");
+ body.Constructor(
+ new(type.Name, TypeDeclarationAccessibility.Public)
{
Parameters =
[
- new ParameterDeclarationOptions(
- "eventId",
- PurviewTypeLibrary.System.Int32.AsTypeReference()
- ),
- new ParameterDeclarationOptions("level", logLevel.AsTypeReference()),
- new ParameterDeclarationOptions(
- "messageTemplate",
- PurviewTypeLibrary.System.String.AsTypeReference()
- )
- {
- DefaultValue = "null",
- },
- new ParameterDeclarationOptions("name", PurviewTypeLibrary.System.String.AsTypeReference())
+ new("eventId", PurviewTypeLibrary.System.Int32.AsTypeReference()),
+ new("level", logLevel.AsTypeReference()),
+ new("messageTemplate", PurviewTypeLibrary.System.String.AsTypeReference())
{
DefaultValue = "null",
},
+ new("name", PurviewTypeLibrary.System.String.AsTypeReference()) { DefaultValue = "null" },
],
},
ctor =>
{
- ctor.WriteAssignment("Level", "level");
- ctor.WriteAssignment("MessageTemplate", "messageTemplate");
- ctor.WriteAssignment("EventId", "eventId");
- ctor.WriteAssignment("Name", "name");
+ ctor.Assignment("Level", "level");
+ ctor.Assignment("MessageTemplate", "messageTemplate");
+ ctor.Assignment("EventId", "eventId");
+ ctor.Assignment("Name", "name");
}
);
@@ -842,18 +1099,34 @@ static void WriteLogAttribute(CodeWriter writer, TypeIdentity type)
body,
"Level",
logLevel.AsTypeReference(),
- $"{logLevel.RenderFullName}.Information"
+ $"{logLevel.RenderFullName}.Information",
+ "Gets or sets the log level of the log entry."
+ );
+ WriteNullableStringProperty(
+ body,
+ "MessageTemplate",
+ "Gets or sets the message template used for the log entry."
+ );
+ WritePublicProperty(
+ body,
+ "EventId",
+ PurviewTypeLibrary.System.Int32.MakeNullable(writer),
+ summary: "Gets or sets the event identifier of the log entry."
+ );
+ WriteNullableStringProperty(body, "Name", "Gets or sets the name of the log entry.");
+ WritePublicProperty(
+ body,
+ "GenerationMode",
+ TypeLibrary.Logging.LoggerGenerationMode.AsTypeReference(),
+ summary: "Gets or sets the log generation mode used for the log entry."
);
- WriteNullableStringProperty(body, "MessageTemplate");
- WritePublicProperty(body, "EventId", PurviewTypeLibrary.System.Int32.MakeNullable(writer));
- WriteNullableStringProperty(body, "Name");
- WritePublicProperty(body, "GenerationMode", TypeLibrary.Logging.LoggerGenerationMode.AsTypeReference());
},
- wrapInExcludeLoggingGuard: true
+ wrapInExcludeLoggingGuard: true,
+ summary: "Marks a method as a log method."
);
}
- static void WriteSpecificLogAttribute(CodeWriter writer, TypeIdentity type)
+ static void WriteSpecificLogAttribute(CodeWriter writer, TypeIdentity type, string? summary = null)
{
EmitAttribute(
writer,
@@ -861,68 +1134,83 @@ static void WriteSpecificLogAttribute(CodeWriter writer, TypeIdentity type)
AttributeTargets.Method,
body =>
{
- WriteMessageTemplateConstructor(body, type);
- WriteEventIdConstructor(body, type);
- body.WriteConstructor(
- new ConstructorDeclarationOptions(type.Name, TypeDeclarationAccessibility.Public)
+ WriteMessageTemplateConstructor(
+ body,
+ type,
+ $"Constructs a new instance specifying the {XmlSee("MessageTemplate")}."
+ );
+ WriteEventIdConstructor(body, type, $"Constructs a new instance specifying the {XmlSee("EventId")}.");
+ body.XmlSummary(
+ $"Constructs a new instance specifying an optional {XmlSee("MessageTemplate")} and {XmlSee("Name")}."
+ );
+ body.XmlParam("messageTemplate", $"The {XmlSee("MessageTemplate")}.");
+ body.XmlParam("name", $"The {XmlSee("Name")}.");
+ body.Constructor(
+ new(type.Name, TypeDeclarationAccessibility.Public)
{
Parameters =
[
- new ParameterDeclarationOptions(
- "messageTemplate",
- PurviewTypeLibrary.System.String.AsTypeReference()
- )
- {
- DefaultValue = "null",
- },
- new ParameterDeclarationOptions("name", PurviewTypeLibrary.System.String.AsTypeReference())
+ new("messageTemplate", PurviewTypeLibrary.System.String.AsTypeReference())
{
DefaultValue = "null",
},
+ new("name", PurviewTypeLibrary.System.String.AsTypeReference()) { DefaultValue = "null" },
],
},
ctor =>
{
- ctor.WriteAssignment("MessageTemplate", "messageTemplate");
- ctor.WriteAssignment("Name", "name");
+ ctor.Assignment("MessageTemplate", "messageTemplate");
+ ctor.Assignment("Name", "name");
}
);
- body.WriteConstructor(
- new ConstructorDeclarationOptions(type.Name, TypeDeclarationAccessibility.Public)
+ body.XmlSummary(
+ $"Constructs a new instance specifying the {XmlSee("EventId")}, optional {XmlSee("MessageTemplate")} and {XmlSee("Name")}."
+ );
+ body.XmlParam("eventId", $"The {XmlSee("EventId")}.");
+ body.XmlParam("messageTemplate", $"The {XmlSee("MessageTemplate")}.");
+ body.XmlParam("name", $"The {XmlSee("Name")}.");
+ body.Constructor(
+ new(type.Name, TypeDeclarationAccessibility.Public)
{
Parameters =
[
- new ParameterDeclarationOptions(
- "eventId",
- PurviewTypeLibrary.System.Int32.AsTypeReference()
- ),
- new ParameterDeclarationOptions(
- "messageTemplate",
- PurviewTypeLibrary.System.String.AsTypeReference()
- )
- {
- DefaultValue = "null",
- },
- new ParameterDeclarationOptions("name", PurviewTypeLibrary.System.String.AsTypeReference())
+ new("eventId", PurviewTypeLibrary.System.Int32.AsTypeReference()),
+ new("messageTemplate", PurviewTypeLibrary.System.String.AsTypeReference())
{
DefaultValue = "null",
},
+ new("name", PurviewTypeLibrary.System.String.AsTypeReference()) { DefaultValue = "null" },
],
},
ctor =>
{
- ctor.WriteAssignment("MessageTemplate", "messageTemplate");
- ctor.WriteAssignment("EventId", "eventId");
- ctor.WriteAssignment("Name", "name");
+ ctor.Assignment("MessageTemplate", "messageTemplate");
+ ctor.Assignment("EventId", "eventId");
+ ctor.Assignment("Name", "name");
}
);
- WriteNullableStringProperty(body, "MessageTemplate");
- WritePublicProperty(body, "EventId", PurviewTypeLibrary.System.Int32.MakeNullable(writer));
- WriteNullableStringProperty(body, "Name");
- WritePublicProperty(body, "GenerationMode", TypeLibrary.Logging.LoggerGenerationMode.AsTypeReference());
+ WriteNullableStringProperty(
+ body,
+ "MessageTemplate",
+ "Gets or sets the message template used for the log entry."
+ );
+ WritePublicProperty(
+ body,
+ "EventId",
+ PurviewTypeLibrary.System.Int32.MakeNullable(writer),
+ summary: "Gets or sets the event identifier of the log entry."
+ );
+ WriteNullableStringProperty(body, "Name", "Gets or sets the name of the log entry.");
+ WritePublicProperty(
+ body,
+ "GenerationMode",
+ TypeLibrary.Logging.LoggerGenerationMode.AsTypeReference(),
+ summary: "Gets or sets the log generation mode used for the log entry."
+ );
},
- wrapInExcludeLoggingGuard: true
+ wrapInExcludeLoggingGuard: true,
+ summary: summary
);
}
@@ -934,27 +1222,32 @@ static void WriteExpandEnumerableAttribute(CodeWriter writer, TypeIdentity type)
AttributeTargets.Parameter,
body =>
{
- body.WriteConstructor(
- new ConstructorDeclarationOptions(type.Name, TypeDeclarationAccessibility.Public)
+ body.XmlSummary(
+ $"Constructs a new instance specifying the maximum number of values to expand ({XmlSee("MaximumValueCount")})."
+ );
+ body.XmlParam(
+ "maximumValueCount",
+ $"The maximum number of values to include ({XmlSee("MaximumValueCount")})."
+ );
+ body.Constructor(
+ new(type.Name, TypeDeclarationAccessibility.Public)
{
Parameters =
[
- new ParameterDeclarationOptions(
- "maximumValueCount",
- PurviewTypeLibrary.System.Int32.AsTypeReference()
- )
+ new("maximumValueCount", PurviewTypeLibrary.System.Int32.AsTypeReference())
{
DefaultValue = "5",
},
],
},
- ctor => ctor.WriteAssignment("MaximumValueCount", "maximumValueCount")
+ ctor => ctor.Assignment("MaximumValueCount", "maximumValueCount")
);
body.XmlSummary("Gets or sets the maximum number of values to include when expanding an enumerable.");
WritePublicProperty(body, "MaximumValueCount", PurviewTypeLibrary.System.Int32.AsTypeReference());
},
- wrapInExcludeLoggingGuard: true
+ wrapInExcludeLoggingGuard: true,
+ summary: "Marks an enumerable parameter to be expanded into multiple log entries."
);
}
@@ -972,42 +1265,40 @@ static void WriteMeterGenerationAttribute(CodeWriter writer, TypeIdentity type)
AttributeTargets.Assembly,
body =>
{
- WriteEmptyConstructor(body, type);
-
- body.WriteConstructor(
- new ConstructorDeclarationOptions(type.Name, TypeDeclarationAccessibility.Public)
+ WriteEmptyConstructor(body, type, $"Constructs a new instance of the {XmlSee(type.Name)}.");
+ body.XmlSummary(
+ "Constructs a new instance specifying the meter name, name-generation type, instrument prefix and name casing defaults."
+ );
+ body.XmlParam("meterName", $"The {XmlSee("MeterName")}.");
+ body.XmlParam("nameGenerationType", $"The {XmlSee("MeterNameGenerationType")}.");
+ body.XmlParam("instrumentPrefix", $"The {XmlSee("InstrumentPrefix")}.");
+ body.XmlParam(
+ "lowercaseInstrumentName",
+ $"Whether instrument names are lowercased ({XmlSee("LowercaseInstrumentName")})."
+ );
+ body.XmlParam("lowercaseTagKeys", $"Whether tag keys are lowercased ({XmlSee("LowercaseTagKeys")}).");
+ body.Constructor(
+ new(type.Name, TypeDeclarationAccessibility.Public)
{
Parameters =
[
- new ParameterDeclarationOptions(
- "meterName",
- PurviewTypeLibrary.System.String.AsTypeReference()
- )
+ new("meterName", PurviewTypeLibrary.System.String.AsTypeReference())
{
DefaultValue = "null",
},
- new ParameterDeclarationOptions("nameGenerationType", nameGenerationType.AsTypeReference())
+ new("nameGenerationType", nameGenerationType.AsTypeReference())
{
DefaultValue = $"{nameGenerationType.RenderFullName}.DotNet",
},
- new ParameterDeclarationOptions(
- "instrumentPrefix",
- PurviewTypeLibrary.System.String.AsTypeReference()
- )
+ new("instrumentPrefix", PurviewTypeLibrary.System.String.AsTypeReference())
{
DefaultValue = "null",
},
- new ParameterDeclarationOptions(
- "lowercaseInstrumentName",
- PurviewTypeLibrary.System.Boolean.AsTypeReference()
- )
+ new("lowercaseInstrumentName", PurviewTypeLibrary.System.Boolean.AsTypeReference())
{
DefaultValue = "true",
},
- new ParameterDeclarationOptions(
- "lowercaseTagKeys",
- PurviewTypeLibrary.System.Boolean.AsTypeReference()
- )
+ new("lowercaseTagKeys", PurviewTypeLibrary.System.Boolean.AsTypeReference())
{
DefaultValue = "true",
},
@@ -1015,36 +1306,49 @@ static void WriteMeterGenerationAttribute(CodeWriter writer, TypeIdentity type)
},
ctor =>
{
- ctor.WriteAssignment("MeterName", "meterName");
- ctor.WriteAssignment("MeterNameGenerationType", "nameGenerationType");
- ctor.WriteAssignment("InstrumentPrefix", "instrumentPrefix");
- ctor.WriteAssignment("LowercaseInstrumentName", "lowercaseInstrumentName");
- ctor.WriteAssignment("LowercaseTagKeys", "lowercaseTagKeys");
+ ctor.Assignment("MeterName", "meterName");
+ ctor.Assignment("MeterNameGenerationType", "nameGenerationType");
+ ctor.Assignment("InstrumentPrefix", "instrumentPrefix");
+ ctor.Assignment("LowercaseInstrumentName", "lowercaseInstrumentName");
+ ctor.Assignment("LowercaseTagKeys", "lowercaseTagKeys");
}
);
- WriteNullableStringProperty(body, "MeterName");
+ WriteNullableStringProperty(body, "MeterName", "Gets or sets the name of the meter.");
WritePublicProperty(
body,
"MeterNameGenerationType",
nameGenerationType.AsTypeReference(),
- $"{nameGenerationType.RenderFullName}.DotNet"
+ $"{nameGenerationType.RenderFullName}.DotNet",
+ "Gets or sets how meter names are generated when not explicitly specified."
+ );
+ WriteNullableStringProperty(
+ body,
+ "InstrumentPrefix",
+ "Gets or sets the prefix applied to instrument names."
+ );
+ WritePlainStringProperty(
+ body,
+ "InstrumentSeparator",
+ "\".\"",
+ "Gets or sets the separator used between instrument name parts."
);
- WriteNullableStringProperty(body, "InstrumentPrefix");
- WritePlainStringProperty(body, "InstrumentSeparator", "\".\"");
WritePublicProperty(
body,
"LowercaseInstrumentName",
PurviewTypeLibrary.System.Boolean.AsTypeReference(),
- "true"
+ "true",
+ "Determines whether instrument names are lowercased."
);
WritePublicProperty(
body,
"LowercaseTagKeys",
PurviewTypeLibrary.System.Boolean.AsTypeReference(),
- "true"
+ "true",
+ "Determines whether tag keys are lowercased."
);
- }
+ },
+ summary: "Specifies the default meter generation behaviour for an assembly."
);
}
@@ -1056,34 +1360,42 @@ static void WriteMeterAttribute(CodeWriter writer, TypeIdentity type)
AttributeTargets.Interface,
body =>
{
- WriteEmptyConstructor(body, type);
- WriteNameConstructor(body, type);
+ WriteEmptyConstructor(body, type, $"Constructs a new instance of the {XmlSee(type.Name)}.");
+ WriteNameConstructor(body, type, $"Constructs a new instance specifying the {XmlSee("Name")}.");
- WriteNullableStringProperty(body, "Name");
- WriteNullableStringProperty(body, "InstrumentPrefix");
+ WriteNullableStringProperty(body, "Name", "Gets or sets the name of the meter.");
+ WriteNullableStringProperty(
+ body,
+ "InstrumentPrefix",
+ "Gets or sets the prefix applied to instrument names."
+ );
WritePublicProperty(
body,
"IncludeAssemblyInstrumentPrefix",
PurviewTypeLibrary.System.Boolean.AsTypeReference(),
- "true"
+ "true",
+ "Determines whether the assembly-level instrument prefix is included."
);
WritePublicProperty(
body,
"LowercaseInstrumentName",
PurviewTypeLibrary.System.Boolean.AsTypeReference(),
- "true"
+ "true",
+ "Determines whether instrument names are lowercased."
);
WritePublicProperty(
body,
"LowercaseTagKeys",
PurviewTypeLibrary.System.Boolean.AsTypeReference(),
- "true"
+ "true",
+ "Determines whether tag keys are lowercased."
);
- }
+ },
+ summary: "Marks an interface as a meter."
);
}
- static void WriteAutoCounterAttribute(CodeWriter writer, TypeIdentity type)
+ static void WriteAutoCounterAttribute(CodeWriter writer, TypeIdentity type, string? summary = null)
{
EmitAttribute(
writer,
@@ -1091,17 +1403,22 @@ static void WriteAutoCounterAttribute(CodeWriter writer, TypeIdentity type)
AttributeTargets.Method,
body =>
{
- WriteEmptyConstructor(body, type);
- WriteNameUnitDescriptionConstructor(body, type);
+ WriteEmptyConstructor(body, type, $"Constructs a new instance of the {XmlSee(type.Name)}.");
+ WriteNameUnitDescriptionConstructor(
+ body,
+ type,
+ summary: $"Constructs a new instance specifying the {XmlSee("Name")}, {XmlSee("Unit")} and {XmlSee("Description")}."
+ );
- WriteNullableStringProperty(body, "Name");
- WriteNullableStringProperty(body, "Unit");
- WriteNullableStringProperty(body, "Description");
- }
+ WriteNullableStringProperty(body, "Name", "Gets or sets the name of the instrument.");
+ WriteNullableStringProperty(body, "Unit", "Gets or sets the measurement unit of the instrument.");
+ WriteNullableStringProperty(body, "Description", "Gets or sets the description of the instrument.");
+ },
+ summary: summary
);
}
- static void WriteCounterLikeAttribute(CodeWriter writer, TypeIdentity type)
+ static void WriteCounterLikeAttribute(CodeWriter writer, TypeIdentity type, string? summary = null)
{
EmitAttribute(
writer,
@@ -1109,31 +1426,40 @@ static void WriteCounterLikeAttribute(CodeWriter writer, TypeIdentity type)
AttributeTargets.Method,
body =>
{
- WriteEmptyConstructor(body, type);
- body.WriteConstructor(
- new ConstructorDeclarationOptions(type.Name, TypeDeclarationAccessibility.Public)
+ WriteEmptyConstructor(body, type, $"Constructs a new instance of the {XmlSee(type.Name)}.");
+ body.XmlSummary(
+ $"Constructs a new instance specifying whether the counter auto-increments ({XmlSee("AutoIncrement")})."
+ );
+ body.XmlParam("autoIncrement", $"Whether the counter auto-increments ({XmlSee("AutoIncrement")}).");
+ body.Constructor(
+ new(type.Name, TypeDeclarationAccessibility.Public)
{
- Parameters =
- [
- new ParameterDeclarationOptions(
- "autoIncrement",
- PurviewTypeLibrary.System.Boolean.AsTypeReference()
- ),
- ],
+ Parameters = [new("autoIncrement", PurviewTypeLibrary.System.Boolean.AsTypeReference())],
},
- ctor => ctor.WriteAssignment("AutoIncrement", "autoIncrement")
+ ctor => ctor.Assignment("AutoIncrement", "autoIncrement")
+ );
+ WriteNameUnitDescriptionConstructor(
+ body,
+ type,
+ appendAutoIncrement: true,
+ summary: $"Constructs a new instance specifying the {XmlSee("Name")}, {XmlSee("Unit")}, {XmlSee("Description")} and whether the counter auto-increments."
);
- WriteNameUnitDescriptionConstructor(body, type, appendAutoIncrement: true);
- WritePublicProperty(body, "AutoIncrement", PurviewTypeLibrary.System.Boolean.AsTypeReference());
- WriteNullableStringProperty(body, "Name");
- WriteNullableStringProperty(body, "Unit");
- WriteNullableStringProperty(body, "Description");
- }
+ WritePublicProperty(
+ body,
+ "AutoIncrement",
+ PurviewTypeLibrary.System.Boolean.AsTypeReference(),
+ summary: "Determines whether the counter auto-increments."
+ );
+ WriteNullableStringProperty(body, "Name", "Gets or sets the name of the instrument.");
+ WriteNullableStringProperty(body, "Unit", "Gets or sets the measurement unit of the instrument.");
+ WriteNullableStringProperty(body, "Description", "Gets or sets the description of the instrument.");
+ },
+ summary: summary
);
}
- static void WriteObservableCounterLikeAttribute(CodeWriter writer, TypeIdentity type)
+ static void WriteObservableCounterLikeAttribute(CodeWriter writer, TypeIdentity type, string? summary = null)
{
EmitAttribute(
writer,
@@ -1141,19 +1467,30 @@ static void WriteObservableCounterLikeAttribute(CodeWriter writer, TypeIdentity
AttributeTargets.Method,
body =>
{
- WriteEmptyConstructor(body, type);
- WriteNameUnitDescriptionConstructor(body, type, appendThrowOnAlreadyInitialized: true);
-
- WritePublicProperty(body, "AutoIncrement", PurviewTypeLibrary.System.Boolean.AsTypeReference());
- WriteNullableStringProperty(body, "Name");
- WriteNullableStringProperty(body, "Unit");
- WriteNullableStringProperty(body, "Description");
+ WriteEmptyConstructor(body, type, $"Constructs a new instance of the {XmlSee(type.Name)}.");
+ WriteNameUnitDescriptionConstructor(
+ body,
+ type,
+ appendThrowOnAlreadyInitialized: true,
+ summary: $"Constructs a new instance specifying the {XmlSee("Name")}, {XmlSee("Unit")}, {XmlSee("Description")} and whether initializing twice throws."
+ );
+ WritePublicProperty(
+ body,
+ "AutoIncrement",
+ PurviewTypeLibrary.System.Boolean.AsTypeReference(),
+ summary: "Determines whether the counter auto-increments."
+ );
+ WriteNullableStringProperty(body, "Name", "Gets or sets the name of the instrument.");
+ WriteNullableStringProperty(body, "Unit", "Gets or sets the measurement unit of the instrument.");
+ WriteNullableStringProperty(body, "Description", "Gets or sets the description of the instrument.");
WritePublicProperty(
body,
"ThrowOnAlreadyInitialized",
- PurviewTypeLibrary.System.Boolean.AsTypeReference()
+ PurviewTypeLibrary.System.Boolean.AsTypeReference(),
+ summary: "Determines whether an exception is thrown when the instrument is initialized more than once."
);
- }
+ },
+ summary: summary
);
}
@@ -1161,23 +1498,38 @@ static void WriteNameUnitDescriptionConstructor(
CodeWriter writer,
TypeIdentity type,
bool appendAutoIncrement = false,
- bool appendThrowOnAlreadyInitialized = false
+ bool appendThrowOnAlreadyInitialized = false,
+ string? summary = null
)
{
- writer.WriteConstructor(
- new ConstructorDeclarationOptions(type.Name, TypeDeclarationAccessibility.Public)
+ if (summary != null)
+ {
+ writer.XmlSummary(summary);
+ writer.XmlParam("name", "The name of the instrument.");
+ writer.XmlParam("unit", "The measurement unit of the instrument.");
+ writer.XmlParam("description", "The description of the instrument.");
+ if (appendAutoIncrement)
+ writer.XmlParam("autoIncrement", "Whether the counter should auto-increment.");
+ if (appendThrowOnAlreadyInitialized)
+ writer.XmlParam(
+ "throwOnAlreadyInitialized",
+ "Whether to throw if the instrument has already been initialized."
+ );
+ }
+ writer.Constructor(
+ new(type.Name, TypeDeclarationAccessibility.Public)
{
Parameters = BuildNameUnitDescriptionParameters(appendAutoIncrement, appendThrowOnAlreadyInitialized),
},
ctor =>
{
- ctor.WriteAssignment("Name", "name");
- ctor.WriteAssignment("Unit", "unit");
- ctor.WriteAssignment("Description", "description");
+ ctor.Assignment("Name", "name");
+ ctor.Assignment("Unit", "unit");
+ ctor.Assignment("Description", "description");
if (appendAutoIncrement)
- ctor.WriteAssignment("AutoIncrement", "autoIncrement");
+ ctor.Assignment("AutoIncrement", "autoIncrement");
if (appendThrowOnAlreadyInitialized)
- ctor.WriteAssignment("ThrowOnAlreadyInitialized", "throwOnAlreadyInitialized");
+ ctor.Assignment("ThrowOnAlreadyInitialized", "throwOnAlreadyInitialized");
}
);
}
@@ -1188,34 +1540,20 @@ bool appendThrowOnAlreadyInitialized
)
{
var parameters = ImmutableArray.Empty;
+ parameters = parameters.Add(new("name", PurviewTypeLibrary.System.String.AsTypeReference()));
parameters = parameters.Add(
- new ParameterDeclarationOptions("name", PurviewTypeLibrary.System.String.AsTypeReference())
- );
- parameters = parameters.Add(
- new ParameterDeclarationOptions("unit", PurviewTypeLibrary.System.String.AsTypeReference())
- {
- DefaultValue = "null",
- }
+ new("unit", PurviewTypeLibrary.System.String.AsTypeReference()) { DefaultValue = "null" }
);
parameters = parameters.Add(
- new ParameterDeclarationOptions("description", PurviewTypeLibrary.System.String.AsTypeReference())
- {
- DefaultValue = "null",
- }
+ new("description", PurviewTypeLibrary.System.String.AsTypeReference()) { DefaultValue = "null" }
);
if (appendAutoIncrement)
parameters = parameters.Add(
- new ParameterDeclarationOptions("autoIncrement", PurviewTypeLibrary.System.Boolean.AsTypeReference())
- {
- DefaultValue = "false",
- }
+ new("autoIncrement", PurviewTypeLibrary.System.Boolean.AsTypeReference()) { DefaultValue = "false" }
);
if (appendThrowOnAlreadyInitialized)
parameters = parameters.Add(
- new ParameterDeclarationOptions(
- "throwOnAlreadyInitialized",
- PurviewTypeLibrary.System.Boolean.AsTypeReference()
- )
+ new("throwOnAlreadyInitialized", PurviewTypeLibrary.System.Boolean.AsTypeReference())
{
DefaultValue = "false",
}
@@ -1231,77 +1569,96 @@ bool appendThrowOnAlreadyInitialized
static void WriteTargetsEnum(CodeWriter writer, TypeIdentity type)
{
writer
- .WriteFileScopedNamespace(TypeLibrary.PurviewTelemetryNamespace)
+ .FileScopedNamespace(TypeLibrary.PurviewTelemetryNamespace)
.XmlSummary("Determines which telemetry targets a parameter is excluded from.")
- .WriteEnum(
- new(type.Name, TypeDeclarationAccessibility.Public)
- {
- Attributes = [new(new TypeIdentity("FlagsAttribute", "System"))],
- },
- new("None", 0, "No telemetry targets are excluded."),
- new("Activities", 1, "Excludes activity (tracing) targets."),
- new("Logging", 2, "Excludes logging targets."),
- new("Metrics", 4, "Excludes metrics targets."),
- new("All", "Activities | Logging | Metrics")
+ .Enum(
+ type.Name,
+ TypeDeclarationAccessibility.Public,
+ fields:
+ [
+ new("None", 0, "No telemetry targets are excluded."),
+ new("Activities", 1, "Excludes activity (tracing) targets."),
+ new("Logging", 2, "Excludes logging targets."),
+ new("Metrics", 4, "Excludes metrics targets."),
+ new("All", "Activities | Logging | Metrics"),
+ ],
+ configure: options => options with { Attributes = [new(new TypeIdentity("FlagsAttribute", "System"))] }
);
}
static void WriteNamingConventionEnum(CodeWriter writer, TypeIdentity type)
{
writer
- .WriteFileScopedNamespace(TypeLibrary.PurviewTelemetryNamespace)
+ .FileScopedNamespace(TypeLibrary.PurviewTelemetryNamespace)
.XmlSummary("Determines the naming convention used for generated telemetry names.")
- .WriteEnum(
- new(type.Name, TypeDeclarationAccessibility.Public),
- new("Legacy", 0, "Uses the legacy naming convention for generated telemetry names."),
- new("OpenTelemetry", 1, "Uses the OpenTelemetry naming convention for generated telemetry names.")
+ .Enum(
+ type.Name,
+ TypeDeclarationAccessibility.Public,
+ fields:
+ [
+ new("Legacy", 0, "Uses the legacy naming convention for generated telemetry names."),
+ new("OpenTelemetry", 1, "Uses the OpenTelemetry naming convention for generated telemetry names."),
+ ]
);
}
static void WriteLogPrefixTypeEnum(CodeWriter writer, TypeIdentity type)
{
- writer.WriteLine("#if !EXCLUDE_PURVIEW_TELEMETRY_LOGGING").NewLine();
- writer
- .WriteFileScopedNamespace(TypeLibrary.PurviewTelemetryNamespace)
- .XmlSummary("Determines the mode used to generate or override the prefix for the log entry.")
- .WriteEnum(
- new(type.Name, TypeDeclarationAccessibility.Public),
- new("Default", 0, "Uses the default log prefix."),
- new("Interface", 1, "Uses the interface name as the log prefix."),
- new("Class", 2, "Uses the class name as the log prefix."),
- new("Custom", 3, "Uses a custom log prefix."),
- new("TrimmedClassName", 4, "Uses the trimmed class name as the log prefix.")
- );
-
- writer.WriteLine("#endif");
+ writer.HashDefines(
+ "!EXCLUDE_PURVIEW_TELEMETRY_LOGGING",
+ hashWriter =>
+ hashWriter
+ .FileScopedNamespace(TypeLibrary.PurviewTelemetryNamespace)
+ .XmlSummary("Determines the mode used to generate or override the prefix for the log entry.")
+ .Enum(
+ type.Name,
+ TypeDeclarationAccessibility.Public,
+ fields:
+ [
+ new("Default", 0, "Uses the default log prefix."),
+ new("Interface", 1, "Uses the interface name as the log prefix."),
+ new("Class", 2, "Uses the class name as the log prefix."),
+ new("Custom", 3, "Uses a custom log prefix."),
+ new("TrimmedClassName", 4, "Uses the trimmed class name as the log prefix."),
+ ]
+ )
+ );
}
static void WriteLoggerGenerationModeEnum(CodeWriter writer, TypeIdentity type)
{
- writer.WriteLine("#if !EXCLUDE_PURVIEW_TELEMETRY_LOGGING").NewLine();
-
- writer
- .WriteFileScopedNamespace(TypeLibrary.PurviewTelemetryNamespace)
- .XmlSummary("Controls the generation mode used for log methods.")
- .WriteEnum(
- new(type.Name, TypeDeclarationAccessibility.Public),
- new("Auto", 0, "Automatically selects the log generation mode."),
- new("V1", 1, "Uses the first-generation log implementation."),
- new("V2", 2, "Uses the second-generation log implementation.")
- );
-
- writer.WriteLine("#endif");
+ writer.HashDefines(
+ "!EXCLUDE_PURVIEW_TELEMETRY_LOGGING",
+ hashWriter =>
+ hashWriter
+ .FileScopedNamespace(TypeLibrary.PurviewTelemetryNamespace)
+ .XmlSummary("Controls the generation mode used for log methods.")
+ .Enum(
+ type.Name,
+ TypeDeclarationAccessibility.Public,
+ fields:
+ [
+ new("Auto", 0, "Automatically selects the log generation mode."),
+ new("V1", 1, "Uses the first-generation log implementation."),
+ new("V2", 2, "Uses the second-generation log implementation."),
+ ]
+ )
+ );
}
static void WriteMeterNameGenerationTypeEnum(CodeWriter writer, TypeIdentity type)
{
writer
- .WriteFileScopedNamespace(TypeLibrary.PurviewTelemetryNamespace)
+ .FileScopedNamespace(TypeLibrary.PurviewTelemetryNamespace)
.XmlSummary("Determines how meter names are generated when not explicitly specified.")
- .WriteEnum(
- new(type.Name, TypeDeclarationAccessibility.Public),
- new("OpenTelemetry", 0, "Generates meter names using the OpenTelemetry convention."),
- new("DotNet", 1, "Generates meter names using the .NET convention.")
+ .Enum(
+ type.Name,
+ TypeDeclarationAccessibility.Public,
+ fields:
+ [
+ new("OpenTelemetry", 0, "Generates meter names using the OpenTelemetry convention."),
+ new("DotNet", 1, "Generates meter names using the .NET convention."),
+ ]
);
}
}
diff --git a/src/src/SourceGenerator/Emitters/LoggerGenTargetClassEmitter.Methods.cs b/src/src/SourceGenerator/Emitters/LoggerGenTargetClassEmitter.Methods.cs
index 736b2624..40d50b89 100644
--- a/src/src/SourceGenerator/Emitters/LoggerGenTargetClassEmitter.Methods.cs
+++ b/src/src/SourceGenerator/Emitters/LoggerGenTargetClassEmitter.Methods.cs
@@ -111,7 +111,7 @@ SourceProductionContext context
writer.NewLine();
using (
- writer.WriteMethodScope(
+ writer.MethodScope(
new MethodDeclarationOptions(
methodName,
returnType,
@@ -154,15 +154,10 @@ SourceProductionContext context
// if (!_logger.IsEnabled(LogLevel.Information)))
// { return; };
// ...but only if it's not been scoped.
- writer
- .Write("if (!")
- .Write(PropertyLibrary.Logging.LoggerFieldName)
- .Write(".IsEnabled(")
- .Write(methodTarget.MSLevel)
- .WriteLine("))");
-
- using (writer.OpenBlockScope())
- writer.WriteLine("return;");
+ writer.IfBlock(
+ "!" + PropertyLibrary.Logging.LoggerFieldName + ".IsEnabled(" + methodTarget.MSLevel + ")",
+ static body => body.Return()
+ );
writer.NewLine();
}
@@ -232,26 +227,27 @@ SourceProductionContext context
if (variables.Length > 0)
{
foreach (var variableDefinition in variables)
- writer.WriteLine(variableDefinition);
+ writer.Line(variableDefinition);
writer.NewLine();
}
var formattedMessageVarName = FindUniqueName("formattedMessage", existingParamNames);
- writer
- .Write("var ")
- .WriteLine("formattedMessage = ")
- .WriteLine("#if NET")
- .Write("string.Create(global::System.Globalization.CultureInfo.InvariantCulture, $")
- .Write(interpolatedMessage.Wrap())
- .WriteLine(");")
- .WriteLine("#else")
- .Write("global::System.FormattableString.Invariant($")
- .Write(interpolatedMessage.Wrap())
- .WriteLine(");")
- .WriteLine("#endif")
- .Write(";")
- .NewLine();
+ writer.Write("var ").Line("formattedMessage = ");
+
+ using (writer.HashDefinesScope("NET"))
+ {
+ writer
+ .Write("string.Create(global::System.Globalization.CultureInfo.InvariantCulture, $")
+ .Write(interpolatedMessage.Wrap())
+ .Line(");");
+
+ writer.HashElse();
+
+ writer.Write("global::System.FormattableString.Invariant($").Write(interpolatedMessage.Wrap()).Line(");");
+ }
+
+ writer.Write(";").NewLine();
OutputState(
writer,
@@ -267,7 +263,7 @@ SourceProductionContext context
.Write(PropertyLibrary.Logging.LoggerFieldName)
.Write(".BeginScope(")
.Write(stateVarName)
- .WriteLine(");");
+ .Line(");");
}
static void EmitNonScopedBody(
@@ -300,7 +296,7 @@ bool useTypedState
var eventId = methodTarget.EventId ?? SharedHelpers.GetNonRandomizedHashCode(methodTarget.MethodName);
writer
.Write(PropertyLibrary.Logging.LoggerFieldName)
- .WriteLine(".Log(")
+ .Line(".Log(")
// Log level
.Write(methodTarget.MSLevel.WithComma(andSpace: false))
// Event Id
@@ -312,7 +308,7 @@ writer.IsNullableContextEnabled is null or true
.Write(eventId.ToString(CultureInfo.InvariantCulture))
.Write(", nameof(")
.Write(methodTarget.LogName)
- .WriteLine(")),")
+ .Line(")),")
// State
.Write(stateVarName.WithComma(andSpace: false))
// Exception
@@ -324,31 +320,35 @@ writer.IsNullableContextEnabled is null or true
.Write(expressionStateVarName)
.Write(", ")
.Write(expressionExceptionVarName ?? "_")
- .WriteLine(") =>")
- .WriteLine("{");
+ .Line(") =>")
+ .Line("{");
if (variables.Length > 0)
{
foreach (var variableDefinition in variables)
- writer.WriteLine(variableDefinition);
+ writer.Line(variableDefinition);
writer.NewLine();
}
- writer
- .WriteLine("#if NET")
- .Write("return string.Create(global::System.Globalization.CultureInfo.InvariantCulture, $")
- .Write(interpolatedMessage.Wrap())
- .WriteLine(");")
- .WriteLine("#else")
- .Write("return global::System.FormattableString.Invariant($")
- .Write(interpolatedMessage.Wrap())
- .WriteLine(");")
- .WriteLine("#endif")
- .Write("}")
- .Write(");");
-
- writer.NewLine().Write(stateVarName).Write(".Clear();").NewLine();
+ using (writer.HashDefinesScope("NET"))
+ {
+ writer
+ .Write("return string.Create(global::System.Globalization.CultureInfo.InvariantCulture, $")
+ .Write(interpolatedMessage.Wrap())
+ .Line(");");
+
+ writer.HashElse();
+
+ writer
+ .Write("return global::System.FormattableString.Invariant($")
+ .Write(interpolatedMessage.Wrap())
+ .Line(");");
+ }
+
+ writer.Write("}").Write(");");
+
+ writer.NewLine().MethodCallOn(stateVarName, "Clear").NewLine();
}
static void EmitTypedStateLogCall(
@@ -373,7 +373,7 @@ [.. methodTarget.Parameters]
writer
.Write(PropertyLibrary.Logging.LoggerFieldName)
- .WriteLine(".Log(")
+ .Line(".Log(")
.Write(methodTarget.MSLevel.WithComma(andSpace: false))
.Write(
writer.IsNullableContextEnabled is null or true
@@ -383,7 +383,7 @@ writer.IsNullableContextEnabled is null or true
.Write(eventId.ToString(CultureInfo.InvariantCulture))
.Write(", nameof(")
.Write(methodTarget.LogName)
- .WriteLine(")),")
+ .Line(")),")
.Write("new ")
.Write(structName)
.Write('(');
@@ -395,7 +395,7 @@ writer.IsNullableContextEnabled is null or true
writer.Write(", ");
}
- writer.WriteLine("),");
+ writer.Line("),");
writer.Write(methodTarget.ExceptionParameter.OrNullKeyword().WithComma(andSpace: false));
writer
@@ -403,19 +403,23 @@ writer.IsNullableContextEnabled is null or true
.Write(expressionStateVarName)
.Write(", ")
.Write(expressionExceptionVarName ?? "_")
- .WriteLine(") =>")
- .WriteLine("{")
- .WriteLine("#if NET")
- .Write("return string.Create(global::System.Globalization.CultureInfo.InvariantCulture, $")
- .Write(interpolatedMessage.Wrap())
- .WriteLine(");")
- .WriteLine("#else")
- .Write("return global::System.FormattableString.Invariant($")
- .Write(interpolatedMessage.Wrap())
- .WriteLine(");")
- .WriteLine("#endif")
- .Write("}")
- .Write(");");
+ .Line(") =>")
+ .Line("{");
+
+ using (writer.HashDefinesScope("NET"))
+ {
+ writer
+ .Write("return string.Create(global::System.Globalization.CultureInfo.InvariantCulture, $")
+ .Write(interpolatedMessage.Wrap())
+ .Line(");");
+ writer.HashElse();
+
+ writer
+ .Write("return global::System.FormattableString.Invariant($")
+ .Write(interpolatedMessage.Wrap())
+ .Line(");");
+ }
+ writer.Write("}").Write(");");
writer.NewLine();
}
@@ -451,11 +455,11 @@ SourceProductionContext context
.Write(" = ")
.Write(TypeLibrary.Logging.MicrosoftExtensions.LoggerMessageHelper)
.Write('.')
- .WriteLine("ThreadLocalState;")
+ .Line("ThreadLocalState;")
.Write(stateVarName)
.Write(".ReserveTagSpace(")
.Write(reservationCount.ToString(CultureInfo.InvariantCulture))
- .WriteLine(");")
+ .Line(");")
.NewLine();
// Original format is always at 0.
@@ -508,7 +512,7 @@ SourceProductionContext context
foreach (var nullableLogProperty in postSetProperties)
{
context.CancellationToken.ThrowIfCancellationRequested();
- writer.WriteLine(nullableLogProperty);
+ writer.Line(nullableLogProperty);
}
}
@@ -544,28 +548,17 @@ List existingParamNames
if (shouldSkipNull)
{
var tmpVarName = FindUniqueName("tmp", existingParamNames);
- logPropertiesWriter
- .Write("{")
- .Write("var ")
- .Write(tmpVarName)
- .Write(" = ")
- .Write(logPropertyValue)
- .WriteLine(";")
- .Write("if (")
- .Write(tmpVarName)
- .WriteLine(" != null)")
- .Write("{");
- logPropertiesWriter.Indent();
-
- logPropertyValue = tmpVarName;
+ logPropertiesWriter.Write("{");
+ logPropertiesWriter.Write("var ").Write(tmpVarName).Write(" = ").Write(logPropertyValue).Line(";");
+ logPropertiesWriter.IfBlock(
+ tmpVarName + " != null",
+ body => OutputState(body, stateVarName, logPropertyName.Wrap(), tmpVarName, null)
+ );
+ logPropertiesWriter.Write("}");
}
-
- OutputState(logPropertiesWriter, stateVarName, logPropertyName.Wrap(), logPropertyValue, null);
-
- if (shouldSkipNull)
+ else
{
- logPropertiesWriter.Unindent();
- logPropertiesWriter.Write("}").Write("}");
+ OutputState(logPropertiesWriter, stateVarName, logPropertyName.Wrap(), logPropertyValue, null);
}
postPropertyDefinitions ??= [];
@@ -639,71 +632,52 @@ LoggerOutputContext output
CodeWriter snippet = new(GenerationSettings.Create(), throwOnUnclosedScopes: false);
var iteratorVarName = FindUniqueName("tmp_i", existingParamNames);
var iteratorItemVarName = FindUniqueName("item", existingParamNames);
- snippet.Write("if (").Write(parameter.Name).WriteLine(" != null)").Write("{");
- snippet.Indent();
- snippet.Write("var ").Write(iteratorVarName).WriteLine(" = 0;");
+ snippet.IfBlock(
+ parameter.Name + " != null",
+ body =>
+ {
+ body.Write("var ").Write(iteratorVarName).Line(" = 0;");
- var maxCount = parameter.ExpandEnumerableAttribute!.Value.MaximumValueCount;
+ var maxCount = parameter.ExpandEnumerableAttribute!.Value.MaximumValueCount;
- if (maxCount < 1)
- maxCount = 1;
+ if (maxCount < 1)
+ maxCount = 1;
- if (maxCount > PropertyLibrary.Logging.UnboundedIEnumerableMaxCountBeforeDiagnostic)
- {
- output.Context.Diagnostic($"Identified {parameter.Name} that has a large unbounded ienumerable max.");
- }
+ if (maxCount > PropertyLibrary.Logging.UnboundedIEnumerableMaxCountBeforeDiagnostic)
+ {
+ output.Context.Diagnostic(
+ $"Identified {parameter.Name} that has a large unbounded ienumerable max."
+ );
+ }
- snippet
- .Write("foreach (var ")
- .Write(iteratorItemVarName)
- .Write(" in ")
- .Write(parameter.Name)
- .WriteLine(")")
- .Write("{");
- snippet.Indent();
- snippet
- .Write("if (")
- .Write(iteratorVarName)
- .Write(" == ")
- .Write(maxCount.ToString(CultureInfo.InvariantCulture))
- .WriteLine(")");
- snippet.WriteLine("{");
- snippet.Indent();
- snippet.WriteLine("break;");
- snippet.Unindent();
- snippet.WriteLine("}");
-
- snippet.NewLine();
-
- OutputState(snippet, stateVarName, $"$\"{parameter.Name}[{{{iteratorVarName}}}]\"", iteratorItemVarName, null);
-
- snippet.Write(iteratorVarName).WriteLine("++;");
-
- snippet.Unindent();
- snippet.Write("}");
- snippet.Unindent();
- snippet.Write("}");
+ body.Foreach(
+ "var " + iteratorItemVarName + " in " + parameter.Name,
+ loopBody =>
+ {
+ loopBody.IfBlock(
+ iteratorVarName + " == " + maxCount.ToString(CultureInfo.InvariantCulture),
+ static breakBody => breakBody.Line("break;")
+ );
+
+ loopBody.NewLine();
+
+ OutputState(
+ loopBody,
+ stateVarName,
+ $"$\"{parameter.Name}[{{{iteratorVarName}}}]\"",
+ iteratorItemVarName,
+ null
+ );
+
+ loopBody.Write(iteratorVarName).Line("++;");
+ }
+ );
+ }
+ );
return snippet.ToString().TrimEnd('\n');
}
- static void EmitParametersAsMethodArgumentList(
- LogMethodTarget methodTarget,
- CodeWriter writer,
- SourceProductionContext context
- )
- {
- for (var i = 0; i < methodTarget.TotalParameterCount; i++)
- {
- context.CancellationToken.ThrowIfCancellationRequested();
-
- writer.Write(methodTarget.Parameters[i].ParameterType).Write(' ').Write(methodTarget.Parameters[i].Name);
-
- if (i < methodTarget.TotalParameterCount - 1)
- writer.Write(", ");
- }
- }
-
static string FindUniqueName(string name, List existingValues)
{
var i = 0;
@@ -888,7 +862,7 @@ static void EmitLogStateStruct(CodeWriter writer, LogMethodTarget methodTarget,
writer.NewLine();
using (
- writer.WriteStructScope(
+ writer.StructScope(
new TypeDeclarationOptions(structName, TypeDeclarationAccessibility.Private)
{
IsReadOnly = true,
@@ -898,10 +872,15 @@ static void EmitLogStateStruct(CodeWriter writer, LogMethodTarget methodTarget,
)
)
{
- writer
- .Write("static readonly string s_originalFormat = ")
- .Write(methodTarget.MessageTemplate.Wrap())
- .WriteLine(";");
+ writer.Field(
+ new FieldDeclarationOptions("s_originalFormat", PurviewTypeLibrary.System.String.AsTypeReference())
+ {
+ IsStatic = true,
+ IsReadOnly = true,
+ Initializer = methodTarget.MessageTemplate.Wrap(),
+ IncludeGeneratedAttributes = false,
+ }
+ );
if (nonExceptionParams.Count > 0)
{
@@ -911,103 +890,115 @@ static void EmitLogStateStruct(CodeWriter writer, LogMethodTarget methodTarget,
{
context.CancellationToken.ThrowIfCancellationRequested();
- writer
- .Write("public readonly ")
- .Write(param.ParameterType)
- .Write(" _")
- .Write(param.UpperCasedName)
- .WriteLine(";");
- }
-
- writer.NewLine().Write("public ").Write(structName).Write('(');
-
- for (var i = 0; i < nonExceptionParams.Count; i++)
- {
- context.CancellationToken.ThrowIfCancellationRequested();
-
- writer.Write(nonExceptionParams[i].ParameterType).Write(' ').Write(nonExceptionParams[i].Name);
-
- if (i < nonExceptionParams.Count - 1)
- writer.Write(", ");
+ writer.Field(
+ new FieldDeclarationOptions($"_{param.UpperCasedName}", param.ParameterType)
+ {
+ Accessibility = TypeDeclarationAccessibility.Public,
+ IsReadOnly = true,
+ IncludeGeneratedAttributes = false,
+ }
+ );
}
- writer.Write(")");
+ writer.NewLine();
- using (writer.OpenBlockScope())
- {
- foreach (var param in nonExceptionParams)
+ writer.Constructor(
+ new ConstructorDeclarationOptions(structName, TypeDeclarationAccessibility.Public)
+ {
+ Parameters =
+ [
+ .. nonExceptionParams.Select(p => new ParameterDeclarationOptions(p.Name, p.ParameterType)),
+ ],
+ IncludeGeneratedAttributes = false,
+ },
+ ctor =>
{
- context.CancellationToken.ThrowIfCancellationRequested();
+ foreach (var param in nonExceptionParams)
+ {
+ context.CancellationToken.ThrowIfCancellationRequested();
- writer.Write("_").Write(param.UpperCasedName).Write(" = ").Write(param.Name).WriteLine(";");
+ ctor.Write("_").Write(param.UpperCasedName).Write(" = ").Write(param.Name).Line(";");
+ }
}
- }
+ );
}
writer
.NewLine()
.NewLine()
- .Write("public int Count => ")
- .Write(count.ToString(CultureInfo.InvariantCulture))
- .WriteLine(";")
- .NewLine()
- .Write($"public {kvpType} this[int index]");
-
- using (writer.OpenBlockScope())
- {
- if (writer.IsNullableContextEnabled is null or true)
- {
- writer.WriteLine("get => index switch {");
- writer.Indent();
- writer.WriteLine("0 => new(\"{OriginalFormat}\", s_originalFormat),");
-
- for (var i = 0; i < nonExceptionParams.Count; i++)
+ .Property(
+ new PropertyDeclarationOptions(
+ "Count",
+ PurviewTypeLibrary.System.Int32.AsTypeReference(),
+ TypeDeclarationAccessibility.Public
+ )
{
- context.CancellationToken.ThrowIfCancellationRequested();
-
- writer
- .Write($"{i + 1} => new(")
- .Write(nonExceptionParams[i].Name.Wrap())
- .Write(", _")
- .Write(nonExceptionParams[i].UpperCasedName)
- .WriteLine("),");
+ ExpressionBody = count.ToString(CultureInfo.InvariantCulture),
+ IncludeGeneratedAttributes = false,
}
+ )
+ .NewLine();
- writer.WriteLine("_ => throw new global::System.IndexOutOfRangeException(nameof(index))");
- writer.Unindent();
- writer.WriteLine("};");
- }
- else
+ writer.Indexer(
+ new IndexerDeclarationOptions(
+ new TypeReference(new TypeIdentity(kvpType, null)),
+ new ParameterDeclarationOptions("index", PurviewTypeLibrary.System.Int32.AsTypeReference())
+ )
{
- writer.Write("get");
- using (writer.OpenBlockScope())
+ Accessibility = TypeDeclarationAccessibility.Public,
+ IncludeGeneratedAttributes = false,
+ },
+ getter =>
+ {
+ if (writer.IsNullableContextEnabled is null or true)
{
- writer.Write("switch (index)");
- using (writer.OpenBlockScope())
+ getter.Line("return index switch {");
+ getter.Indent();
+ getter.Line("0 => new(\"{OriginalFormat}\", s_originalFormat),");
+
+ for (var i = 0; i < nonExceptionParams.Count; i++)
{
- writer.WriteLine(
- "case 0: return new " + kvpType + "(\"{OriginalFormat}\", s_originalFormat);"
- );
+ context.CancellationToken.ThrowIfCancellationRequested();
+
+ getter
+ .Write($"{i + 1} => new(")
+ .Write(nonExceptionParams[i].Name.Wrap())
+ .Write(", _")
+ .Write(nonExceptionParams[i].UpperCasedName)
+ .Line("),");
+ }
+
+ getter.Line("_ => throw new global::System.IndexOutOfRangeException(nameof(index))");
+ getter.Unindent();
+ getter.Line("};");
+ }
+ else
+ {
+ getter.Write("switch (index)");
+ using (getter.OpenBlockScope())
+ {
+ getter.Line("case 0: return new " + kvpType + "(\"{OriginalFormat}\", s_originalFormat);");
for (var i = 0; i < nonExceptionParams.Count; i++)
{
context.CancellationToken.ThrowIfCancellationRequested();
- writer
+ getter
.Write($"case {i + 1}: return new " + kvpType + "(")
.Write(nonExceptionParams[i].Name.Wrap())
.Write(", _")
.Write(nonExceptionParams[i].UpperCasedName)
- .WriteLine(");");
+ .Line(");");
}
- writer.WriteLine(
- "default: throw new global::System.IndexOutOfRangeException(nameof(index));"
- );
+ getter
+ .Write("default: throw new global::System.IndexOutOfRangeException(nameof(index))")
+ .Line(";");
}
}
- }
- }
+ },
+ null
+ );
EmitStructEnumerator(writer, structName, kvpType, ienumeratorType, ienumerableType, ienumerableKvpType);
}
@@ -1024,12 +1015,10 @@ static void EmitStructEnumerator(
string ienumerableKvpType
)
{
- var currentPropertyType =
- $"{PurviewTypeLibrary.System.Object.MakeNullable(writer)} global::System.Collections.IEnumerator.Current => Current;";
writer.NewLine();
using (
- writer.WriteStructScope(
+ writer.StructScope(
new TypeDeclarationOptions("Enumerator", TypeDeclarationAccessibility.Public)
{
IncludeGeneratedAttributes = false,
@@ -1038,49 +1027,120 @@ string ienumerableKvpType
)
)
{
- writer
- .Write("readonly ")
- .Write(structName)
- .WriteLine(" _state;")
- .Write("int _index;")
- .NewLine()
- .Write("public Enumerator(")
- .Write(structName)
- .WriteLine(" state)");
+ writer.Field(
+ new FieldDeclarationOptions("_state", new TypeReference(new TypeIdentity(structName, null)))
+ {
+ IsReadOnly = true,
+ IncludeGeneratedAttributes = false,
+ }
+ );
- using (writer.OpenBlockScope())
- {
- writer.Write("_state = state;").NewLine();
- writer.Write("_index = -1;").NewLine();
- }
+ writer.Field(
+ new FieldDeclarationOptions("_index", PurviewTypeLibrary.System.Int32.AsTypeReference())
+ {
+ IncludeGeneratedAttributes = false,
+ }
+ );
+
+ writer.NewLine();
+
+ writer.Constructor(
+ new ConstructorDeclarationOptions("Enumerator", TypeDeclarationAccessibility.Public)
+ {
+ Parameters =
+ [
+ new ParameterDeclarationOptions("state", new TypeReference(new TypeIdentity(structName, null))),
+ ],
+ IncludeGeneratedAttributes = false,
+ },
+ ctor =>
+ {
+ ctor.Assignment("_state", "state").NewLine();
+ ctor.Assignment("_index", "-1").NewLine();
+ }
+ );
writer
.NewLine()
- .Write($"public {kvpType} Current => _state[_index];")
+ .Property(
+ new PropertyDeclarationOptions(
+ "Current",
+ new TypeReference(new TypeIdentity(kvpType, null)),
+ TypeDeclarationAccessibility.Public
+ )
+ {
+ ExpressionBody = "_state[_index]",
+ IncludeGeneratedAttributes = false,
+ }
+ )
.NewLine()
.NewLine()
- .Write(currentPropertyType)
+ .Write(PurviewTypeLibrary.System.Object.MakeNullable(writer))
+ .Write(" global::System.Collections.IEnumerator.Current")
+ .Line(" => Current;")
.NewLine()
.NewLine()
- .Write("public bool MoveNext() => ++_index < _state.Count;")
+ .MethodExpression(
+ new MethodDeclarationOptions(
+ "MoveNext",
+ PurviewTypeLibrary.System.Boolean.AsTypeReference(),
+ TypeDeclarationAccessibility.Public
+ )
+ {
+ ExpressionBody = "++_index < _state.Count",
+ IncludeGeneratedAttributes = false,
+ }
+ )
.NewLine()
.NewLine()
- .Write("public void Reset() => _index = -1;")
+ .MethodExpression(
+ new MethodDeclarationOptions(
+ "Reset",
+ PurviewTypeLibrary.System.Void.AsTypeReference(),
+ TypeDeclarationAccessibility.Public
+ )
+ {
+ ExpressionBody = "_index = -1",
+ IncludeGeneratedAttributes = false,
+ }
+ )
.NewLine()
.NewLine()
- .Write("public void Dispose() { }");
+ .Method(
+ new MethodDeclarationOptions(
+ "Dispose",
+ PurviewTypeLibrary.System.Void.AsTypeReference(),
+ TypeDeclarationAccessibility.Public
+ )
+ {
+ IncludeGeneratedAttributes = false,
+ },
+ _ => { }
+ );
}
writer
.NewLine()
.NewLine()
- .Write("public Enumerator GetEnumerator() => new Enumerator(this);")
+ .MethodExpression(
+ new MethodDeclarationOptions(
+ "GetEnumerator",
+ new TypeReference(new TypeIdentity("Enumerator", null)),
+ TypeDeclarationAccessibility.Public
+ )
+ {
+ ExpressionBody = "new Enumerator(this)",
+ IncludeGeneratedAttributes = false,
+ }
+ )
.NewLine()
.NewLine()
- .Write($"{ienumeratorType} {ienumerableKvpType}.GetEnumerator() => GetEnumerator();")
+ .Write(ienumeratorType + " " + ienumerableKvpType + ".GetEnumerator() => GetEnumerator()")
+ .Line(";")
.NewLine()
.NewLine()
- .Write($"{ienumerableType} global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();");
+ .Write(ienumerableType + " global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator()")
+ .Line(";");
}
static void EmitScopeStateStruct(CodeWriter writer, LogMethodTarget methodTarget, SourceProductionContext context)
@@ -1099,7 +1159,7 @@ static void EmitScopeStateStruct(CodeWriter writer, LogMethodTarget methodTarget
writer.NewLine();
using (
- writer.WriteStructScope(
+ writer.StructScope(
new TypeDeclarationOptions(structName, TypeDeclarationAccessibility.Private)
{
IsReadOnly = true,
@@ -1109,10 +1169,15 @@ static void EmitScopeStateStruct(CodeWriter writer, LogMethodTarget methodTarget
)
)
{
- writer
- .Write("static readonly string s_originalFormat = ")
- .Write(methodTarget.MessageTemplate.Wrap())
- .WriteLine(";");
+ writer.Field(
+ new FieldDeclarationOptions("s_originalFormat", PurviewTypeLibrary.System.String.AsTypeReference())
+ {
+ IsStatic = true,
+ IsReadOnly = true,
+ Initializer = methodTarget.MessageTemplate.Wrap(),
+ IncludeGeneratedAttributes = false,
+ }
+ );
if (nonExceptionParams.Count > 0)
{
@@ -1122,37 +1187,37 @@ static void EmitScopeStateStruct(CodeWriter writer, LogMethodTarget methodTarget
{
context.CancellationToken.ThrowIfCancellationRequested();
- writer
- .Write("public readonly ")
- .Write(param.ParameterType)
- .Write(" _")
- .Write(param.UpperCasedName)
- .WriteLine(";");
- }
-
- writer.NewLine().Write("public ").Write(structName).Write('(');
-
- for (var i = 0; i < nonExceptionParams.Count; i++)
- {
- context.CancellationToken.ThrowIfCancellationRequested();
-
- writer.Write(nonExceptionParams[i].ParameterType).Write(' ').Write(nonExceptionParams[i].Name);
-
- if (i < nonExceptionParams.Count - 1)
- writer.Write(", ");
+ writer.Field(
+ new FieldDeclarationOptions($"_{param.UpperCasedName}", param.ParameterType)
+ {
+ Accessibility = TypeDeclarationAccessibility.Public,
+ IsReadOnly = true,
+ IncludeGeneratedAttributes = false,
+ }
+ );
}
- writer.Write(")");
+ writer.NewLine();
- using (writer.OpenBlockScope())
- {
- foreach (var param in nonExceptionParams)
+ writer.Constructor(
+ new ConstructorDeclarationOptions(structName, TypeDeclarationAccessibility.Public)
{
- context.CancellationToken.ThrowIfCancellationRequested();
+ Parameters =
+ [
+ .. nonExceptionParams.Select(p => new ParameterDeclarationOptions(p.Name, p.ParameterType)),
+ ],
+ IncludeGeneratedAttributes = false,
+ },
+ ctor =>
+ {
+ foreach (var param in nonExceptionParams)
+ {
+ context.CancellationToken.ThrowIfCancellationRequested();
- writer.Write("_").Write(param.UpperCasedName).Write(" = ").Write(param.Name).WriteLine(";");
+ ctor.Write("_").Write(param.UpperCasedName).Write(" = ").Write(param.Name).Line(";");
+ }
}
- }
+ );
}
// Lazy ToString() — format is deferred until a provider actually needs the string.
@@ -1163,86 +1228,112 @@ static void EmitScopeStateStruct(CodeWriter writer, LogMethodTarget methodTarget
[.. methodTarget.Parameters]
);
- writer.NewLine().NewLine().Write("public override string ToString()");
-
- using (writer.OpenBlockScope())
- {
- writer
- .WriteLine("#if NET")
- .Write("return string.Create(global::System.Globalization.CultureInfo.InvariantCulture, $")
- .Write(interpolatedMessage.Wrap())
- .WriteLine(");")
- .WriteLine("#else")
- .Write("return global::System.FormattableString.Invariant($")
- .Write(interpolatedMessage.Wrap())
- .WriteLine(");")
- .WriteLine("#endif");
- }
-
writer
.NewLine()
.NewLine()
- .Write("public int Count => ")
- .Write(count.ToString(CultureInfo.InvariantCulture))
- .WriteLine(";")
- .NewLine()
- .Write($"public {kvpType} this[int index]");
-
- using (writer.OpenBlockScope())
- {
- if (writer.IsNullableContextEnabled is null or true)
- {
- writer.WriteLine("get => index switch {");
- writer.Indent();
- writer.WriteLine("0 => new(\"{OriginalFormat}\", s_originalFormat),");
+ .Method(
+ new MethodDeclarationOptions(
+ "ToString",
+ PurviewTypeLibrary.System.String.AsTypeReference(),
+ TypeDeclarationAccessibility.Public
+ )
+ {
+ IsOverride = true,
+ IncludeGeneratedAttributes = false,
+ },
+ body =>
+ body.HashDefines(
+ "NET",
+ hashWriter =>
+ hashWriter
+ .Write(
+ "return string.Create(global::System.Globalization.CultureInfo.InvariantCulture, $"
+ )
+ .Write(interpolatedMessage.Wrap())
+ .Line(");")
+ .HashElse()
+ .Write("return global::System.FormattableString.Invariant($")
+ .Write(interpolatedMessage.Wrap())
+ .Line(");")
+ )
+ );
- for (var i = 0; i < nonExceptionParams.Count; i++)
+ writer
+ .NewLine()
+ .NewLine()
+ .Property(
+ new PropertyDeclarationOptions(
+ "Count",
+ PurviewTypeLibrary.System.Int32.AsTypeReference(),
+ TypeDeclarationAccessibility.Public
+ )
{
- context.CancellationToken.ThrowIfCancellationRequested();
-
- writer
- .Write($"{i + 1} => new(")
- .Write(nonExceptionParams[i].Name.Wrap())
- .Write(", _")
- .Write(nonExceptionParams[i].UpperCasedName)
- .WriteLine("),");
+ ExpressionBody = count.ToString(CultureInfo.InvariantCulture),
+ IncludeGeneratedAttributes = false,
}
+ )
+ .NewLine();
- writer.WriteLine("_ => throw new global::System.IndexOutOfRangeException(nameof(index))");
- writer.Unindent();
- writer.WriteLine("};");
- }
- else
+ writer.Indexer(
+ new IndexerDeclarationOptions(
+ new TypeReference(new TypeIdentity(kvpType, null)),
+ new ParameterDeclarationOptions("index", PurviewTypeLibrary.System.Int32.AsTypeReference())
+ )
{
- writer.Write("get");
- using (writer.OpenBlockScope())
+ Accessibility = TypeDeclarationAccessibility.Public,
+ IncludeGeneratedAttributes = false,
+ },
+ getter =>
+ {
+ if (writer.IsNullableContextEnabled is null or true)
{
- writer.Write("switch (index)");
- using (writer.OpenBlockScope())
+ getter.Line("return index switch {");
+ getter.Indent();
+ getter.Line("0 => new(\"{OriginalFormat}\", s_originalFormat),");
+
+ for (var i = 0; i < nonExceptionParams.Count; i++)
{
- writer.WriteLine(
- "case 0: return new " + kvpType + "(\"{OriginalFormat}\", s_originalFormat);"
- );
+ context.CancellationToken.ThrowIfCancellationRequested();
+
+ getter
+ .Write($"{i + 1} => new(")
+ .Write(nonExceptionParams[i].Name.Wrap())
+ .Write(", _")
+ .Write(nonExceptionParams[i].UpperCasedName)
+ .Line("),");
+ }
+
+ getter.Line("_ => throw new global::System.IndexOutOfRangeException(nameof(index))");
+ getter.Unindent();
+ getter.Line("};");
+ }
+ else
+ {
+ getter.Write("switch (index)");
+ using (getter.OpenBlockScope())
+ {
+ getter.Line("case 0: return new " + kvpType + "(\"{OriginalFormat}\", s_originalFormat);");
for (var i = 0; i < nonExceptionParams.Count; i++)
{
context.CancellationToken.ThrowIfCancellationRequested();
- writer
+ getter
.Write($"case {i + 1}: return new " + kvpType + "(")
.Write(nonExceptionParams[i].Name.Wrap())
.Write(", _")
.Write(nonExceptionParams[i].UpperCasedName)
- .WriteLine(");");
+ .Line(");");
}
- writer.WriteLine(
- "default: throw new global::System.IndexOutOfRangeException(nameof(index));"
- );
+ getter
+ .Write("default: throw new global::System.IndexOutOfRangeException(nameof(index))")
+ .Line(";");
}
}
- }
- }
+ },
+ null
+ );
EmitStructEnumerator(writer, structName, kvpType, ienumeratorType, ienumerableType, ienumerableKvpType);
}
@@ -1259,22 +1350,27 @@ SourceProductionContext context
{
output.Context.Debug($"Building public delegating logging method: {methodTarget.MethodName}");
- writer.NewLine().Write("public ");
-
- // When Logging owns the public method (with Metrics), return void
- // (Logging without Activity means the return type is void or IDisposable for scoped)
- if (methodTarget.IsScoped)
- writer.Write(TypeLibrary.System.IDisposable.MakeNullable(writer));
- else
- writer.Write(PurviewTypeLibrary.System.Void);
-
- writer.Write(' ').Write(methodTarget.MethodName).Write('(');
-
- EmitParametersAsMethodArgumentList(methodTarget, writer, context);
+ var returnType = methodTarget.IsScoped
+ ? TypeLibrary.System.IDisposable.MakeNullable(writer)
+ : PurviewTypeLibrary.System.Void.AsTypeReference();
- writer.Write(")");
+ writer.NewLine();
- using (writer.OpenBlockScope())
+ using (
+ writer.MethodScope(
+ new MethodDeclarationOptions(methodTarget.MethodName, returnType, TypeDeclarationAccessibility.Public)
+ {
+ Parameters =
+ [
+ .. methodTarget.Parameters.Select(p => new ParameterDeclarationOptions(
+ p.Name,
+ p.ParameterType
+ )),
+ ],
+ IncludeGeneratedAttributes = false,
+ }
+ )
+ )
{
// Call the private Logging method
if (methodTarget.IsScoped)
@@ -1333,7 +1429,7 @@ SourceProductionContext context
// Return if scoped
if (methodTarget.IsScoped)
{
- writer.NewLine().Write("return loggingResult;");
+ writer.NewLine().Return("loggingResult");
}
}
diff --git a/src/src/SourceGenerator/Emitters/LoggerGenTargetClassEmitter.cs b/src/src/SourceGenerator/Emitters/LoggerGenTargetClassEmitter.cs
index 07e90dfd..4c5320ae 100644
--- a/src/src/SourceGenerator/Emitters/LoggerGenTargetClassEmitter.cs
+++ b/src/src/SourceGenerator/Emitters/LoggerGenTargetClassEmitter.cs
@@ -13,7 +13,7 @@ public static void GenerateImplementation(LoggerOutputContext output, SourceProd
var writer = output.CreateWriter();
- using (writer.WriteBlockNamespaceScope(target.ClassNamespace))
+ using (writer.BlockNamespaceScope(target.ClassNamespace))
{
List parentScopes = [];
if (target.TelemetryGeneration.TelemetryNamesNamespace == null)
@@ -21,7 +21,7 @@ public static void GenerateImplementation(LoggerOutputContext output, SourceProd
foreach (var parent in target.ParentClasses)
{
parentScopes.Add(
- writer.WriteClassScope(
+ writer.ClassScope(
new TypeDeclarationOptions(parent) { IsSealed = false, IncludeGeneratedAttributes = false }
)
);
@@ -81,14 +81,16 @@ static void EmitFields(LoggerOutputContext output, CodeWriter writer, SourceProd
context.CancellationToken.ThrowIfCancellationRequested();
writer
- .Write("readonly ")
- .Write(TypeLibrary.Logging.MicrosoftExtensions.ILogger)
- .Write('<')
- .Write(target.InterfaceType)
- .Write('>')
- .Write(' ')
- .Write(PropertyLibrary.Logging.LoggerFieldName)
- .Write(';')
+ .Field(
+ new FieldDeclarationOptions(
+ PropertyLibrary.Logging.LoggerFieldName,
+ TypeLibrary.Logging.MicrosoftExtensions.ILogger.MakeGeneric(target.InterfaceType).AsTypeReference()
+ )
+ {
+ IsReadOnly = true,
+ IncludeGeneratedAttributes = false,
+ }
+ )
.NewLine();
foreach (var methodTarget in target.LogMethods)
diff --git a/src/src/SourceGenerator/Emitters/LoggerTargetClassEmitter.Fields.cs b/src/src/SourceGenerator/Emitters/LoggerTargetClassEmitter.Fields.cs
index 16a6a459..c83f567c 100644
--- a/src/src/SourceGenerator/Emitters/LoggerTargetClassEmitter.Fields.cs
+++ b/src/src/SourceGenerator/Emitters/LoggerTargetClassEmitter.Fields.cs
@@ -14,14 +14,16 @@ static void EmitFields(LoggerOutputContext output, CodeWriter writer, SourceProd
context.CancellationToken.ThrowIfCancellationRequested();
writer
- .Write("readonly ")
- .Write(TypeLibrary.Logging.MicrosoftExtensions.ILogger)
- .Write('<')
- .Write(target.InterfaceType)
- .Write('>')
- .Write(' ')
- .Write(PropertyLibrary.Logging.LoggerFieldName)
- .Write(';')
+ .Field(
+ new FieldDeclarationOptions(
+ PropertyLibrary.Logging.LoggerFieldName,
+ TypeLibrary.Logging.MicrosoftExtensions.ILogger.MakeGeneric(target.InterfaceType).AsTypeReference()
+ )
+ {
+ IsReadOnly = true,
+ IncludeGeneratedAttributes = false,
+ }
+ )
.NewLine()
.NewLine();
@@ -85,70 +87,58 @@ static void EmitFields(LoggerOutputContext output, CodeWriter writer, SourceProd
internal static void EmitLogActionField(CodeWriter writer, LogMethodTarget methodTarget)
{
- writer
- .Write("static readonly ")
- .Write(methodTarget.IsScoped ? PurviewTypeLibrary.System.Func : PurviewTypeLibrary.System.Action)
- .Write('<')
- .Write(TypeLibrary.Logging.MicrosoftExtensions.ILogger)
- .Write(", ");
-
- foreach (var parameter in methodTarget.ParametersSansException)
- writer.Write(parameter.ParameterType).Write(", ");
-
- if (methodTarget.IsScoped)
- {
- writer.Write(TypeLibrary.System.IDisposable.MakeNullable(writer));
- writer.Write("> ");
- }
- else
- {
- writer.Write(TypeLibrary.System.Exception.MakeNullable(writer));
- writer.Write("> ");
- }
-
- writer
- .Write(methodTarget.LoggerActionFieldName)
- .Write(" = ")
- .Write(TypeLibrary.Logging.MicrosoftExtensions.LoggerMessage)
- .Write(".Define");
-
- if (methodTarget.IsScoped)
- writer.Write("Scope");
-
- if (methodTarget.ParameterCountSansException > 0)
- {
- writer.Write('<');
-
- var i = 0;
- foreach (var parameter in methodTarget.ParametersSansException)
+ var useNullable = writer.IsNullableContextEnabled is null or true;
+
+ var typeName =
+ (methodTarget.IsScoped ? "global::System.Func<" : "global::System.Action<")
+ + TypeLibrary.Logging.MicrosoftExtensions.ILogger.RenderFullNameForNullable(useNullable)
+ + string.Concat(
+ methodTarget.ParametersSansException.Select(p =>
+ ", " + p.ParameterType.RenderFullNameForNullable(useNullable)
+ )
+ )
+ + ", "
+ + (
+ methodTarget.IsScoped
+ ? TypeLibrary.System.IDisposable.MakeNullable(writer).RenderFullNameForNullable(useNullable)
+ : TypeLibrary.System.Exception.MakeNullable(writer).RenderFullNameForNullable(useNullable)
+ )
+ + ">";
+
+ var genericArguments =
+ methodTarget.ParameterCountSansException > 0
+ ? "<"
+ + string.Join(
+ ", ",
+ methodTarget.ParametersSansException.Select(p =>
+ p.ParameterType.RenderFullNameForNullable(useNullable)
+ )
+ )
+ + ">"
+ : "";
+
+ var eventId = methodTarget.EventId ?? SharedHelpers.GetNonRandomizedHashCode(methodTarget.MethodName);
+ var arguments = methodTarget.IsScoped
+ ? $"\"{methodTarget.MessageTemplate}\""
+ : $"{methodTarget.MSLevel}, new global::Microsoft.Extensions.Logging.EventId({eventId.ToString(CultureInfo.InvariantCulture)}, \"{methodTarget.LogName}\"), \"{methodTarget.MessageTemplate}\"";
+
+ var initializer =
+ $"global::Microsoft.Extensions.Logging.LoggerMessage.Define"
+ + (methodTarget.IsScoped ? "Scope" : "")
+ + genericArguments
+ + $"({arguments})";
+
+ writer.Field(
+ new FieldDeclarationOptions(
+ methodTarget.LoggerActionFieldName,
+ new TypeReference(new TypeIdentity(typeName, null))
+ )
{
- writer.Write(parameter.ParameterType);
- if (i < methodTarget.ParameterCountSansException - 1)
- writer.Write(", ");
-
- i++;
+ IsStatic = true,
+ IsReadOnly = true,
+ Initializer = initializer,
+ IncludeGeneratedAttributes = false,
}
-
- writer.Write('>');
- }
-
- writer.Write('(');
-
- if (!methodTarget.IsScoped)
- {
- writer.Write(methodTarget.MSLevel).Write(", ");
-
- var eventId = methodTarget.EventId ?? SharedHelpers.GetNonRandomizedHashCode(methodTarget.MethodName);
- writer
- .Write("new ")
- .Write(TypeLibrary.Logging.MicrosoftExtensions.EventId)
- .Write('(')
- .Write(eventId.ToString(CultureInfo.InvariantCulture))
- .Write(", \"")
- .Write(methodTarget.LogName)
- .Write("\"), ");
- }
-
- writer.Write('"').Write(methodTarget.MessageTemplate).Write('"').Write(");").NewLine();
+ );
}
}
diff --git a/src/src/SourceGenerator/Emitters/LoggerTargetClassEmitter.Methods.cs b/src/src/SourceGenerator/Emitters/LoggerTargetClassEmitter.Methods.cs
index 16c932c8..6d8f72cf 100644
--- a/src/src/SourceGenerator/Emitters/LoggerTargetClassEmitter.Methods.cs
+++ b/src/src/SourceGenerator/Emitters/LoggerTargetClassEmitter.Methods.cs
@@ -14,7 +14,7 @@ internal static void EmitThrowStub(CodeWriter writer, LogMethodTarget methodTarg
writer.NewLine();
using (
- writer.WriteMethodScope(
+ writer.MethodScope(
new(methodTarget.MethodName, returnType, TypeDeclarationAccessibility.Public)
{
Parameters =
@@ -114,7 +114,7 @@ SourceProductionContext context
writer.NewLine();
using (
- writer.WriteMethodScope(
+ writer.MethodScope(
new MethodDeclarationOptions(
methodName,
returnType,
@@ -143,15 +143,10 @@ SourceProductionContext context
}
else
{
- writer
- .Write("if (!")
- .Write(PropertyLibrary.Logging.LoggerFieldName)
- .Write(".IsEnabled(")
- .Write(methodTarget.MSLevel)
- .WriteLine("))");
-
- using (writer.OpenBlockScope())
- writer.WriteLine("return;");
+ writer.IfBlock(
+ "!" + PropertyLibrary.Logging.LoggerFieldName + ".IsEnabled(" + methodTarget.MSLevel + ")",
+ static body => body.Return()
+ );
writer
.NewLine()
@@ -202,7 +197,7 @@ SourceProductionContext context
writer.NewLine();
using (
- writer.WriteMethodScope(
+ writer.MethodScope(
new MethodDeclarationOptions(methodTarget.MethodName, returnType, TypeDeclarationAccessibility.Public)
{
Parameters =
@@ -274,7 +269,7 @@ SourceProductionContext context
// Return if scoped
if (methodTarget.IsScoped)
{
- writer.NewLine().Write("return loggingResult;");
+ writer.NewLine().Return("loggingResult");
}
}
diff --git a/src/src/SourceGenerator/Emitters/LoggerTargetClassEmitter.cs b/src/src/SourceGenerator/Emitters/LoggerTargetClassEmitter.cs
index b49c315c..b29e20c7 100644
--- a/src/src/SourceGenerator/Emitters/LoggerTargetClassEmitter.cs
+++ b/src/src/SourceGenerator/Emitters/LoggerTargetClassEmitter.cs
@@ -11,7 +11,7 @@ public static void GenerateImplementation(LoggerOutputContext output, SourceProd
output.Context.Debug($"Generating logging class for: {target.FullyQualifiedName}");
var writer = output.CreateWriter();
- using (writer.WriteBlockNamespaceScope(target.ClassNamespace))
+ using (writer.BlockNamespaceScope(target.ClassNamespace))
{
List parentScopes = [];
if (target.TelemetryGeneration.TelemetryNamesNamespace == null)
@@ -19,7 +19,7 @@ public static void GenerateImplementation(LoggerOutputContext output, SourceProd
foreach (var parent in target.ParentClasses)
{
parentScopes.Add(
- writer.WriteClassScope(
+ writer.ClassScope(
new TypeDeclarationOptions(parent) { IsSealed = false, IncludeGeneratedAttributes = false }
)
);
diff --git a/src/src/SourceGenerator/Emitters/MeterTargetClassEmitter.Fields.cs b/src/src/SourceGenerator/Emitters/MeterTargetClassEmitter.Fields.cs
index 39156f24..3b13d4ea 100644
--- a/src/src/SourceGenerator/Emitters/MeterTargetClassEmitter.Fields.cs
+++ b/src/src/SourceGenerator/Emitters/MeterTargetClassEmitter.Fields.cs
@@ -23,20 +23,31 @@ static void EmitFields(
if (readonlyFields)
{
writer
- .Write("readonly ")
- .Write((string)TypeLibrary.Metrics.SystemDiagnostics.Meter)
- .Write(' ')
- .Write(MeterFieldName)
- .WriteLine(";")
+ .Field(
+ new FieldDeclarationOptions(
+ MeterFieldName,
+ TypeLibrary.Metrics.SystemDiagnostics.Meter.AsTypeReference()
+ )
+ {
+ IsReadOnly = true,
+ IncludeGeneratedAttributes = false,
+ }
+ )
.NewLine();
}
else
{
writer
- .Write(TypeLibrary.Metrics.SystemDiagnostics.Meter)
- .Write(' ')
- .Write(MeterFieldName)
- .WriteLine(writer.IsNullableContextEnabled is null or true ? " = default!;" : " = default;")
+ .Field(
+ new FieldDeclarationOptions(
+ MeterFieldName,
+ TypeLibrary.Metrics.SystemDiagnostics.Meter.AsTypeReference()
+ )
+ {
+ Initializer = writer.IsNullableContextEnabled is null or true ? "default!" : "default",
+ IncludeGeneratedAttributes = false,
+ }
+ )
.NewLine();
}
@@ -64,15 +75,23 @@ static void EmitFields(
if (emitReadonly)
{
- writer.Write("readonly ").Write((string)type).Write(' ').Write(method.FieldName).WriteLine(";");
+ writer.Field(
+ new FieldDeclarationOptions(method.FieldName, type)
+ {
+ IsReadOnly = true,
+ IncludeGeneratedAttributes = false,
+ }
+ );
}
else
{
- writer
- .Write(type)
- .Write(' ')
- .Write(method.FieldName)
- .WriteLine(writer.IsNullableContextEnabled is null or true ? " = default!;" : " = default;");
+ writer.Field(
+ new FieldDeclarationOptions(method.FieldName, type)
+ {
+ Initializer = writer.IsNullableContextEnabled is null or true ? "default!" : "default",
+ IncludeGeneratedAttributes = false,
+ }
+ );
}
}
}
diff --git a/src/src/SourceGenerator/Emitters/MeterTargetClassEmitter.InitializationMethod.cs b/src/src/SourceGenerator/Emitters/MeterTargetClassEmitter.InitializationMethod.cs
index 89fc6efa..efddda15 100644
--- a/src/src/SourceGenerator/Emitters/MeterTargetClassEmitter.InitializationMethod.cs
+++ b/src/src/SourceGenerator/Emitters/MeterTargetClassEmitter.InitializationMethod.cs
@@ -12,31 +12,38 @@ static void EmitInitializationMethod(MeterOutputContext output, CodeWriter write
context.CancellationToken.ThrowIfCancellationRequested();
- writer.NewLine().Write("void ").Write(PropertyLibrary.Metrics.MeterInitializationMethod).Write('(');
-
- if (supportsIMeterFactory)
- {
- writer
- .Write(TypeLibrary.Metrics.SystemDiagnostics.IMeterFactory)
- .Write(' ')
- .Write(PropertyLibrary.Metrics.MeterFactoryParameterName);
- }
-
- writer.Write(")");
-
- using (writer.OpenBlockScope())
+ writer.NewLine();
+
+ using (
+ writer.MethodScope(
+ new MethodDeclarationOptions(
+ PropertyLibrary.Metrics.MeterInitializationMethod,
+ PurviewTypeLibrary.System.Void.AsTypeReference()
+ )
+ {
+ Parameters = supportsIMeterFactory
+ ?
+ [
+ new ParameterDeclarationOptions(
+ PropertyLibrary.Metrics.MeterFactoryParameterName,
+ TypeLibrary.Metrics.SystemDiagnostics.IMeterFactory.AsTypeReference()
+ ),
+ ]
+ : [],
+ IncludeGeneratedAttributes = false,
+ }
+ )
+ )
{
// Double-init guard: prevents re-initialization when the method path is used
// (occurs in Logging+Metrics multi-target where Logging owns the constructor).
- writer.Write("if (").Write(MeterFieldName).WriteLine(" != null)");
-
- using (writer.OpenBlockScope())
- {
- writer
- .Write("throw new ")
- .Write(TypeLibrary.System.Exception)
- .WriteLine("(\"The meters have already been initialized.\");");
- }
+ writer.IfBlock(
+ MeterFieldName + " != null",
+ body =>
+ body.Throw(
+ "new " + TypeLibrary.System.Exception + "(\"The meters have already been initialized.\")"
+ )
+ );
writer.NewLine();
@@ -54,19 +61,25 @@ static void EmitInlineConstructor(MeterOutputContext output, CodeWriter writer,
context.CancellationToken.ThrowIfCancellationRequested();
- writer.NewLine().Write("public ").Write(target.ClassNameToGenerate).Write('(');
-
- if (supportsIMeterFactory)
- {
- writer
- .Write(TypeLibrary.Metrics.SystemDiagnostics.IMeterFactory)
- .Write(' ')
- .Write(PropertyLibrary.Metrics.MeterFactoryParameterName);
- }
-
- writer.Write(")");
-
- using (writer.OpenBlockScope())
+ writer.NewLine();
+
+ using (
+ writer.ConstructorScope(
+ new ConstructorDeclarationOptions(target.ClassNameToGenerate, TypeDeclarationAccessibility.Public)
+ {
+ Parameters = supportsIMeterFactory
+ ?
+ [
+ new ParameterDeclarationOptions(
+ PropertyLibrary.Metrics.MeterFactoryParameterName,
+ TypeLibrary.Metrics.SystemDiagnostics.IMeterFactory.AsTypeReference()
+ ),
+ ]
+ : [],
+ IncludeGeneratedAttributes = false,
+ }
+ )
+ )
{
EmitInitializationBodyContent(output, writer);
}
@@ -79,16 +92,9 @@ static void EmitInitializationBodyContent(MeterOutputContext output, CodeWriter
const string meterTagsVariableName = "meterTags";
var dictType = GetDictionaryType(writer);
- writer
- .Write((string)dictType)
- .Write(' ')
- .Write(meterTagsVariableName)
- .Write(" = new ")
- .Write((string)dictType)
- .WriteLine("();")
- .NewLine();
+ writer.Assignment((string)dictType, meterTagsVariableName, "new " + (string)dictType + "()").NewLine();
- writer.Write(PartialMeterTagsMethod).Write('(').Write(meterTagsVariableName).WriteLine(");").NewLine();
+ writer.Write(PartialMeterTagsMethod).Write('(').Write(meterTagsVariableName).Line(");").NewLine();
if (supportsIMeterFactory)
{
@@ -103,8 +109,8 @@ static void EmitInitializationBodyContent(MeterOutputContext output, CodeWriter
.Write(") {")
.NewLine();
writer.Indent();
- writer.WriteLine("Version = null,");
- writer.WriteLine($"Tags = {meterTagsVariableName}");
+ writer.Line("Version = null,");
+ writer.Line($"Tags = {meterTagsVariableName}");
writer.Unindent();
writer.Write("});").NewLine();
}
@@ -116,7 +122,7 @@ static void EmitInitializationBodyContent(MeterOutputContext output, CodeWriter
.Write(TypeLibrary.Metrics.SystemDiagnostics.Meter)
.Write('(')
.Write(target.MeterName!.Wrap())
- .WriteLine(");")
+ .Line(");")
.NewLine();
}
@@ -137,17 +143,12 @@ static void EmitInitialiseInstrumentVariable(InstrumentTarget method, CodeWriter
var dictType = GetDictionaryType(writer);
writer
- .Write((string)dictType)
- .Write(' ')
- .Write(tagVariableName)
- .Write(" = new ")
- .Write((string)dictType)
- .WriteLine("();")
+ .Assignment((string)dictType, tagVariableName, "new " + (string)dictType + "()")
.NewLine()
.Write(method.TagPopulateMethodName)
.Write('(')
.Write(tagVariableName)
- .WriteLine(");")
+ .Line(");")
.NewLine();
writer
@@ -166,7 +167,7 @@ static void EmitInitialiseInstrumentVariable(InstrumentTarget method, CodeWriter
.Write(description)
.Write(", tags: ")
.Write(tagVariableName)
- .WriteLine(");");
+ .Line(");");
}
}
}
diff --git a/src/src/SourceGenerator/Emitters/MeterTargetClassEmitter.Methods.cs b/src/src/SourceGenerator/Emitters/MeterTargetClassEmitter.Methods.cs
index 0f753732..69114ad6 100644
--- a/src/src/SourceGenerator/Emitters/MeterTargetClassEmitter.Methods.cs
+++ b/src/src/SourceGenerator/Emitters/MeterTargetClassEmitter.Methods.cs
@@ -9,21 +9,33 @@ partial class MeterTargetClassEmitter
{
static void EmitThrowStub(CodeWriter writer, InstrumentTarget methodTarget)
{
- writer.NewLine().Write("public ").Write(methodTarget.ReturnType);
-
- writer.Write(' ').Write(methodTarget.MethodName).Write('(');
+ writer.NewLine();
- for (var i = 0; i < methodTarget.Parameters.Count; i++)
+ using (
+ writer.MethodScope(
+ new MethodDeclarationOptions(
+ methodTarget.MethodName,
+ methodTarget.ReturnType,
+ TypeDeclarationAccessibility.Public
+ )
+ {
+ Parameters =
+ [
+ .. methodTarget.Parameters.Select(p => new ParameterDeclarationOptions(
+ p.ParameterName,
+ p.ParameterType
+ )),
+ ],
+ ExpressionBody = "throw new global::System.NotSupportedException()",
+ IncludeGeneratedAttributes = false,
+ }
+ )
+ )
{
- if (i > 0)
- writer.Write(", ");
- writer
- .Write(methodTarget.Parameters[i].ParameterType)
- .Write(' ')
- .Write(methodTarget.Parameters[i].ParameterName);
+ //
}
- writer.Write(") => throw new global::System.NotSupportedException();").NewLine();
+ writer.NewLine();
}
static void EmitMethods(MeterOutputContext output, CodeWriter writer, SourceProductionContext context)
@@ -73,11 +85,12 @@ static void EmitPartialMethods(MeterOutputContext output, CodeWriter writer, Sou
var dictType = GetDictionaryType(writer);
writer
.NewLine()
- .Write("partial void ")
+ .Write("partial")
+ .Write(" void ")
.Write(PartialMeterTagsMethod)
.Write('(')
.Write(dictType)
- .WriteLine(" meterTags);")
+ .Line(" meterTags);")
.NewLine();
foreach (var instrument in target.InstrumentationMethods)
@@ -89,11 +102,12 @@ static void EmitPartialMethods(MeterOutputContext output, CodeWriter writer, Sou
continue;
writer
- .Write("partial void ")
+ .Write("partial")
+ .Write(" void ")
.Write(instrument.TagPopulateMethodName)
.Write('(')
.Write(dictType)
- .WriteLine(" instrumentTags);")
+ .Line(" instrumentTags);")
.NewLine();
}
}
@@ -132,7 +146,7 @@ SourceProductionContext context
writer.NewLine();
using (
- writer.WriteMethodScope(
+ writer.MethodScope(
new MethodDeclarationOptions(
methodName,
returnType,
@@ -212,29 +226,30 @@ .. methodTarget.Parameters.Select(p =>
static void EmitObservableInstrumentBodyTest(CodeWriter writer, InstrumentTarget method)
{
- writer.Write("if (").Write(method.FieldName).WriteLine(" != null)");
-
- using (writer.OpenBlockScope())
- {
- if (method.InstrumentAttribute?.ThrowOnAlreadyInitialized == true)
- {
- writer
- .Write("throw new ")
- .Write(TypeLibrary.System.Exception)
- .Write("(\"")
- .Write(method.MetricName)
- .WriteLine(" has already been initialized.\");");
- }
- else
+ writer.IfBlock(
+ method.FieldName + " != null",
+ body =>
{
- writer.Write("return");
-
- if (method.ReturnsBool)
- writer.WriteLine(" false;");
+ if (method.InstrumentAttribute?.ThrowOnAlreadyInitialized == true)
+ {
+ writer
+ .Write("throw new ")
+ .Write(TypeLibrary.System.Exception)
+ .Write("(\"")
+ .Write(method.MetricName)
+ .Line(" has already been initialized.\");");
+ }
else
- writer.Write(";").NewLine();
+ {
+ writer.Write("return");
+
+ if (method.ReturnsBool)
+ writer.Line(" false;");
+ else
+ writer.Write(";").NewLine();
+ }
}
- }
+ );
writer.NewLine();
}
@@ -263,14 +278,14 @@ static void EmitObservableInstrumentBody(CodeWriter writer, InstrumentTarget met
if (tagVariableName != null)
{
- writer.WriteLine().Write(", tags: ").WriteLine(tagVariableName);
+ writer.NewLine().Write(", tags: ").Line(tagVariableName);
}
- writer.WriteLine(");");
+ writer.Line(");");
if (method.ReturnsBool)
{
- writer.NewLine().Write("return true;");
+ writer.NewLine().Return("true");
}
}
@@ -335,7 +350,7 @@ static void EmitInstrumentBody(
if (methodTarget.ReturnsBool)
{
- writer.NewLine().Write("return true;");
+ writer.NewLine().Return("true");
}
}
@@ -358,39 +373,20 @@ static void EmitInstrumentBody(
}
var tagVariableName = Utilities.LowercaseFirstChar(methodTarget.MethodName + "TagList");
- writer
- .Write(TypeLibrary.System.TagList)
- .Write(' ')
- .Write(tagVariableName)
- .Write(" = new")
- .WriteLine("();")
- .NewLine();
+ writer.Assignment(TypeLibrary.System.TagList, tagVariableName, "new()").NewLine();
foreach (var param in methodTarget.Tags)
{
if (param.SkipOnNullOrEmpty)
{
- writer.Write("if (").Write(param.ParameterName).WriteLine(" != default)");
- using (writer.OpenBlockScope())
- {
- writer
- .Write(tagVariableName)
- .Write(".Add(")
- .Write(param.GeneratedName.Wrap())
- .Write(", ")
- .Write(param.ParameterName)
- .WriteLine(");");
- }
+ writer.IfBlock(
+ param.ParameterName + " != default",
+ body => body.MethodCallOn(tagVariableName, "Add", param.GeneratedName.Wrap(), param.ParameterName)
+ );
}
else
{
- writer
- .Write(tagVariableName)
- .Write(".Add(")
- .Write(param.GeneratedName.Wrap())
- .Write(", ")
- .Write(param.ParameterName)
- .WriteLine(");");
+ writer.MethodCallOn(tagVariableName, "Add", param.GeneratedName.Wrap(), param.ParameterName);
}
}
diff --git a/src/src/SourceGenerator/Emitters/MeterTargetClassEmitter.cs b/src/src/SourceGenerator/Emitters/MeterTargetClassEmitter.cs
index 20b93053..e0a339ee 100644
--- a/src/src/SourceGenerator/Emitters/MeterTargetClassEmitter.cs
+++ b/src/src/SourceGenerator/Emitters/MeterTargetClassEmitter.cs
@@ -21,7 +21,7 @@ public static void GenerateImplementation(MeterOutputContext output, SourceProdu
output.Context.Debug($"Generating metric class for: {target.FullyQualifiedName}");
var writer = output.CreateWriter();
- using (writer.WriteBlockNamespaceScope(target.ClassNamespace))
+ using (writer.BlockNamespaceScope(target.ClassNamespace))
{
List parentScopes = [];
if (target.TelemetryGeneration.TelemetryNamesNamespace == null)
@@ -29,7 +29,7 @@ public static void GenerateImplementation(MeterOutputContext output, SourceProdu
foreach (var parent in target.ParentClasses)
{
parentScopes.Add(
- writer.WriteClassScope(
+ writer.ClassScope(
new TypeDeclarationOptions(parent) { IsSealed = false, IncludeGeneratedAttributes = false }
)
);
diff --git a/src/src/SourceGenerator/Emitters/TelemetryNamesEmitter.cs b/src/src/SourceGenerator/Emitters/TelemetryNamesEmitter.cs
index d3732ea3..02f5b558 100644
--- a/src/src/SourceGenerator/Emitters/TelemetryNamesEmitter.cs
+++ b/src/src/SourceGenerator/Emitters/TelemetryNamesEmitter.cs
@@ -21,10 +21,11 @@ GenerationContext generationContext
var writer = output.CreateWriter();
var hasNamespace = !string.IsNullOrWhiteSpace(rootNamespace);
- using (writer.WriteBlockNamespaceScope(rootNamespace))
+ using (writer.BlockNamespaceScope(rootNamespace))
{
+ writer.XmlSummary("Contains the names of the meters and activity sources generated for the assembly.");
using (
- writer.WriteClassScope(
+ writer.ClassScope(
new(className)
{
IsStatic = true,
@@ -35,7 +36,8 @@ GenerationContext generationContext
)
{
var stringArrayType = PurviewTypeLibrary.System.String.AsTypeReference().MakeArray();
- writer.WriteField(
+ writer.XmlSummary("Gets the names of the meters generated for the assembly.");
+ writer.Field(
new("MeterNames", stringArrayType, TypeDeclarationAccessibility.Public)
{
IsStatic = true,
@@ -45,7 +47,8 @@ GenerationContext generationContext
}
);
- writer.WriteField(
+ writer.XmlSummary("Gets the names of the activity sources generated for the assembly.");
+ writer.Field(
new("ActivitySourceNames", stringArrayType, TypeDeclarationAccessibility.Public)
{
IsStatic = true,
diff --git a/src/src/SourceGenerator/Helpers/TypeLibrary.cs b/src/src/SourceGenerator/Helpers/TypeLibrary.cs
index 233be40b..4124f34e 100644
--- a/src/src/SourceGenerator/Helpers/TypeLibrary.cs
+++ b/src/src/SourceGenerator/Helpers/TypeLibrary.cs
@@ -338,12 +338,4 @@ public static ImmutableArray GetGeneratedTypes() =>
ExcludeTargetsAttribute,
];
}
-
- public static ImmutableArray GetAllGeneratedTypes() =>
- [
- .. Activities.GetGeneratedTypes(),
- .. Logging.GetGeneratedTypes(),
- .. Metrics.GetGeneratedTypes(),
- .. TelemetryShared.GetGeneratedTypes(),
- ];
}
diff --git a/src/src/SourceGenerator/Helpers/Utilities.cs b/src/src/SourceGenerator/Helpers/Utilities.cs
index 7135ad90..d0a6c234 100644
--- a/src/src/SourceGenerator/Helpers/Utilities.cs
+++ b/src/src/SourceGenerator/Helpers/Utilities.cs
@@ -76,6 +76,9 @@ GenerationType requestedType
if (attribute.AttributeClass == null)
continue;
+ if (attribute.AttributeClass.TypeKind == TypeKind.Error)
+ continue;
+
var attributeType = TypeReference.Create(attribute.AttributeClass);
if (IsActivityAttribute(attributeType))
diff --git a/src/src/SourceGenerator/Records/OutputContexts.cs b/src/src/SourceGenerator/Records/OutputContexts.cs
index c70c148b..9f132eda 100644
--- a/src/src/SourceGenerator/Records/OutputContexts.cs
+++ b/src/src/SourceGenerator/Records/OutputContexts.cs
@@ -10,17 +10,17 @@ readonly record struct ActivityOutputContext(
GenerationContext Context
)
{
- public CodeWriter CreateWriter() => Context.CreateCodeWriter().WriteAutoGeneratedHeader();
+ public CodeWriter CreateWriter() => Context.CreateCodeWriter().AutoGeneratedHeader();
}
readonly record struct LoggerOutputContext(LoggerTarget Target, GenerationContext Context)
{
- public CodeWriter CreateWriter() => Context.CreateCodeWriter().WriteAutoGeneratedHeader();
+ public CodeWriter CreateWriter() => Context.CreateCodeWriter().AutoGeneratedHeader();
}
readonly record struct MeterOutputContext(MeterTarget Target, GenerationContext Context)
{
- public CodeWriter CreateWriter() => Context.CreateCodeWriter().WriteAutoGeneratedHeader();
+ public CodeWriter CreateWriter() => Context.CreateCodeWriter().AutoGeneratedHeader();
}
readonly record struct TelemetryNamesOutputContext(
@@ -30,5 +30,5 @@ readonly record struct TelemetryNamesOutputContext(
GenerationContext Context
)
{
- public CodeWriter CreateWriter() => Context.CreateCodeWriter().WriteAutoGeneratedHeader();
+ public CodeWriter CreateWriter() => Context.CreateCodeWriter().AutoGeneratedHeader();
}
diff --git a/src/tests/SourceGenerator.IntegrationTests/Infra/TestHelpers.cs b/src/tests/SourceGenerator.IntegrationTests/Infra/TestHelpers.cs
index 5f8801a7..c650649a 100644
--- a/src/tests/SourceGenerator.IntegrationTests/Infra/TestHelpers.cs
+++ b/src/tests/SourceGenerator.IntegrationTests/Infra/TestHelpers.cs
@@ -10,6 +10,7 @@ public static string ReplaceOrdinal(this string value, string oldValue, string n
value.Replace(oldValue, newValue, StringComparison.Ordinal);
#endif
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0057:Use range operator")]
public static List GetCasePermutations(string input)
{
List result = [];