File: Logging\BinaryLogger\Postprocessing\ArchiveFile.cs
Web Access
Project: ..\..\..\src\Build\Microsoft.Build.csproj (Microsoft.Build)
// 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.IO;
using System.IO.Compression;
using System.Text;
using Microsoft.Build.Shared;
 
namespace Microsoft.Build.Logging
{
    /// <summary>
    /// An object model for binlog embedded files.
    /// Used in <see cref="IBuildEventArgsReaderNotifications.ArchiveFileEncountered"/> event.
    /// </summary>
    public abstract class ArchiveData : IDisposable
    {
        private protected ArchiveData(string fullPath) => FullPath = fullPath;
 
        /// <summary>
        /// Full path of the original file before it was put in the embedded archive.
        /// </summary>
        public string FullPath { get; }
 
        /// <summary>
        /// Materializes the whole content of the embedded file in memory as a string.
        /// </summary>
        /// <returns></returns>
        public abstract ArchiveFile ToArchiveFile();
 
        public virtual void Dispose()
        { }
    }
 
    /// <summary>
    /// Fully materialized (in-memory) embedded file.
    /// Easier to work with (the content is expressed in a single string), but more memory greedy.
    /// </summary>
    public sealed class ArchiveFile : ArchiveData
    {
        public ArchiveFile(string fullPath, string content)
            : base(fullPath)
            => Content = content;
 
        /// <summary>
        /// The content of the original file.
        /// </summary>
        public string Content { get; }
 
        /// <inheritdoc cref="ArchiveData.ToArchiveFile" />
        public override ArchiveFile ToArchiveFile()
            => this;
    }
 
    /// <summary>
    /// Lazy (streaming) embedded file.
    /// Might be favorable for large files, as it doesn't materialize the whole content in memory.
    /// </summary>
    public sealed class ArchiveStream : ArchiveData
    {
        public ArchiveStream(string fullPath, StreamReader contentReader)
            : base(fullPath)
            => ContentReader = contentReader;
 
        /// <summary>
        /// Stream over the content of the archived file.
        /// </summary>
        public StreamReader ContentReader { get; }
 
        /// <summary>
        /// Creates an externally exposable embedded file representation from a <see cref="ZipArchiveEntry"/> (which is an implementation detail currently).
        /// </summary>
        /// <param name="entry"></param>
        /// <returns></returns>
        internal static ArchiveStream From(ZipArchiveEntry entry)
        {
            return new ArchiveStream(entry.FullName, new StreamReader(entry.Open()));
        }
 
        /// <inheritdoc cref="ArchiveData.ToArchiveFile" />
        public override ArchiveFile ToArchiveFile()
        {
            var content = ContentReader.ReadToEnd();
            ContentReader.Dispose();
            return new ArchiveFile(FullPath, content);
        }
 
        public override void Dispose()
            => ContentReader.Dispose();
    }
}