2020-07-19 17:19:13 -04:00
using System ;
using System.Collections.Generic ;
using System.Globalization ;
using System.Text ;
using GitHub.DistributedTask.Pipelines.Validation ;
namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
{
internal sealed class ReferenceNameBuilder
{
internal void AppendSegment ( String value )
{
if ( String . IsNullOrEmpty ( value ))
{
return ;
}
if ( m_name . Length == 0 )
{
var first = value [ 0 ];
if (( first >= 'a' && first <= 'z' ) ||
( first >= 'A' && first <= 'Z' ) ||
first == '_' )
{
// Legal first char
}
else if (( first >= '0' && first <= '9' ) || first == '-' )
{
// Illegal first char, but legal char.
// Prepend "_".
m_name . Append ( "_" );
}
else
{
// Illegal char
}
}
else
{
// Separator
m_name . Append ( c_separator );
}
foreach ( var c in value )
{
if (( c >= 'a' && c <= 'z' ) ||
( c >= 'A' && c <= 'Z' ) ||
( c >= '0' && c <= '9' ) ||
c == '_' ||
c == '-' )
{
// Legal
m_name . Append ( c );
}
else
{
// Illegal
m_name . Append ( "_" );
}
}
}
internal String Build ()
{
var original = m_name . Length > 0 ? m_name . ToString () : "job" ;
var attempt = 1 ;
var suffix = default ( String );
while ( true )
{
if ( attempt == 1 )
{
suffix = String . Empty ;
}
else if ( attempt < 1000 )
{
suffix = String . Format ( CultureInfo . InvariantCulture , "_{0}" , attempt );
}
else
{
throw new InvalidOperationException ( "Unable to create a unique name" );
}
var candidate = original . Substring ( 0 , Math . Min ( original . Length , PipelineConstants . MaxNodeNameLength - suffix . Length )) + suffix ;
if ( m_distinctNames . Add ( candidate ))
{
m_name . Clear ();
return candidate ;
}
attempt ++;
}
}
internal Boolean TryAddKnownName (
String value ,
out String error )
{
if (! NameValidation . IsValid ( value , allowHyphens : true ) && value . Length < PipelineConstants . MaxNodeNameLength )
{
2025-02-17 23:24:15 +08:00
error = $"The identifier '{value}' is invalid. IDs may only contain alphanumeric characters, '_', and '-'. IDs must start with a letter or '_' and must be less than {PipelineConstants.MaxNodeNameLength} characters." ;
2020-07-19 17:19:13 -04:00
return false ;
}
else if (! m_distinctNames . Add ( value ))
{
error = $"The identifier '{value}' may not be used more than once within the same scope." ;
return false ;
}
else
{
error = null ;
return true ;
}
}
private const String c_separator = "_" ;
private readonly HashSet < String > m_distinctNames = new HashSet < String >( StringComparer . OrdinalIgnoreCase );
private readonly StringBuilder m_name = new StringBuilder ();
}
}