File: FakeRedactor.cs
Web Access
Project: src\src\Libraries\Microsoft.Extensions.Compliance.Testing\Microsoft.Extensions.Compliance.Testing.csproj (Microsoft.Extensions.Compliance.Testing)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Globalization;
#if NET8_0_OR_GREATER
using System.Text;
#endif
using System.Threading;
using Microsoft.Extensions.Compliance.Redaction;
using Microsoft.Extensions.Options;
using Microsoft.Shared.Diagnostics;
 
namespace Microsoft.Extensions.Compliance.Testing;
 
/// <summary>
/// Redactor designed for use in tests.
/// </summary>
public class FakeRedactor : Redactor
{
#if NET8_0_OR_GREATER
    private readonly CompositeFormat _format;
#else
    private readonly string _format;
#endif
 
    private int _redactedSoFar;
 
    /// <summary>
    /// Gets the collector of redaction events.
    /// </summary>
    public FakeRedactionCollector EventCollector { get; }
 
    /// <summary>
    /// Initializes a new instance of the <see cref="FakeRedactor"/> class.
    /// </summary>
    /// <param name="options">The options to control the redactor's behavior.</param>
    /// <param name="collector">Collects info about redacted values.</param>
    public FakeRedactor(IOptions<FakeRedactorOptions>? options = null, FakeRedactionCollector? collector = null)
    {
        var opt = options ?? Microsoft.Extensions.Options.Options.Create(new FakeRedactorOptions());
 
        IValidateOptions<FakeRedactorOptions> validator = new FakeRedactorOptionsAutoValidator();
        var r = validator.Validate(nameof(options), opt.Value);
        if (r.Succeeded)
        {
            validator = new FakeRedactorOptionsCustomValidator();
            r = validator.Validate(nameof(options), opt.Value);
        }
 
        if (r.Failed)
        {
            Throw.ArgumentException(nameof(options), r.ToString());
        }
 
        EventCollector = collector ?? new FakeRedactionCollector();
 
#if NET8_0_OR_GREATER
        _format = CompositeFormat.Parse(opt.Value.RedactionFormat);
#else
        _format = opt.Value.RedactionFormat;
#endif
    }
 
    /// <summary>
    /// Initializes a new instance of the <see cref="FakeRedactor"/> class.
    /// </summary>
    /// <param name="options">The options to control behavior of redactor.</param>
    /// <param name="collector">Collects info about redacted values.</param>
    /// <returns>New instance of <see cref="FakeRedactor"/>.</returns>
    public static FakeRedactor Create(FakeRedactorOptions? options = null, FakeRedactionCollector? collector = null) => new(Options.Options.Create(options ?? new FakeRedactorOptions()), collector);
 
    /// <inheritdoc/>
    public override int Redact(ReadOnlySpan<char> source, Span<char> destination)
    {
        var sourceString = source.ToString();
        var str = string.Format(CultureInfo.InvariantCulture, _format, sourceString);
 
        Throw.IfBufferTooSmall(destination.Length, str.Length, nameof(destination));
 
        str.AsSpan().CopyTo(destination);
 
        var order = Interlocked.Increment(ref _redactedSoFar);
        EventCollector.Append(new RedactedData(sourceString, destination.Slice(0, str.Length).ToString(), order));
 
        return str.Length;
    }
 
    /// <inheritdoc/>
    public override int GetRedactedLength(ReadOnlySpan<char> input)
    {
        return string.Format(CultureInfo.InvariantCulture, _format, input.ToString()).Length;
    }
}