// Copyright Keysight Technologies 2012-2019 // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, you can obtain one at http://mozilla.org/MPL/2.0/. using System; using System.Text.RegularExpressions; namespace OpenTap.Package { /// This interface specifies how to convert from a version string to a SemanticVersion. public interface IVersionConverter : ITapPlugin { /// Try to convert 'versionString' to a SemanticVersion. Will throw on exceptions. SemanticVersion Convert(string versionString); } /// Plugin type like IVersionConverter, that can TryConvert, which does will not throw an exception. public interface IVersionTryConverter : ITapPlugin { /// Try to convert 'versionString' to a SemanticVersion. Returns false on failure. bool TryConvert(string versionString, out SemanticVersion version); } [Display("ConvertMajorMinorBuildRevision", "Supports a four value number (x.x.x.x) which will be interpreted as Major.Minor.BuildMetadata.Patch. This is compatible with Microsofts definition of version numbers (e.g. for .NET assemblies), see https://docs.microsoft.com/en-us/dotnet/api/system.version", Order: 2)] internal class MajorMinorBuildRevisionVersionConverter : IVersionTryConverter { public SemanticVersion Convert(string versionString) { string[] parts = versionString.Split('.'); if (parts.Length != 4) throw new ArgumentException("Version number must have 4 values."); return new SemanticVersion(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[3]),null,parts[2]); } public bool TryConvert(string versionString, out SemanticVersion version) { version = null; string[] parts = versionString.Split('.'); if (parts.Length != 4) return false; if(int.TryParse(parts[0], out var maj) && int.TryParse(parts[1], out var min) && int.TryParse(parts[3], out var rev)) { version = new SemanticVersion(maj, min, rev, null, parts[2]); return true; } return false; } } [Display("ConvertFourValue", "Supports a four value number (x.y.z.w) which will be converted to the semantic version number x.y.z+w.", Order: 1)] internal class FourValueVersionConverter : IVersionTryConverter { public SemanticVersion Convert(string versionString) { string[] parts = versionString.Split('.'); if (parts.Length != 4) throw new ArgumentException("Version number must have 4 values."); return new SemanticVersion(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), null, parts[3]); } public bool TryConvert(string versionString, out SemanticVersion version) { version = null; string[] parts = versionString.Split('.'); if (parts.Length != 4) return false; if (int.TryParse(parts[0], out var maj) && int.TryParse(parts[1], out var min) && int.TryParse(parts[2], out var rev)) { version = new SemanticVersion(maj, min, rev, null, parts[3]); return true; } return false; } } [Display("Compatibility", "For compatibility with TAP 8.x parsing.", Order: 1)] internal class Tap8CompatibilityVersionConverter : IVersionConverter { public SemanticVersion Convert(string versionString) { int major, minor, build = 0; // Otherwise fall back to legacy code. var full_parts = versionString.Trim().Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries); if (full_parts.Length == 0) return new SemanticVersion(0,0,0,null,null); var version_string = full_parts[0]; var parts = version_string.Split('.'); bool isok = int.TryParse(parts[0], out major); if (!isok) return new SemanticVersion(0, 0, 0, null, null); if (parts.Length == 1) return new SemanticVersion(major, 0, 0, null, null); ; isok = int.TryParse(parts[1], out minor); if (!isok) { if (parts[1].Length > 0 && parts.Length == 2) { return new SemanticVersion(major, 0, 0, null, parts[1]); } return new SemanticVersion(major, 0, 0, null, null); } if (parts.Length > 2) isok = int.TryParse(parts[2], out build); else if (full_parts.Length > 1) { // When developing and using a OpenTAP version that is built on the local machine // (not by pushing to git and have the CI system do the build), we would like // for OpenTAP not to complain about incompatible build versions. string type = full_parts[1]; if (type == "Development") build = int.MaxValue; } string commit = null; string prerelease = null; if (parts.Length > 3) commit = parts[3]; if (!isok) { var match = Regex.Match(version_string, ".*?-(.*?)(\\+|$)"); if (match.Success && match.Groups.Count > 1 && match.Groups[1].Value != "") prerelease = match.Groups[1].Value; match = Regex.Match(parts[2], ".*?\\+(.*)"); if (match.Success && match.Groups.Count > 1) commit = match.Groups[1].Value; version_string = parts[2]; version_string = version_string.Split('-')[0]; version_string = version_string.Split('+')[0]; isok = int.TryParse(version_string, out build); if (isok == false) build = 0; } return new SemanticVersion(major,minor,build,prerelease,commit); } } }