Dependency Injection Style

AOT Compiling
Switched to Secret from Development json file
Added Kanbn
Humanizer
HelperCreateNoteFiles.CleanExistingFiles
HelperPackageFilesByDate
Added SRP
Helper Hardcoded File Search and Sort
Set Date from Zip Entry
This commit is contained in:
2023-07-08 10:05:52 -07:00
parent 81472165f7
commit 229b508ae1
39 changed files with 1584 additions and 781 deletions

618
Helpers/HelperMarkdown.cs Normal file
View File

@ -0,0 +1,618 @@
using Humanizer;
using System.Text;
using System.Text.Json;
namespace File_Folder_Helper.Helpers;
internal static partial class HelperMarkdown
{
/// <summary>
/// Determines a text file's encoding by analyzing its byte order mark (BOM).
/// Defaults to ASCII when detection of the text file's endianness fails.
/// </summary>
/// <param name="filename">The text file to analyze.</param>
/// <returns>The detected encoding.</returns>
internal static Encoding? GetEncoding(string filename)
{
Encoding? result;
byte[] bom = new byte[4];
using FileStream file = new(filename, FileMode.Open, FileAccess.Read);
_ = file.Read(bom, 0, 4);
if (bom[0] == 0x2b && bom[1] == 0x2f && bom[2] == 0x76)
#pragma warning disable SYSLIB0001
result = Encoding.UTF7;
#pragma warning restore SYSLIB0001
if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf)
result = Encoding.UTF8;
if (bom[0] == 0xff && bom[1] == 0xfe && bom[2] == 0 && bom[3] == 0)
result = Encoding.UTF32; //UTF-32LE
if (bom[0] == 0xff && bom[1] == 0xfe)
result = Encoding.Unicode; //UTF-16LE
if (bom[0] == 0xfe && bom[1] == 0xff)
result = Encoding.BigEndianUnicode; //UTF-16BE
if (bom[0] == 0 && bom[1] == 0 && bom[2] == 0xfe && bom[3] == 0xff)
result = new UTF32Encoding(true, true); //UTF-32BE
else
result = null;
return result;
}
internal static string[] GetFiles(Models.AppSettings appSettings, string directory)
{
string[] files = Directory.GetFiles(directory, "*.md", SearchOption.AllDirectories).
Where(l => !appSettings.Exclude.Any(m => l.Contains(m))).ToArray();
return files;
}
private static (string type, string h1) GetTypeAndH1(Models.AppSettings appSettings, string h1, List<string> lines, LineNumber lineNumber)
{
string type = lineNumber.Type is null ? appSettings.DefaultNoteType : lines[lineNumber.Type.Value].Replace("type: ", string.Empty);
string h1FromFile = lineNumber.H1 is null ? h1 : lines[lineNumber.H1.Value][2..];
return (type, h1FromFile);
}
internal static (List<string>, LineNumber) GetStatusAndMetaEndLineNumbers(FileInfo fileInfo)
{
string line;
int? h1LineNumber = null;
int? typeLineNumber = null;
int? statusLineNumber = null;
int? createdLineNumber = null;
int? updatedLineNumber = null;
int? metaEndLineNumber = null;
Encoding? encoding = GetEncoding(fileInfo.FullName) ?? Encoding.Default;
string[] lines = File.ReadAllLines(fileInfo.FullName, encoding);
for (int i = 1; i < lines.Length; i++)
{
line = lines[i];
if (line.Length < 3)
continue;
if (line[..3] == "---")
{
metaEndLineNumber = i;
continue;
}
if (line.Length > 6 && line[..6] == "type: ")
{
typeLineNumber = i;
continue;
}
if (line.Length > 8 && line[..8] == "status: ")
{
statusLineNumber = i;
continue;
}
if (line.Length > 9 && line[..9] == "created: ")
{
createdLineNumber = i;
continue;
}
if (line.Length > 9 && line[..9] == "updated: ")
{
updatedLineNumber = i;
continue;
}
if (h1LineNumber is null && line.Length > 2 && line[..2] == "# ")
{
h1LineNumber = i;
continue;
}
}
LineNumber lineNumber = new(createdLineNumber,
h1LineNumber,
metaEndLineNumber,
statusLineNumber,
typeLineNumber,
updatedLineNumber);
return (lines.ToList(), lineNumber);
}
internal static List<(MarkdownFile, string[])> GetCollection(Models.AppSettings appSettings, string[] files)
{
List<(MarkdownFile, string[])> results = new();
string h1;
string type;
FileInfo fileInfo;
List<string> lines;
LineNumber lineNumber;
MarkdownFile markdownFile;
string fileNameWithoutExtension;
foreach (string file in files)
{
fileInfo = new(file);
if (fileInfo.DirectoryName is null)
continue;
(lines, lineNumber) = GetStatusAndMetaEndLineNumbers(fileInfo);
fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileInfo.FullName);
h1 = fileNameWithoutExtension.ToLower().Hyphenate();
if (lines.Any())
(type, h1) = GetTypeAndH1(appSettings, h1, lines, lineNumber);
else
{
type = "note";
File.WriteAllLines(file, new string[] { "---", $"type: {type}", "---", string.Empty, $"# {h1}" });
lines = File.ReadAllLines(file).ToList();
}
markdownFile = new(file, fileInfo.DirectoryName, fileInfo.Name, fileNameWithoutExtension, fileInfo.Extension, fileInfo.CreationTime, fileInfo.LastWriteTime, lineNumber, type, h1);
results.Add(new(markdownFile, lines.ToArray()));
}
return results;
}
internal static bool SetFrontMatterAndH1(Models.AppSettings appSettings, List<(MarkdownFile, string[])> collection)
{
bool result = false;
string h1Line;
string typeLine;
string createdLine;
string updatedLine;
DateTime creationDateTime;
string createdLineCompare;
string updatedLineCompare;
List<string> results = new();
foreach ((MarkdownFile markdownFile, string[] lines) in collection)
{
if (markdownFile.FileName == "board.md")
continue;
if (!lines.Any())
continue;
results.Clear();
results.AddRange(lines);
creationDateTime = markdownFile.CreationDateTime > markdownFile.LastWriteDateTime ? markdownFile.LastWriteDateTime : markdownFile.CreationDateTime;
typeLine = $"type: {appSettings.DefaultNoteType}";
h1Line = $"# {markdownFile.FileNameWithoutExtension}";
createdLineCompare = $"created: {creationDateTime.ToUniversalTime():yyyy-MM-dd}";
createdLine = $"created: {creationDateTime.ToUniversalTime():yyyy-MM-ddTHH:mm:ss.fffZ}";
updatedLineCompare = $"updated: {markdownFile.LastWriteDateTime.ToUniversalTime():yyyy-MM-dd}";
updatedLine = $"updated: {markdownFile.LastWriteDateTime.ToUniversalTime():yyyy-MM-ddTHH:mm:ss.fffZ}";
if (markdownFile.LineNumber.MetaEnd is null)
{
if (markdownFile.LineNumber.H1 is null)
{
results.Insert(0, string.Empty);
results.Insert(0, h1Line);
results.Insert(0, string.Empty);
}
results.Insert(0, "---");
results.Insert(0, updatedLine);
results.Insert(0, createdLine);
results.Insert(0, typeLine);
results.Insert(0, "---");
}
else
{
if (markdownFile.LineNumber.H1 is null)
{
results.Insert(markdownFile.LineNumber.MetaEnd.Value + 1, string.Empty);
results.Insert(markdownFile.LineNumber.MetaEnd.Value + 1, h1Line);
results.Insert(markdownFile.LineNumber.MetaEnd.Value + 1, string.Empty);
}
if (markdownFile.LineNumber.Type is null)
results.Insert(markdownFile.LineNumber.MetaEnd.Value, typeLine);
if (markdownFile.LineNumber.Updated is null)
results.Insert(markdownFile.LineNumber.MetaEnd.Value, updatedLine);
else
{
if (results[markdownFile.LineNumber.Updated.Value].Contains('$'))
continue;
if (results[markdownFile.LineNumber.Updated.Value][..updatedLineCompare.Length] == updatedLineCompare)
continue;
results[markdownFile.LineNumber.Updated.Value] = updatedLine;
}
if (markdownFile.LineNumber.Created is null)
results.Insert(markdownFile.LineNumber.MetaEnd.Value, createdLine);
else if (results[markdownFile.LineNumber.Created.Value][..createdLineCompare.Length] != createdLineCompare)
results[markdownFile.LineNumber.Created.Value] = createdLine;
}
if (!result)
result = true;
File.WriteAllLines(markdownFile.File, results);
File.SetLastWriteTime(markdownFile.File, markdownFile.LastWriteDateTime);
}
return result;
}
internal static bool CircularReference(List<(MarkdownFile, string[])> collection)
{
bool result = false;
string line;
string check;
bool circularReference;
foreach ((MarkdownFile markdownFile, string[] lines) in collection)
{
if (lines.Length < 1)
continue;
circularReference = false;
for (int i = 0; i < lines.Length; i++)
{
check = $"[[{markdownFile.FileNameWithoutExtension}]]";
if (!lines[i].Contains(check))
continue;
line = lines[i].Replace(check, $"~~{markdownFile.FileName}~~");
if (lines[i] == line)
continue;
lines[i] = line;
if (!circularReference)
circularReference = true;
}
for (int i = 0; i < lines.Length; i++)
{
check = $"{markdownFile.FileNameWithoutExtension}|{markdownFile.FileNameWithoutExtension}]]";
if (!lines[i].Contains(check))
continue;
line = lines[i].Replace(check, $"~~{markdownFile.FileName}~~");
if (lines[i] == line)
continue;
lines[i] = line;
if (!circularReference)
circularReference = true;
}
for (int i = 0; i < lines.Length; i++)
{
check = $"[{markdownFile.FileNameWithoutExtension}]({markdownFile.FileName})";
if (!lines[i].Contains(check))
continue;
line = lines[i].Replace(check, $"~~{markdownFile.FileName}~~");
if (lines[i] == line)
continue;
lines[i] = line;
if (!circularReference)
circularReference = true;
}
if (circularReference)
{
if (!result)
result = true;
File.WriteAllLines(markdownFile.File, lines);
}
}
return result;
}
internal static bool FindReplace(List<(MarkdownFile, string[])> collection)
{
bool result = false;
bool found;
string line;
string check;
foreach ((MarkdownFile markdownFile, string[] lines) in collection)
{
if (lines.Length < 1)
continue;
found = false;
for (int i = 0; i < lines.Length; i++)
{
check = $"[[K-A/";
if (!lines[i].Contains(check))
continue;
line = lines[i].Replace(check, "[[.kanbn/Archive/");
if (lines[i] == line)
continue;
lines[i] = line;
if (!found)
found = true;
}
for (int i = 0; i < lines.Length; i++)
{
check = $"[[K-T/";
if (!lines[i].Contains(check))
continue;
line = lines[i].Replace(check, "[[.kanbn/Tasks/");
if (lines[i] == line)
continue;
lines[i] = line;
if (!found)
found = true;
}
if (found)
{
if (!result)
result = true;
File.WriteAllLines(markdownFile.File, lines);
}
}
return result;
}
private static Dictionary<string, List<MarkdownFile>> GetKeyValuePairs(List<(MarkdownFile MarkdownFile, string[] Lines)> collection)
{
Dictionary<string, List<MarkdownFile>> results = new();
List<MarkdownFile>? markdownFiles;
foreach ((MarkdownFile markdownFile, _) in collection)
{
if (!results.TryGetValue(markdownFile.FileNameWithoutExtension, out markdownFiles))
{
results.Add(markdownFile.FileNameWithoutExtension, new());
if (!results.TryGetValue(markdownFile.FileNameWithoutExtension, out markdownFiles))
throw new NotSupportedException();
}
markdownFiles.Add(markdownFile);
}
return results;
}
private static (string?, string?) GetRelativePath(Dictionary<string, List<MarkdownFile>> keyValuePairs, MarkdownFile markdownFile, string file)
{
string? result;
string? match;
string? title;
List<MarkdownFile>? markdownFiles;
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file);
if (keyValuePairs.TryGetValue(fileNameWithoutExtension, out markdownFiles))
{
if (markdownFiles.Count != 1)
(match, title) = (null, null);
else
(match, title) = (markdownFiles.First().File, markdownFiles.First().H1);
}
else
{
if (!keyValuePairs.TryGetValue(fileNameWithoutExtension.ToLower(), out markdownFiles))
(match, title) = (null, null);
else
{
if (markdownFiles.Count != 1)
(match, title) = (null, null);
else
(match, title) = (markdownFiles.First().File, markdownFiles.First().H1);
}
}
if (match is null)
{
List<string> files = new();
List<string> fileNames = new();
foreach (KeyValuePair<string, List<MarkdownFile>> keyValuePair in keyValuePairs)
{
foreach (MarkdownFile keyValue in keyValuePair.Value)
{
files.Add(keyValue.File);
fileNames.Add(keyValue.FileNameWithoutExtension);
}
}
string[] matches = fileNames.Where(l => l.Length == fileNameWithoutExtension.Length && l.Contains(fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase)).ToArray();
if (matches.Length == 1)
match = matches.First();
else
{
string checkName = fileNameWithoutExtension.ToLower().Replace("%20", "-").Replace(' ', '-');
matches = fileNames.Where(l => l.Length == checkName.Length && l.Contains(checkName, StringComparison.OrdinalIgnoreCase)).ToArray();
if (matches.Length == 1)
match = matches.First();
else
{
if (!matches.Any())
match = null;
else
{
checkName = matches.First();
matches = files.Where(l => l.Contains(checkName, StringComparison.OrdinalIgnoreCase)).ToArray();
if (matches.Length == 1)
match = matches.First();
else
{
checkName = $"{checkName}{markdownFile.Extension}";
matches = files.Where(l => l.EndsWith(checkName, StringComparison.OrdinalIgnoreCase)).ToArray();
if (matches.Length == 1)
match = matches.First();
else
{
checkName = $"\\{checkName}";
matches = files.Where(l => l.EndsWith(checkName, StringComparison.OrdinalIgnoreCase)).ToArray();
if (matches.Length == 1)
match = matches.First();
else
match = null;
}
}
}
}
}
}
result = match is null ? null : Path.GetRelativePath(markdownFile.Directory, Path.GetFullPath(match));
return (result, title);
}
internal static bool ConvertToRelativePath(List<(MarkdownFile MarkdownFile, string[] Lines)> collection)
{
bool result = false;
bool write;
string line;
string after;
string before;
string? title;
string[] segmentsA;
string[] segmentsB;
string[] segmentsC;
string? relativePath;
Dictionary<string, List<MarkdownFile>> keyValuePairs = GetKeyValuePairs(collection);
foreach ((MarkdownFile markdownFile, string[] lines) in collection)
{
if (lines.Length < 1)
continue;
write = false;
for (int i = 0; i < lines.Length; i++)
{
segmentsA = lines[i].Split("]]");
if (segmentsA.Length is not 2 or 3)
continue;
segmentsB = segmentsA.First().Split("[[");
if (segmentsB.Length is not 2 or 3)
continue;
after = segmentsA.Last();
before = segmentsB.First();
segmentsC = segmentsB.Last().Split('|');
(relativePath, title) = GetRelativePath(keyValuePairs, markdownFile, segmentsC.First());
if (relativePath is null)
continue;
if (title is null)
{
title = segmentsC.Last().Humanize(LetterCasing.Title);
if (title.Length != segmentsC.Last().Length)
title = segmentsC.Last();
}
line = $"{before}[{title}]({relativePath.Replace('\\', '/')}){after}";
if (lines[i] == line)
continue;
lines[i] = line;
if (!write)
write = true;
}
if (write)
{
if (!result)
result = true;
File.WriteAllLines(markdownFile.File, lines);
}
}
return result;
}
internal static bool ConvertFileToSlugName(List<(MarkdownFile MarkdownFile, string[] Lines)> collection)
{
bool result = false;
bool write;
string h1;
string file;
string line;
string? title;
string h1Check;
string fileName;
string checkName;
string? directory;
string[] segmentsA;
string[] segmentsB;
string[] segmentsC;
string checkFileName;
string segmentsALast;
string segmentsBFirst;
string relativeDirectory;
string formattedRelativeDirectory;
Dictionary<string, List<MarkdownFile>> keyValuePairs = GetKeyValuePairs(collection);
foreach ((MarkdownFile markdownFile, string[] lines) in collection)
{
if (markdownFile.FileNameWithoutExtension == "index")
continue;
if (!File.Exists(markdownFile.File))
continue;
write = false;
for (int i = 0; i < lines.Length; i++)
{
segmentsA = lines[i].Split("](");
if (segmentsA.Length != 2)
continue;
segmentsALast = segmentsA.Last();
if (segmentsALast.StartsWith("http:") || segmentsALast.StartsWith("https:") || segmentsALast.StartsWith("rdp:") || segmentsALast.StartsWith("onenote:"))
continue;
segmentsB = segmentsALast.Split(")");
if (segmentsB.Length != 2)
continue;
segmentsBFirst = segmentsB.First();
file = Path.GetFullPath(Path.Combine(markdownFile.Directory, segmentsBFirst));
fileName = Path.GetFileName(file);
directory = Path.GetDirectoryName(file);
if (string.IsNullOrEmpty(directory))
continue;
relativeDirectory = segmentsBFirst[..^fileName.Length];
formattedRelativeDirectory = relativeDirectory.Replace(" ", "%20");
checkFileName = fileName.ToLower().Replace("%20", "-").Replace(' ', '-');
checkName = Path.Combine(directory, checkFileName);
segmentsC = segmentsA.First().Split('[');
(_, title) = GetRelativePath(keyValuePairs, markdownFile, file);
if (title is null)
{
title = segmentsC.Last().Humanize(LetterCasing.Title);
if (title.Length != segmentsC.Last().Length)
title = segmentsC.Last();
}
line = $"{segmentsC.First()}[{title}]({Path.Combine(formattedRelativeDirectory, checkFileName)}){segmentsB.Last()}";
if (lines[i] == line)
continue;
if (fileName.Contains(' ') || fileName.Contains("%20"))
{
if (!File.Exists(file))
continue;
if (File.Exists(checkName))
continue;
File.Move(file, checkName);
}
else if (fileName != fileName.ToLower())
{
if (file != checkName)
{
if (!File.Exists(file))
continue;
File.Move(file, checkName);
}
}
lines[i] = line;
if (!write)
write = true;
}
if (write)
{
if (!result)
result = true;
File.WriteAllLines(markdownFile.File, lines);
}
}
if (!result)
{
foreach ((MarkdownFile markdownFile, string[] lines) in collection)
{
if (markdownFile.LineNumber.H1 is not null)
{
h1 = lines[markdownFile.LineNumber.H1.Value];
if (h1.Length > 2)
{
h1Check = $"# {h1[2..].Humanize(LetterCasing.Title)}";
if (h1Check.Length == h1.Length && h1Check != h1)
{
lines[markdownFile.LineNumber.H1.Value] = h1Check;
if (!result)
result = true;
File.WriteAllLines(markdownFile.File, lines);
}
}
}
checkFileName = markdownFile.FileName.ToLower().Replace("%20", "-").Replace(' ', '-');
if (checkFileName == markdownFile.FileName)
continue;
if (!File.Exists(markdownFile.File))
continue;
checkName = Path.Combine(markdownFile.Directory, checkFileName);
if (checkName == markdownFile.File)
continue;
if (!result)
result = true;
File.Move(markdownFile.File, checkName);
}
}
return result;
}
internal static void MarkdownWikiLinkVerification(Models.AppSettings appSettings, string argsZero)
{
string fullPath = Path.GetFullPath(argsZero);
List<(MarkdownFile MarkdownFile, string[] Lines)> collection;
collection = GetCollection(appSettings, GetFiles(appSettings, fullPath));
if (SetFrontMatterAndH1(appSettings, collection))
collection = GetCollection(appSettings, GetFiles(appSettings, fullPath));
if (CircularReference(collection))
collection = GetCollection(appSettings, GetFiles(appSettings, fullPath));
if (FindReplace(collection))
collection = GetCollection(appSettings, GetFiles(appSettings, fullPath));
if (ConvertToRelativePath(collection))
collection = GetCollection(appSettings, GetFiles(appSettings, fullPath));
if (ConvertFileToSlugName(collection))
collection = GetCollection(appSettings, GetFiles(appSettings, fullPath));
string directory = Path.Combine(Environment.CurrentDirectory, ".vscode");
if (!Directory.Exists(directory))
{
JsonSerializerOptions jsonSerializerOptions = new() { WriteIndented = true };
string json = JsonSerializer.Serialize(collection.Select(l => l.MarkdownFile), jsonSerializerOptions);
File.WriteAllText($"{DateTime.Now.Ticks}.json", json);
}
}
}