src/Cli/func/Common/Utilities.cs (241 lines of code) (raw):
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using Azure.Functions.Cli.Common;
using Azure.Functions.Cli.Diagnostics;
using Colors.Net;
using Colors.Net.StringColorExtensions;
using Microsoft.Azure.WebJobs.Logging;
using Microsoft.Azure.WebJobs.Script;
using Microsoft.Azure.WebJobs.Script.Configuration;
using Microsoft.Azure.WebJobs.Script.Diagnostics;
using Microsoft.Azure.WebJobs.Script.Workers;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
namespace Azure.Functions.Cli
{
internal static class Utilities
{
public const string LogLevelSection = "loglevel";
public const string LogLevelDefaultSection = "Default";
internal static void PrintLogo()
{
ColoredConsole.WriteLine($@"
{AlternateLogoColor("%%%%%%")}
{AlternateLogoColor("%%%%%%")}
@ {AlternateLogoColor("%%%%%%")} @
@@ {AlternateLogoColor("%%%%%%")} @@
@@@ {AlternateLogoColor("%%%%%%%%%%%", 3)} @@@
@@ {AlternateLogoColor("%%%%%%%%%%", 7)} @@
@@ {AlternateLogoColor("%%%%", 1)} @@
@@ {AlternateLogoColor("%%%")} @@
@@ {AlternateLogoColor("%%")} @@
{AlternateLogoColor("%%")}
{AlternateLogoColor("%")}"
.Replace("@", "@".DarkCyan().ToString()))
.WriteLine();
}
internal static void PrintVersion()
{
ColoredConsole
.WriteLine($"\nAzure Functions Core Tools")
.WriteLine($"Core Tools Version: {Constants.CliDetailedVersion + (Environment.Is64BitProcess ? " (64-bit)" : " (32-bit)")}".DarkGray())
.WriteLine($"Function Runtime Version: {ScriptHost.Version}\n".DarkGray());
}
private static RichString AlternateLogoColor(string str, int firstColorCount = -1)
{
if (str.Length == 1)
{
return str.DarkYellow();
}
else if (firstColorCount != -1)
{
return str.Substring(0, firstColorCount).Yellow() + str.Substring(firstColorCount).DarkYellow();
}
else
{
return str.Substring(0, str.Length / 2).Yellow() + str.Substring(str.Length / 2).DarkYellow();
}
}
internal static string SanitizeNameSpace(string nameSpace)
{
return SanitizeLiteral(nameSpace, allowed: ".", removeRegex: "\\.[0-9]");
}
internal static string SanitizeClassName(string className)
{
return SanitizeLiteral(className);
}
internal static string SanitizeLiteral(string unsanitized, string allowed = "", string removeRegex = "")
{
var fillerChar = '_';
if (string.IsNullOrEmpty(unsanitized))
{
return unsanitized;
}
// Literals are allowed to start with '_' and '@'
var sanitized = !char.IsLetter(unsanitized[0]) && !new[] { '_', '@' }.Contains(unsanitized[0])
? new StringBuilder(fillerChar + unsanitized.Substring(0, 1))
: new StringBuilder(unsanitized.Substring(0, 1));
foreach (char character in unsanitized.Substring(1))
{
if (!char.IsLetterOrDigit(character) && !allowed.Contains(character))
{
sanitized.Append(fillerChar);
}
else
{
sanitized.Append(character);
}
}
var sanitizedString = sanitized.ToString();
if (!string.IsNullOrEmpty(removeRegex))
{
Match match = Regex.Match(sanitizedString, removeRegex);
string matchString;
// Keep removing the matching regex until no more match is found
while (!string.IsNullOrEmpty(matchString = match.Value))
{
sanitizedString = sanitizedString.Replace(matchString, new string(fillerChar, matchString.Length));
match = Regex.Match(sanitizedString, removeRegex);
}
}
return sanitizedString;
}
internal static bool EqualsIgnoreCaseAndSpace(string str, string another)
{
return str.Replace(" ", string.Empty).Equals(another.Replace(" ", string.Empty), StringComparison.OrdinalIgnoreCase);
}
// https://stackoverflow.com/a/281679
internal static string BytesToHumanReadable(double length)
{
string[] sizes = { "B", "KB", "MB", "GB", "TB" };
int order = 0;
while (length >= 1024 && order < sizes.Length - 1)
{
order++;
length = length / 1024;
}
// Adjust the format string to your preferences. For example "{0:0.#}{1}" would
// show a single decimal place, and no space.
return string.Format("{0:0.##} {1}", length, sizes[order]);
}
// https://github.com/dotnet/corefx/issues/10361
internal static void OpenBrowser(string url)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Process.Start(new ProcessStartInfo("cmd", $"/c start {url}"));
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
Process.Start("open", url);
}
else
{
Process.Start("xdg-open", url);
}
}
internal static async Task<T> SafeExecution<T>(Func<Task<T>> action)
{
try
{
return await action();
}
catch
{
return default(T);
}
}
internal static string EnsureCoreToolsLocalData()
{
var appDataDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
if (string.IsNullOrEmpty(appDataDir))
{
throw new Exception("Cannot find the Local Application Data.");
}
var localPath = Path.Combine(appDataDir, "azure-functions-core-tools");
FileSystemHelpers.EnsureDirectory(localPath);
return localPath;
}
internal static bool LogLevelExists(IConfigurationRoot hostJsonConfig, string category, out LogLevel outLogLevel)
{
string categoryKey = ConfigurationPath.Combine(ConfigurationSectionNames.JobHost, ConfigurationSectionNames.Logging, LogLevelSection, category);
try
{
if (Enum.TryParse(hostJsonConfig[categoryKey], true, out outLogLevel))
{
return true;
}
}
catch
{
}
outLogLevel = LogLevel.Information;
return false;
}
internal static bool JobHostConfigSectionExists(IConfigurationRoot hostJsonConfig, string sectioName)
{
string configSection = ConfigurationPath.Combine(ConfigurationSectionNames.JobHost, sectioName);
try
{
if (hostJsonConfig.GetSection(configSection).Exists())
{
return true;
}
}
catch
{
// ignored
}
return false;
}
internal static IEnumerable<KeyValuePair<string, string>> BuildUserSecrets(string userSecretsId, IConfigurationRoot hostJsonConfig, bool? verboseLogging)
{
var configureBuilder = new UserSecretsConfigurationBuilder(userSecretsId, new LoggingFilterHelper(hostJsonConfig, verboseLogging), new LoggerFilterOptions());
var configurationBuilder = new ConfigurationBuilder();
configureBuilder.Configure(configurationBuilder);
var root = configurationBuilder.Build();
return root.AsEnumerable();
}
/// <summary>
/// For user logs, returns true if actualLevel of the log is >= default user log level - Information unless overridden in host.json.
/// For system logs, returns true if actualLevel of the log is >= default system log level - Warning unless overridden in host.json.
/// </summary>
/// <param name="category">Category.</param>
/// <param name="actualLevel">Actual log Level.</param>
/// <param name="userLogMinLevel">User minimum log level.</param>
/// <param name="systemLogMinLevel">System minimum log level.</param>
/// <returns>See summary.</returns>
internal static bool DefaultLoggingFilter(string category, LogLevel actualLevel, LogLevel userLogMinLevel, LogLevel systemLogMinLevel)
{
if (LogCategories.IsFunctionUserCategory(category)
|| LogCategories.IsFunctionCategory(category)
|| category.Equals(WorkerConstants.ConsoleLogCategoryName, StringComparison.OrdinalIgnoreCase))
{
return actualLevel >= userLogMinLevel;
}
if (IsSystemLogCategory(category))
{
// System logs
return actualLevel >= systemLogMinLevel;
}
// consider any other category as user log
return actualLevel >= userLogMinLevel;
}
internal static bool IsSystemLogCategory(string category)
{
return ScriptConstants.SystemLogCategoryPrefixes.Where(p => category.StartsWith(p)).Any();
}
internal static IConfigurationRoot BuildHostJsonConfigutation(ScriptApplicationHostOptions hostOptions)
{
IConfigurationBuilder builder = new ConfigurationBuilder();
builder.Add(new HostJsonFileConfigurationSource(hostOptions, SystemEnvironment.Instance, loggerFactory: NullLoggerFactory.Instance, metricsLogger: new MetricsLogger()));
var configuration = builder.Build();
return configuration;
}
internal static bool IsMinifiedVersion()
{
IConfigurationBuilder builder = new ConfigurationBuilder();
builder.AddJsonFile(Constants.ArtifactsConfigFileName, optional: true);
var config = builder.Build();
try
{
var section = config.GetSection(Constants.MinifiedVersionConfigSectionName);
if (section.Exists())
{
string value = section.Value;
return bool.TryParse(value, out bool isValue) && isValue;
}
}
catch
{
}
return false;
}
}
}