cmdlets/Get-FolderSizeFast.psm1

# # Add C# code inline to PowerShell
using module .\Get-BestSizeUnit.psm1
using module ../libs/phwriter/phwriter.psm1

Add-Type -TypeDefinition @"
using System;
using System.IO;
 
public static class FastFolderSize
{
    public static long GetFolderSize(string folderPath)
    {
        if (!Directory.Exists(folderPath))
            throw new DirectoryNotFoundException("Directory not found: " + folderPath);
         
        return GetDirectorySize(new DirectoryInfo(folderPath));
    }
     
    public static long GetFolderSizeWithSubfolders(string folderPath, out int fileCount, out int folderCount)
    {
        fileCount = 0;
        folderCount = 0;
         
        if (!Directory.Exists(folderPath))
            throw new DirectoryNotFoundException("Directory not found: " + folderPath);
         
        return GetDirectorySizeDetailed(new DirectoryInfo(folderPath), ref fileCount, ref folderCount);
    }
     
    private static long GetDirectorySize(DirectoryInfo directory)
    {
        long size = 0;
         
        try
        {
            // Add file sizes
            FileInfo[] files = directory.GetFiles();
            foreach (FileInfo file in files)
            {
                try
                {
                    size += file.Length;
                }
                catch
                {
                    // Skip files we can't access
                }
            }
             
            // Add subdirectory sizes
            DirectoryInfo[] subdirectories = directory.GetDirectories();
            foreach (DirectoryInfo subdirectory in subdirectories)
            {
                try
                {
                    size += GetDirectorySize(subdirectory);
                }
                catch
                {
                    // Skip directories we can't access
                }
            }
        }
        catch
        {
            // Skip if we can't enumerate the directory
        }
         
        return size;
    }
     
    private static long GetDirectorySizeDetailed(DirectoryInfo directory, ref int fileCount, ref int folderCount)
    {
        long size = 0;
         
        try
        {
            folderCount++;
             
            // Add file sizes
            FileInfo[] files = directory.GetFiles();
            foreach (FileInfo file in files)
            {
                try
                {
                    size += file.Length;
                    fileCount++;
                }
                catch
                {
                    // Skip files we can't access
                }
            }
             
            // Add subdirectory sizes
            DirectoryInfo[] subdirectories = directory.GetDirectories();
            foreach (DirectoryInfo subdirectory in subdirectories)
            {
                try
                {
                    size += GetDirectorySizeDetailed(subdirectory, ref fileCount, ref folderCount);
                }
                catch
                {
                    // Skip directories we can't access
                }
            }
        }
        catch
        {
            // Skip if we can't enumerate the directory
        }
         
        return size;
    }
}
"@


<#
.SYNOPSIS
    Quickly calculates the size of a folder in bytes, MB, and GB.
.DESCRIPTION
    Get-FolderSizeFast is a PowerShell cmdlet that efficiently computes the total size of a specified folder, including all its subfolders and files. It provides options for detailed output, including file and folder counts, and supports error handling for inaccessible files or directories.
.PARAMETER Path
    The path of the folder to calculate the size for. This parameter is mandatory and accepts pipeline input.
.PARAMETER Detailed
    If specified, the cmdlet returns additional details including file count, folder count, and calculation time.
.PARAMETER json
    If specified, the output will be formatted as JSON.
.EXAMPLE
    Get-FolderSizeFast -Path "C:\MyFolder"
    Calculates the size of "C:\MyFolder" and returns the size in bytes, MB, and GB.
.EXAMPLE
    Get-FolderSizeFast -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-FolderSizeFast -Detailed
    Uses pipeline input to calculate the size of "C:\MyFolder" with detailed output.
.NOTES
#>

function Get-FolderSizeFast {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [string]$Path,
        
        [Parameter(Mandatory = $false)]
        [switch]$Detailed,

        [Parameter(Mandatory = $false)]
        [ValidateSet('json', 'xml')]                    
        [switch]$Format,

        [parameter(mandatory=$false)]
        [switch] $help
    )
    
    process {
        
        if ($help) {
            New-PHWriter -JsonFile "./libs/help_metadata/Get-FolderSizeFast_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()

            if ($Detailed) {
                $fileCount = 0
                $folderCount = 0
                $size = [FastFolderSize]::GetFolderSizeWithSubfolders($resolvedPath.Path, [ref]$fileCount, [ref]$folderCount)
                
                $stopwatch.stop()
                
                [PSCustomObject]@{
                    Path        = $resolvedPath.Path
                    SizeBytes   = $size
                    SizeMB      = [Math]::Round($size / 1MB, 2)
                    SizeGB      = [Math]::Round($size / 1GB, 3)
                    FileCount   = $fileCount
                    FolderCount = $folderCount - 1  # Subtract 1 to exclude root folder
                    BestUnit          = Get-BestSizeUnit -Bytes $size
                    CalculationTimeMs = $stopwatch.ElapsedMilliseconds
                    CalculationTimeSec = [Math]::Round($stopwatch.Elapsed.TotalSeconds, 2)                              
                    CalculationTimeMin = [Math]::Round($stopwatch.Elapsed.TotalMinutes, 2)
                }
            }
            else {
                $size = [FastFolderSize]::GetFolderSize($resolvedPath.Path)
                
                [PSCustomObject]@{
                    Path      = $resolvedPath.Path
                    SizeBytes = $size
                    SizeMB    = [Math]::Round($size / 1MB, 2)
                    SizeGB    = [Math]::Round($size / 1GB, 3)
                }
            }
        }
        catch {
            Write-Error "Error calculating folder size for '$Path': $($_.Exception.Message)"
        }
    }
}