2025-11-07 14:18:52 -06:00
#nullable disable // Consider removing in the future to minimize likelihood of NullReferenceException; refer https://learn.microsoft.com/en-us/dotnet/csharp/nullable-references
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Threading ;
using GitHub.Actions.Expressions ;
using GitHub.Actions.WorkflowParser.ObjectTemplating.Schema ;
using GitHub.Actions.WorkflowParser.ObjectTemplating.Tokens ;
namespace GitHub.Actions.WorkflowParser.ObjectTemplating
{
/// <summary>
/// Context object that is flowed through while loading and evaluating object templates
/// </summary>
public sealed class TemplateContext
{
internal CancellationToken CancellationToken { get ; set ; }
internal TemplateValidationErrors Errors
{
get
{
if ( m_errors = = null )
{
m_errors = new TemplateValidationErrors ( ) ;
}
return m_errors ;
}
set
{
m_errors = value ;
}
}
/// <summary>
/// Available functions within expression contexts
/// </summary>
internal IList < IFunctionInfo > ExpressionFunctions
{
get
{
if ( m_expressionFunctions = = null )
{
m_expressionFunctions = new List < IFunctionInfo > ( ) ;
}
return m_expressionFunctions ;
}
}
/// <summary>
/// Available values within expression contexts
/// </summary>
internal IDictionary < String , Object > ExpressionValues
{
get
{
if ( m_expressionValues = = null )
{
m_expressionValues = new Dictionary < String , Object > ( StringComparer . OrdinalIgnoreCase ) ;
}
return m_expressionValues ;
}
}
internal TemplateMemory Memory { get ; set ; }
internal TemplateSchema Schema { get ; set ; }
/// <summary>
/// Gets or sets the telemetry data collected during parsing.
/// </summary>
public Telemetry Telemetry
{
get
{
if ( m_telemetry = = null )
{
m_telemetry = new Telemetry ( ) ;
}
return m_telemetry ;
}
set
{
m_telemetry = value ;
}
}
/// <summary>
/// State data for the current evaluation
/// </summary>
public IDictionary < String , Object > State
{
get
{
if ( m_state = = null )
{
m_state = new Dictionary < String , Object > ( StringComparer . OrdinalIgnoreCase ) ;
}
return m_state ;
}
}
/// <summary>
/// Gets or sets a value indicating whether the expression function fromJson performs strict JSON parsing.
/// Used during evaluation only.
/// </summary>
internal Boolean StrictJsonParsing { get ; set ; }
2025-12-17 10:57:05 -05:00
/// <summary>
/// Gets or sets a value indicating whether the case expression function is allowed.
/// Defaults to true. Set to false to disable the case function.
/// </summary>
internal Boolean AllowCaseFunction { get ; set ; } = true ;
2025-11-07 14:18:52 -06:00
internal ITraceWriter TraceWriter { get ; set ; }
private IDictionary < String , Int32 > FileIds
{
get
{
if ( m_fileIds = = null )
{
m_fileIds = new Dictionary < String , Int32 > ( StringComparer . OrdinalIgnoreCase ) ;
}
return m_fileIds ;
}
set
{
m_fileIds = value ;
}
}
private List < String > FileNames
{
get
{
if ( m_fileNames = = null )
{
m_fileNames = new List < String > ( ) ;
}
return m_fileNames ;
}
set
{
m_fileNames = value ;
}
}
internal void Error (
TemplateToken value ,
Exception ex )
{
Error ( value ? . FileId , value ? . Line , value ? . Column , ex ) ;
}
internal void Error (
Int32 ? fileId ,
Int32 ? line ,
Int32 ? column ,
Exception ex )
{
var prefix = GetErrorPrefix ( fileId , line , column ) ;
Errors . Add ( prefix , ex ) ;
TraceWriter . Error ( prefix , ex ) ;
}
internal void Error (
TemplateToken value ,
String message )
{
Error ( value ? . FileId , value ? . Line , value ? . Column , message ) ;
}
internal void Error (
Int32 ? fileId ,
Int32 ? line ,
Int32 ? column ,
String message )
{
var prefix = GetErrorPrefix ( fileId , line , column ) ;
if ( ! String . IsNullOrEmpty ( prefix ) )
{
message = $"{prefix} {message}" ;
}
Errors . Add ( message ) ;
TraceWriter . Error ( message ) ;
}
internal INamedValueInfo [ ] GetExpressionNamedValues ( )
{
if ( m_expressionValues ? . Count > 0 )
{
return m_expressionValues . Keys . Select ( x = > new NamedValueInfo < ContextValueNode > ( x ) ) . ToArray ( ) ;
}
return null ;
}
internal Int32 GetFileId ( String file )
{
if ( ! FileIds . TryGetValue ( file , out Int32 id ) )
{
id = FileIds . Count + 1 ;
FileIds . Add ( file , id ) ;
FileNames . Add ( file ) ;
Memory . AddBytes ( file ) ;
}
return id ;
}
internal String GetFileName ( Int32 fileId )
{
return FileNames . Count > = fileId ? FileNames [ fileId - 1 ] : null ;
}
internal IReadOnlyList < String > GetFileTable ( )
{
return FileNames . AsReadOnly ( ) ;
}
private String GetErrorPrefix (
Int32 ? fileId ,
Int32 ? line ,
Int32 ? column )
{
var fileName = fileId . HasValue ? GetFileName ( fileId . Value ) : null ;
if ( ! String . IsNullOrEmpty ( fileName ) )
{
if ( line ! = null & & column ! = null )
{
return $"{fileName} {TemplateStrings.LineColumn(line, column)}:" ;
}
else
{
return $"{fileName}:" ;
}
}
else if ( line ! = null & & column ! = null )
{
return $"{TemplateStrings.LineColumn(line, column)}:" ;
}
else
{
return String . Empty ;
}
}
private TemplateValidationErrors m_errors ;
private IList < IFunctionInfo > m_expressionFunctions ;
private IDictionary < String , Object > m_expressionValues ;
private IDictionary < String , Int32 > m_fileIds ;
private List < String > m_fileNames ;
private IDictionary < String , Object > m_state ;
private Telemetry m_telemetry ;
}
}