Jsonify.psm1

using namespace System.Collections.Generic
using namespace System.Collections
using namespace System.Management.Automation.Language
using namespace System.Management.Automation
using namespace System.Management

$script:ModuleConfig = @{
    ExportCoercionFunctions = $true
}
# class SummarizePropertyRecord {
# [PSMemberTypes]$MemberType = [PSMemberTypes]::All
# [object]$FullName
# [string]$ShortName

# # SummarizePropertyRecord( $Options ) {

# # }
# }
# function js.Summarize.SingleProperty {
# param(
# $InputObject
# )

# $meta = @{
# Name = 'Name'
# MemberKind =
# }
# [PSMemberTypes]

# [pscustomobject]$meta
# }

# function js.DescribeType {
# param(
# $InputObject
# )
# $Obj = $InputObject
# $TrueNull = $Null = $Obj
# $Tinfo = ($Obj)?.GetType()

# $meta = [ordered]@{
# PSTypeName = 'jsonify.{0}.Record' -f $MyInvocation.MyCommand.Name
# Count = $Obj.Count
# Len = $Obj.Length
# Is = [ordered]@{
# Null = $Null -eq $Obj
# EmptyStr = [String]::IsNullOrEmpty( $Obj )
# Blank = [String]::IsNullOrWhiteSpace( $Obj )
# Array = 'nyi'
# }
# Implements = [ordered]@{
# IList = 'nyi'
# ICollection = 'nyi'
# IEnumerable = 'nyi'

# }
# Value = $InputObject
# PropsFromType = [List[Object]]::New()
# PropsFromObject = [List[Object]]::New()
# }


# [pscustomobject]$Meta

# # update-typedata to hide Value ?

# }

function WarnIf.NotType {
    param(
        [Parameter(Mandatory)]
        [object]$InputObject,

        [Parameter(Mandatory)]
        [string]$TypeName
    )
    if(-not( $InputObject -is $TypeName)) {
        'Jsonify: WarnIfNotType: expected {0} but found {1}' -f @(
            $TypeName
            $InputObject.GetType().Name
        ) | write-warning
    }
}

function CoerceFrom.Datetime {
    [OutputType('System.string')]
    param(
        [Parameter(mandatory, ValueFromPipeline)]
        [object]$InputObject,
        [Alias('IgnoreProp', 'DropProperty', 'Drop')]
        [string[]]$ExcludeProperty = @()
    )
    process {
        WarnIf.NotType $InputObject -type 'datetime'

        return $InputObject.ToString('o')
    }
}

function CoerceFrom.FileSystemInfo {
    [Alias('CoerceFrom.File')]
    param(
        [object]$InputObject,

        [ValidateSet(
            'Basic',
            'Minify'
        )]
        [string]$TemplateName = 'Basic',

        [Alias('IgnoreProp', 'DropProperty', 'Drop')]
        [string[]]$ExcludeProperty = @(
            'PSPath'
            'LastAccessTime'
            'Exists'
            # 'Extension'
            'PSChildName'
            'PSDrive'
            'PSIsContainer'
            'PSParentPath'
            'PSPath'
            'PSProvider'
            'ResolvedTarget'
            'Target'
            'UnixFileMode'
            'Attributes'
            'Mode'
            'IsReadOnly'
            # 'Length'
            'VersionInfo'
        )


    )
    $Obj = $InputObject
    $tinfo = $InputObject.GetType()
    $meta = [ordered]@{
        PSTypeName = 'Jsonify.File'
        TypeName = $Tinfo.Name
    }
    'AutoJson using TemplateName {0} on {1}' -f @( $TemplateName ; $Obj.GetType().Name )
            | write-verbose

    switch( $tinfo.FullName ) {
        { $_ -in @( 'System.IO.DirectoryInfo' ) } {
            # Shared props
            $meta.Name                = $Obj.Name.ToString()
            $meta.BaseName            = $Obj.Name.ToString()
            $meta.FullName            = $Obj.FullName.ToString()
            $meta.PSPath              = $Obj.PSPath.ToString()
            $meta.Length              = $Obj.Length
            $meta.CreationTime        = AutoJsonify.From.Datetime $Obj.CreationTime
            $meta.CreationTimeUtc     = AutoJsonify.From.Datetime $Obj.CreationTimeUtc
            $meta.LastWriteTime       = AutoJsonify.From.Datetime $Obj.LastWriteTime
            $meta.LastWriteTimeUtc    = AutoJsonify.From.Datetime $Obj.LastWriteTimeUtc
            $meta.LastAccessTime      = AutoJsonify.From.Datetime $Obj.LastWriteTime
            $meta.LastAccessTimeUtc   = AutoJsonify.From.Datetime $Obj.LastWriteTimeUtc
            $meta.Attributes          = [string]$Obj.Attributes
            $meta.Exists              = [string]$Obj.Exists
            $meta.Extension           = [string]$Obj.Extension
            $meta.LinkTarget          = [string]$Obj.LinkTarget
            $meta.LinkType            = [string]$Obj.LinkType
            $meta.Mode                = [string]$Obj.Mode
            $meta.ModeWithoutHardLink = [string]$Obj.ModeWithoutHardLink
            $meta.PSChildName         = [string]$Obj.PSChildName
            $meta.PSDrive             = [string]$Obj.PSDrive
            $meta.PSIsContainer       = [string]$Obj.PSIsContainer
            $meta.PSParentPath        = [string]$Obj.PSParentPath
            $meta.PSPath              = [string]$Obj.PSPath
            $meta.PSProvider          = [string]$Obj.PSProvider
            $meta.ResolvedTarget      = [string]$Obj.ResolvedTarget
            $meta.Target              = [string]$Obj.Target
            $meta.UnixFileMode        = [string]$Obj.UnixFileMode
        }
        'System.IO.DirectoryInfo' {
            <#
             props built by command
                Dot.List.Contains ( aj.Props (get-item .) -NameOnly) (aj.Props (get-item .\get-item-auto.json) -NameOnly) A.NotIn.B
            #>

            $meta.Parent = [string]$Obj.Parent
            $meta.Root   = [string]$Obj.Root
        }
        'System.IO.FileInfo' {
            <#
            props built by command
 
            Dot.List.Contains ( aj.Props (get-item .) -NameOnly) (aj.Props (get-item .\get-item-auto.json) -NameOnly) A.NotIn.B
                Directory
                DirectoryName
                IsReadOnly
                Length
                VersionInfo
            #>

            $meta.Directory     = [string]$Obj.Directory
            $meta.DirectoryName = [string]$Obj.DirectoryName
            $meta.IsReadOnly    = [string]$Obj.IsReadOnly
            $meta.Length        = [string]$Obj.Length
            $meta.VersionInfo   = [string]$Obj.VersionInfo
        }
        default {
            throw "AutoJsonify.From.FilesSystemInfo::UnhandledType: $( $InputObject.GetType() )"
        }
    }


    switch($TemplateName) {
        'Basic' {
            #n o-op atm
        }
        'Minify' {
            # remove almost all properties
            $toRemove = $meta.Keys.clone().where{ $_ -notin @(
                'Name'
                'FullName'
                'Length'
                'LastWriteTimeUtc'
            ) }
            $ExcludeProperty =  @( $ExcludeProperty ; $toRemove )
            write-debug 'minify using template'
        }

        default { throw "UnhandledTemplateName: $TemplateName"}
    }

    # $simplfiy
    foreach($name in @( $ExcludeProperty )) {
        $meta.remove( $Name )
    }
    [pscustomobject]$meta
}

function Jsonify.CoerceType {
    <#
    .SYNOPSIS
        Delegate to custom type handlers
    #>

    [OutputType('System.String')]
    [Alias('CoerceFrom.Any')]
    param(
        [object]$InputObject
    )


    if($_ -is 'IO.FileSystemInfo') {
        $new = CoerceFrom.FileSystemInfo -InputObject $_
    } else {
        $new = $new
    }
    return $new
}

function Jsonify.GetCommands {
    param(
        $InputObject
    )
    gcm -m Ninmonkey.Console *jsonif* -ea 'ignore'
        | Join-string -sep ', ' -op 'see also: Ninmonkey.Console\ => [ ' -os ' ] '
        | Write-verbose -verbose

    (Get-module 'Jsonify' ).ExportedCommands | Join-String -sep ', ' -op 'Commands: '
        | write-Verbose
}

function ConvertTo-Jsonify {
    <#
    .SYNOPSIS
        core entry point for the proxy of ConvertTo-Json
    #>

    [Alias(
        'AutoJsonify', 'Jsonify', 'aj.Json'
    )]

    param(
        [Alias('Obj', 'Data', 'InpObj', 'In')]
        [Parameter(Mandatory, Position = 0, ValueFromPipeline)]
        [AllowNull()]
        [Object]${InputObject},

        [ValidateRange(0, 100)]
        [int]${Depth} = 6,
        [switch]${Compress},
        [switch]${EnumsAsStrings} = $true,
        [switch]${AsArray} =  $true,

        #[Newtonsoft.Json.StringEscapeHandling]
        [ValidateSet( 'Default', 'EscapeNonAscii', 'EscapeHtml' )]
        ${EscapeHandling})

    begin {
        try {
            # $outBuffer = $null
            # if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) {
            # $PSBoundParameters['OutBuffer'] = 1
            # }
            $newParams = [ordered]@{} + $PSBoundParameters
            $newParams['WarningAction'] = 'ignore'
            $commandName = 'Microsoft.PowerShell.Utility\ConvertTo-Json'
            $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand(
                <# commandName: #> $commandName,
                <# type: #> [CommandTypes]::Cmdlet )

            $scriptCmd = { & $wrappedCmd @newParams }

            $steppablePipeline = $scriptCmd.GetSteppablePipeline(
                $myInvocation.CommandOrigin)
            $steppablePipeline.Begin($PSCmdlet)
        }
        catch {
            throw
        }
    }

    process {
        try {
            # write-verbose 'Aj.Json :: Process'
            $new = Jsonify.CoerceType -InputObject $_
            $steppablePipeline.Process( $new )
        }
        catch {
            throw
        }
    }

    end {
        # write-verbose 'Aj.Json :: End'
        try {
            $steppablePipeline.End()
        }
        catch {
            throw
        }
    }

    clean {
        if ($null -ne $steppablePipeline) {
            $steppablePipeline.Clean()
        }
    }
}

Export-ModuleMember -Function @(
    'ConvertTo-Jsonify'
    'AutoJson.*'
    'AutoJsonify.*'
    'aj.*'
    'Jsonify.*',
    'Json.*'

    if( $ModuleConfig.ExportCoercionFunctions ) {
        'CoerceFrom.*'
    }
) -Alias @(
    'AutoJson.*'
    'AutoJsonify.*'
    'aj.*'
    'Jsonify.*',
    'Json.*'
    if( $ModuleConfig.ExportCoercionFunctions ) {
        'CoerceFrom.*'
    }
)