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) { 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."; 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 m_distinctNames = new HashSet(StringComparer.OrdinalIgnoreCase); private readonly StringBuilder m_name = new StringBuilder(); } }