diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/Adv-FolderSize.sln b/Adv-FolderSize.sln new file mode 100644 index 0000000..ff38d3a --- /dev/null +++ b/Adv-FolderSize.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30907.101 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Adv-FolderSize", "Adv-FolderSize\Adv-FolderSize.csproj", "{50E3AA47-DCF6-4C5B-969D-9D1744B13A9D}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FolderAnalysisFSLib", "FolderAnalysisFSLib\FolderAnalysisFSLib.fsproj", "{BA6F8578-098B-438E-A2ED-630267396879}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {50E3AA47-DCF6-4C5B-969D-9D1744B13A9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50E3AA47-DCF6-4C5B-969D-9D1744B13A9D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50E3AA47-DCF6-4C5B-969D-9D1744B13A9D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50E3AA47-DCF6-4C5B-969D-9D1744B13A9D}.Release|Any CPU.Build.0 = Release|Any CPU + {BA6F8578-098B-438E-A2ED-630267396879}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BA6F8578-098B-438E-A2ED-630267396879}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BA6F8578-098B-438E-A2ED-630267396879}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BA6F8578-098B-438E-A2ED-630267396879}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {38342B4B-018B-4E83-9286-0458B30F2D79} + EndGlobalSection +EndGlobal diff --git a/Adv-FolderSize/Adv-FolderSize.csproj b/Adv-FolderSize/Adv-FolderSize.csproj new file mode 100644 index 0000000..69aa649 --- /dev/null +++ b/Adv-FolderSize/Adv-FolderSize.csproj @@ -0,0 +1,17 @@ + + + + Exe + net5.0 + Adv_FolderSize + + + + + + + + + + + diff --git a/Adv-FolderSize/Args.cs b/Adv-FolderSize/Args.cs new file mode 100644 index 0000000..ffb1fb4 --- /dev/null +++ b/Adv-FolderSize/Args.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Adv_FolderSize +{ + public record ArgSet + { + // empty -> arg without key + public string Key { get; init; } = ""; + + // null -> value is required + // not null -> value is optional + // empty -> arg without value + public string Default { get; init; } = ""; + } + + public static class Args + { + public static string[] Interprete(ArgSet[] argsets, string args, bool remainder = false) + { + var values = new List(); + var leftargs = new List(); + + if (remainder) + leftargs.Add(args); + else if (args != "") + leftargs.AddRange(args.Split(' ')); + + foreach (var argset in argsets) + { + var key = argset.Key.ToUpper(); + var defaultvalue = argset.Default; + + var matchedarg = leftargs.Find(arg => arg.ToUpper().StartsWith(key)); + + if (matchedarg != null) + { + var matchedvalue = matchedarg[key.Length..]; + if ((defaultvalue == "" && matchedvalue != "") // empty -> arg without value + || (defaultvalue == null && matchedvalue == "")) // null -> value is required + return null; + values.Add(matchedvalue); + leftargs.Remove(matchedarg); + } + else if (defaultvalue == null) // null -> value is required + { + return null; + } + else + { + values.Add(defaultvalue); + } + } + + // Check the unregistered case + if (leftargs.Count == 0) + return values.ToArray(); + else + return null; + } + } +} diff --git a/Adv-FolderSize/ColorConsole.cs b/Adv-FolderSize/ColorConsole.cs new file mode 100644 index 0000000..73c5bb3 --- /dev/null +++ b/Adv-FolderSize/ColorConsole.cs @@ -0,0 +1,18 @@ +using System; + +namespace Adv_FolderSize +{ + public static class ColorConsole + { + public static void Write(string contents, ConsoleColor color, bool asline = true) + { + var curcolor = Console.ForegroundColor; + Console.ForegroundColor = color; + if (asline) + Console.WriteLine(contents); + else + Console.Write(contents); + Console.ForegroundColor = curcolor; + } + } +} diff --git a/Adv-FolderSize/FolderAnalysis.cs b/Adv-FolderSize/FolderAnalysis.cs new file mode 100644 index 0000000..5d46ed5 --- /dev/null +++ b/Adv-FolderSize/FolderAnalysis.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using FolderAnalysisFSLib; + +namespace Adv_FolderSize +{ + public class FolderAnalysis + { + private readonly FolderAnalysisBase _fABase; + private readonly List _selectableTree = new List(); + private readonly List _selectableList = new List(); + + public FolderAnalysis(string path) + => _fABase = new FolderAnalysisBase(path); + + public async Task StartAnalysisAsync() + => await Task.Run(() => _fABase.StartAnalysis()); + + public bool RedirectTo(int idx) + { + if (idx >= 0 && idx < _selectableTree.Count) + return _fABase.RedirectTo(_selectableTree[idx]); + else + return false; + } + + public bool Back() + => _fABase.Back(); + + public string GetListElemPath(int idx) + { + if (idx >= 0 && idx < _selectableList.Count) + return _selectableList[idx]; + else + return null; + } + + public string GetTreeDirPath(int idx) + { + if (idx >= 0 && idx < _selectableTree.Count) + return _selectableTree[idx]; + else + return null; + } + + private static string SizeBar(long stand, long size, int length) + { + if (stand != 0) + return "■".Repeat((int)(size * length / stand)); + else + return ""; + } + + + private static string DirAbbrevName(string path) + => Path.DirectorySeparatorChar + Path.GetFileName(path); + + public void PrintDirTree(string measure, int depthLimt, int dirExpLimt, int fileExpLimt) + { + // Length = 4 + var diridx = "│ "; + var sym = "├── "; + var tabidx = " "; + + var printline = _fABase.GetPrintableTree(depthLimt, dirExpLimt, fileExpLimt); + + _selectableTree.Clear(); + var idxcount = -1; + var dirtopsize = _fABase.GetDirList(top: true, 1); + var filetopsize = _fABase.GetFileList(top: true, 1); + var dirsizestand = dirtopsize.Length != 0 ? dirtopsize[0].Item2 : 0; + var filesizestand = filetopsize.Length != 0 ? filetopsize[0].Item2 : 0; + + foreach (var item in printline) + { + var type = item.Item1; + var name = item.Item2; + var size = item.Item3; + var depth = item.Item4; + + switch (type) + { + case "F": + Console.WriteLine( + $"{SizeBar(filesizestand, size, 16),16} {ByteMeasure.byteToString(measure, size),10} " + + $"{tabidx.Repeat(depth)}{sym}{name}"); + break; + case "D": + ColorConsole.Write( + $"{SizeBar(dirsizestand, size, 16),16} {ByteMeasure.byteToString(measure, size),10} " + , ConsoleColor.Cyan, asline: false); + Console.Write($"{diridx.Repeat(depth)}{sym}"); + + var print = ""; + if (idxcount == -1) + { + print = name; + idxcount++; + } + else if (depth == 1) + { + _selectableTree.Add(name); + print = $"[{idxcount}] {DirAbbrevName(name)}"; + idxcount++; + } + else + { + print = DirAbbrevName(name); + } + ColorConsole.Write(print, ConsoleColor.Cyan); + break; + case "FH": + Console.Write($"{"",16} {"",-10} {tabidx.Repeat(depth)}{sym}"); + ColorConsole.Write($"... {name} files are hided", ConsoleColor.Yellow); + break; + case "DH": + Console.Write($"{"",16} {"",-10} {tabidx.Repeat(depth + 1)}{sym}"); + ColorConsole.Write($"... {name} directories are hided", ConsoleColor.Yellow); + break; + case "DF": + Console.Write($"{"",16} {"",-10} {tabidx.Repeat(depth + 1)}{sym}"); + ColorConsole.Write($"... {name} directories above this directory are folded", ConsoleColor.Yellow); + break; + default: + break; + } + } + } + + public void PrintDirList(string measure = "AUTO", int num = 10) + { + _selectableList.Clear(); + var idxcount = 0; + + var dirlist = _fABase.GetDirList(top: true, num); + var dirsizestand = dirlist.Length != 0 ? dirlist[0].Item2 : 0; + + foreach (var item in dirlist) + { + var name = item.Item1; + var size = item.Item2; + ColorConsole.Write( + $"{SizeBar(dirsizestand, size, 16),16} {ByteMeasure.byteToString(measure, size),10} " + + $"[{idxcount}] {name}" + , ConsoleColor.Cyan); + + _selectableList.Add(name); + idxcount++; + } + } + + public void PrintFileList(string measure = "AUTO", int num = 10) + { + _selectableList.Clear(); + var idxcount = 0; + + var filelist = _fABase.GetFileList(top: true, num); + var filesizestand = filelist.Length != 0 ? filelist[0].Item2 : 0; + + foreach (var item in filelist) + { + var name = item.Item1; + var size = item.Item2; + Console.WriteLine( + $"{SizeBar(filesizestand, size, 16),16} {ByteMeasure.byteToString(measure, size),10} " + + $"[{idxcount}] {name}"); + + _selectableList.Add(name); + idxcount++; + } + } + } +} diff --git a/Adv-FolderSize/NuGet/TriggerLib.1.1.1.nupkg b/Adv-FolderSize/NuGet/TriggerLib.1.1.1.nupkg new file mode 100644 index 0000000..f32580f Binary files /dev/null and b/Adv-FolderSize/NuGet/TriggerLib.1.1.1.nupkg differ diff --git a/Adv-FolderSize/Program.cs b/Adv-FolderSize/Program.cs new file mode 100644 index 0000000..6303fe6 --- /dev/null +++ b/Adv-FolderSize/Program.cs @@ -0,0 +1,176 @@ +using System; +using System.IO; +using System.Diagnostics; +using TriggerLib; +using FolderAnalysisFSLib; + +namespace Adv_FolderSize +{ + class Program + { + private const string _helpMsg = +@"Enter help(h) to get help message +Enter scan(s) [path] to scan all directories and files below the path specified +Enter tree(t) to print the scan\'s result + Option: Measure /m[auto|b|kb|mb|gb] Default is auto + Dir depth limit /dp[number] Default is 3 + Dir expand limit /de[number] Default is 3 + File expand limit /fe[number] Default is 3 +Enter dirlist(d) to print the directories\' paths descending by size + Option: Measure /m[auto|b|kb|mb|gb] Default is auto + Number to display /n[number] Default is 10 +Enter filelist(f) to print the files\' paths descending by size + Option: Measure /m[auto|b|kb|mb|gb] Default is auto + Number to display /n[number] Default is 10 +Enter redirect(r) [index] to move into the specified tree +Enter back(b) to back to the previous tree +Enter open(o) [index] to open the folder or the file specified +Enter exit(e) to exit"; + + static void Main(string[] args) + { + var fa = new FolderAnalysis(""); + var lastdisplayed = 0b0; // 0b0 -> tree 0b1 -> list + + Console.WriteLine("Message: Enter help(h) to get help message"); + ColorConsole.Write("Suggest command: scan(s) [path]", ConsoleColor.DarkGreen); + while (true) + { + try + { + Console.Write("> "); + var line = Console.ReadLine(); + var result = LineInterpreter(line, out var opt); + + switch (result) + { + case "HELP" or "H": + Console.WriteLine(_helpMsg); + break; + case "SCAN" or "S": + if (!Directory.Exists(opt[0])) + throw new DirectoryNotFoundException(); + + var trigger = new TriggerSource(300, () => + Console.WriteLine("... This scaning operation will take several seconds")); + + fa = new FolderAnalysis(opt[0]); + fa.StartAnalysisAsync().Wait(); + + trigger.Cancel(); + Console.WriteLine("Scaning finished."); + + ColorConsole.Write( + "Suggest command: tree(t) [/m /dp /de /fe] | dirlist(d) [/m /n] | filelist(f) [/m /n]", + ConsoleColor.DarkGreen); + break; + case "TREE" or "T": + fa.PrintDirTree(opt[0], int.Parse(opt[1]), int.Parse(opt[2]), int.Parse(opt[3])); + + lastdisplayed = 0b0; + ColorConsole.Write( + "Suggest command: dirlist(d) [/m /n] | filelist(f) [/m /n] | redirect(r) [index]", + ConsoleColor.DarkGreen); + break; + case "DIRLIST" or "D": + fa.PrintDirList(opt[0], int.Parse(opt[1])); + + lastdisplayed = 0b1; + ColorConsole.Write( + "Suggest command: tree(t) [/m /dp /de /fe] | filelist(f) [/m /n]", ConsoleColor.DarkGreen); + break; + case "FILELIST" or "F": + fa.PrintFileList(opt[0], int.Parse(opt[1])); + + lastdisplayed = 0b1; + ColorConsole.Write("Suggest command: tree(t) [/m /dp /de /fe] | dirlist(d) [/m /n]", + ConsoleColor.DarkGreen); + break; + case "REDIRECT" or "R": + if (!fa.RedirectTo(int.Parse(opt[0]))) + throw new Exception("The specified index does not exist."); + + ColorConsole.Write("Suggest command: tree(t) [/m /dp /de /fe] ", ConsoleColor.DarkGreen); + break; + case "BACK" or "B": + if (!fa.Back()) + throw new Exception("Can not go back anymore."); + + ColorConsole.Write("Suggest command: tree(t) [/m /dp /de /fe] ", ConsoleColor.DarkGreen); + break; + case "OPEN" or "O": + var path = ""; + + if (lastdisplayed == 0b0) + path = fa.GetTreeDirPath(int.Parse(opt[0])); + else + path = fa.GetListElemPath(int.Parse(opt[0])); + + if (path == null) + throw new Exception("The specified index does not exist."); + + OpenExplorer(path); + break; + case "EXIT" or "E": + Environment.Exit(0); + break; + default: + throw new Exception("Invalid command."); + } + } + catch (NotAnalyzedYetException ex) + { + Console.WriteLine($"Error: {ex.Message}"); + ColorConsole.Write("Suggest command: scan(s) [path]", ConsoleColor.DarkGreen); + } + catch (Exception ex) + { + Console.WriteLine($"Error: {ex.Message}"); + } + + Console.WriteLine(); + } + } + + static string LineInterpreter(string line, out string[] options) + { + var splited = line.Trim().Split(' '); + var cmd = splited[0].ToUpper(); + var args = string.Join(' ', splited[1..]); + + options = cmd switch + { + "HELP" or "H" or "BACK" or "B" or "EXIT" or "E" => Args.Interprete(new[] { new ArgSet() }, args), + "SCAN" or "S" => Args.Interprete(new[] { new ArgSet { Default = null } }, args, remainder: true), + "TREE" or "T" => Args.Interprete(new[] { + new ArgSet { Key = "/M" ,Default = "AUTO" }, + new ArgSet { Key="/DP", Default = "3" }, + new ArgSet { Key ="/DE", Default = "3" }, + new ArgSet { Key="/FE", Default = "3" } }, args), + "DIRLIST" or "D" => Args.Interprete(new[] { + new ArgSet { Key = "/M", Default = "AUTO" }, + new ArgSet { Key = "/N", Default = "10" } }, args), + "FILELIST" or "F" => Args.Interprete(new[] { + new ArgSet { Key = "/M", Default = "AUTO" }, + new ArgSet { Key = "/N", Default = "10" } }, args), + "REDIRECT" or "R" => Args.Interprete(new[] { new ArgSet { Default = null } }, args), + "OPEN" or "O" => Args.Interprete(new[] { new ArgSet { Default = null } }, args), + _ => null, + }; + return options != null ? cmd : null; + } + + static void OpenExplorer(string path) + { + var platform = Environment.OSVersion.Platform; + + if (platform == PlatformID.Win32NT) + Process.Start("explorer.exe", "/select," + path); + else if (platform == PlatformID.Unix) + Process.Start("open", $"-R \"{path}\""); + else + throw new Exception("Unknown operating system."); + } + } +} + diff --git a/Adv-FolderSize/Properties/PublishProfiles/FolderProfile.pubxml b/Adv-FolderSize/Properties/PublishProfiles/FolderProfile.pubxml new file mode 100644 index 0000000..548a293 --- /dev/null +++ b/Adv-FolderSize/Properties/PublishProfiles/FolderProfile.pubxml @@ -0,0 +1,17 @@ + + + + + Release + Any CPU + bin\Release\net5.0\publish\ + FileSystem + net5.0 + false + win-x86 + True + False + + \ No newline at end of file diff --git a/Adv-FolderSize/StringExtension.cs b/Adv-FolderSize/StringExtension.cs new file mode 100644 index 0000000..ac4deba --- /dev/null +++ b/Adv-FolderSize/StringExtension.cs @@ -0,0 +1,19 @@ +using System; +using System.Linq; + +namespace Adv_FolderSize +{ + public static class StringExtension + { + public static string Repeat(this string str, int num) + => string.Concat(Enumerable.Repeat(str, num)); + + public static string LenLimit(this string str, int len, string abbr) + { + if (str.Length > len) + return str[0..(len - 1)] + abbr; + else + return str; + } + } +} diff --git a/FolderAnalysisFSLib/ByteMeasure.fs b/FolderAnalysisFSLib/ByteMeasure.fs new file mode 100644 index 0000000..45cab67 --- /dev/null +++ b/FolderAnalysisFSLib/ByteMeasure.fs @@ -0,0 +1,27 @@ +module FolderAnalysisFSLib.ByteMeasure + +open System + +let private kilo = 1024.0 +let private bToKB byte = byte / kilo +let private (|ToUpper|)(measure: string) = measure.ToUpper() +let private round(num: float) = Math.Round(num, 2) + +let rec byteToString measure (byte: int64) = + let num = float byte + + match measure with + | ToUpper "B" -> (num |> string) + " B" + | ToUpper "KB" -> (num |> bToKB |> round |> string) + " KB" + | ToUpper "MB" -> (num |> bToKB |> bToKB |> round |> string) + " MB" + | ToUpper "GB" -> (num |> bToKB |> bToKB |> bToKB |> round |> string) + " GB" + | ToUpper "AUTO" -> + if num < kilo then + byteToString "B" byte + elif num < kilo * kilo then + byteToString "KB" byte + elif num < kilo * kilo * kilo then + byteToString "MB" byte + else + byteToString "GB" byte + | _ -> failwith "Failed with matching measure specified." diff --git a/FolderAnalysisFSLib/FolderAnalysisBase.fs b/FolderAnalysisFSLib/FolderAnalysisBase.fs new file mode 100644 index 0000000..be69837 --- /dev/null +++ b/FolderAnalysisFSLib/FolderAnalysisBase.fs @@ -0,0 +1,59 @@ +namespace FolderAnalysisFSLib + +open System.Collections.Generic +open FolderAnalysisTools + +//exception NotAnalyzedYetException of string + +[] +type FolderAnalysisBase(path) = + let _dirTreeHistory = new Stack() + + member private this.CheckIsCreated() = + if _dirTreeHistory.Count = 0 then + raise <| new NotAnalyzedYetException("Not analyzed yet.") + + member this.StartAnalysis() = + if _dirTreeHistory.Count = 0 then + _dirTreeHistory.Push(dirTree path) + + member this.RedirectTo(dirName) = + this.CheckIsCreated() + + let peeked = + _dirTreeHistory.Peek().DirList |> List.tryFind (fun item -> item.Info.Name = dirName) + + match peeked with + | Some(dirTree) -> + _dirTreeHistory.Push(dirTree) + true + | None -> false + + member this.Back() = + this.CheckIsCreated() + + if _dirTreeHistory.Count > 1 then + _dirTreeHistory.Pop() |> ignore + true + else + false + + member this.GetPrintableTree(depthLimt, dirExpLimt, fileExpLimt) = + this.CheckIsCreated() + + printableTree (_dirTreeHistory.Peek()) depthLimt dirExpLimt fileExpLimt + |> List.toArray + + member private this.GetList(list, top, num) = + this.CheckIsCreated() + + if top then + _dirTreeHistory.Peek() |> list |> topSize num |> List.toArray + else + _dirTreeHistory.Peek() |> list |> bottomSize num |> List.toArray + + member this.GetDirList(top, num) = + this.GetList(dirList, top, num) + + member this.GetFileList(top, num) = + this.GetList(fileList, top, num) \ No newline at end of file diff --git a/FolderAnalysisFSLib/FolderAnalysisFSLib.fsproj b/FolderAnalysisFSLib/FolderAnalysisFSLib.fsproj new file mode 100644 index 0000000..6661eaa --- /dev/null +++ b/FolderAnalysisFSLib/FolderAnalysisFSLib.fsproj @@ -0,0 +1,15 @@ + + + + netstandard2.1 + Library + + + + + + + + + + diff --git a/FolderAnalysisFSLib/FolderAnalysisTools.fs b/FolderAnalysisFSLib/FolderAnalysisTools.fs new file mode 100644 index 0000000..30af68d --- /dev/null +++ b/FolderAnalysisFSLib/FolderAnalysisTools.fs @@ -0,0 +1,104 @@ +module internal FolderAnalysisFSLib.FolderAnalysisTools + +open System.IO + +type Info = { Name: string; Size: int64 } +(* + Dir + / | \ + Info FileList DirList + / | \ | \ + Info Info Info Dir Dir +*) +type Dir = { Info: Info; FileList: Info list; DirList: Dir list } + +let dirTree basePath = + let enumerationOption = + new EnumerationOptions(IgnoreInaccessible = true, AttributesToSkip = FileAttributes.System) + + let rec dirTreeSafe path = + let fileInfoList = + Directory.GetFiles(path, "*", enumerationOption) + |> Seq.map + (fun file -> + let fileInfo = FileInfo(file) + { Name = fileInfo.Name; Size = fileInfo.Length }) + |> Seq.toList + + let dirList = + [ for subDir in Directory.GetDirectories(path, "*", enumerationOption) -> + dirTreeSafe subDir ] + + let sumSize = + (fileInfoList |> List.fold (fun acc item -> acc + item.Size) 0L) + + (dirList |> List.fold (fun acc item -> acc + item.Info.Size) 0L) + + { Info = { Name = path; Size = sumSize }; FileList = fileInfoList; DirList = dirList } + + basePath |> dirTreeSafe + +// (Type string, Name string, Size string, Depth int) list +// F = Fold +// D = Dir +// FH = Fold hided +// DH = Dir hided +// DF = Dir folded +let printableTree dirTree depthLimt dirExpLimt fileExpLimt = + let rec printableTreeTR dirTree depthLimt curDepth acc = + let dirInfo = ("D", dirTree.Info.Name, dirTree.Info.Size, curDepth) + + let fileInfoList = + let sortedFL = dirTree.FileList |> List.sortByDescending (fun item -> item.Size) + + let fileInfoRevL list = + list + |> List.map (fun info -> ("F", info.Name, info.Size, curDepth + 1)) + |> List.rev + + if fileExpLimt >= 0 && dirTree.FileList.Length > fileExpLimt then + ("FH", $"%d{dirTree.FileList.Length - fileExpLimt}", 0L, curDepth + 1) + :: (sortedFL.[0..(fileExpLimt - 1)] |> fileInfoRevL) + else + sortedFL |> fileInfoRevL + + let dirInfoRevL list = + List.fold + (fun ac item -> (printableTreeTR item (depthLimt - 1) (curDepth + 1) acc) @ ac) + [] + list + + let infoAcc = fileInfoList @ (dirInfo :: acc) + + if depthLimt <> 0 then + let sortedDL = dirTree.DirList |> List.sortByDescending (fun item -> item.Info.Size) + + if dirExpLimt >= 0 && dirTree.DirList.Length > dirExpLimt then + (("DH", $"%d{dirTree.DirList.Length - dirExpLimt}", 0L, curDepth) + :: (sortedDL.[0..(dirExpLimt - 1)] |> dirInfoRevL)) + @ infoAcc + elif dirTree.DirList.Length <> 0 then + (sortedDL |> dirInfoRevL) @ infoAcc + else + infoAcc + else if dirTree.DirList.Length > 0 then + ("DF", $"%d{dirTree.DirList.Length}", 0L, curDepth) :: infoAcc + else + infoAcc + + printableTreeTR dirTree depthLimt 0 [] |> List.rev + +let rec dirList dirTree = + dirTree.Info :: List.foldBack (fun item acc -> (dirList item) @ acc) dirTree.DirList [] + +let rec fileList dirTree = + (dirTree.FileList + |> List.map (fun item -> { item with Name = Path.Combine(dirTree.Info.Name, item.Name) })) + @ List.foldBack (fun item acc -> (fileList item) @ acc) dirTree.DirList [] + +let topSize number list = + (List.sortByDescending (fun item -> item.Size) list).[0..(number - 1)] + |> List.map (fun item -> (item.Name, item.Size)) + +let bottomSize number list = + (List.sortBy (fun item -> item.Size) list).[0..(number - 1)] + |> List.map (fun item -> (item.Name, item.Size)) \ No newline at end of file diff --git a/FolderAnalysisFSLib/NotAnalyzedYetException.fs b/FolderAnalysisFSLib/NotAnalyzedYetException.fs new file mode 100644 index 0000000..930b538 --- /dev/null +++ b/FolderAnalysisFSLib/NotAnalyzedYetException.fs @@ -0,0 +1,13 @@ +namespace FolderAnalysisFSLib + +open System + +[] +type NotAnalyzedYetException = + inherit Exception + + new() = { inherit Exception() } + + new(message) = { inherit Exception(message) } + + new(message, (innerException: Exception)) = { inherit Exception(message, innerException) } \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..64fa289 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# Adv-FolderSize + Analyze the folder size quickly(win/macOS). Help to find out the big files and folders.