Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Get-FileEncoding cmdlet #42

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ project.lock.json
Microsoft.PowerShell.TextUtility.xml
# VSCode directories that are not at the repository root
/**/.vscode/
# Visual Studio IDE directory
.vs/
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@ Return a base64 encoded representation of a string.
## ConvertFrom-TextTable

This will convert tabular data and convert it to an object.

## Get-FileEncoding

This cmdlet returns encoding for a file.
30 changes: 30 additions & 0 deletions TextUtility.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.002.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{CBF81F7C-6E0A-4695-AA0F-35C61676B7EB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerShell.TextUtility", "src\code\Microsoft.PowerShell.TextUtility.csproj", "{B5249E6A-DBD6-49AD-B78B-DFF09CB9D26F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B5249E6A-DBD6-49AD-B78B-DFF09CB9D26F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B5249E6A-DBD6-49AD-B78B-DFF09CB9D26F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B5249E6A-DBD6-49AD-B78B-DFF09CB9D26F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B5249E6A-DBD6-49AD-B78B-DFF09CB9D26F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{B5249E6A-DBD6-49AD-B78B-DFF09CB9D26F} = {CBF81F7C-6E0A-4695-AA0F-35C61676B7EB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {EAC68D5E-4DA0-4F37-88B6-CAF32652C01F}
EndGlobalSection
EndGlobal
2 changes: 1 addition & 1 deletion src/Microsoft.PowerShell.TextUtility.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
PowerShellVersion = '5.1'
FormatsToProcess = @('Microsoft.PowerShell.TextUtility.format.ps1xml')
CmdletsToExport = @(
'Compare-Text','ConvertFrom-Base64','ConvertTo-Base64',"ConvertFrom-TextTable"
'Compare-Text','ConvertFrom-Base64','ConvertTo-Base64',"ConvertFrom-TextTable","Get-FileEncoding"
)
PrivateData = @{
PSData = @{
Expand Down
70 changes: 70 additions & 0 deletions src/code/GetFileEncodingCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.IO;
using System.Management.Automation;
using System.Text;

namespace Microsoft.PowerShell.TextUtility
{
/// <summary>
/// This class implements the Get-FileEncoding command.
/// </summary>
[Cmdlet(VerbsCommon.Get, "FileEncoding", DefaultParameterSetName = PathParameterSet)]
[OutputType(typeof(Encoding))]
public sealed class GetFileEncodingCommand : PSCmdlet
{
#region Parameter Sets

private const string PathParameterSet = "ByPath";
private const string LiteralPathParameterSet = "ByLiteralPath";

#endregion

#region Parameters

/// <summary>
/// Gets or sets path from from which to get encoding.
/// </summary>
[Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = PathParameterSet)]
public string Path { get; set; }

/// <summary>
/// Gets or sets literal path from which to get encoding.
/// </summary>
[Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = LiteralPathParameterSet)]
[Alias("PSPath", "LP")]
public string LiteralPath
{
get
{
return _isLiteralPath ? Path : null;
}

set
{
Path = value;
_isLiteralPath = true;
}
}

private bool _isLiteralPath;

#endregion

/// <summary>
/// Process paths to get file encoding.
/// </summary>
protected override void ProcessRecord()
{
string resolvedPath = PathUtils.ResolveFilePath(Path, this, _isLiteralPath);

if (!File.Exists(resolvedPath))
{
PathUtils.ReportPathNotFound(Path, this);
}

WriteObject(PathUtils.GetPathEncoding(resolvedPath));
}
}
}
17 changes: 16 additions & 1 deletion src/code/Microsoft.PowerShell.TextUtility.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<PackageReference Include="PowerShellStandard.Library" Version="5.1.0-*">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="System.Text.Json" Version="6.0.6" >
<PackageReference Include="System.Text.Json" Version="6.0.6">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
Expand All @@ -28,4 +28,19 @@
</Content>
</ItemGroup>

<ItemGroup>
<Compile Update="Resources\PathUtilityStrings.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>PathUtilityStrings.resx</DependentUpon>
</Compile>
</ItemGroup>

<ItemGroup>
<EmbeddedResource Update="Resources\PathUtilityStrings.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>PathUtilityStrings.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>

</Project>
115 changes: 115 additions & 0 deletions src/code/PathUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
using System;
using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
using System.Management.Automation;
using System.Text;
using Microsoft.PowerShell.TextUtility.Resources;

namespace Microsoft.PowerShell.TextUtility
{
/// <summary>
/// Defines generic path utilities and helper methods for TextUtility.
/// </summary>
internal static class PathUtils
{
/// <summary>
/// Resolves user provided path using file system provider.
/// </summary>
/// <param name="path">The path to resolve.</param>
/// <param name="command">The command.</param>
/// <param name="isLiteralPath">True if the wildcard resolution should not be attempted.</param>
/// <returns>The resolved (absolute) path.</returns>
internal static string ResolveFilePath(string path, PSCmdlet command, bool isLiteralPath)
{
string resolvedPath;

try
{
ProviderInfo provider = null;
PSDriveInfo drive = null;

PathIntrinsics sessionStatePath = command.SessionState.Path;

if (isLiteralPath)
{
resolvedPath = sessionStatePath.GetUnresolvedProviderPathFromPSPath(path, out provider, out drive);
}
else
{
Collection<string> filePaths = sessionStatePath.GetResolvedProviderPathFromPSPath(path, out provider);

if (!provider.Name.Equals("FileSystem", StringComparison.OrdinalIgnoreCase))
{
ReportOnlySupportsFileSystemPaths(path, command);
}

if (filePaths.Count > 1)
{
ReportMultipleFilesNotSupported(command);
}

resolvedPath = filePaths[0];
}
}
catch (ItemNotFoundException)
{
resolvedPath = null;
}

return resolvedPath;
}

/// <summary>
/// Throws terminating error for not using file system provider.
/// </summary>
/// <param name="path">The path to report.</param>
/// <param name="command">The command.</param>
internal static void ReportOnlySupportsFileSystemPaths(string path, PSCmdlet command)
{
var errorMessage = string.Format(CultureInfo.CurrentCulture, PathUtilityStrings.OnlySupportsFileSystemPaths, path);
var exception = new ArgumentException(errorMessage);
var errorRecord = new ErrorRecord(exception, "OnlySupportsFileSystemPaths", ErrorCategory.InvalidArgument, path);
command.ThrowTerminatingError(errorRecord);
}

/// <summary>
/// Throws terminating error for path not found.
/// </summary>
/// <param name="path">The path to report.</param>
/// <param name="command">The command.</param>
internal static void ReportPathNotFound(string path, PSCmdlet command)
{
var errorMessage = string.Format(CultureInfo.CurrentCulture, PathUtilityStrings.PathNotFound, path);
var exception = new ArgumentException(errorMessage);
var errorRecord = new ErrorRecord(exception, "PathNotFound", ErrorCategory.ObjectNotFound, path);
command.ThrowTerminatingError(errorRecord);
}

/// <summary>
/// Throws terminating error for multiple files being used.
/// </summary>
/// <param name="command">The command.</param>
internal static void ReportMultipleFilesNotSupported(PSCmdlet command)
{
var errorMessage = string.Format(CultureInfo.CurrentCulture, PathUtilityStrings.MultipleFilesNotSupported);
var exception = new ArgumentException(errorMessage);
var errorRecord = new ErrorRecord(exception, "MultipleFilesNotSupported", ErrorCategory.InvalidArgument, null);
command.ThrowTerminatingError(errorRecord);
}

/// <summary>
/// Gets encoding for path.
/// </summary>
/// <param name="path">The path to get file encoding.</param>
/// <returns>The encoding of file.</returns>
internal static Encoding GetPathEncoding(string path)
{
using (var reader = new StreamReader(path, Encoding.Default, detectEncodingFromByteOrderMarks: true))
{
_ = reader.Read();
return reader.CurrentEncoding;
}
}
}
}
105 changes: 105 additions & 0 deletions src/code/Resources/PathUtilityStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading