-Moved CMSIS-PAck specific version parsing out of SemanticVersion classes to keep SemanticVersion clean and conformant, while still handling the lrelaxed parsing needs of CMSIS
- refactored more of the XSD generated classes - Added Support to the SemanticVersion Grammar and parser to handle superset of valid syntax. All Valid Semantic versions parse, and a number of officially invalid ones do. This allows re-use of the primary parsing with other variants that have more lax syntax constraints. - Added VersionQualifier to handle special cases of optional leading character in some forms of relaxed syntax
This commit is contained in:
Родитель
0f64195066
Коммит
1bf0a6ea05
|
@ -48,6 +48,7 @@
|
|||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Xml" />
|
||||
|
@ -75,7 +76,7 @@
|
|||
<Compile Include="PackDescription\DebugConfig.cs" />
|
||||
<Compile Include="PackDescription\DebugInterface.cs" />
|
||||
<Compile Include="PackDescription\DebugPort.cs" />
|
||||
<Compile Include="PackDescription\DebugType.cs" />
|
||||
<Compile Include="PackDescription\Debug.cs" />
|
||||
<Compile Include="PackDescription\DebugVarsType.cs" />
|
||||
<Compile Include="PackDescription\DescriptionType.cs" />
|
||||
<Compile Include="PackDescription\DeviceFeatureType.cs" />
|
||||
|
@ -123,6 +124,7 @@
|
|||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="RepositoryPackage.cs" />
|
||||
<Compile Include="RepositoryUpdateEventArgs.cs" />
|
||||
<Compile Include="VersionParser.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
|
|
@ -43,7 +43,7 @@ namespace CMSIS.Pack.PackDescription
|
|||
get { return ApiVersion.ToString(); }
|
||||
set
|
||||
{
|
||||
ApiVersion = SemanticVersion.Parse( value );
|
||||
ApiVersion = VersionParser.Parse( value );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,8 +22,7 @@ namespace CMSIS.Pack.PackDescription
|
|||
get { return Version.ToString( ); }
|
||||
set
|
||||
{
|
||||
// NOTE: Gracefully handle some real-world PDSC files with simple versions
|
||||
Version = SemanticVersion.Parse( value, ParseOptions.PatchOptional );
|
||||
Version = VersionParser.Parse( value );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -43,8 +43,7 @@ namespace CMSIS.Pack.PackDescription
|
|||
get { return Version.ToString( ); }
|
||||
set
|
||||
{
|
||||
// NOTE: Gracefully handle some real-world PDSC files with simple versions
|
||||
Version = SemanticVersion.Parse( value, ParseOptions.PatchOptional );
|
||||
Version = VersionParser.Parse( value );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace CMSIS.Pack.PackDescription
|
|||
public string RawCversionString
|
||||
{
|
||||
get { return Version.ToString(); }
|
||||
set { Version = SemanticVersion.Parse( value ); }
|
||||
set { Version = VersionParser.Parse( value ); }
|
||||
}
|
||||
|
||||
[XmlIgnore]
|
||||
|
@ -51,7 +51,7 @@ namespace CMSIS.Pack.PackDescription
|
|||
public string RawCapiversionString
|
||||
{
|
||||
get { return ApiVersion.ToString(); }
|
||||
set { ApiVersion = SemanticVersion.Parse( value ); }
|
||||
set { ApiVersion = VersionParser.Parse( value ); }
|
||||
}
|
||||
|
||||
[XmlIgnore]
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using Sprache;
|
||||
|
||||
namespace CMSIS.Pack.PackDescription
|
||||
{
|
||||
[Serializable( )]
|
||||
public class Debug
|
||||
{
|
||||
/// <remarks/>
|
||||
[XmlElement( "datapatch")]
|
||||
public DebugDataPatch[] Datapatch { get; set; }
|
||||
|
||||
/// <remarks/>
|
||||
[XmlAttribute( "__dp", Form=System.Xml.Schema.XmlSchemaForm.Qualified)]
|
||||
[DefaultValue( 0 )]
|
||||
public uint DebugPortId { get; set; } = 0;
|
||||
|
||||
/// <remarks/>
|
||||
[XmlAttribute( "__ap", Form=System.Xml.Schema.XmlSchemaForm.Qualified)]
|
||||
[DefaultValue( 0 )]
|
||||
public uint AccessPortIndex { get; set; } = 0;
|
||||
|
||||
/// <remarks/>
|
||||
[XmlAttribute( "svd", Form=System.Xml.Schema.XmlSchemaForm.Qualified)]
|
||||
public string SvdFile { get; set; }
|
||||
|
||||
/// <remarks/>
|
||||
[XmlAttribute( "Pname", Form = System.Xml.Schema.XmlSchemaForm.Qualified )]
|
||||
public string ProcessorName
|
||||
{
|
||||
get { return ProcessorName_; }
|
||||
set { ProcessorName_ = Parsers.RestrictedString.Parse( value ); }
|
||||
}
|
||||
private string ProcessorName_;
|
||||
|
||||
/// <remarks/>
|
||||
[XmlAnyAttribute( )]
|
||||
public XmlAttribute[ ] AnyAttr { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,111 +0,0 @@
|
|||
using System;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using Sprache;
|
||||
|
||||
namespace CMSIS.Pack.PackDescription
|
||||
{
|
||||
[Serializable( )]
|
||||
public class DebugType {
|
||||
|
||||
private DebugDataPatch[] datapatchField;
|
||||
|
||||
private uint @__dpField;
|
||||
|
||||
private bool @__dpFieldSpecified;
|
||||
|
||||
private uint @__apField;
|
||||
|
||||
private bool @__apFieldSpecified;
|
||||
|
||||
private string svdField;
|
||||
|
||||
private XmlAttribute[] anyAttrField;
|
||||
|
||||
/// <remarks/>
|
||||
[XmlElement( "datapatch")]
|
||||
public DebugDataPatch[] datapatch {
|
||||
get {
|
||||
return datapatchField;
|
||||
}
|
||||
set {
|
||||
datapatchField = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
[XmlAttribute( Form=System.Xml.Schema.XmlSchemaForm.Qualified)]
|
||||
public uint @__dp {
|
||||
get {
|
||||
return @__dpField;
|
||||
}
|
||||
set {
|
||||
@__dpField = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
[XmlIgnore( )]
|
||||
public bool @__dpSpecified {
|
||||
get {
|
||||
return @__dpFieldSpecified;
|
||||
}
|
||||
set {
|
||||
@__dpFieldSpecified = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
[XmlAttribute( Form=System.Xml.Schema.XmlSchemaForm.Qualified)]
|
||||
public uint @__ap {
|
||||
get {
|
||||
return @__apField;
|
||||
}
|
||||
set {
|
||||
@__apField = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
[XmlIgnore( )]
|
||||
public bool @__apSpecified {
|
||||
get {
|
||||
return @__apFieldSpecified;
|
||||
}
|
||||
set {
|
||||
@__apFieldSpecified = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
[XmlAttribute( Form=System.Xml.Schema.XmlSchemaForm.Qualified)]
|
||||
public string svd {
|
||||
get {
|
||||
return svdField;
|
||||
}
|
||||
set {
|
||||
svdField = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
[XmlAttribute( "Pname", Form = System.Xml.Schema.XmlSchemaForm.Qualified )]
|
||||
public string ProcessorName
|
||||
{
|
||||
get { return ProcessorName_; }
|
||||
set { ProcessorName_ = Parsers.RestrictedString.Parse( value ); }
|
||||
}
|
||||
private string ProcessorName_;
|
||||
|
||||
/// <remarks/>
|
||||
[XmlAnyAttribute( )]
|
||||
public XmlAttribute[ ] AnyAttr {
|
||||
get {
|
||||
return anyAttrField;
|
||||
}
|
||||
set {
|
||||
anyAttrField = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,42 +1,31 @@
|
|||
using System;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using SemVer.NET;
|
||||
using Sprache;
|
||||
|
||||
namespace CMSIS.Pack.PackDescription
|
||||
{
|
||||
[Serializable( )]
|
||||
public class DebugVarsType {
|
||||
|
||||
private string configfileField;
|
||||
|
||||
private string versionField;
|
||||
|
||||
private XmlAttribute[] anyAttrField;
|
||||
|
||||
private string valueField;
|
||||
|
||||
public class DebugVarsType
|
||||
{
|
||||
/// <remarks/>
|
||||
[XmlAttribute( Form=System.Xml.Schema.XmlSchemaForm.Qualified)]
|
||||
public string configfile {
|
||||
get {
|
||||
return configfileField;
|
||||
}
|
||||
set {
|
||||
configfileField = value;
|
||||
[XmlAttribute( "configfile", Form=System.Xml.Schema.XmlSchemaForm.Qualified )]
|
||||
public string ConfigFile { get; set; }
|
||||
|
||||
[XmlAttribute( "version", Form = System.Xml.Schema.XmlSchemaForm.Qualified )]
|
||||
[System.ComponentModel.EditorBrowsable( System.ComponentModel.EditorBrowsableState.Never )]
|
||||
public string RawVersionString
|
||||
{
|
||||
get { return Version.ToString( ); }
|
||||
set
|
||||
{
|
||||
Version = VersionParser.Parse( value );
|
||||
}
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
[XmlAttribute( Form=System.Xml.Schema.XmlSchemaForm.Qualified)]
|
||||
public string version {
|
||||
get {
|
||||
return versionField;
|
||||
}
|
||||
set {
|
||||
versionField = value;
|
||||
}
|
||||
}
|
||||
[XmlIgnore]
|
||||
public SemanticVersion Version { get; set; }
|
||||
|
||||
/// <remarks/>
|
||||
[XmlAttribute( "Pname", Form = System.Xml.Schema.XmlSchemaForm.Qualified )]
|
||||
|
@ -49,24 +38,10 @@ namespace CMSIS.Pack.PackDescription
|
|||
|
||||
/// <remarks/>
|
||||
[XmlAnyAttribute( )]
|
||||
public XmlAttribute[ ] AnyAttr {
|
||||
get {
|
||||
return anyAttrField;
|
||||
}
|
||||
set {
|
||||
anyAttrField = value;
|
||||
}
|
||||
}
|
||||
public XmlAttribute[ ] AnyAttr { get; set; }
|
||||
|
||||
/// <remarks/>
|
||||
[XmlText( )]
|
||||
public string Value {
|
||||
get {
|
||||
return valueField;
|
||||
}
|
||||
set {
|
||||
valueField = value;
|
||||
}
|
||||
}
|
||||
public string Value { get; set; }
|
||||
}
|
||||
}
|
|
@ -27,7 +27,7 @@ namespace CMSIS.Pack.PackDescription
|
|||
|
||||
private DebugPort[] debugportField;
|
||||
|
||||
private DebugType[] debugField;
|
||||
private Debug[] debugField;
|
||||
|
||||
private TraceType[] traceField;
|
||||
|
||||
|
@ -150,7 +150,7 @@ namespace CMSIS.Pack.PackDescription
|
|||
|
||||
/// <remarks/>
|
||||
[XmlElement( "debug")]
|
||||
public DebugType[] debug {
|
||||
public Debug[] debug {
|
||||
get {
|
||||
return debugField;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace CMSIS.Pack.PackDescription
|
|||
|
||||
private DebugPort[] debugportField;
|
||||
|
||||
private DebugType[] debugField;
|
||||
private Debug[] debugField;
|
||||
|
||||
private TraceType[] traceField;
|
||||
|
||||
|
@ -149,7 +149,7 @@ namespace CMSIS.Pack.PackDescription
|
|||
|
||||
/// <remarks/>
|
||||
[XmlElement( "debug")]
|
||||
public DebugType[] debug {
|
||||
public Debug[] debug {
|
||||
get {
|
||||
return debugField;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace CMSIS.Pack.PackDescription
|
|||
|
||||
private DebugPort[] debugportField;
|
||||
|
||||
private DebugType[] debugField;
|
||||
private Debug[] debugField;
|
||||
|
||||
private TraceType[] traceField;
|
||||
|
||||
|
@ -155,7 +155,7 @@ namespace CMSIS.Pack.PackDescription
|
|||
|
||||
/// <remarks/>
|
||||
[XmlElement( "debug")]
|
||||
public DebugType[] debug {
|
||||
public Debug[] debug {
|
||||
get {
|
||||
return debugField;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace CMSIS.Pack.PackDescription
|
|||
get { return Version.ToString( ); }
|
||||
set
|
||||
{
|
||||
Version = SemanticVersion.Parse( value );
|
||||
Version = VersionParser.Parse( value );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace CMSIS.Pack.PackDescription
|
|||
|
||||
private DebugPort[] debugportField;
|
||||
|
||||
private DebugType[] debugField;
|
||||
private Debug[] debugField;
|
||||
|
||||
private TraceType[] traceField;
|
||||
|
||||
|
@ -151,7 +151,7 @@ namespace CMSIS.Pack.PackDescription
|
|||
|
||||
/// <remarks/>
|
||||
[XmlElement( "debug")]
|
||||
public DebugType[] debug {
|
||||
public Debug[] debug {
|
||||
get {
|
||||
return debugField;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using SemVer.NET;
|
||||
using Sprache;
|
||||
|
||||
namespace CMSIS.Pack
|
||||
{
|
||||
// CMSIS-Pack uses a relaxed syntax for semantic versions.
|
||||
// Deviations from the official spec are:
|
||||
// 1) Trailing 0s on Minor and Patch are ignored
|
||||
// 2) Patch is optional, if not present a default of 0 is assumed
|
||||
// 3) The hyphen prerelease delimiter is optional if the first
|
||||
// character of the prerelease identifier is a letter
|
||||
//
|
||||
// This utility class provides for relaxed parsing to generate
|
||||
// a full SemanticVersion that, when converted back to a string
|
||||
// will conform to the SemanticVersion 2.0.0 specs. Thus, this
|
||||
// class serves to normalize the nonstandard versions into the
|
||||
// standard form.
|
||||
internal class VersionParser
|
||||
{
|
||||
internal static SemanticVersion Parse( string version)
|
||||
{
|
||||
var parts = Grammar.SemanticVersion.Parse( version );
|
||||
|
||||
// handle case #2
|
||||
var patch = string.IsNullOrWhiteSpace( parts.Patch ) ? "0" : parts.Patch;
|
||||
|
||||
// handle case #1
|
||||
patch = patch.Length > 1 ? patch.TrimEnd('0') : patch;
|
||||
var minor = parts.Minor.Length > 1 ? parts.Minor.TrimEnd('0') : parts.Minor;
|
||||
|
||||
if( parts.LeadingV.HasValue )
|
||||
throw new FormatException( "Leading 'v' characters not supported in CMSIS-PACK Versions" );
|
||||
|
||||
return new SemanticVersion( BigInteger.Parse( parts.Major )
|
||||
, BigInteger.Parse( minor )
|
||||
, BigInteger.Parse( patch )
|
||||
, GetPrereleaseQualifiers( parts.Prerelease )
|
||||
, parts.BuildMetadata?.Identifiers ?? Enumerable.Empty<string>( )
|
||||
);
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetPrereleaseQualifiers( VersionQualifier qualifier )
|
||||
{
|
||||
if( qualifier == null )
|
||||
yield break;
|
||||
|
||||
var firstIdentifier = qualifier.Identifiers.FirstOrDefault( );
|
||||
if( firstIdentifier == null )
|
||||
yield break;
|
||||
|
||||
// handles case #3
|
||||
if( char.IsLetter( qualifier.LeadingChar ) )
|
||||
firstIdentifier = $"{qualifier.LeadingChar}{firstIdentifier}";
|
||||
|
||||
yield return firstIdentifier;
|
||||
|
||||
foreach( var identifier in qualifier.Identifiers.Skip( 1 ) )
|
||||
yield return identifier;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -70,6 +70,10 @@ namespace SemVer.NET
|
|||
public static Parser<string> BuildNumber = NumericIdentifier.Text();
|
||||
|
||||
/// <summary>Parser monad to parse a semantic version string into a <see cref="ParseResult"/></summary>
|
||||
/// <remarks>
|
||||
/// This parser allows for a number of variations of SemanticVersions in the wild by parsing a
|
||||
/// superset of SemanticVersion
|
||||
/// </remarks>
|
||||
public static Parser<ParseResult> SemanticVersion
|
||||
= from leadingV in LeadingV.Optional()
|
||||
from major in BuildNumber
|
||||
|
@ -79,20 +83,21 @@ namespace SemVer.NET
|
|||
from patchValue in BuildNumber
|
||||
select patchValue
|
||||
).Optional()
|
||||
from preRelease in ( from start in StartPreRelease
|
||||
from preRelease in ( from start in StartPreRelease.Or( Letter )
|
||||
from preRelIds in DotSeparatedReleaseIdentifiers
|
||||
select preRelIds
|
||||
select new VersionQualifier( start, preRelIds )
|
||||
).Optional()
|
||||
from buildMetadata in ( from start in StartBuild
|
||||
from buildIds in DotSeparatedBuildIdentifiers
|
||||
select buildIds
|
||||
select new VersionQualifier( start, buildIds )
|
||||
).Optional()
|
||||
select new ParseResult( leadingV.IsDefined ? new char?( leadingV.Get() ) : null
|
||||
, major
|
||||
, minor
|
||||
, patch.GetOrElse( string.Empty )
|
||||
, preRelease.GetOrElse( Enumerable.Empty<string>( ) )
|
||||
, buildMetadata.GetOrElse( Enumerable.Empty<string>( ) )
|
||||
, preRelease.GetOrDefault( )
|
||||
, buildMetadata.GetOrDefault( )
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,19 @@ using System.Collections.Generic;
|
|||
|
||||
namespace SemVer.NET
|
||||
{
|
||||
/// <summary>Parse result of a Version Prerelease or build qualifier</summary>
|
||||
public class VersionQualifier
|
||||
{
|
||||
public VersionQualifier( char leadingChar, IEnumerable<string> identifiers)
|
||||
{
|
||||
LeadingChar = leadingChar;
|
||||
Identifiers = identifiers;
|
||||
}
|
||||
|
||||
public char LeadingChar { get; }
|
||||
public IEnumerable<string> Identifiers { get; }
|
||||
}
|
||||
|
||||
/// <summary>Contains the results of parsing a Semantic version string</summary>
|
||||
/// <remarks>
|
||||
/// <para>In order to support variations on Semantic versions as well as gracefully
|
||||
|
@ -27,8 +40,8 @@ namespace SemVer.NET
|
|||
, string major
|
||||
, string minor
|
||||
, string patch
|
||||
, IEnumerable<string> prereleaseParts
|
||||
, IEnumerable<string> buildParts
|
||||
, VersionQualifier prereleaseParts
|
||||
, VersionQualifier buildParts
|
||||
)
|
||||
{
|
||||
if( major == null )
|
||||
|
@ -40,12 +53,6 @@ namespace SemVer.NET
|
|||
if( patch == null )
|
||||
throw new ArgumentNullException( nameof( patch ) );
|
||||
|
||||
if( prereleaseParts == null )
|
||||
throw new ArgumentNullException( nameof( prereleaseParts ) );
|
||||
|
||||
if( buildParts == null )
|
||||
throw new ArgumentNullException( nameof( buildParts ) );
|
||||
|
||||
LeadingV = leadingV;
|
||||
Major = major;
|
||||
Minor = minor;
|
||||
|
@ -83,9 +90,9 @@ namespace SemVer.NET
|
|||
public string Patch { get; }
|
||||
|
||||
/// <summary>Gets a collection of prerelease identifier strings</summary>
|
||||
public IEnumerable<string> Prerelease { get; }
|
||||
public VersionQualifier Prerelease { get; }
|
||||
|
||||
/// <summary>Gets a collection of the build metadata identifier strings</summary>
|
||||
public IEnumerable<string> BuildMetadata { get; }
|
||||
public VersionQualifier BuildMetadata { get; }
|
||||
}
|
||||
}
|
|
@ -37,6 +37,7 @@
|
|||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Numerics" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Grammar.cs" />
|
||||
|
|
|
@ -2,36 +2,21 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using Sprache;
|
||||
|
||||
namespace SemVer.NET
|
||||
{
|
||||
/// <summary>Option flags for parsing a semantic version string</summary>
|
||||
[Flags]
|
||||
public enum ParseOptions
|
||||
{
|
||||
/// <summary>No special handling, parsing proceeds according to Semantic Version Specification</summary>
|
||||
None = 0,
|
||||
/// <summary>Patch build number is optional, and if not present in the string a default of "0" is assumed</summary>
|
||||
PatchOptional = 1,
|
||||
/// <summary>Allow a leading 'v' or 'V' as a common convention for version numbers</summary>
|
||||
AllowLeadingV = 2,
|
||||
}
|
||||
|
||||
// TODO: Add additional Comparer<SemanticVersion> implementation to handle build metadata in precedence
|
||||
// see: https://github.com/mojombo/semver/issues/200
|
||||
//
|
||||
|
||||
/// <summary>Version structure for versions based on Semantic Versioning v2.0 as defined by https://github.com/mojombo/semver/blob/master/semver.md </summary>
|
||||
/// <summary>Version structure for versions based on SemanticVersion 2.0.0</summary>
|
||||
/// <remarks>
|
||||
/// <para>This class implements creating, parsing, and comparing semantic version values. In
|
||||
/// addition to the standard support, this class includes an additional optional optimization
|
||||
/// where parsing a version string can assume a default patch value of 0 if none is specified.
|
||||
/// According to the formal Semantic Versioning v2.0 spec. the patch value is required, however
|
||||
/// some real world applications allow the looser definition.</para>
|
||||
/// <para>Technically the Major, Minor, and Patch numbers have no length limits, thus this uses</para>
|
||||
/// This class implements creating, parsing, and comparing semantic version values.
|
||||
/// <note type="note">
|
||||
/// Technically, the Major, Minor, and Patch numbers have no length limits, thus this
|
||||
/// class uses <see cref="BigInteger"/> as the type for each of the numeric parts.
|
||||
/// </note>
|
||||
/// </remarks>
|
||||
/// <seealso cref="https://github.com/mojombo/semver/blob/master/semver.md"/>
|
||||
public struct SemanticVersion
|
||||
: IComparable
|
||||
, IComparable<SemanticVersion>
|
||||
|
@ -43,7 +28,7 @@ namespace SemVer.NET
|
|||
/// <param name="patch">Patch version number</param>
|
||||
/// <param name="preReleaseParts">Array of individual prerelease parts (not including the separating '.')</param>
|
||||
/// <param name="metadataParts">Array of individual Build Metadata parts (not including the separating '.')</param>
|
||||
public SemanticVersion( int major, int minor, int patch, IEnumerable<string> preReleaseParts, IEnumerable<string> metadataParts )
|
||||
public SemanticVersion( BigInteger major, BigInteger minor, BigInteger patch, IEnumerable<string> preReleaseParts, IEnumerable<string> metadataParts )
|
||||
{
|
||||
if( major < 0 )
|
||||
throw new ArgumentOutOfRangeException( nameof( major ) );
|
||||
|
@ -57,16 +42,19 @@ namespace SemVer.NET
|
|||
Major = major;
|
||||
Minor = minor;
|
||||
Patch = patch;
|
||||
PreReleaseParts_ = ( preReleaseParts ?? Enumerable.Empty<string>() ).ToArray();
|
||||
BuildMetadata_ = ( metadataParts ?? Enumerable.Empty<string>() ).ToArray();
|
||||
var prereleaseList = preReleaseParts?.ToList().AsReadOnly() ?? EmptyStringList;
|
||||
var buildMetadataList = metadataParts?.ToList().AsReadOnly() ?? EmptyStringList;
|
||||
|
||||
// Validate each part conforms to an "identifier" as defined by the spec
|
||||
if( !ValidatePrereleaseIdentifierParts( PreReleaseParts_ ) )
|
||||
if( !ValidatePrereleaseIdentifierParts( prereleaseList ) )
|
||||
throw new ArgumentException( "Invalid identifier for prerelease part", nameof( preReleaseParts ) );
|
||||
|
||||
if( !ValidateBuildIdentifierParts( BuildMetadata_ ) )
|
||||
if( !ValidateBuildIdentifierParts( buildMetadataList ) )
|
||||
throw new ArgumentException( "Invalid identifier for build metadata part", nameof( metadataParts ) );
|
||||
|
||||
PreReleaseParts_ = prereleaseList;
|
||||
BuildMetadata_ = buildMetadataList;
|
||||
|
||||
HashCode = null;
|
||||
}
|
||||
|
||||
|
@ -94,13 +82,13 @@ namespace SemVer.NET
|
|||
public bool IsValid => Major >= 0 && Minor >= 0 && Patch >= 0;
|
||||
|
||||
/// <summary>Major version number</summary>
|
||||
public int Major { get; }
|
||||
public BigInteger Major { get; }
|
||||
|
||||
/// <summary>Minor version number</summary>
|
||||
public int Minor { get; }
|
||||
public BigInteger Minor { get; }
|
||||
|
||||
/// <summary>Patch version number</summary>
|
||||
public int Patch { get; }
|
||||
public BigInteger Patch { get; }
|
||||
|
||||
/// <summary>List of identifier parts forming the prerelease value</summary>
|
||||
/// <remarks>
|
||||
|
@ -108,8 +96,8 @@ namespace SemVer.NET
|
|||
/// value can consist of multiple parts separated by a '.', this list contains the
|
||||
/// individual parts without the leading '-' or separating '.'.
|
||||
/// </remarks>
|
||||
public IReadOnlyList<string> PreReleaseParts => Array.AsReadOnly( PreReleaseParts_ ?? EmptyStringArray );
|
||||
private readonly string[ ] PreReleaseParts_;
|
||||
public IReadOnlyList<string> PreReleaseParts => PreReleaseParts_ ?? EmptyStringList;
|
||||
private IReadOnlyList<string> PreReleaseParts_;
|
||||
|
||||
/// <summary>List of identifier parts forming the build Metadata value</summary>
|
||||
/// <remarks>
|
||||
|
@ -117,8 +105,8 @@ namespace SemVer.NET
|
|||
/// value can consist of multiple parts separated by a '.', this list contains
|
||||
/// the individual parts without the leading '+' or separating '.'.
|
||||
/// </remarks>
|
||||
public IReadOnlyList<string> BuildMetadata => Array.AsReadOnly( BuildMetadata_ ?? EmptyStringArray );
|
||||
private readonly string[ ] BuildMetadata_;
|
||||
public IReadOnlyList<string> BuildMetadata => BuildMetadata_ ?? EmptyStringList;
|
||||
private IReadOnlyList<string> BuildMetadata_;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode( )
|
||||
|
@ -239,13 +227,13 @@ namespace SemVer.NET
|
|||
public string ToString( bool includeBuildMetadata )
|
||||
{
|
||||
var bldr = new StringBuilder( string.Format( CultureInfo.InvariantCulture, "{0}.{1}.{2}", Major, Minor, Patch ) );
|
||||
if( PreReleaseParts.Count > 0 )
|
||||
if( ( PreReleaseParts?.Count ?? 0 ) > 0 )
|
||||
{
|
||||
bldr.Append( '-' );
|
||||
bldr.Append( string.Join( ".", PreReleaseParts ) );
|
||||
}
|
||||
|
||||
if( BuildMetadata.Count > 0 && includeBuildMetadata )
|
||||
if( ( BuildMetadata?.Count ?? 0 ) > 0 && includeBuildMetadata )
|
||||
{
|
||||
bldr.Append( '+' );
|
||||
bldr.Append( string.Join( ".", BuildMetadata ) );
|
||||
|
@ -257,22 +245,23 @@ namespace SemVer.NET
|
|||
/// <summary>Parse a semantic version string into it's component parts</summary>
|
||||
/// <param name="versionString">String containing the version to parse</param>
|
||||
/// <returns>Parsed version details</returns>
|
||||
public static SemanticVersion Parse( string versionString ) => Parse( versionString, ParseOptions.None );
|
||||
|
||||
/// <summary>Parse a semantic version string into it's component parts</summary>
|
||||
/// <param name="versionString">String containing the version to parse</param>
|
||||
/// <param name="patchOptional">Flag indicating non-standard optional default (0) for the patch if not present</param>
|
||||
/// <returns>Parsed version details</returns>
|
||||
/// <remarks>
|
||||
/// This overload of Parse allows for a non-standard version where the Patch value
|
||||
/// defaults to 0 if not present, instead of triggering an exception.
|
||||
/// </remarks>
|
||||
public static SemanticVersion Parse( string versionString, ParseOptions options )
|
||||
public static SemanticVersion Parse( string versionString )
|
||||
{
|
||||
try
|
||||
{
|
||||
var parts = Grammar.SemanticVersion.Parse( versionString );
|
||||
return new SemanticVersion( parts, options );
|
||||
if( string.IsNullOrWhiteSpace( parts.Patch ) )
|
||||
throw new FormatException( "Patch component of Semantic Version is required" );
|
||||
|
||||
if( parts.LeadingV.HasValue )
|
||||
throw new FormatException( "Leading 'v' characters not supported in strict SemanticVersion" );
|
||||
|
||||
return new SemanticVersion( BigInteger.Parse( parts.Major )
|
||||
, BigInteger.Parse( parts.Minor )
|
||||
, BigInteger.Parse( parts.Patch )
|
||||
, parts.Prerelease?.Identifiers ?? Enumerable.Empty<string>()
|
||||
, parts.BuildMetadata?.Identifiers ?? Enumerable.Empty<string>( )
|
||||
);
|
||||
}
|
||||
catch( ParseException ex )
|
||||
{
|
||||
|
@ -280,25 +269,41 @@ namespace SemVer.NET
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>Attempts to parse a version string into a new SemanticVersion instance</summary>
|
||||
/// <param name="versionString">String to parse</param>
|
||||
/// <param name="version">Version instance to construct</param>
|
||||
/// <returns>true if the string is a valid semantic version string that was successfully parsed into <paramref name="version"/></returns>
|
||||
public static bool TryParse( string versionString, out SemanticVersion version ) => TryParse( versionString, ParseOptions.None, out version );
|
||||
|
||||
/// <summary>Attempts to parse a version string into a new SemanticVersion instance</summary>
|
||||
/// <param name="versionString">String to parse</param>
|
||||
/// <param name="options">Options flags to control parsing variants and ambiguities in the spec</param>
|
||||
/// <param name="version">Version instance to construct</param>
|
||||
/// <returns>true if the string is a valid semantic version string that was successfully parsed into <paramref name="version"/></returns>
|
||||
public static bool TryParse( string versionString, ParseOptions options, out SemanticVersion version )
|
||||
public static bool TryParse( string versionString, out SemanticVersion version )
|
||||
{
|
||||
version = new SemanticVersion();
|
||||
version = default( SemanticVersion );
|
||||
var result = Grammar.SemanticVersion.TryParse( versionString );
|
||||
if( !result.WasSuccessful )
|
||||
return false;
|
||||
|
||||
version = new SemanticVersion( result.Value, options );
|
||||
var parts = result.Value;
|
||||
if( string.IsNullOrWhiteSpace( parts.Patch ) )
|
||||
return false;
|
||||
|
||||
if( parts.LeadingV.HasValue )
|
||||
return false;
|
||||
|
||||
BigInteger major,minor,patch;
|
||||
if( !BigInteger.TryParse( parts.Major, out major ) )
|
||||
return false;
|
||||
|
||||
if( !BigInteger.TryParse( parts.Minor, out minor ) )
|
||||
return false;
|
||||
|
||||
if( !BigInteger.TryParse( parts.Patch, out patch ) )
|
||||
return false;
|
||||
|
||||
version = new SemanticVersion( major
|
||||
, minor
|
||||
, patch
|
||||
, parts.Prerelease?.Identifiers ?? Enumerable.Empty<string>( )
|
||||
, parts.BuildMetadata?.Identifiers ?? Enumerable.Empty<string>( )
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -334,7 +339,7 @@ namespace SemVer.NET
|
|||
|
||||
private int ComputeHashCode( ) => ToString( false ).GetHashCode( );
|
||||
|
||||
// this is intentionally not a read-only field
|
||||
// This is intentionally not a read-only field
|
||||
// its a private field and used like a C++ mutable.
|
||||
// It caches the HashCode that's expensive to compute
|
||||
// so it is only done once.
|
||||
|
@ -343,21 +348,6 @@ namespace SemVer.NET
|
|||
// When using the default constructor the prerelease and build meta arrays will be null
|
||||
// The property accessors for those arrays will test for null and use this singleton empty
|
||||
// array if null to prevent null reference issues.
|
||||
static readonly string[] EmptyStringArray = new string [0];
|
||||
|
||||
private SemanticVersion( ParseResult parts, ParseOptions options )
|
||||
: this( int.Parse( parts.Major )
|
||||
, int.Parse( parts.Minor )
|
||||
, string.IsNullOrWhiteSpace( parts.Patch ) ? 0 : int.Parse( parts.Patch )
|
||||
, parts.Prerelease
|
||||
, parts.BuildMetadata
|
||||
)
|
||||
{
|
||||
if( !options.HasFlag( ParseOptions.PatchOptional ) && string.IsNullOrWhiteSpace( parts.Patch ) )
|
||||
throw new FormatException( "Patch component of Semantic Version is required" );
|
||||
|
||||
if( !options.HasFlag( ParseOptions.AllowLeadingV ) && parts.LeadingV.HasValue )
|
||||
throw new FormatException( "Leading 'v' characters not supported in SemanticVersion. Use ParseOptions.AllowLeadingV to enable this non-standard extension" );
|
||||
}
|
||||
static readonly IReadOnlyList<string> EmptyStringList = Enumerable.Empty<string>().ToList().AsReadOnly();
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using SemVer.NET;
|
||||
|
||||
|
@ -126,39 +127,40 @@ namespace UnitTests
|
|||
VerifyToStringReverseParse( ver );
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void StaticParseDefaultPatchTest()
|
||||
{
|
||||
var ver = SemanticVersion.Parse( "0.1-alpha.beta+foo-bar.baz", ParseOptions.PatchOptional );
|
||||
Assert.AreEqual( 0, ver.Major );
|
||||
Assert.AreEqual( 1, ver.Minor );
|
||||
Assert.AreEqual( 0, ver.Patch );
|
||||
Assert.IsTrue( ver.IsValid );
|
||||
Assert.IsTrue( ver.IsDevelopment );
|
||||
Assert.IsTrue( ver.IsPrerelease );
|
||||
Assert.AreEqual( 2, ver.PreReleaseParts.Count );
|
||||
Assert.AreEqual( "alpha", ver.PreReleaseParts[ 0 ] );
|
||||
Assert.AreEqual( "beta", ver.PreReleaseParts[ 1 ] );
|
||||
Assert.AreEqual( 2, ver.BuildMetadata.Count );
|
||||
Assert.AreEqual( "foo-bar", ver.BuildMetadata[ 0 ] );
|
||||
Assert.AreEqual( "baz", ver.BuildMetadata[ 1 ] );
|
||||
Assert.AreEqual( "0.1.0-alpha.beta+foo-bar.baz", ver.ToString( ) );
|
||||
VerifyToStringReverseParse( ver );
|
||||
}
|
||||
// TODO: move to CMSIS-Pack specific tests
|
||||
//[TestMethod]
|
||||
//public void StaticParseDefaultPatchTest()
|
||||
//{
|
||||
// var ver = SemanticVersion.Parse( "0.1-alpha.beta+foo-bar.baz", ParseOptions.PatchOptional );
|
||||
// Assert.AreEqual( 0, ver.Major );
|
||||
// Assert.AreEqual( 1, ver.Minor );
|
||||
// Assert.AreEqual( 0, ver.Patch );
|
||||
// Assert.IsTrue( ver.IsValid );
|
||||
// Assert.IsTrue( ver.IsDevelopment );
|
||||
// Assert.IsTrue( ver.IsPrerelease );
|
||||
// Assert.AreEqual( 2, ver.PreReleaseParts.Count );
|
||||
// Assert.AreEqual( "alpha", ver.PreReleaseParts[ 0 ] );
|
||||
// Assert.AreEqual( "beta", ver.PreReleaseParts[ 1 ] );
|
||||
// Assert.AreEqual( 2, ver.BuildMetadata.Count );
|
||||
// Assert.AreEqual( "foo-bar", ver.BuildMetadata[ 0 ] );
|
||||
// Assert.AreEqual( "baz", ver.BuildMetadata[ 1 ] );
|
||||
// Assert.AreEqual( "0.1.0-alpha.beta+foo-bar.baz", ver.ToString( ) );
|
||||
// VerifyToStringReverseParse( ver );
|
||||
//}
|
||||
|
||||
[TestMethod]
|
||||
public void StaticParseSimpleMajorMinorOnlyTest( )
|
||||
{
|
||||
var ver = SemanticVersion.Parse( "2.1", ParseOptions.PatchOptional );
|
||||
Assert.AreEqual( 2, ver.Major );
|
||||
Assert.AreEqual( 1, ver.Minor );
|
||||
Assert.AreEqual( 0, ver.Patch );
|
||||
Assert.IsTrue( ver.IsValid );
|
||||
Assert.IsFalse( ver.IsDevelopment );
|
||||
Assert.IsFalse( ver.IsPrerelease );
|
||||
Assert.AreEqual( 0, ver.PreReleaseParts.Count );
|
||||
VerifyToStringReverseParse( ver );
|
||||
}
|
||||
//[TestMethod]
|
||||
//public void StaticParseSimpleMajorMinorOnlyTest( )
|
||||
//{
|
||||
// var ver = SemanticVersion.Parse( "2.1", ParseOptions.PatchOptional );
|
||||
// Assert.AreEqual( 2, ver.Major );
|
||||
// Assert.AreEqual( 1, ver.Minor );
|
||||
// Assert.AreEqual( 0, ver.Patch );
|
||||
// Assert.IsTrue( ver.IsValid );
|
||||
// Assert.IsFalse( ver.IsDevelopment );
|
||||
// Assert.IsFalse( ver.IsPrerelease );
|
||||
// Assert.AreEqual( 0, ver.PreReleaseParts.Count );
|
||||
// VerifyToStringReverseParse( ver );
|
||||
//}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException( typeof( FormatException ))]
|
||||
|
@ -170,7 +172,7 @@ namespace UnitTests
|
|||
[TestMethod]
|
||||
public void StaticParseNumericIdentifier()
|
||||
{
|
||||
var ver = SemanticVersion.Parse( "2.0.1-2.alpha", ParseOptions.PatchOptional );
|
||||
var ver = SemanticVersion.Parse( "2.0.1-2.alpha" );
|
||||
Assert.AreEqual( 2, ver.Major );
|
||||
Assert.AreEqual( 0, ver.Minor );
|
||||
Assert.AreEqual( 1, ver.Patch );
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Numerics" />
|
||||
</ItemGroup>
|
||||
<Choose>
|
||||
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
|
||||
|
|
Загрузка…
Ссылка в новой задаче