cmdlets/Get-FolderSizeParallel.psm1

using module .\Get-BestSizeUnit.psm1
using module ..\libs\phwriter\phwriter.psm1

Add-Type -TypeDefinition @"
using System;
using System.IO;
using System.Threading.Tasks;
using System.Collections.Concurrent;
 
public static class ParallelFolderSize
{
    public static long GetFolderSizeParallel(string folderPath)
    {
        if (!Directory.Exists(folderPath))
            throw new DirectoryNotFoundException("Directory not found: " + folderPath);
         
        return GetDirectorySizeParallel(new DirectoryInfo(folderPath));
    }
     
    private static long GetDirectorySizeParallel(DirectoryInfo directory)
    {
        long size = 0;
         
        try
        {
            // Get files in current directory
            FileInfo[] files = directory.GetFiles();
             
            // Calculate file sizes in parallel
            Parallel.ForEach(files, file =>
            {
                try
                {
                    System.Threading.Interlocked.Add(ref size, file.Length);
                }
                catch
                {
                    // Skip files we can't access
                }
            });
             
            // Get subdirectories
            DirectoryInfo[] subdirectories = directory.GetDirectories();
             
            // Process subdirectories in parallel
            var subSizes = new long[subdirectories.Length];
            Parallel.For(0, subdirectories.Length, i =>
            {
                try
                {
                    subSizes[i] = GetDirectorySizeParallel(subdirectories[i]);
                }
                catch
                {
                    subSizes[i] = 0;
                }
            });
             
            // Add all subdirectory sizes
            for (int i = 0; i < subSizes.Length; i++)
            {
                size += subSizes[i];
            }
        }
        catch
        {
            // Skip if we can't enumerate the directory
        }
         
        return size;
    }
}
"@


<#
    .SYNOPSIS
        Calculates the size of a folder using parallel processing to enhance performance.
 
    .DESCRIPTION
 
        Calculates the size of a folder using parallel processing to enhance performance. This cmdlet processes files and subdirectories in parallel, which can significantly speed up the calculation process.
 
    .PARAMETER Path
 
        The path of the folder to calculate the size for. This parameter is mandatory and accepts pipeline input.
 
    .PARAMETER Format
 
        The format to use for the output. Valid options are 'json' and 'xml'. Default is 'json'.
 
    .PARAMETER Help
 
        If specified, the cmdlet returns help information for the command.
 
    .EXAMPLE
 
        Get-FolderSizeParallel -Path "C:\MyFolder"
        Calculates the size of "C:\MyFolder" and returns the size in bytes, MB, and GB.
 
    .EXAMPLE
 
        Get-FolderSizeParallel -Path "C:\MyFolder" -Detailed
        Calculates the size of "C:\MyFolder" and returns detailed information including file count, folder count, and calculation time.
 
    .EXAMPLE
 
        "C:\MyFolder" | Get-FolderSizeParallel -Detailed
        Uses pipeline input to calculate the size of "C:\MyFolder" with detailed output.
 
    .NOTES
     
#>


function Get-FolderSizeParallel {
    [CmdletBinding()]
    [Alias('fscsizep')]
    param(
        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [string]$Path,
        [Parameter(Mandatory = $false)]
        [ValidateSet('json', 'xml')]                    
        [string]$Format,
        [Parameter(Mandatory = $false)]
        [switch]$Help
    )
    process {

        if ($help) {
            New-PHWriter -JsonFile "$($global:__fastfsc.rootpath)\libs\help_metadata\get-foldersizeparallel_phwriter_metadata.json"
            return;
        }
        if(!$Path -and !$Help) {
            Write-Error "🤖≈ Path parameter is required. Use -Help for usage information."
            return;
        }

        try {
            $resolvedPath = Resolve-Path $Path -ErrorAction Stop
            $stopwatch = [System.Diagnostics.Stopwatch]::StartNew()

            [console]::writeline("🤖≈ Generating <parallel> fastfsc folder report on '$Path' ...")

            $fileCount = 0
            $folderCount = 0
            $size = [FastFolderSize]::GetFolderSizeWithSubfolders($resolvedPath.Path, [ref]$fileCount, [ref]$folderCount)
            
            $stopwatch.Stop()
            
            $folderstats = [PSCustomObject]@{
                Path              = $resolvedPath.Path
                SizeBytes         = $size
                FileCount         = $fileCount
                FolderCount       = $folderCount
                SizeKB            = [Math]::Round($size / 1KB, 2)
                SizeMB            = [Math]::Round($size / 1MB, 2)
                SizeGB            = [Math]::Round($size / 1GB, 3)
                SizeTB            = [Math]::Round($size / 1TB, 4)
                SizePB            = [Math]::Round($size / 1PB, 5)
                BestUnit          = Get-BestSizeUnit -Bytes $size
                CalculationTimeMs = $stopwatch.ElapsedMilliseconds
                CalculationTimeSec = [Math]::Round($stopwatch.Elapsed.TotalSeconds, 2)                              
                CalculationTimeMin = [Math]::Round($stopwatch.Elapsed.TotalMinutes, 2)
            }
        }
        catch {
            [console]::writeline("🤖≈ Error: $($_.Exception.Message)")
        }
        if ($Format -eq 'json') {
            return $folderstats | ConvertTo-Json -Depth 5
        } elseif($Format -eq 'xml'){
            return $folderstats | ConvertTo-Xml -NoTypeInformation -Depth 5
        } else {
            return $folderstats
        }
    }
}