Thanks for your interest in improving QsNet! This project welcomes PRs, issues, and discussion.
Please read this guide before contributing.
A friendly reminder: this project follows a Code of Conduct. See
CODE-OF-CONDUCT.md.
- .NET: 8.0+
- C#: 12.0+
- IDE: JetBrains Rider, Visual Studio 2022, or Visual Studio Code
- Testing: xUnit + FluentAssertions
If you find breakage on newer .NET versions, open an issue with repro details.
# Clone
git clone https://github.com/techouse/QsNet.git
cd QsNet
# Restore dependencies
dotnet restore
# Run the full test suite
dotnet test
# Run tests with detailed output
dotnet test --logger "console;verbosity=detailed"
# Run a specific test class
dotnet test --filter "ClassName=DecodeTests"
# Run a specific test method
dotnet test --filter "MethodName=ShouldDecodeNestedObjects"
# Build (without running tests)
dotnet buildThis repo uses EditorConfig and follows C# coding conventions.
IDE setup (JetBrains Rider)
- Rider should automatically pick up the
.editorconfigfile in the repo root. - (Recommended) Turn on Reformat code on Save (Settings → Tools → Actions on Save → Reformat and cleanup code).
- Enable Code cleanup on save with the default profile.
Visual Studio setup
- Visual Studio automatically respects
.editorconfigsettings. - Enable Format document on save (Tools → Options → Text Editor → Code Cleanup → Configure code cleanup on save).
General style guidelines
- 4-space indentation, meaningful names, small/focused methods where reasonable.
- Use
varfor local variables when the type is obvious. - Prefer expression-bodied members for simple properties and methods.
- Keep hot-path methods allocation-light; use
Span<T>andStringBuilderwhere appropriate. - Follow standard C# naming conventions (PascalCase for public members, camelCase for private fields).
We use xUnit with FluentAssertions for unit tests. When you change code paths that touch parsing or encoding, add or update tests.
- Run all tests:
dotnet test - Run with coverage:
dotnet test --collect:"XPlat Code Coverage" - HTML coverage report: Use
reportgeneratortool or IDE coverage features - Run tests in watch mode:
dotnet watch test
tests/QsNet.Tests/
DecodeTests.cs # Core decoding functionality
EncodeTests.cs # Core encoding functionality
ExampleTests.cs # Real-world usage examples
EndToEndTests.cs # Integration tests
UtilsTests.cs # Utility method tests
Fixtures/
Data/ # Test data and cases
DummyEnum.cs # Test enums and types
- Use descriptive test method names:
ShouldDecodeNestedObjectsWithBracketNotation - Use FluentAssertions for readable assertions:
result.Should().ContainKey("foo") - Group related tests in nested classes or use test collections
- Add test cases for edge cases and error conditions
Example test structure:
public class DecodeTests
{
[Fact]
public void ShouldDecodeSimpleKeyValuePair()
{
// Act
var result = Qs.Decode("a=b");
// Assert
result.Should().ContainKey("a")
.WhoseValue.Should().Be("b");
}
}src/
QsNet/
Qs.cs # Public API (Decode/Encode static methods)
Extensions.cs # Extension methods
Constants/
HexTable.cs # Hex encoding lookup tables
Enums/
Duplicates.cs # How to handle duplicate keys
Format.cs # RFC format options
ListFormat.cs # Array serialization formats
Sentinel.cs # Charset sentinel handling
Internal/
Decoder.cs # Core decoding logic
Encoder.cs # Core encoding logic
Utils.cs # Utility methods and helpers
SideChannelFrame.cs # Side-channel data for nested parsing
Models/
DecodeOptions.cs # Configuration for decoding
EncodeOptions.cs # Configuration for encoding
Delimiter.cs # Parameter delimiter abstractions
Filter.cs # Value filtering abstractions
Undefined.cs # Represents undefined values
QsNet.AspNetCore/ # ASP.NET Core adapter
QsNet.Flurl/ # Flurl adapter
QsNet.Refit/ # Refit adapter
QsNet.RestSharp/ # RestSharp adapter
tests/
QsNet.Tests/
*Tests.cs # xUnit test classes
Fixtures/ # Test data and helper types
QsNet.AspNetCore.Tests/
QsNet.Flurl.Tests/
QsNet.Refit.Tests/
QsNet.RestSharp.Tests/
QsNet.Comparison/ # C# versus JS qs parity harness
This port aims to mirror the semantics of qs (including edge cases).
If you notice divergent behavior, please:
- Add a failing test that demonstrates the difference.
- Reference the
qstest or behavior you expect. - Propose a fix, or open a focused issue.
- Hot paths (parameter splitting, bracket scanning, URL encoding/decoding) should use
Span<T>and avoid allocations where possible. - Prefer
StringBuilderand pre-sized collections when possible. - Use
StringComparison.Ordinalfor performance-critical string operations. - Avoid creating intermediate dictionaries/lists in tight loops.
- Watch for algorithmic complexity (e.g., nested scans, recursive parsing).
- Consider using
ArrayPool<T>for temporary arrays in hot paths.
If you submit performance changes, include a short note and—if available—a benchmark using BenchmarkDotNet.
- Open an issue first for big changes to align on approach.
- Small, focused PRs are easier to review and land quickly.
- Add tests that cover new behavior and edge cases.
- Keep public API stable unless we agree on a version bump.
- Update documentation if you change public APIs or behavior.
- Changelog entry (in the PR description is fine) for user-visible changes.
- Clear, descriptive commits. Conventional Commits welcome but not required.
- Reference issues as needed, e.g., "Fixes #123".
- Prefer present tense: "Add X", "Fix Y".
- Keep commits focused and atomic.
Use a short, descriptive branch: fix/latin1-entities, feat/custom-delimiters, perf/span-optimization, etc.
Before submitting your PR, please verify:
- All tests pass:
dotnet test - Code builds without warnings:
dotnet build --configuration Release --verbosity normal - New functionality has corresponding tests
- Public API changes are documented
- Performance-sensitive changes include benchmarks or profiling data
- Code follows the established patterns and style
- Update version in
Directory.Build.props(<QsNetPackageVersion>property). - Update
CHANGELOG.mdwith release notes. - Ensure
dotnet testanddotnet build --configuration Releasepass. - Create a git tag:
git tag v1.x.y - Push tag:
git push origin v1.x.y - The
publish.ymlworkflow packs and publishes all NuGet packages using NuGet trusted publishing. The repository or environment variableNUGET_USERmust match the nuget.org profile name for the trusted publishing policy owner. - Create GitHub release with release notes:
- Added/Changed/Fixed
- Breaking changes (if any)
- Migration guide for major versions
If you believe you've found a vulnerability, please do not open a public issue.
Email the maintainer instead (see GitHub profile). We'll coordinate a fix and disclosure timeline.
See SECURITY.md for more details on our security policy.
Open a discussion or issue with as much detail as possible (input, expected vs actual output, environment, .NET version).
Include code samples and stack traces when applicable.
Thanks again for helping make QsNet robust and performant!