-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:
Steve Maillet 2016-04-07 22:00:44 -07:00
Родитель 0f64195066
Коммит 1bf0a6ea05
20 изменённых файлов: 273 добавлений и 295 удалений

Просмотреть файл

@ -48,6 +48,7 @@
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
<Reference Include="System.Numerics" />
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
@ -75,7 +76,7 @@
<Compile Include="PackDescription\DebugConfig.cs" /> <Compile Include="PackDescription\DebugConfig.cs" />
<Compile Include="PackDescription\DebugInterface.cs" /> <Compile Include="PackDescription\DebugInterface.cs" />
<Compile Include="PackDescription\DebugPort.cs" /> <Compile Include="PackDescription\DebugPort.cs" />
<Compile Include="PackDescription\DebugType.cs" /> <Compile Include="PackDescription\Debug.cs" />
<Compile Include="PackDescription\DebugVarsType.cs" /> <Compile Include="PackDescription\DebugVarsType.cs" />
<Compile Include="PackDescription\DescriptionType.cs" /> <Compile Include="PackDescription\DescriptionType.cs" />
<Compile Include="PackDescription\DeviceFeatureType.cs" /> <Compile Include="PackDescription\DeviceFeatureType.cs" />
@ -123,6 +124,7 @@
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RepositoryPackage.cs" /> <Compile Include="RepositoryPackage.cs" />
<Compile Include="RepositoryUpdateEventArgs.cs" /> <Compile Include="RepositoryUpdateEventArgs.cs" />
<Compile Include="VersionParser.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" /> <None Include="packages.config" />

Просмотреть файл

@ -43,7 +43,7 @@ namespace CMSIS.Pack.PackDescription
get { return ApiVersion.ToString(); } get { return ApiVersion.ToString(); }
set set
{ {
ApiVersion = SemanticVersion.Parse( value ); ApiVersion = VersionParser.Parse( value );
} }
} }

Просмотреть файл

@ -22,8 +22,7 @@ namespace CMSIS.Pack.PackDescription
get { return Version.ToString( ); } get { return Version.ToString( ); }
set set
{ {
// NOTE: Gracefully handle some real-world PDSC files with simple versions Version = VersionParser.Parse( value );
Version = SemanticVersion.Parse( value, ParseOptions.PatchOptional );
} }
} }

Просмотреть файл

@ -43,8 +43,7 @@ namespace CMSIS.Pack.PackDescription
get { return Version.ToString( ); } get { return Version.ToString( ); }
set set
{ {
// NOTE: Gracefully handle some real-world PDSC files with simple versions Version = VersionParser.Parse( value );
Version = SemanticVersion.Parse( value, ParseOptions.PatchOptional );
} }
} }

Просмотреть файл

@ -39,7 +39,7 @@ namespace CMSIS.Pack.PackDescription
public string RawCversionString public string RawCversionString
{ {
get { return Version.ToString(); } get { return Version.ToString(); }
set { Version = SemanticVersion.Parse( value ); } set { Version = VersionParser.Parse( value ); }
} }
[XmlIgnore] [XmlIgnore]
@ -51,7 +51,7 @@ namespace CMSIS.Pack.PackDescription
public string RawCapiversionString public string RawCapiversionString
{ {
get { return ApiVersion.ToString(); } get { return ApiVersion.ToString(); }
set { ApiVersion = SemanticVersion.Parse( value ); } set { ApiVersion = VersionParser.Parse( value ); }
} }
[XmlIgnore] [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,43 +1,32 @@
using System; using System;
using System.Xml; using System.Xml;
using System.Xml.Serialization; using System.Xml.Serialization;
using SemVer.NET;
using Sprache; using Sprache;
namespace CMSIS.Pack.PackDescription namespace CMSIS.Pack.PackDescription
{ {
[Serializable( )] [Serializable( )]
public class DebugVarsType { public class DebugVarsType
{
private string configfileField;
private string versionField;
private XmlAttribute[] anyAttrField;
private string valueField;
/// <remarks/> /// <remarks/>
[XmlAttribute( Form=System.Xml.Schema.XmlSchemaForm.Qualified)] [XmlAttribute( "configfile", Form=System.Xml.Schema.XmlSchemaForm.Qualified )]
public string configfile { public string ConfigFile { get; set; }
get {
return configfileField; [XmlAttribute( "version", Form = System.Xml.Schema.XmlSchemaForm.Qualified )]
} [System.ComponentModel.EditorBrowsable( System.ComponentModel.EditorBrowsableState.Never )]
set { public string RawVersionString
configfileField = value; {
} get { return Version.ToString( ); }
} set
{
/// <remarks/> Version = VersionParser.Parse( value );
[XmlAttribute( Form=System.Xml.Schema.XmlSchemaForm.Qualified)]
public string version {
get {
return versionField;
}
set {
versionField = value;
} }
} }
[XmlIgnore]
public SemanticVersion Version { get; set; }
/// <remarks/> /// <remarks/>
[XmlAttribute( "Pname", Form = System.Xml.Schema.XmlSchemaForm.Qualified )] [XmlAttribute( "Pname", Form = System.Xml.Schema.XmlSchemaForm.Qualified )]
public string ProcessorName public string ProcessorName
@ -49,24 +38,10 @@ namespace CMSIS.Pack.PackDescription
/// <remarks/> /// <remarks/>
[XmlAnyAttribute( )] [XmlAnyAttribute( )]
public XmlAttribute[ ] AnyAttr { public XmlAttribute[ ] AnyAttr { get; set; }
get {
return anyAttrField;
}
set {
anyAttrField = value;
}
}
/// <remarks/> /// <remarks/>
[XmlText( )] [XmlText( )]
public string Value { public string Value { get; set; }
get {
return valueField;
}
set {
valueField = value;
}
}
} }
} }

Просмотреть файл

@ -27,7 +27,7 @@ namespace CMSIS.Pack.PackDescription
private DebugPort[] debugportField; private DebugPort[] debugportField;
private DebugType[] debugField; private Debug[] debugField;
private TraceType[] traceField; private TraceType[] traceField;
@ -150,7 +150,7 @@ namespace CMSIS.Pack.PackDescription
/// <remarks/> /// <remarks/>
[XmlElement( "debug")] [XmlElement( "debug")]
public DebugType[] debug { public Debug[] debug {
get { get {
return debugField; return debugField;
} }

Просмотреть файл

@ -28,7 +28,7 @@ namespace CMSIS.Pack.PackDescription
private DebugPort[] debugportField; private DebugPort[] debugportField;
private DebugType[] debugField; private Debug[] debugField;
private TraceType[] traceField; private TraceType[] traceField;
@ -149,7 +149,7 @@ namespace CMSIS.Pack.PackDescription
/// <remarks/> /// <remarks/>
[XmlElement( "debug")] [XmlElement( "debug")]
public DebugType[] debug { public Debug[] debug {
get { get {
return debugField; return debugField;
} }

Просмотреть файл

@ -28,7 +28,7 @@ namespace CMSIS.Pack.PackDescription
private DebugPort[] debugportField; private DebugPort[] debugportField;
private DebugType[] debugField; private Debug[] debugField;
private TraceType[] traceField; private TraceType[] traceField;
@ -155,7 +155,7 @@ namespace CMSIS.Pack.PackDescription
/// <remarks/> /// <remarks/>
[XmlElement( "debug")] [XmlElement( "debug")]
public DebugType[] debug { public Debug[] debug {
get { get {
return debugField; return debugField;
} }

Просмотреть файл

@ -17,7 +17,7 @@ namespace CMSIS.Pack.PackDescription
get { return Version.ToString( ); } get { return Version.ToString( ); }
set set
{ {
Version = SemanticVersion.Parse( value ); Version = VersionParser.Parse( value );
} }
} }

Просмотреть файл

@ -28,7 +28,7 @@ namespace CMSIS.Pack.PackDescription
private DebugPort[] debugportField; private DebugPort[] debugportField;
private DebugType[] debugField; private Debug[] debugField;
private TraceType[] traceField; private TraceType[] traceField;
@ -151,7 +151,7 @@ namespace CMSIS.Pack.PackDescription
/// <remarks/> /// <remarks/>
[XmlElement( "debug")] [XmlElement( "debug")]
public DebugType[] debug { public Debug[] debug {
get { get {
return debugField; 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(); public static Parser<string> BuildNumber = NumericIdentifier.Text();
/// <summary>Parser monad to parse a semantic version string into a <see cref="ParseResult"/></summary> /// <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 public static Parser<ParseResult> SemanticVersion
= from leadingV in LeadingV.Optional() = from leadingV in LeadingV.Optional()
from major in BuildNumber from major in BuildNumber
@ -79,20 +83,21 @@ namespace SemVer.NET
from patchValue in BuildNumber from patchValue in BuildNumber
select patchValue select patchValue
).Optional() ).Optional()
from preRelease in ( from start in StartPreRelease from preRelease in ( from start in StartPreRelease.Or( Letter )
from preRelIds in DotSeparatedReleaseIdentifiers from preRelIds in DotSeparatedReleaseIdentifiers
select preRelIds select new VersionQualifier( start, preRelIds )
).Optional() ).Optional()
from buildMetadata in ( from start in StartBuild from buildMetadata in ( from start in StartBuild
from buildIds in DotSeparatedBuildIdentifiers from buildIds in DotSeparatedBuildIdentifiers
select buildIds select new VersionQualifier( start, buildIds )
).Optional() ).Optional()
select new ParseResult( leadingV.IsDefined ? new char?( leadingV.Get() ) : null select new ParseResult( leadingV.IsDefined ? new char?( leadingV.Get() ) : null
, major , major
, minor , minor
, patch.GetOrElse( string.Empty ) , patch.GetOrElse( string.Empty )
, preRelease.GetOrElse( Enumerable.Empty<string>( ) ) , preRelease.GetOrDefault( )
, buildMetadata.GetOrElse( Enumerable.Empty<string>( ) ) , buildMetadata.GetOrDefault( )
); );
} }
} }

Просмотреть файл

@ -3,6 +3,19 @@ using System.Collections.Generic;
namespace SemVer.NET 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> /// <summary>Contains the results of parsing a Semantic version string</summary>
/// <remarks> /// <remarks>
/// <para>In order to support variations on Semantic versions as well as gracefully /// <para>In order to support variations on Semantic versions as well as gracefully
@ -27,8 +40,8 @@ namespace SemVer.NET
, string major , string major
, string minor , string minor
, string patch , string patch
, IEnumerable<string> prereleaseParts , VersionQualifier prereleaseParts
, IEnumerable<string> buildParts , VersionQualifier buildParts
) )
{ {
if( major == null ) if( major == null )
@ -40,12 +53,6 @@ namespace SemVer.NET
if( patch == null ) if( patch == null )
throw new ArgumentNullException( nameof( patch ) ); throw new ArgumentNullException( nameof( patch ) );
if( prereleaseParts == null )
throw new ArgumentNullException( nameof( prereleaseParts ) );
if( buildParts == null )
throw new ArgumentNullException( nameof( buildParts ) );
LeadingV = leadingV; LeadingV = leadingV;
Major = major; Major = major;
Minor = minor; Minor = minor;
@ -83,9 +90,9 @@ namespace SemVer.NET
public string Patch { get; } public string Patch { get; }
/// <summary>Gets a collection of prerelease identifier strings</summary> /// <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> /// <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" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="System.Numerics" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Grammar.cs" /> <Compile Include="Grammar.cs" />

Просмотреть файл

@ -2,36 +2,21 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Numerics;
using System.Text; using System.Text;
using Sprache; using Sprache;
namespace SemVer.NET namespace SemVer.NET
{ {
/// <summary>Option flags for parsing a semantic version string</summary> /// <summary>Version structure for versions based on SemanticVersion 2.0.0</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>
/// <remarks> /// <remarks>
/// <para>This class implements creating, parsing, and comparing semantic version values. In /// This class implements creating, parsing, and comparing semantic version values.
/// addition to the standard support, this class includes an additional optional optimization /// <note type="note">
/// where parsing a version string can assume a default patch value of 0 if none is specified. /// Technically, the Major, Minor, and Patch numbers have no length limits, thus this
/// According to the formal Semantic Versioning v2.0 spec. the patch value is required, however /// class uses <see cref="BigInteger"/> as the type for each of the numeric parts.
/// some real world applications allow the looser definition.</para> /// </note>
/// <para>Technically the Major, Minor, and Patch numbers have no length limits, thus this uses</para>
/// </remarks> /// </remarks>
/// <seealso cref="https://github.com/mojombo/semver/blob/master/semver.md"/>
public struct SemanticVersion public struct SemanticVersion
: IComparable : IComparable
, IComparable<SemanticVersion> , IComparable<SemanticVersion>
@ -43,7 +28,7 @@ namespace SemVer.NET
/// <param name="patch">Patch version number</param> /// <param name="patch">Patch version number</param>
/// <param name="preReleaseParts">Array of individual prerelease parts (not including the separating '.')</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> /// <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 ) if( major < 0 )
throw new ArgumentOutOfRangeException( nameof( major ) ); throw new ArgumentOutOfRangeException( nameof( major ) );
@ -57,15 +42,18 @@ namespace SemVer.NET
Major = major; Major = major;
Minor = minor; Minor = minor;
Patch = patch; Patch = patch;
PreReleaseParts_ = ( preReleaseParts ?? Enumerable.Empty<string>() ).ToArray(); var prereleaseList = preReleaseParts?.ToList().AsReadOnly() ?? EmptyStringList;
BuildMetadata_ = ( metadataParts ?? Enumerable.Empty<string>() ).ToArray(); var buildMetadataList = metadataParts?.ToList().AsReadOnly() ?? EmptyStringList;
// Validate each part conforms to an "identifier" as defined by the spec // 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 ) ); 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 ) ); throw new ArgumentException( "Invalid identifier for build metadata part", nameof( metadataParts ) );
PreReleaseParts_ = prereleaseList;
BuildMetadata_ = buildMetadataList;
HashCode = null; HashCode = null;
} }
@ -94,13 +82,13 @@ namespace SemVer.NET
public bool IsValid => Major >= 0 && Minor >= 0 && Patch >= 0; public bool IsValid => Major >= 0 && Minor >= 0 && Patch >= 0;
/// <summary>Major version number</summary> /// <summary>Major version number</summary>
public int Major { get; } public BigInteger Major { get; }
/// <summary>Minor version number</summary> /// <summary>Minor version number</summary>
public int Minor { get; } public BigInteger Minor { get; }
/// <summary>Patch version number</summary> /// <summary>Patch version number</summary>
public int Patch { get; } public BigInteger Patch { get; }
/// <summary>List of identifier parts forming the prerelease value</summary> /// <summary>List of identifier parts forming the prerelease value</summary>
/// <remarks> /// <remarks>
@ -108,8 +96,8 @@ namespace SemVer.NET
/// value can consist of multiple parts separated by a '.', this list contains the /// value can consist of multiple parts separated by a '.', this list contains the
/// individual parts without the leading '-' or separating '.'. /// individual parts without the leading '-' or separating '.'.
/// </remarks> /// </remarks>
public IReadOnlyList<string> PreReleaseParts => Array.AsReadOnly( PreReleaseParts_ ?? EmptyStringArray ); public IReadOnlyList<string> PreReleaseParts => PreReleaseParts_ ?? EmptyStringList;
private readonly string[ ] PreReleaseParts_; private IReadOnlyList<string> PreReleaseParts_;
/// <summary>List of identifier parts forming the build Metadata value</summary> /// <summary>List of identifier parts forming the build Metadata value</summary>
/// <remarks> /// <remarks>
@ -117,8 +105,8 @@ namespace SemVer.NET
/// value can consist of multiple parts separated by a '.', this list contains /// value can consist of multiple parts separated by a '.', this list contains
/// the individual parts without the leading '+' or separating '.'. /// the individual parts without the leading '+' or separating '.'.
/// </remarks> /// </remarks>
public IReadOnlyList<string> BuildMetadata => Array.AsReadOnly( BuildMetadata_ ?? EmptyStringArray ); public IReadOnlyList<string> BuildMetadata => BuildMetadata_ ?? EmptyStringList;
private readonly string[ ] BuildMetadata_; private IReadOnlyList<string> BuildMetadata_;
/// <inheritdoc/> /// <inheritdoc/>
public override int GetHashCode( ) public override int GetHashCode( )
@ -239,13 +227,13 @@ namespace SemVer.NET
public string ToString( bool includeBuildMetadata ) public string ToString( bool includeBuildMetadata )
{ {
var bldr = new StringBuilder( string.Format( CultureInfo.InvariantCulture, "{0}.{1}.{2}", Major, Minor, Patch ) ); 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( '-' );
bldr.Append( string.Join( ".", PreReleaseParts ) ); bldr.Append( string.Join( ".", PreReleaseParts ) );
} }
if( BuildMetadata.Count > 0 && includeBuildMetadata ) if( ( BuildMetadata?.Count ?? 0 ) > 0 && includeBuildMetadata )
{ {
bldr.Append( '+' ); bldr.Append( '+' );
bldr.Append( string.Join( ".", BuildMetadata ) ); bldr.Append( string.Join( ".", BuildMetadata ) );
@ -257,22 +245,23 @@ namespace SemVer.NET
/// <summary>Parse a semantic version string into it's component parts</summary> /// <summary>Parse a semantic version string into it's component parts</summary>
/// <param name="versionString">String containing the version to parse</param> /// <param name="versionString">String containing the version to parse</param>
/// <returns>Parsed version details</returns> /// <returns>Parsed version details</returns>
public static SemanticVersion Parse( string versionString ) => Parse( versionString, ParseOptions.None ); public static SemanticVersion Parse( string versionString )
/// <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 )
{ {
try try
{ {
var parts = Grammar.SemanticVersion.Parse( versionString ); 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 ) 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> /// <summary>Attempts to parse a version string into a new SemanticVersion instance</summary>
/// <param name="versionString">String to parse</param> /// <param name="versionString">String to parse</param>
/// <param name="options">Options flags to control parsing variants and ambiguities in the spec</param> /// <param name="options">Options flags to control parsing variants and ambiguities in the spec</param>
/// <param name="version">Version instance to construct</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> /// <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 ); var result = Grammar.SemanticVersion.TryParse( versionString );
if( !result.WasSuccessful ) if( !result.WasSuccessful )
return false; 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; return true;
} }
@ -334,7 +339,7 @@ namespace SemVer.NET
private int ComputeHashCode( ) => ToString( false ).GetHashCode( ); 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. // its a private field and used like a C++ mutable.
// It caches the HashCode that's expensive to compute // It caches the HashCode that's expensive to compute
// so it is only done once. // 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 // 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 // The property accessors for those arrays will test for null and use this singleton empty
// array if null to prevent null reference issues. // array if null to prevent null reference issues.
static readonly string[] EmptyStringArray = new string [0]; static readonly IReadOnlyList<string> EmptyStringList = Enumerable.Empty<string>().ToList().AsReadOnly();
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" );
}
} }
} }

Просмотреть файл

@ -1,4 +1,5 @@
using System; using System;
using System.Numerics;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using SemVer.NET; using SemVer.NET;
@ -126,39 +127,40 @@ namespace UnitTests
VerifyToStringReverseParse( ver ); VerifyToStringReverseParse( ver );
} }
[TestMethod] // TODO: move to CMSIS-Pack specific tests
public void StaticParseDefaultPatchTest() //[TestMethod]
{ //public void StaticParseDefaultPatchTest()
var ver = SemanticVersion.Parse( "0.1-alpha.beta+foo-bar.baz", ParseOptions.PatchOptional ); //{
Assert.AreEqual( 0, ver.Major ); // var ver = SemanticVersion.Parse( "0.1-alpha.beta+foo-bar.baz", ParseOptions.PatchOptional );
Assert.AreEqual( 1, ver.Minor ); // Assert.AreEqual( 0, ver.Major );
Assert.AreEqual( 0, ver.Patch ); // Assert.AreEqual( 1, ver.Minor );
Assert.IsTrue( ver.IsValid ); // Assert.AreEqual( 0, ver.Patch );
Assert.IsTrue( ver.IsDevelopment ); // Assert.IsTrue( ver.IsValid );
Assert.IsTrue( ver.IsPrerelease ); // Assert.IsTrue( ver.IsDevelopment );
Assert.AreEqual( 2, ver.PreReleaseParts.Count ); // Assert.IsTrue( ver.IsPrerelease );
Assert.AreEqual( "alpha", ver.PreReleaseParts[ 0 ] ); // Assert.AreEqual( 2, ver.PreReleaseParts.Count );
Assert.AreEqual( "beta", ver.PreReleaseParts[ 1 ] ); // Assert.AreEqual( "alpha", ver.PreReleaseParts[ 0 ] );
Assert.AreEqual( 2, ver.BuildMetadata.Count ); // Assert.AreEqual( "beta", ver.PreReleaseParts[ 1 ] );
Assert.AreEqual( "foo-bar", ver.BuildMetadata[ 0 ] ); // Assert.AreEqual( 2, ver.BuildMetadata.Count );
Assert.AreEqual( "baz", ver.BuildMetadata[ 1 ] ); // Assert.AreEqual( "foo-bar", ver.BuildMetadata[ 0 ] );
Assert.AreEqual( "0.1.0-alpha.beta+foo-bar.baz", ver.ToString( ) ); // Assert.AreEqual( "baz", ver.BuildMetadata[ 1 ] );
VerifyToStringReverseParse( ver ); // Assert.AreEqual( "0.1.0-alpha.beta+foo-bar.baz", ver.ToString( ) );
} // VerifyToStringReverseParse( ver );
//}
[TestMethod] //[TestMethod]
public void StaticParseSimpleMajorMinorOnlyTest( ) //public void StaticParseSimpleMajorMinorOnlyTest( )
{ //{
var ver = SemanticVersion.Parse( "2.1", ParseOptions.PatchOptional ); // var ver = SemanticVersion.Parse( "2.1", ParseOptions.PatchOptional );
Assert.AreEqual( 2, ver.Major ); // Assert.AreEqual( 2, ver.Major );
Assert.AreEqual( 1, ver.Minor ); // Assert.AreEqual( 1, ver.Minor );
Assert.AreEqual( 0, ver.Patch ); // Assert.AreEqual( 0, ver.Patch );
Assert.IsTrue( ver.IsValid ); // Assert.IsTrue( ver.IsValid );
Assert.IsFalse( ver.IsDevelopment ); // Assert.IsFalse( ver.IsDevelopment );
Assert.IsFalse( ver.IsPrerelease ); // Assert.IsFalse( ver.IsPrerelease );
Assert.AreEqual( 0, ver.PreReleaseParts.Count ); // Assert.AreEqual( 0, ver.PreReleaseParts.Count );
VerifyToStringReverseParse( ver ); // VerifyToStringReverseParse( ver );
} //}
[TestMethod] [TestMethod]
[ExpectedException( typeof( FormatException ))] [ExpectedException( typeof( FormatException ))]
@ -170,7 +172,7 @@ namespace UnitTests
[TestMethod] [TestMethod]
public void StaticParseNumericIdentifier() 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( 2, ver.Major );
Assert.AreEqual( 0, ver.Minor ); Assert.AreEqual( 0, ver.Minor );
Assert.AreEqual( 1, ver.Patch ); Assert.AreEqual( 1, ver.Patch );

Просмотреть файл

@ -49,6 +49,7 @@
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Numerics" />
</ItemGroup> </ItemGroup>
<Choose> <Choose>
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'"> <When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">