mirror of
https://github.com/actions/runner.git
synced 2025-12-10 20:36:49 +00:00
304 lines
11 KiB
C#
304 lines
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
namespace GitHub.DistributedTask.Pipelines.ContextData
|
|
{
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public static class PipelineContextDataExtensions
|
|
{
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public static ArrayContextData AssertArray(
|
|
this PipelineContextData value,
|
|
String objectDescription)
|
|
{
|
|
if (value is ArrayContextData array)
|
|
{
|
|
return array;
|
|
}
|
|
|
|
throw new ArgumentException($"Unexpected type '{value?.GetType().Name}' encountered while reading '{objectDescription}'. The type '{nameof(ArrayContextData)}' was expected.");
|
|
}
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public static DictionaryContextData AssertDictionary(
|
|
this PipelineContextData value,
|
|
String objectDescription)
|
|
{
|
|
if (value is DictionaryContextData dictionary)
|
|
{
|
|
return dictionary;
|
|
}
|
|
|
|
throw new ArgumentException($"Unexpected type '{value?.GetType().Name}' encountered while reading '{objectDescription}'. The type '{nameof(DictionaryContextData)}' was expected.");
|
|
}
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public static CaseSensitiveDictionaryContextData AssertCaseSensitiveDictionary(
|
|
this PipelineContextData value,
|
|
String objectDescription)
|
|
{
|
|
if (value is CaseSensitiveDictionaryContextData dictionary)
|
|
{
|
|
return dictionary;
|
|
}
|
|
|
|
throw new ArgumentException($"Unexpected type '{value?.GetType().Name}' encountered while reading '{objectDescription}'. The type '{nameof(CaseSensitiveDictionaryContextData)}' was expected.");
|
|
}
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public static StringContextData AssertString(
|
|
this PipelineContextData value,
|
|
String objectDescription)
|
|
{
|
|
if (value is StringContextData str)
|
|
{
|
|
return str;
|
|
}
|
|
|
|
throw new ArgumentException($"Unexpected type '{value?.GetType().Name}' encountered while reading '{objectDescription}'. The type '{nameof(StringContextData)}' was expected.");
|
|
}
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public static BooleanContextData AssertBoolean(
|
|
this PipelineContextData value,
|
|
String objectDescription)
|
|
{
|
|
if (value is BooleanContextData boolValue)
|
|
{
|
|
return boolValue;
|
|
}
|
|
|
|
throw new ArgumentException($"Unexpected type '{value?.GetType().Name}' encountered while reading '{objectDescription}'. The type '{nameof(BooleanContextData)}' was expected.");
|
|
}
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public static NumberContextData AssertNumber(
|
|
this PipelineContextData value,
|
|
String objectDescription)
|
|
{
|
|
if (value is NumberContextData num)
|
|
{
|
|
return num;
|
|
}
|
|
|
|
throw new ArgumentException($"Unexpected type '{value?.GetType().Name}' encountered while reading '{objectDescription}'. The type '{nameof(NumberContextData)}' was expected.");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns all context data objects (depth first)
|
|
/// </summary>
|
|
internal static IEnumerable<PipelineContextData> Traverse(this PipelineContextData value)
|
|
{
|
|
return Traverse(value, omitKeys: false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns all context data objects (depth first)
|
|
/// </summary>
|
|
internal static IEnumerable<PipelineContextData> Traverse(
|
|
this PipelineContextData value,
|
|
Boolean omitKeys)
|
|
{
|
|
yield return value;
|
|
|
|
if (value is ArrayContextData || value is DictionaryContextData)
|
|
{
|
|
var state = new TraversalState(null, value);
|
|
while (state != null)
|
|
{
|
|
if (state.MoveNext(omitKeys))
|
|
{
|
|
value = state.Current;
|
|
yield return value;
|
|
|
|
if (value is ArrayContextData || value is DictionaryContextData)
|
|
{
|
|
state = new TraversalState(state, value);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
state = state.Parent;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal static JToken ToJToken(this PipelineContextData value)
|
|
{
|
|
JToken result;
|
|
|
|
if (value is StringContextData str)
|
|
{
|
|
result = str.Value ?? String.Empty;
|
|
}
|
|
else if (value is BooleanContextData booleanValue)
|
|
{
|
|
result = booleanValue.Value;
|
|
}
|
|
else if (value is NumberContextData num)
|
|
{
|
|
result = num.Value;
|
|
}
|
|
else if (value is ArrayContextData array)
|
|
{
|
|
var jarray = new JArray();
|
|
|
|
foreach (var item in array)
|
|
{
|
|
jarray.Add(item.ToJToken()); // Recurse
|
|
}
|
|
|
|
result = jarray;
|
|
}
|
|
else if (value is DictionaryContextData dictionary)
|
|
{
|
|
var jobject = new JObject();
|
|
|
|
foreach (var pair in dictionary)
|
|
{
|
|
var key = pair.Key ?? String.Empty;
|
|
var value2 = pair.Value.ToJToken(); // Recurse
|
|
|
|
if (value2 != null)
|
|
{
|
|
jobject[key] = value2;
|
|
}
|
|
}
|
|
|
|
result = jobject;
|
|
}
|
|
else
|
|
{
|
|
throw new InvalidOperationException("Internal error reading the template. Expected a string, an array, or a dictionary");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
internal static TemplateToken ToTemplateToken(this PipelineContextData data)
|
|
{
|
|
if (data is null)
|
|
{
|
|
return new NullToken(null, null, null);
|
|
}
|
|
|
|
switch (data.Type)
|
|
{
|
|
case PipelineContextDataType.Dictionary:
|
|
var dictionary = data.AssertDictionary("dictionary");
|
|
var mapping = new MappingToken(null, null, null);
|
|
if (dictionary.Count > 0)
|
|
{
|
|
foreach (var pair in dictionary)
|
|
{
|
|
var key = new StringToken(null, null, null, pair.Key);
|
|
var value = pair.Value.ToTemplateToken();
|
|
mapping.Add(key, value);
|
|
}
|
|
}
|
|
return mapping;
|
|
|
|
case PipelineContextDataType.Array:
|
|
var array = data.AssertArray("array");
|
|
var sequence = new SequenceToken(null, null, null);
|
|
if (array.Count > 0)
|
|
{
|
|
foreach (var item in array)
|
|
{
|
|
sequence.Add(item.ToTemplateToken());
|
|
}
|
|
}
|
|
return sequence;
|
|
|
|
case PipelineContextDataType.String:
|
|
var stringData = data as StringContextData;
|
|
return new StringToken(null, null, null, stringData.Value);
|
|
|
|
case PipelineContextDataType.Boolean:
|
|
var booleanData = data as BooleanContextData;
|
|
return new BooleanToken(null, null, null, booleanData.Value);
|
|
|
|
case PipelineContextDataType.Number:
|
|
var numberData = data as NumberContextData;
|
|
return new NumberToken(null, null, null, numberData.Value);
|
|
|
|
default:
|
|
throw new NotSupportedException($"Unexpected {nameof(PipelineContextDataType)} type '{data.Type}'");
|
|
}
|
|
}
|
|
|
|
private sealed class TraversalState
|
|
{
|
|
public TraversalState(
|
|
TraversalState parent,
|
|
PipelineContextData data)
|
|
{
|
|
Parent = parent;
|
|
m_data = data;
|
|
}
|
|
|
|
public Boolean MoveNext(Boolean omitKeys)
|
|
{
|
|
switch (m_data.Type)
|
|
{
|
|
case PipelineContextDataType.Array:
|
|
var array = m_data.AssertArray("array");
|
|
if (++m_index < array.Count)
|
|
{
|
|
Current = array[m_index];
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
Current = null;
|
|
return false;
|
|
}
|
|
|
|
case PipelineContextDataType.Dictionary:
|
|
var dictionary = m_data.AssertDictionary("dictionary");
|
|
|
|
// Return the value
|
|
if (m_isKey)
|
|
{
|
|
m_isKey = false;
|
|
Current = dictionary[m_index].Value;
|
|
return true;
|
|
}
|
|
|
|
if (++m_index < dictionary.Count)
|
|
{
|
|
// Skip the key, return the value
|
|
if (omitKeys)
|
|
{
|
|
m_isKey = false;
|
|
Current = dictionary[m_index].Value;
|
|
return true;
|
|
}
|
|
|
|
// Return the key
|
|
m_isKey = true;
|
|
Current = new StringContextData(dictionary[m_index].Key);
|
|
return true;
|
|
}
|
|
|
|
Current = null;
|
|
return false;
|
|
|
|
default:
|
|
throw new NotSupportedException($"Unexpected {nameof(PipelineContextData)} type '{m_data.Type}'");
|
|
}
|
|
}
|
|
|
|
private PipelineContextData m_data;
|
|
private Int32 m_index = -1;
|
|
private Boolean m_isKey;
|
|
public PipelineContextData Current;
|
|
public TraversalState Parent;
|
|
}
|
|
}
|
|
}
|