Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 6.x
dotnet-version: 8.x
- name: Restore dependencies
run: dotnet restore
- name: Build
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 6.x
dotnet-version: 8.x

- name: Verify commit exists in origin/master
run: |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<OutputType>Exe</OutputType>
<RootNamespace>TopDownProteomics.Benchmarks</RootNamespace>
<AssemblyName>TopDownProteomics.Benchmarks</AssemblyName>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<Authors>Consortium for Top-Down Proteomics</Authors>
</PropertyGroup>

Expand Down
29 changes: 24 additions & 5 deletions src/TopDownProteomics/ProForma/ProFormaParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,26 @@ namespace TopDownProteomics.ProForma
/// </summary>
public class ProFormaParser
{
private readonly Func<char, bool> _residueValidator;

/// <summary>
/// Initializes a new instance of the <see cref="ProFormaParser"/> class.
/// </summary>
public ProFormaParser() { }
public ProFormaParser() : this(char.IsUpper) { }

/// <summary>
/// Initializes a new instance of the <see cref="ProFormaParser"/> class with a residue validator.
/// </summary>
/// <param name="residueValidator">Determines whether a character is a valid residue.</param>
/// <exception cref="ArgumentNullException"><paramref name="residueValidator"/> is <see langword="null"/>.</exception>
/// <remarks>
/// For workflows that create a proteoform group or chemical hash, use a matching
/// <see cref="TopDownProteomics.Biochemistry.IResidueProvider"/> with the custom residue validator.
/// </remarks>
public ProFormaParser(Func<char, bool> residueValidator)
{
_residueValidator = residueValidator ?? throw new ArgumentNullException(nameof(residueValidator));
}

#if !NETSTANDARD2_1
/// <summary>
Expand Down Expand Up @@ -218,8 +234,8 @@ public ProFormaTerm ParseString(ReadOnlySpan<char> proFormaString)
else
{
// Validate amino acid character
if (!char.IsUpper(current))
throw new ProFormaParseException($"{current} is not an upper case letter.");
if (!_residueValidator(current))
throw new ProFormaParseException($"{current} is not a valid residue.");

// Reset the range as soon as we see an amino acid
if (endRange.HasValue)
Expand Down Expand Up @@ -263,9 +279,12 @@ private void HandleGlobalModification(ref IDictionary<string, ProFormaTagGroup>?
targets = new List<char>();
for (int k = atSymbolIndex + 1; k < tagText.Length; k++)
{
if (char.IsUpper(tagText[k]))
if (tagText[k] == ',')
continue;

if (_residueValidator(tagText[k]))
targets.Add(tagText[k]);
else if (tagText[k] != ',')
else
throw new ProFormaParseException($"Unexpected character {tagText[k]} in global modification target list.");
Comment thread
trishorts marked this conversation as resolved.
}
}
Expand Down
19 changes: 19 additions & 0 deletions tests/TopDownProteomics.Tests/ProForma/ProFormaParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public void InvalidProFormaStrings()
{
Assert.Throws<ArgumentNullException>(() => _parser.ParseString((Span<char>)null));
Assert.Throws<ArgumentNullException>(() => _parser.ParseString(string.Empty));
Assert.Throws<ArgumentNullException>(() => new ProFormaParser(null!));
}

[Test]
Expand All @@ -27,6 +28,24 @@ public void NoModifications()
Assert.IsNull(term.Tags);
}

[Test]
public void ResidueValidatorControlsSequenceAndGlobalModificationTargets()
{
var parser = new ProFormaParser(residue => residue == 'A' || residue == '*');

var term = parser.ParseString("<[Oxidation]@*,A>A*");

Assert.AreEqual("A*", term.Sequence);
CollectionAssert.AreEquivalent(new[] { '*', 'A' }, term.GlobalModifications.Single().TargetAminoAcids);
var exception = Assert.Throws<ProFormaParseException>(() => parser.ParseString("AB"));
Assert.AreEqual("B is not a valid residue.", exception.Message);
Assert.Throws<ProFormaParseException>(() => _parser.ParseString("A*"));

var permissiveParser = new ProFormaParser(_ => true);
term = permissiveParser.ParseString("<[Oxidation]@A,C>A");
CollectionAssert.AreEquivalent(new[] { 'A', 'C' }, term.GlobalModifications.Single().TargetAminoAcids);
}

[Test]
[TestCase("PRO[info:test]TEOFORM", "PROTEOFORM", "test")]
[TestCase("PRO[info:test[nested]]TEOFORM", "PROTEOFORM", "test[nested]")]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0;net472</TargetFrameworks>
<TargetFrameworks>net8.0;net472</TargetFrameworks>
Comment thread
trishorts marked this conversation as resolved.
<RootNamespace>TopDownProteomics.Tests</RootNamespace>
<Authors>Consortium for Top-Down Proteomics</Authors>
<LangVersion>latest</LangVersion>
Expand Down
Loading