File: LibraryDependencyTargetUtils.cs
Web Access
Project: src\src\nuget-client\src\NuGet.Core\NuGet.LibraryModel\NuGet.LibraryModel.csproj (NuGet.LibraryModel)
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;

namespace NuGet.LibraryModel
{
    /// <summary>
    /// Helper methods for dealing with LibraryDependencyTarget strings.
    /// </summary>
    public static class LibraryDependencyTargetUtils
    {
        /// <summary>
        /// Convert flag string into a LibraryTypeFlag.
        /// </summary>
        public static LibraryDependencyTarget Parse(string? flag)
        {
            // If the LibraryDependency does not have a flag value it is considered all
            if (string.IsNullOrEmpty(flag))
            {
                return LibraryDependencyTarget.All;
            }

            var flagEnd = flag!.IndexOf(',');
            if (flagEnd == -1)
            {
                var segment = StringSegment.CreateTrimmed(flag, 0, flag.Length - 1);
                if (segment.Length == 0)
                {
                    return LibraryDependencyTarget.All;
                }

                return ParseSingleFlag(segment);
            }
            else
            {
                return ParseMultiFlag(flag, flagEnd);
            }
        }

        private static LibraryDependencyTarget ParseMultiFlag(string flag, int end)
        {
            var result = LibraryDependencyTarget.None;
            var flagsAdded = 0;
            var start = 0;
            do
            {
                var segment = StringSegment.CreateTrimmed(flag, start, end - 1);
                if (segment.Length > 0)
                {
                    flagsAdded++;
                    result |= ParseSingleFlag(segment);
                }

                start = end + 1;
                if (start >= flag.Length)
                {
                    // Reached end, don't look for next comma
                    break;
                }

                end = flag.IndexOf(',', start);
                if (end == -1)
                {
                    end = flag.Length;
                }
            } while (true);

            return flagsAdded > 0 ? result : LibraryDependencyTarget.All;
        }

        private static LibraryDependencyTarget ParseSingleFlag(StringSegment flag)
        {
            var result = LibraryDependencyTarget.None;

            if ("package".Length == flag.Length && string.Compare(flag.String, flag.Start, "package", 0, flag.Length, StringComparison.OrdinalIgnoreCase) == 0)
            {
                result = LibraryDependencyTarget.Package;
            }
            else if ("project".Length == flag.Length && string.Compare(flag.String, flag.Start, "project", 0, flag.Length, StringComparison.OrdinalIgnoreCase) == 0)
            {
                result = LibraryDependencyTarget.Project;
            }
            else if ("externalproject".Length == flag.Length && string.Compare(flag.String, flag.Start, "externalproject", 0, flag.Length, StringComparison.OrdinalIgnoreCase) == 0)
            {
                result = LibraryDependencyTarget.ExternalProject;
            }
            else if ("reference".Length == flag.Length && string.Compare(flag.String, flag.Start, "reference", 0, flag.Length, StringComparison.OrdinalIgnoreCase) == 0)
            {
                result = LibraryDependencyTarget.Reference;
            }
            else if ("assembly".Length == flag.Length && string.Compare(flag.String, flag.Start, "assembly", 0, flag.Length, StringComparison.OrdinalIgnoreCase) == 0)
            {
                result = LibraryDependencyTarget.Assembly;
            }
            else if ("winmd".Length == flag.Length && string.Compare(flag.String, flag.Start, "winmd", 0, flag.Length, StringComparison.OrdinalIgnoreCase) == 0)
            {
                result = LibraryDependencyTarget.WinMD;
            }
            else if ("all".Length == flag.Length && string.Compare(flag.String, flag.Start, "all", 0, flag.Length, StringComparison.OrdinalIgnoreCase) == 0)
            {
                result = LibraryDependencyTarget.All;
            }

            return result;
        }

        /// <summary>
        /// Convert type flags to a friendly string.
        /// </summary>
        public static string GetFlagString(LibraryDependencyTarget flags)
        {
            if (flags == LibraryDependencyTarget.None)
            {
                return "none";
            }

            if (flags == LibraryDependencyTarget.All)
            {
                return "all";
            }

            var flagStrings = new List<string>();

            foreach (LibraryDependencyTarget value in EnumUtility.GetValues<LibraryDependencyTarget>())
            {
                if (value != LibraryDependencyTarget.None && flags.HasFlag(value))
                {
                    flagStrings.Add(value.ToString().ToLowerInvariant());
                }
            }

            return string.Join(",", flagStrings);
        }

        private static readonly ConcurrentDictionary<LibraryDependencyTarget, string> LibraryDependencyTargetCache = new();

        /// <summary>
        /// Efficiently converts <see cref="LibraryDependencyTarget"/> to it's <see cref="string"/> representation.
        /// </summary>
        /// <param name="includeFlags">The <see cref="LibraryDependencyTarget"/> instance to get the <see cref="string"/> representation for.</param>
        /// <returns>The <see cref="string"/> representation of <paramref name="includeFlags"/>.</returns>
        public static string AsString(this LibraryDependencyTarget includeFlags)
        {
            if (!LibraryDependencyTargetCache.TryGetValue(includeFlags, out string? enumAsString))
            {
                enumAsString = includeFlags.ToString();
                LibraryDependencyTargetCache.TryAdd(includeFlags, enumAsString);
            }

            return enumAsString;
        }

        [DebuggerDisplay("{String.Substring(Start, Length)}")]
        private struct StringSegment
        {
            public static StringSegment CreateTrimmed(string String, int start, int end)
            {
                for (; start <= end; start++)
                {
                    if (!char.IsWhiteSpace(String[start])) break;
                }

                for (; end >= start; end--)
                {
                    if (!char.IsWhiteSpace(String[end])) break;
                }

                return new StringSegment
                {
                    Start = start,
                    Length = end - start + 1,
                    String = String
                };
            }

            public int Start;
            public int Length;
            public string String;
        }
    }
}