Skip to content

Commit 3921aff

Browse files
committed
Add PSAppDeployToolkit.CodeGen.PowerShellSyntaxSerializer into project.
1 parent 0714b2a commit 3921aff

5 files changed

Lines changed: 560 additions & 0 deletions

File tree

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright (C) 2026 Devicie Pty Ltd. All rights reserved.
3+
*
4+
* This file is part of PSAppDeployToolkit.
5+
*
6+
* PSAppDeployToolkit is free software: you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public License
8+
* as published by the Free Software Foundation, either version 3
9+
* of the License, or (at your option) any later version.
10+
*
11+
* PSAppDeployToolkit is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14+
*
15+
* See the GNU Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public License
18+
* along with PSAppDeployToolkit. If not, see <https://www.gnu.org/licenses/>.
19+
*/
20+
21+
using System;
22+
using PSADT.ProcessManagement;
23+
24+
namespace PSAppDeployToolkit.CodeGen.Converters
25+
{
26+
/// <summary>
27+
/// Converts a ProcessDefinition object to its PowerShell syntax representation.
28+
/// </summary>
29+
/// <remarks>This converter serializes ProcessDefinition instances into PowerShell hashtable syntax using
30+
/// a PowerShellSyntaxWriter. It is intended for internal use within the PowerShell deployment toolkit to facilitate
31+
/// script generation and serialization tasks.</remarks>
32+
internal sealed class ProcessDefinitionSyntaxConverter : PowerShellSyntaxConverter<ProcessDefinition>
33+
{
34+
/// <inheritdoc/>
35+
public override void Write(ProcessDefinition value, PowerShellSyntaxWriter writer)
36+
{
37+
ArgumentNullException.ThrowIfNull(value);
38+
ArgumentNullException.ThrowIfNull(writer);
39+
writer.WriteStartHashtable();
40+
writer.WritePropertyName("Name");
41+
writer.WriteStringValue(value.Name);
42+
if (value.Description is not null)
43+
{
44+
writer.WritePropertyName("Description");
45+
writer.WriteStringValue(value.Description);
46+
}
47+
writer.WriteEndHashtable();
48+
}
49+
}
50+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright (C) 2026 Devicie Pty Ltd. All rights reserved.
3+
*
4+
* This file is part of PSAppDeployToolkit.
5+
*
6+
* PSAppDeployToolkit is free software: you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public License
8+
* as published by the Free Software Foundation, either version 3
9+
* of the License, or (at your option) any later version.
10+
*
11+
* PSAppDeployToolkit is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14+
*
15+
* See the GNU Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public License
18+
* along with PSAppDeployToolkit. If not, see <https://www.gnu.org/licenses/>.
19+
*/
20+
21+
using System;
22+
23+
namespace PSAppDeployToolkit.CodeGen
24+
{
25+
/// <summary>
26+
/// Provides a base class for converting objects to their PowerShell syntax representation.
27+
/// </summary>
28+
/// <remarks>Implement this class to define custom serialization logic for specific .NET types when
29+
/// generating PowerShell syntax. The converter is used by the serialization infrastructure to handle type-specific
30+
/// formatting and emission of PowerShell tokens.</remarks>
31+
internal abstract class PowerShellSyntaxConverter
32+
{
33+
/// <summary>
34+
/// Gets the <see cref="Type"/> this converter handles.
35+
/// </summary>
36+
public abstract Type TargetType { get; }
37+
38+
/// <summary>
39+
/// Writes the specified value to the output using the provided PowerShell syntax writer and serializer.
40+
/// </summary>
41+
/// <param name="value">The object to be written to the output.</param>
42+
/// <param name="writer">The PowerShellSyntaxWriter instance used to write the output.</param>
43+
internal abstract void WriteCore(object value, PowerShellSyntaxWriter writer);
44+
}
45+
46+
/// <summary>
47+
/// Provides a base class for converting strongly-typed values to their PowerShell syntax representation.
48+
/// </summary>
49+
/// <remarks>Implement this class to define custom serialization logic for specific types when emitting
50+
/// PowerShell syntax. Use the provided serializer to handle nested or complex values recursively.</remarks>
51+
/// <typeparam name="T">The type of value to be converted to PowerShell syntax.</typeparam>
52+
internal abstract class PowerShellSyntaxConverter<T> : PowerShellSyntaxConverter
53+
{
54+
/// <inheritdoc/>
55+
public sealed override Type TargetType => typeof(T);
56+
57+
/// <summary>
58+
/// Writes the specified value to the output using the provided PowerShell syntax writer and serializer.
59+
/// </summary>
60+
/// <param name="value">The value to be written to the output.</param>
61+
/// <param name="writer">The PowerShell syntax writer used to generate the output.</param>
62+
public abstract void Write(T value, PowerShellSyntaxWriter writer);
63+
64+
/// <inheritdoc/>
65+
internal sealed override void WriteCore(object value, PowerShellSyntaxWriter writer)
66+
{
67+
Write((T)value, writer);
68+
}
69+
}
70+
}
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/*
2+
* Copyright (C) 2026 Devicie Pty Ltd. All rights reserved.
3+
*
4+
* This file is part of PSAppDeployToolkit.
5+
*
6+
* PSAppDeployToolkit is free software: you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public License
8+
* as published by the Free Software Foundation, either version 3
9+
* of the License, or (at your option) any later version.
10+
*
11+
* PSAppDeployToolkit is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14+
*
15+
* See the GNU Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public License
18+
* along with PSAppDeployToolkit. If not, see <https://www.gnu.org/licenses/>.
19+
*/
20+
21+
using System;
22+
using System.Collections;
23+
using System.Collections.Specialized;
24+
using System.Globalization;
25+
using System.Management.Automation;
26+
27+
namespace PSAppDeployToolkit.CodeGen
28+
{
29+
/// <summary>
30+
/// Provides functionality to serialize .NET objects into their PowerShell syntax representation.
31+
/// </summary>
32+
/// <remarks>The PowerShellSyntaxSerializer supports custom converters through configuration and handles a
33+
/// variety of common .NET types, including dictionaries, lists, arrays, primitives, and PowerShell-specific types.
34+
/// It is typically used to generate PowerShell code or scripts that recreate the serialized objects.</remarks>
35+
public static class PowerShellSyntaxSerializer
36+
{
37+
/// <summary>
38+
/// Serializes the specified value to a PowerShell-formatted string.
39+
/// </summary>
40+
/// <param name="value">The object to serialize. Can be null.</param>
41+
/// <param name="indentChars">The string to use for indentation in the output. Defaults to four spaces.</param>
42+
/// <returns>A string containing the PowerShell-formatted representation of the value.</returns>
43+
public static string Serialize(object? value, string indentChars = " ")
44+
{
45+
PowerShellSyntaxWriter writer = new(indentChars);
46+
WriteValue(value, writer);
47+
return writer.ToString();
48+
}
49+
50+
/// <summary>
51+
/// Writes the specified value into the given <see cref="PowerShellSyntaxWriter"/>,
52+
/// checking registered converters first, then falling back to built-in handling.
53+
/// </summary>
54+
/// <param name="value">The value to write. May be null.</param>
55+
/// <param name="writer">The writer to emit tokens into. Cannot be null.</param>
56+
internal static void WriteValue(object? value, PowerShellSyntaxWriter writer)
57+
{
58+
// Unwrap PSObject wrappers before dispatching.
59+
ArgumentNullException.ThrowIfNull(writer);
60+
while (value is PSObject psObject && psObject.BaseObject is not PSCustomObject)
61+
{
62+
value = psObject.BaseObject;
63+
}
64+
65+
// Check for a registered converter before falling through to built-ins.
66+
if (value is not null && Settings.Converters.TryGetValue(value.GetType(), out PowerShellSyntaxConverter? converter))
67+
{
68+
converter.WriteCore(value, writer);
69+
return;
70+
}
71+
72+
// Handle built-in types.
73+
switch (value)
74+
{
75+
case null:
76+
writer.WriteNullValue();
77+
break;
78+
79+
case IDictionary dict:
80+
writer.WriteStartHashtable(dict is OrderedDictionary);
81+
foreach (DictionaryEntry entry in dict)
82+
{
83+
writer.WritePropertyName(entry.Key.ToString() ?? string.Empty);
84+
WriteValue(entry.Value, writer);
85+
}
86+
writer.WriteEndHashtable();
87+
break;
88+
89+
case Array array:
90+
writer.WriteStartArray();
91+
for (int i = 0; i < array.Length; i++)
92+
{
93+
if (i > 0)
94+
{
95+
writer.WriteArraySeparator();
96+
}
97+
WriteValue(array.GetValue(i), writer);
98+
}
99+
writer.WriteEndArray();
100+
break;
101+
102+
case IList list:
103+
writer.WriteStartArray();
104+
for (int i = 0; i < list.Count; i++)
105+
{
106+
if (i > 0)
107+
{
108+
writer.WriteArraySeparator();
109+
}
110+
WriteValue(list[i], writer);
111+
}
112+
writer.WriteEndArray();
113+
break;
114+
115+
case string str:
116+
writer.WriteStringValue(str);
117+
break;
118+
119+
case bool b:
120+
writer.WriteBooleanValue(b);
121+
break;
122+
123+
case SwitchParameter sw:
124+
writer.WriteBooleanValue(sw.IsPresent);
125+
break;
126+
127+
case byte:
128+
case sbyte:
129+
case short:
130+
case ushort:
131+
case int:
132+
case uint:
133+
case long:
134+
case ulong:
135+
writer.WriteRawValue(string.Format(CultureInfo.InvariantCulture, "{0}", value));
136+
break;
137+
138+
case float f:
139+
writer.WriteRawValue(f.ToString("G", CultureInfo.InvariantCulture));
140+
break;
141+
142+
case double d:
143+
writer.WriteRawValue(d.ToString("G", CultureInfo.InvariantCulture));
144+
break;
145+
146+
case decimal m:
147+
writer.WriteRawValue(m.ToString(CultureInfo.InvariantCulture));
148+
break;
149+
150+
case DateTime dt:
151+
writer.WriteRawValue("[System.DateTime]'");
152+
writer.WriteRawValue(dt.ToString("o", CultureInfo.InvariantCulture));
153+
writer.WriteRawValue("'");
154+
break;
155+
156+
case Enum e:
157+
writer.WriteRawValue($"[{e.GetType().FullName}]::{e}");
158+
break;
159+
160+
case ScriptBlock sb:
161+
writer.WriteRawValue("{ ");
162+
writer.WriteRawValue(sb.ToString());
163+
writer.WriteRawValue(" }");
164+
break;
165+
166+
default:
167+
throw new InvalidOperationException("Unsupported type: " + value.GetType().FullName);
168+
}
169+
}
170+
171+
/// <summary>
172+
/// Holds the serializer settings used to control PowerShell syntax serialization behavior.
173+
/// </summary>
174+
/// <remarks>If no settings are provided, the default serializer settings are used. This field is
175+
/// intended for internal use to configure serialization options.</remarks>
176+
private static readonly PowerShellSyntaxSerializerSettings Settings = PowerShellSyntaxSerializerSettings.Default;
177+
}
178+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright (C) 2026 Devicie Pty Ltd. All rights reserved.
3+
*
4+
* This file is part of PSAppDeployToolkit.
5+
*
6+
* PSAppDeployToolkit is free software: you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public License
8+
* as published by the Free Software Foundation, either version 3
9+
* of the License, or (at your option) any later version.
10+
*
11+
* PSAppDeployToolkit is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14+
*
15+
* See the GNU Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public License
18+
* along with PSAppDeployToolkit. If not, see <https://www.gnu.org/licenses/>.
19+
*/
20+
21+
using System;
22+
using System.Collections.Generic;
23+
using System.Collections.ObjectModel;
24+
using PSAppDeployToolkit.CodeGen.Converters;
25+
26+
namespace PSAppDeployToolkit.CodeGen
27+
{
28+
/// <summary>
29+
/// Provides configuration settings for the PowerShell syntax serializer, including the set of registered syntax
30+
/// converters.
31+
/// </summary>
32+
/// <remarks>Use this class to specify which syntax converters are available when serializing or
33+
/// deserializing PowerShell objects. The default settings include the built-in converters provided by
34+
/// PSAppDeployToolkit. Custom instances can be created to register additional or alternative converters as
35+
/// needed.</remarks>
36+
internal sealed class PowerShellSyntaxSerializerSettings
37+
{
38+
/// <summary>
39+
/// Gets the default settings for the PowerShell syntax serializer.
40+
/// </summary>
41+
/// <remarks>The default settings include a predefined set of syntax converters suitable for most
42+
/// serialization scenarios. Use this instance when custom configuration is not required.</remarks>
43+
internal static readonly PowerShellSyntaxSerializerSettings Default = new([
44+
new ProcessDefinitionSyntaxConverter(),
45+
]);
46+
47+
/// <summary>
48+
/// Initializes a new instance of the
49+
/// <see cref="PowerShellSyntaxSerializerSettings"/> class with the specified converters.
50+
/// </summary>
51+
/// <param name="converters">The converters to register. Duplicate target types are not permitted.</param>
52+
/// <exception cref="ArgumentException">Thrown if two or more converters share the same <see cref="PowerShellSyntaxConverter.TargetType"/>.</exception>
53+
private PowerShellSyntaxSerializerSettings(params IReadOnlyList<PowerShellSyntaxConverter> converters)
54+
{
55+
ArgumentNullException.ThrowIfNull(converters);
56+
Dictionary<Type, PowerShellSyntaxConverter> dict = new(converters.Count);
57+
foreach (PowerShellSyntaxConverter converter in converters)
58+
{
59+
ArgumentNullException.ThrowIfNull(converter);
60+
if (dict.ContainsKey(converter.TargetType))
61+
{
62+
throw new ArgumentException($"A converter for type '{converter.TargetType.FullName}' is already registered.", nameof(converters));
63+
}
64+
dict.Add(converter.TargetType, converter);
65+
}
66+
Converters = new(dict);
67+
}
68+
69+
/// <summary>
70+
/// A read-only dictionary of the registered converters keyed by their target type.
71+
/// </summary>
72+
internal readonly ReadOnlyDictionary<Type, PowerShellSyntaxConverter> Converters;
73+
}
74+
}

0 commit comments

Comments
 (0)