# encoding: ascii
# api: powershell
# title: New-Struct
# description: A code-generating and emitting magic function for creating type-safe struct classes for use in PowerShell!
# version: 0.1
# type: class
# author: Joel Bennett
# license: CC0
# function: New-Struct
# x-poshcode-id: 143
# x-archived: 2016-03-02T15:21:43
# x-published: 2008-02-16T15:12:00
#
#
## New-Struct
##   Creates a Struct class and emits it into memory
##   The Struct includes a constructor which takes the parameters in order...
## 
## Usage:
##   # Assuming you have a csv file with no header and columns: artist,name,length
##   New-Struct Song @{
##     Artist=[string];
##     Name=[string];
##     Length=[TimeSpan];
##   }
##   $songs = gc C:\Scripts\songlist.csv | % { new-object Song @($_ -split ",") }
##
function New-Struct {
	param([string]$Name,[HashTable]$Properties)
	switch($Properties.Keys){{$_ -isnot [String]}{throw "Invalid Syntax"}}
	switch($Properties.Values){{$_ -isnot [type]}{throw "Invalid Syntax"}}

	# CODE GENERATION MAGIKS!
	$code = @"
using System;
public struct $Name {

  $($Properties.Keys | % { "  public {0} {1};`n" -f $Properties[$_],($_.ToUpper()[0] + $_.SubString(1)) })
  public $Name ($( ($Properties.Keys | % { "{0} {1}" -f $Properties[$_],($_.ToLower()) }) -join ", " )) {
    $($Properties.Keys | % { "    {0} = {1};`n" -f ($_.ToUpper()[0] + $_.SubString(1)),($_.ToLower()) })
  }
}
"@

	## Obtains an ICodeCompiler from a CodeDomProvider class.
	$provider = New-Object Microsoft.CSharp.CSharpCodeProvider
	## Get the location for System.Management.Automation DLL
	$dllName = [PsObject].Assembly.Location
	## Configure the compiler parameters
	$compilerParameters = New-Object System.CodeDom.Compiler.CompilerParameters
	$assemblies = @("System.dll", $dllName)

	$compilerParameters.ReferencedAssemblies.AddRange($assemblies)
	$compilerParameters.IncludeDebugInformation = $true
	$compilerParameters.GenerateInMemory = $true

	$compilerResults = $provider.CompileAssemblyFromSource($compilerParameters, $code)
	if($compilerResults.Errors.Count -gt 0) {
	  $compilerResults.Errors | % { Write-Error $_.Line + ":`t" + $_.ErrorText }
	}
}
