Private/Console/Syntax.psm1
|
using namespace System using namespace System.Collections using namespace System.Collections.Generic using namespace System.Globalization using namespace System.Text using module .\Colors.psm1 using module ..\Enums.psm1 using module ..\Abstracts.psm1 using module .\Rendering.psm1 class JsonSyntax : IRenderable { static [Style]$PunctuationStyle = [Style]::new([Color]::FromName('Grey62')) static [Style]$MemberStyle = [Style]::new([Color]::FromName('DeepSkyBlue1')) static [Style]$StringStyle = [Style]::new([Color]::FromName('SpringGreen3')) static [Style]$NumberStyle = [Style]::new([Color]::FromName('MediumPurple1')) static [Style]$BooleanStyle = [Style]::new([Color]::FromName('Yellow1')) static [Style]$NullStyle = [Style]::new([Color]::FromName('Grey58'), [Color]::Default, [Decoration]::Italic) [Measurement] Measure([RenderOptions]$options, [int]$maxWidth) { $max = 0 foreach ($line in [Segment]::SplitLines($this.Render($options, [Math]::Max(1, $maxWidth)), [Math]::Max(1, $maxWidth))) { $max = [Math]::Max($max, $line.CellCount()) } return [Measurement]::new([Math]::Min($max, $maxWidth), [Math]::Min($max, $maxWidth)) } [object[]] Render([RenderOptions]$options, [int]$maxWidth) { $segments = [List[Segment]]::new() $this.Write($segments, 0) return $segments.ToArray() } [void] Write([List[Segment]]$segments, [int]$indent) { } static [JsonSyntax] FromObject([object]$value) { if ($null -eq $value) { return [JsonNull]::new() } if ($value -is [JsonSyntax]) { return [JsonSyntax]$value } if ($value -is [string] -or $value -is [char]) { return [JsonString]::new([string]$value) } if ($value -is [bool]) { return [JsonBoolean]::new([bool]$value) } if ([JsonSyntax]::IsNumber($value)) { return [JsonNumber]::new($value) } if ($value -is [IDictionary]) { $object = [JsonObject]::new() foreach ($key in ([IDictionary]$value).Keys) { $object.Add([string]$key, [JsonSyntax]::FromObject(([IDictionary]$value)[$key])) } return $object } if ($value -is [psobject] -and $value.psobject.Properties.Count -gt 0 -and $value -isnot [IEnumerable]) { $object = [JsonObject]::new() foreach ($property in $value.psobject.Properties) { $object.Add($property.Name, [JsonSyntax]::FromObject($property.Value)) } return $object } if ($value -is [IEnumerable] -and $value -isnot [string]) { $array = [JsonArray]::new() foreach ($item in [IEnumerable]$value) { $array.Add([JsonSyntax]::FromObject($item)) } return $array } return [JsonString]::new($value.ToString()) } static hidden [bool] IsNumber([object]$value) { return $value -is [byte] -or $value -is [sbyte] -or $value -is [int16] -or $value -is [uint16] -or $value -is [int] -or $value -is [uint32] -or $value -is [long] -or $value -is [uint64] -or $value -is [single] -or $value -is [double] -or $value -is [decimal] } static hidden [void] AddIndent([List[Segment]]$segments, [int]$indent) { if ($indent -gt 0) { $segments.Add([Segment]::new(' ' * $indent, [Style]::Plain)) } } static hidden [string] EscapeString([string]$value) { if ($null -eq $value) { return '' } $builder = [StringBuilder]::new() foreach ($ch in $value.ToCharArray()) { switch ($ch) { '"' { [void]$builder.Append('\"'); break } '\' { [void]$builder.Append('\\'); break } "`b" { [void]$builder.Append('\b'); break } "`f" { [void]$builder.Append('\f'); break } "`n" { [void]$builder.Append('\n'); break } "`r" { [void]$builder.Append('\r'); break } "`t" { [void]$builder.Append('\t'); break } default { if ([char]::IsControl($ch)) { [void]$builder.Append('\u{0:x4}' -f [int]$ch) } else { [void]$builder.Append($ch) } } } } return $builder.ToString() } } class JsonArray : JsonSyntax { [List[JsonSyntax]]$Items JsonArray() { $this.Items = [List[JsonSyntax]]::new() } [void] Add([JsonSyntax]$item) { $this.Items.Add(($null -ne $item) ? $item : [JsonNull]::new()) } [void] Write([List[Segment]]$segments, [int]$indent) { $segments.Add([Segment]::new('[', [JsonSyntax]::PunctuationStyle)) if ($this.Items.Count -eq 0) { $segments.Add([Segment]::new(']', [JsonSyntax]::PunctuationStyle)) return } for ($i = 0; $i -lt $this.Items.Count; $i++) { $segments.Add([Segment]::LineBreak) [JsonSyntax]::AddIndent($segments, $indent + 2) $this.Items[$i].Write($segments, $indent + 2) if ($i -lt $this.Items.Count - 1) { $segments.Add([Segment]::new(',', [JsonSyntax]::PunctuationStyle)) } } $segments.Add([Segment]::LineBreak) [JsonSyntax]::AddIndent($segments, $indent) $segments.Add([Segment]::new(']', [JsonSyntax]::PunctuationStyle)) } } class JsonBoolean : JsonSyntax { [bool]$Value JsonBoolean([bool]$value) { $this.Value = $value } [void] Write([List[Segment]]$segments, [int]$indent) { $segments.Add([Segment]::new($this.Value.ToString().ToLowerInvariant(), [JsonSyntax]::BooleanStyle)) } } class JsonMember : JsonSyntax { [string]$Name [JsonSyntax]$Value JsonMember([string]$name, [JsonSyntax]$value) { $this.Name = $name $this.Value = ($null -ne $value) ? $value : [JsonNull]::new() } [void] Write([List[Segment]]$segments, [int]$indent) { $segments.Add([Segment]::new('"' + [JsonSyntax]::EscapeString($this.Name) + '"', [JsonSyntax]::MemberStyle)) $segments.Add([Segment]::new(': ', [JsonSyntax]::PunctuationStyle)) $this.Value.Write($segments, $indent) } } class JsonNull : JsonSyntax { JsonNull() { } [void] Write([List[Segment]]$segments, [int]$indent) { $segments.Add([Segment]::new('null', [JsonSyntax]::NullStyle)) } } class JsonNumber : JsonSyntax { [string]$Raw JsonNumber([object]$value) { if ($null -eq $value) { $this.Raw = '0' } elseif ($value -is [IFormattable]) { $this.Raw = ([IFormattable]$value).ToString($null, [CultureInfo]::InvariantCulture) } else { $this.Raw = $value.ToString() } } [void] Write([List[Segment]]$segments, [int]$indent) { $segments.Add([Segment]::new($this.Raw, [JsonSyntax]::NumberStyle)) } } class JsonObject : JsonSyntax { [List[JsonMember]]$Members JsonObject() { $this.Members = [List[JsonMember]]::new() } [void] Add([string]$name, [JsonSyntax]$value) { $this.Members.Add([JsonMember]::new($name, $value)) } [void] Write([List[Segment]]$segments, [int]$indent) { $segments.Add([Segment]::new('{', [JsonSyntax]::PunctuationStyle)) if ($this.Members.Count -eq 0) { $segments.Add([Segment]::new('}', [JsonSyntax]::PunctuationStyle)) return } for ($i = 0; $i -lt $this.Members.Count; $i++) { $segments.Add([Segment]::LineBreak) [JsonSyntax]::AddIndent($segments, $indent + 2) $this.Members[$i].Write($segments, $indent + 2) if ($i -lt $this.Members.Count - 1) { $segments.Add([Segment]::new(',', [JsonSyntax]::PunctuationStyle)) } } $segments.Add([Segment]::LineBreak) [JsonSyntax]::AddIndent($segments, $indent) $segments.Add([Segment]::new('}', [JsonSyntax]::PunctuationStyle)) } } class JsonString : JsonSyntax { [string]$Value JsonString([string]$value) { $this.Value = $value } [void] Write([List[Segment]]$segments, [int]$indent) { $segments.Add([Segment]::new('"' + [JsonSyntax]::EscapeString($this.Value) + '"', [JsonSyntax]::StringStyle)) } } |