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

Completed missing parts of IFileStorage in DevToys CLI #1048

Merged
merged 1 commit into from
Feb 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/app/dev/DevToys.Core/FileHelper.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System.Collections.Concurrent;
using Microsoft.Extensions.Logging;

namespace DevToys.Core;

public static class FileHelper
{
private static readonly ILogger logger = typeof(FileHelper).Log();

private static readonly ConcurrentBag<FileInfo> tempFiles = new();

public static FileInfo CreateTempFile(string baseFolder, string? desiredFileExtension)
Expand All @@ -28,6 +31,8 @@ public static FileInfo CreateTempFile(string baseFolder, string? desiredFileExte

public static void ClearTempFiles(string baseFolder)
{
DateTime startTime = DateTime.UtcNow;

FileInfo[] files = tempFiles.ToArray();
foreach (FileInfo tempFile in files)
{
Expand Down Expand Up @@ -65,5 +70,8 @@ public static void ClearTempFiles(string baseFolder)
}
}
}

double elapsedMilliseconds = (DateTime.UtcNow - startTime).TotalMilliseconds;
logger.LogInformation("Cleared temp files in {elapsedMilliseconds}ms", elapsedMilliseconds);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,38 +63,187 @@ public FileStream OpenWriteFile(string relativeOrAbsoluteFilePath, bool replaceI

public ValueTask<FileStream?> PickSaveFileAsync(params string[] fileTypes)
{
// TODO: Limit input to the indicated file types.
Console.WriteLine(CliStrings.PromptSaveFile);
string? filePath = Console.ReadLine();

if (string.IsNullOrWhiteSpace(filePath))
do
{
return new ValueTask<FileStream?>(Task.FromResult<FileStream?>(null));
}
Console.WriteLine(CliStrings.PromptSaveFile);
string? filePath = Console.ReadLine();

if (filePath is null || string.IsNullOrWhiteSpace(filePath))
{
return new ValueTask<FileStream?>(Task.FromResult<FileStream?>(null));
}

// Remove quotes in case the user copy-pasted the file path from the file explorer.
filePath = TrimFilePath(filePath);

if (IsFileOfType(filePath, fileTypes))
{
return new ValueTask<FileStream?>(File.OpenWrite(filePath));
}

return new ValueTask<FileStream?>(File.OpenWrite(filePath!));
Console.Error.WriteLine(CliStrings.InvalidFileType, string.Join(", ", fileTypes));
} while (true);
}

public ValueTask<SandboxedFileReader?> PickOpenFileAsync(params string[] fileTypes)
{
// TODO: prompt the user to type in the console a relative or absolute file path that has one of the file types indicated.
throw new NotImplementedException();
do
{
Console.WriteLine(CliStrings.PromptOpenFile);
string? filePath = Console.ReadLine();

if (filePath is null || string.IsNullOrWhiteSpace(filePath))
{
return new ValueTask<SandboxedFileReader?>(Task.FromResult<SandboxedFileReader?>(null));
}

// Remove quotes in case the user copy-pasted the file path from the file explorer.
filePath = TrimFilePath(filePath);

var fileInfo = new FileInfo(filePath);
if (fileInfo.Exists)
{
if (IsFileOfType(filePath, fileTypes))
{
return new ValueTask<SandboxedFileReader?>(SandboxedFileReader.FromFileInfo(fileInfo));
}
else
{
Console.Error.WriteLine(CliStrings.InvalidFileType, string.Join(", ", fileTypes));
}
}
else
{
Console.Error.WriteLine(CliStrings.FileNotFound, fileInfo.FullName);
}
} while (true);
}

public ValueTask<SandboxedFileReader[]> PickOpenFilesAsync(params string[] fileTypes)
{
// TODO: prompt the user to type in the console a relative or absolute file path that has one of the file types indicated.
throw new NotImplementedException();
do
{
Console.WriteLine(CliStrings.PromptOpenFiles);
string? filePaths = Console.ReadLine();

if (filePaths is null || string.IsNullOrWhiteSpace(filePaths))
{
return new ValueTask<SandboxedFileReader[]>(Task.FromResult(Array.Empty<SandboxedFileReader>()));
}

string[] paths = filePaths.Split(',', StringSplitOptions.RemoveEmptyEntries);
var filesInfo = new FileInfo[paths.Length];
bool succeeded = true;

for (int i = 0; i < paths.Length; i++)
{
string filePath = paths[i];

// Remove quotes in case the user copy-pasted the file path from the file explorer.
filePath = TrimFilePath(filePath);

var fileInfo = new FileInfo(filePath);
if (fileInfo.Exists)
{
if (IsFileOfType(filePath, fileTypes))
{
filesInfo[i] = fileInfo;
}
else
{
succeeded = false;
Console.Error.WriteLine(CliStrings.InvalidFileType, string.Join(", ", fileTypes));
break;
}
}
else
{
succeeded = false;
Console.Error.WriteLine(CliStrings.FileNotFound, fileInfo.FullName);
break;
}
}

if (succeeded)
{
var readers = new SandboxedFileReader[filesInfo.Length];
for (int i = 0; i < filesInfo.Length; i++)
{
readers[i] = SandboxedFileReader.FromFileInfo(filesInfo[i]);
}

return new ValueTask<SandboxedFileReader[]>(Task.FromResult(readers));
}
} while (true);
}

public ValueTask<string?> PickFolderAsync()
{
// TODO: prompt the user to type in the console a relative or absolute file path that has one of the file types indicated.
throw new NotImplementedException();
Console.WriteLine(CliStrings.PromptOpenFolder);
string? folderPath = Console.ReadLine();

if (folderPath is null || string.IsNullOrWhiteSpace(folderPath))
{
return new ValueTask<string?>(Task.FromResult<string?>(null));
}

// Remove quotes in case the user copy-pasted the file path from the file explorer.
folderPath = TrimFilePath(folderPath);

DirectoryInfo directoryInfo = Directory.CreateDirectory(folderPath);

return new ValueTask<string?>(Task.FromResult<string?>(directoryInfo.FullName));
}

public FileInfo CreateSelfDestroyingTempFile(string? desiredFileExtension = null)
{
return FileHelper.CreateTempFile(Constants.AppTempFolder, desiredFileExtension);
}

private static bool IsFileOfType(string filePath, string[] fileTypes)
{
if (AreAnyFileTypesValid(fileTypes))
{
return true;
}

string fileExtension = Path.GetExtension(filePath);
for (int i = 0; i < fileTypes.Length; i++)
{
string fileType = fileTypes[i];
string editedFileType = "." + fileType.Trim('*').Trim('.').ToLower();
if (string.Equals(fileExtension, editedFileType, StringComparison.CurrentCultureIgnoreCase))
{
return true;
}
}

return false;
}

private static bool AreAnyFileTypesValid(string[] fileTypes)
{
if (fileTypes is null || fileTypes.Length == 0)
{
return true;
}

return Array.Exists(fileTypes, fileType => string.Equals(fileType, "*.*", StringComparison.CurrentCultureIgnoreCase));
}

private static string TrimFilePath(string filePath)
{
// Remove quotes in case the user copy-pasted the file path from the file explorer.
// We do these trim in a loop in case the user input is something like ` "" C:\file.txt "" `.

int fileLength;

do
{
fileLength = filePath.Length;
filePath = filePath.Trim(' ').Trim('\"').Trim('\'');
} while (filePath.Length != fileLength);

return filePath;
}
}

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

Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,22 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="FileNotFound" xml:space="preserve">
<value>The file '{0}' doesn't exist.</value>
</data>
<data name="InvalidFileType" xml:space="preserve">
<value>Invalid file path. The file should have one of the following extensions: {0}</value>
</data>
<data name="PromptOpenFile" xml:space="preserve">
<value>Please type a relative or absolute file path to read (or press Enter to cancel):</value>
</data>
<data name="PromptOpenFiles" xml:space="preserve">
<value>Please type one or many relative or absolute file paths to read, separated by a comma (or press Enter to cancel):</value>
</data>
<data name="PromptOpenFolder" xml:space="preserve">
<value>Please type a relative or absolute folder path (or press Enter to cancel):</value>
</data>
<data name="PromptSaveFile" xml:space="preserve">
<value>Please type a relative or absolute file path to write in:</value>
<value>Please type a relative or absolute file path to write in (or press Enter to cancel):</value>
</data>
</root>