// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; namespace EpicGames.Perforce { /// /// Represents a change view from a clientspec or stream /// public class PerforceChangeView { /// /// List of entries for this change view /// public List Entries { get; } = new List(); /// /// Determines if a file revision is visible /// /// Path to the depot file /// Change number of the file public bool IsVisible(string depotFile, int change) { bool visible = true; foreach (PerforceChangeViewEntry entry in Entries) { if (entry.Matches(depotFile)) { visible = change <= entry.Change; } } return visible; } /// /// Parse a change view from a specification /// /// Lines for the change view /// Whether this will be evaluated in the context of a case-insensitive server public static PerforceChangeView Parse(IEnumerable lines, bool ignoreCase) { PerforceChangeView changeView = new PerforceChangeView(); foreach (string line in lines) { if (!String.IsNullOrWhiteSpace(line)) { // Skip over lines with unsupported formatting (generally @label instead of @changenumber) try { changeView.Entries.Add(new PerforceChangeViewEntry(line, ignoreCase)); } catch (FormatException) { // don't do anything } } } return changeView; } } /// /// Entry for a change view /// public class PerforceChangeViewEntry { /// /// Pattern to match /// public string Path { get; } /// /// Change to be imported at /// public long Change { get; } readonly Regex _pathRegex; /// /// Constructor /// public PerforceChangeViewEntry(string pattern, bool ignoreCase) { int atIdx = pattern.LastIndexOf('@'); Path = pattern.Substring(0, atIdx); Change = Int64.Parse(pattern.AsSpan(atIdx + 1), NumberStyles.None); string regexPattern = Regex.Escape(Path); regexPattern = regexPattern.Replace(@"\?", ".", StringComparison.Ordinal); regexPattern = regexPattern.Replace(@"\.\.\.", ".*", StringComparison.Ordinal); _pathRegex = new Regex($"^{regexPattern}$", ignoreCase ? RegexOptions.IgnoreCase : RegexOptions.None); } /// /// Determine /// /// /// public bool Matches(string depotPath) => _pathRegex.IsMatch(depotPath); } }