main b8c26d0a2489 cached
33 files
111.4 KB
25.6k tokens
155 symbols
1 requests
Download .txt
Repository: tavis-software/Tavis.UriTemplates
Branch: main
Commit: b8c26d0a2489
Files: 33
Total size: 111.4 KB

Directory structure:
gitextract_a58_z357/

├── .gitattributes
├── .github/
│   ├── CODEOWNERS
│   ├── dependabot.yml
│   └── workflows/
│       ├── auto-merge-dependabot.yml
│       ├── buildAndDeploy.yml
│       └── codeql-analysis.yml
├── .gitignore
├── .gitmodules
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── UriTemplateKey.snk
├── UriTemplates.sln
├── build.cmd
└── src/
    ├── UriTemplateTests/
    │   ├── App.config
    │   ├── ParameterMatchingTests.cs
    │   ├── SpecTests.cs
    │   ├── UriExtensionTests.cs
    │   ├── UriTemplateConverterTests.cs
    │   ├── UriTemplateExtensionsTests.cs
    │   ├── UriTemplateTableTests.cs
    │   ├── UriTemplateTests.csproj
    │   └── UsageTests.cs
    └── UriTemplates/
        ├── OperatorInfo.cs
        ├── QueryStringParameterOrder.cs
        ├── Result.cs
        ├── UriTemplate.cs
        ├── UriTemplateConverter.cs
        ├── UriTemplateExtensions.cs
        ├── UriTemplateTable.cs
        ├── UriTemplates.csproj
        └── VarSpec.cs

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitattributes
================================================
*.doc  diff=astextplain
*.DOC	diff=astextplain
*.docx	diff=astextplain
*.DOCX	diff=astextplain
*.dot	diff=astextplain
*.DOT	diff=astextplain
*.pdf	diff=astextplain
*.PDF	diff=astextplain
*.rtf	diff=astextplain
*.RTF	diff=astextplain

*.jpg  	binary
*.png 	binary
*.gif 	binary

*.cs text diff=csharp 
*.vb text
*.c text
*.cpp text
*.cxx text
*.h text
*.hxx text
*.py text
*.rb text
*.java text
*.html text
*.htm text
*.css text
*.scss text
*.sass text
*.less text
*.js text
*.lisp text
*.clj text
*.sql text
*.php text
*.lua text
*.m text
*.asm text
*.erl text
*.fs text
*.fsx text
*.hs text

*.csproj text merge=union 
*.vbproj text merge=union 
*.fsproj text merge=union 
*.dbproj text merge=union 
*.sln text eol=crlf merge=union

================================================
FILE: .github/CODEOWNERS
================================================
* @darrelmiller


================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: nuget
  directory: "/"
  schedule:
    interval: daily
  open-pull-requests-limit: 10
- package-ecosystem: github-actions
  directory: "/"
  schedule:
    interval: daily
  open-pull-requests-limit: 10
- package-ecosystem: gitsubmodule
  directory: "/"
  schedule:
    interval: daily
  open-pull-requests-limit: 10


================================================
FILE: .github/workflows/auto-merge-dependabot.yml
================================================
name: Auto-merge dependabot updates

on:
  pull_request:
    branches: [ main ]

permissions:
  pull-requests: write
  contents: write

jobs:

  dependabot-merge:

    runs-on: ubuntu-latest

    if: ${{ github.actor == 'dependabot[bot]' }}

    steps:
      - name: Dependabot metadata
        id: metadata
        uses: dependabot/fetch-metadata@v2.4.0
        with:
          github-token: "${{ secrets.GITHUB_TOKEN }}"

      - name: Enable auto-merge for Dependabot PRs
        # Only if version bump is not a major version change
        if: ${{steps.metadata.outputs.update-type != 'version-update:semver-major'}}
        run: gh pr merge --auto --merge "$PR_URL"
        env:
          PR_URL: ${{github.event.pull_request.html_url}}
          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}


================================================
FILE: .github/workflows/buildAndDeploy.yml
================================================
name: Build and Test

on:
  workflow_dispatch:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      solutionName: UriTemplates.sln
      outputFolder: ./buildArtifacts

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          submodules: true

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: 6.0.x

      - name: Restore dependencies
        run: dotnet restore ${{ env.solutionName }}

      - name: Check formatting
        run: dotnet format --verify-no-changes --verbosity diagnostic

      - name: Build
        run: dotnet build ${{ env.solutionName }} --no-restore -c Release

      - name: Test
        run: dotnet test ${{ env.solutionName }} --no-build --verbosity normal -c Release /p:CollectCoverage=true /p:CoverletOutput=./TestResults/ /p:CoverletOutputFormat=opencover

      - name: Pack
        run: dotnet pack ${{ env.solutionName }} /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg --no-build --output ${{ env.outputFolder }} -c Release

      - name: Upload Nuget Package and Symbols
        uses: actions/upload-artifact@v4
        with:
          name: drop
          path: |
            ${{ env.outputFolder }}/*.nupkg
            ${{ env.outputFolder }}/*.snupkg

  deploy:
    if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
    environment:
      name: production
    runs-on: ubuntu-latest
    needs: [build]
    steps:
      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: 6.0.x
          
      - name: Download artifacts
        uses: actions/download-artifact@v4
        with:
          name: drop
      
      - name: Nuget push
        run: dotnet nuget push "*.nupkg" --skip-duplicate -s https://api.nuget.org/v3/index.json -k ${{ secrets.PUBLISH_GH_TOKEN }}
      


================================================
FILE: .github/workflows/codeql-analysis.yml
================================================
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"

on:
  workflow_dispatch:
  push:
    branches: [main]
  pull_request:
    # The branches below must be a subset of the branches above
  schedule:
    - cron: "20 9 * * 5"

jobs:
  analyze:
    name: Analyze
    runs-on: ubuntu-latest

    strategy:
      fail-fast: false
      matrix:
        language: ["csharp"]
        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
        # Learn more:
        # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          submodules: true

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: 6.0.x

      # Initializes the CodeQL tools for scanning.
      - name: Initialize CodeQL
        uses: github/codeql-action/init@v3
        with:
          languages: ${{ matrix.language }}
          # If you wish to specify custom queries, you can do so here or in a config file.
          # By default, queries listed here will override any specified in a config file.
          # Prefix the list here with "+" to use these queries and those in the config file.
          # queries: ./path/to/local/query, your-org/your-repo/queries@main

      # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
      # If this step fails, then you should remove it and run the build manually (see below)
      # - name: Autobuild
      #   uses: github/codeql-action/autobuild@v2

      # ℹ️ Command-line programs to run using the OS shell.
      # 📚 https://git.io/JvXDl

      # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
      #    and modify them (or add more) to build your code if your project
      #    uses a compiled language

      - name: Restore workloads
        run: |
          dotnet workload restore
          dotnet workload install wasm-tools
      - name: Restore dependencies
        run: dotnet restore
      - name: Build
        run: dotnet build --no-restore -c Release

      - name: Perform CodeQL Analysis
        uses: github/codeql-action/analyze@v3


================================================
FILE: .gitignore
================================================

#ignore thumbnails created by windows
Thumbs.db
#Ignore files build by Visual Studio
*.obj
*.exe
*.pdb
*.user
*.aps
*.pch
*.vspscc
*_i.c
*_p.c
*.ncb
*.suo
*.tlb
*.tlh
*.bak
*.cache
*.ilk
*.log
ClientBin
[Bb]in
[Dd]ebug*/
*.lib
*.sbr
obj/
[Rr]elease*/
_ReSharper*/
[Tt]est[Rr]esult*
Examples*/
SerializerTest*/
*.csproj.user
*.resharper*
Download/
#Ignore files from MonoDevelop
*.pidb
*.userprefs
packages
artifacts
*.nupkg
publish-tavis*.ps1
project.lock.json
.vs/
.idea

================================================
FILE: .gitmodules
================================================
[submodule "lib/uritemplate-test"]
	path = lib/uritemplate-test
	url = https://github.com/uri-templates/uritemplate-test.git


================================================
FILE: .travis.yml
================================================
language: csharp
sudo: false   # use the new container-based Travis infrastructure
install:
  - nuget restore UriTemplates.sln
script:
  - xbuild /p:Configuration=Release /t:Compile build/Build.proj


================================================
FILE: CHANGELOG.md
================================================
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- Added support for netstandard 2.1 [#69](https://github.com/tavis-software/Tavis.UriTemplates/issues/69)

### Changed

## [2.0.0]

- [Breaking] Updated Target Framework Moniker to `netstandard2.0` and drops `net35`, `net40`, `net45` and `netstandard1.0`.

## [1.1.2]

- Added Type converter support.

## [1.1.1]

- Fixed bug parsing query parameter with comma delimited values

## [1.1.0]

- Updated Target Framework Moniker from dotnet to netstandard1.0

## [1.0.0]

- This project has lived too long as a 0.x release.  I believe it has seen enough production use to be considered a 1.0 release.

## [0.6.6]

- Bugfix

## [0.6.5]

- Added ability to retrieve URITemplates from UriTemplateTable
- Parameter matching

## [0.6.4]

- Added .net4 version of assembly
- Updated nuget to put portable lib in dotnet folder to enable coreclr support
- Made Resolve() thread safe by ensuring it does not share any state from one invocation to the next.
- Added support for profile92 to allow including in Portable libraries that target .net4
- Added support for case insensitive parameter names.

## [0.6.3]

- Added ToString() overload to allow retrieving unresolved template

## [0.6.2]

- URI Template Extension AddParameters now uses IDictionary instead of Dictionary

## [0.6.1]

- Added ClearParameter to unset a template parameter
- Added MakeTemplate URI extension for creating a Uri template based on the query string parameters of a URI
- Added GetQueryStringParameters URI extension for building dictionary of query parameters and values
- Added AddParameters overload that accepts a dictionary of template parameters

## [0.6.0]

- Added the ability to partially resolve templates using a new constructor parameter.
- Added new fluent interface using extension methods for quickly creating a template and resolving it.
- Created a .net 45 project
- Restructured folders to comply with recommendations made by David Fowler
- Added many more usage tests with real world scenarios
- Added support for Windows 8.1
- Fixed Unicode encoding problem
- Added support for passing non-string lists as parameters
- Added support for passing parameters values that are non-string.


================================================
FILE: LICENSE
================================================

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS


   Copyright 2012 Tavis Software Inc.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

================================================
FILE: README.md
================================================
# Uri Templates # 

[![Build and deploy](https://github.com/tavis-software/Tavis.UriTemplates/actions/workflows/buildAndDeploy.yml/badge.svg)](https://github.com/tavis-software/Tavis.UriTemplates/actions/workflows/buildAndDeploy.yml) [![CodeQL](https://github.com/tavis-software/Tavis.UriTemplates/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/tavis-software/Tavis.UriTemplates/actions/workflows/codeql-analysis.yml) [![NuGet](https://img.shields.io/nuget/v/Tavis.UriTemplates.svg?label=NuGet)](https://www.nuget.org/packages/Tavis.UriTemplates/)

.NET implementation of the [URI Template Spec RFC6570](http://tools.ietf.org/html/rfc6570). 

Library implements Level 4 compliance and is tested against test cases from [UriTemplate test suite](https://github.com/uri-templates/uritemplate-test).


Here are some basic usage examples:

Replacing a path segment parameter,

```csharp
[Fact]
public void UpdatePathParameter()
{
    var url = new UriTemplate("http://example.org/{tenant}/customers")
        .AddParameter("tenant", "acmé")
        .Resolve();

    Assert.Equal("http://example.org/acm%C3%A9/customers", url);
}
```

Setting query string parameters,

```csharp
[Fact]
public void ShouldResolveUriTemplateWithNonStringParameter()
{
    var url = new UriTemplate("http://example.org/location{?lat,lng}")
        .AddParameters(new { lat = 31.464, lng = 74.386 })
        .Resolve();

    Assert.Equal("http://example.org/location?lat=31.464&lng=74.386", url);
}
```


Resolving a URI when parameters are not set will simply remove the parameters,

```csharp
[Fact]
public void SomeParametersFromAnObject()
{
    var url = new UriTemplate("http://example.org{/environment}{/version}/customers{?active,country}")
        .AddParameters(new
        {
            version = "v2",
            active = "true"
        })
        .Resolve();

    Assert.Equal("http://example.org/v2/customers?active=true", url);
}
```

You can even pass lists as parameters

```csharp
[Fact]
public void ApplyParametersObjectWithAListofInts()
{
    var url = new UriTemplate("http://example.org/customers{?ids,order}")
        .AddParameters(new
        {
            order = "up",
            ids = new[] {21, 75, 21}
        })
        .Resolve();

    Assert.Equal("http://example.org/customers?ids=21,75,21&order=up", url);
}
```

And dictionaries,

```csharp
[Fact]
public void ApplyDictionaryToQueryParameters()
{
    var url = new UriTemplate("http://example.org/foo{?coords*}")
        .AddParameter("coords", new Dictionary<string, string>
        {
            {"x", "1"},
            {"y", "2"},
        })
        .Resolve();

    Assert.Equal("http://example.org/foo?x=1&y=2", url);
}
```

We also handle all the complex URI encoding rules automatically.

```csharp
[Fact]
public void TestExtremeEncoding()
{
    var url = new UriTemplate("http://example.org/sparql{?query}")
            .AddParameter("query", "PREFIX dc: <http://purl.org/dc/elements/1.1/> SELECT ?book ?who WHERE { ?book dc:creator ?who }")
            .Resolve();
    Assert.Equal("http://example.org/sparql?query=PREFIX%20dc%3A%20%3Chttp%3A%2F%2Fpurl.org%2Fdc%2Felements%2F1.1%2F%3E%20SELECT%20%3Fbook%20%3Fwho%20WHERE%20%7B%20%3Fbook%20dc%3Acreator%20%3Fwho%20%7D", url);
}
```

There is a [blogpost](http://bizcoder.com/constructing-urls-the-easy-way) that discusses these examples and more in detail.

As well as having a set of regular usage tests, this library also executes tests based on a standard test suite.  This test suite is pulled in as a Git Submodule, therefore when cloning this repo, you will need use the `--recursive` switch,

        git clone --recursive git@github.com:tavis-software/Tavis.UriTemplates.git


Current this library does not pass all of the failure tests.  I.e. If you pass an invalid URI Template, you may not get an exception.


================================================
FILE: UriTemplates.sln
================================================

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.489
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0C994FD2-43CE-4412-BCCD-628DBD8130DF}"
	ProjectSection(SolutionItems) = preProject
		License.txt = License.txt
		Readme.md = Readme.md
	EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{718037C6-0F82-456A-B7EB-531694D035B4}"
	ProjectSection(SolutionItems) = preProject
		ReleaseNotes.md = ReleaseNotes.md
	EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UriTemplates", "src\UriTemplates\UriTemplates.csproj", "{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UriTemplateTests", "src\UriTemplateTests\UriTemplateTests.csproj", "{2D43074E-C8E2-4D1D-B64D-7A267908840B}"
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|Any CPU = Debug|Any CPU
		Debug|Mixed Platforms = Debug|Mixed Platforms
		Debug|x86 = Debug|x86
		Release|Any CPU = Release|Any CPU
		Release|Mixed Platforms = Release|Mixed Platforms
		Release|x86 = Release|x86
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
		{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
		{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}.Debug|x86.ActiveCfg = Debug|Any CPU
		{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}.Debug|x86.Build.0 = Debug|Any CPU
		{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}.Release|Any CPU.Build.0 = Release|Any CPU
		{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
		{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}.Release|Mixed Platforms.Build.0 = Release|Any CPU
		{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}.Release|x86.ActiveCfg = Release|Any CPU
		{F60E3FBC-22FE-43A7-A46B-93C30B74B72A}.Release|x86.Build.0 = Release|Any CPU
		{2D43074E-C8E2-4D1D-B64D-7A267908840B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{2D43074E-C8E2-4D1D-B64D-7A267908840B}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{2D43074E-C8E2-4D1D-B64D-7A267908840B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
		{2D43074E-C8E2-4D1D-B64D-7A267908840B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
		{2D43074E-C8E2-4D1D-B64D-7A267908840B}.Debug|x86.ActiveCfg = Debug|Any CPU
		{2D43074E-C8E2-4D1D-B64D-7A267908840B}.Debug|x86.Build.0 = Debug|Any CPU
		{2D43074E-C8E2-4D1D-B64D-7A267908840B}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{2D43074E-C8E2-4D1D-B64D-7A267908840B}.Release|Any CPU.Build.0 = Release|Any CPU
		{2D43074E-C8E2-4D1D-B64D-7A267908840B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
		{2D43074E-C8E2-4D1D-B64D-7A267908840B}.Release|Mixed Platforms.Build.0 = Release|Any CPU
		{2D43074E-C8E2-4D1D-B64D-7A267908840B}.Release|x86.ActiveCfg = Release|Any CPU
		{2D43074E-C8E2-4D1D-B64D-7A267908840B}.Release|x86.Build.0 = Release|Any CPU
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
	GlobalSection(ExtensibilityGlobals) = postSolution
		SolutionGuid = {C814C2F5-05C7-4FAD-A250-2EB1FC0364A0}
	EndGlobalSection
EndGlobal


================================================
FILE: build.cmd
================================================
@echo Off
set config=%1
if "%config%" == "" (
   set config=debug
)
md artifacts
dotnet build --configuration %config% --verbosity normal /m /v:M /fl /flp:LogFile=msbuild.log; /nr:false
dotnet pack --no-build --configuration %config% --output %cd%\artifacts

================================================
FILE: src/UriTemplateTests/App.config
================================================
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <uri>
      <idn enabled="All" />
      <iriParsing enabled="true" />
    </uri>
</configuration>

================================================
FILE: src/UriTemplateTests/ParameterMatchingTests.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Tavis.UriTemplates;
using Xunit;
using Xunit.Extensions;

namespace UriTemplateTests
{
    public class ParameterMatchingTests
    {


        [Fact]
        public void MatchUriToTemplate()
        {
            var uri = new Uri("http://example.com/foo/bar");

            var sTemplate = "http://example.com/{p1}/{p2}";

            var x = UriTemplate.CreateMatchingRegex(sTemplate);

            var match = Regex.IsMatch(uri.AbsoluteUri, x);
            Assert.True(match);
        }

        [Fact]
        public void GetParameters()
        {
            var uri = new Uri("http://example.com/foo/bar");

            var sTemplate = "http://example.com/{p1}/{p2}";

            var x = UriTemplate.CreateMatchingRegex(sTemplate);
            var regex = new Regex(x);

            var match = regex.Match(uri.AbsoluteUri);

            Assert.Equal("foo", match.Groups["p1"].Value);
            Assert.Equal("bar", match.Groups["p2"].Value);
        }

        [Fact]
        public void GetParametersWithOperators()
        {
            var uri = new Uri("http://example.com/foo/bar");

            var template = new UriTemplate("http://example.com/{+p1}/{p2*}");

            var parameters = template.GetParameters(uri);

            Assert.Equal(2, parameters.Count);
            Assert.Equal("foo", parameters["p1"]);
            Assert.Equal("bar", parameters["p2"]);
        }

        [Fact]
        public void GetParametersFromQueryString()
        {
            var uri = new Uri("http://example.com/foo/bar?blur=45");

            var template = new UriTemplate("http://example.com/{+p1}/{p2*}{?blur}");

            var parameters = template.GetParameters(uri);

            Assert.Equal(3, parameters.Count);

            Assert.Equal("foo", parameters["p1"]);
            Assert.Equal("bar", parameters["p2"]);
            Assert.Equal("45", parameters["blur"]);
        }

        [Fact]
        public void GetParametersFromMultipleQueryString()
        {
            var uri = new Uri("http://example.com/foo/bar?blur=45");

            var template = new UriTemplate("http://example.com/{+p1}/{p2*}{?blur,blob}");

            var parameters = template.GetParameters(uri);

            Assert.Equal(3, parameters.Count);
            Assert.Equal("foo", parameters["p1"]);
            Assert.Equal("bar", parameters["p2"]);
            Assert.Equal("45", parameters["blur"]);

        }
        [Fact]
        public void GetParametersFromMultipleQueryStringWithTwoParamValues()
        {
            var uri = new Uri("http://example.com/foo/bar?blur=45&blob=23");

            var template = new UriTemplate("http://example.com/{+p1}/{p2*}{?blur,blob}");

            var parameters = template.GetParameters(uri);

            Assert.Equal(4, parameters.Count);
            Assert.Equal("foo", parameters["p1"]);
            Assert.Equal("bar", parameters["p2"]);
            Assert.Equal("45", parameters["blur"]);
            Assert.Equal("23", parameters["blob"]);

        }

        [Fact]
        public void GetParameterFromArrayParameter()
        {
            var uri = new Uri("http://example.com?blur=45,23");

            var template = new UriTemplate("http://example.com{?blur}");

            var parameters = template.GetParameters(uri);

            Assert.Single(parameters);
            Assert.Equal("45,23", parameters["blur"]);

        }

        [Fact]
        public void GetParametersFromMultipleQueryStringWithOptionalAndMandatoryParameters()
        {
            var uri = new Uri("http://example.com/foo/bar?blur=45&blob=23");

            var template = new UriTemplate("http://example.com/{+p1}/{p2*}{?blur}{&blob}");

            var parameters = template.GetParameters(uri);

            Assert.Equal(4, parameters.Count);
            Assert.Equal("foo", parameters["p1"]);
            Assert.Equal("bar", parameters["p2"]);
            Assert.Equal("45", parameters["blur"]);
            Assert.Equal("23", parameters["blob"]);

        }

        [Fact]
        public void GetParametersFromMultipleQueryStringWithOptionalParameters()
        {
            var uri = new Uri("http://example.com/foo/bar");

            var template = new UriTemplate("http://example.com/{+p1}/{p2*}{?blur,blob}");

            var parameters = template.GetParameters(uri);

            Assert.Equal("foo", parameters["p1"]);
            Assert.Equal("bar", parameters["p2"]);

        }


        [Fact]
        public void TestGlimpseUrl()
        {
            var uri = new Uri("http://example.com/Glimpse.axd?n=glimpse_ajax&parentRequestId=123232323&hash=23ADE34FAE&callback=http%3A%2F%2Fexample.com%2Fcallback");

            var template = new UriTemplate("http://example.com/Glimpse.axd?n=glimpse_ajax&parentRequestId={parentRequestId}{&hash,callback}");

            var parameters = template.GetParameters(uri);

            Assert.Equal(3, parameters.Count);
            Assert.Equal("123232323", parameters["parentRequestId"]);
            Assert.Equal("23ADE34FAE", parameters["hash"]);
            Assert.Equal("http://example.com/callback", parameters["callback"]);

        }

        [Fact]
        public void TestUrlWithQuestionMarkAsFirstCharacter()
        {

            var parameters = new UriTemplate("?hash={hash}").GetParameters(new Uri("http://localhost:5000/glimpse/metadata?hash=123")); ;

            Assert.Single(parameters);
            Assert.Equal("123", parameters["hash"]);

        }



        [Fact]
        public void TestExactParameterCount()
        {
            var uri = new Uri("http://example.com/foo?bar=10");

            var template = new UriTemplate("http://example.com/foo{?bar}");

            var parameters = template.GetParameters(uri);

            Assert.Single(parameters);

        }

        [Fact]
        public void SimplePerfTest()
        {
            var uri = new Uri("http://example.com/Glimpse.axd?n=glimpse_ajax&parentRequestId=123232323&hash=23ADE34FAE&callback=http%3A%2F%2Fexample.com%2Fcallback");

            var template = new UriTemplate("http://example.com/Glimpse.axd?n=glimpse_ajax&parentRequestId={parentRequestId}{&hash,callback}");

            for (int i = 0; i < 100000; i++)
            {
                var parameters = template.GetParameters(uri);

            }


        }


        [Fact]
        public void Level1Decode()
        {
            var uri = new Uri("/Hello%20World", UriKind.RelativeOrAbsolute);

            var template = new UriTemplate("/{p1}");

            var parameters = template.GetParameters(uri);

            Assert.Equal("Hello World", parameters["p1"]);

        }


        //[Fact]
        //public void Level2Decode()
        //{
        //    var uri = new Uri("/foo?path=Hello/World", UriKind.RelativeOrAbsolute);

        //    var template = new UriTemplate("/foo?path={+p1}");

        //    var parameters = template.GetParameters(uri);

        //    Assert.Equal("Hello/World", parameters["p1"]);

        //}

        [Fact]
        public void FragmentParam()
        {
            var uri = new Uri("/foo#Hello%20World!", UriKind.RelativeOrAbsolute);

            var template = new UriTemplate("/foo{#p1}");

            var parameters = template.GetParameters(uri);

            Assert.Equal("Hello World!", parameters["p1"]);

        }


        [Fact]
        public void FragmentParams()
        {
            var uri = new Uri("/foo#Hello%20World!,blurg", UriKind.RelativeOrAbsolute);

            var template = new UriTemplate("/foo{#p1,p2}");

            var parameters = template.GetParameters(uri);

            Assert.Equal("Hello World!", parameters["p1"]);
            Assert.Equal("blurg", parameters["p2"]);

        }

        [Fact]
        public void OptionalPathParam()
        {
            var uri = new Uri("/foo/yuck/bob", UriKind.RelativeOrAbsolute);

            var template = new UriTemplate("/foo{/bar}/bob");

            var parameters = template.GetParameters(uri);

            Assert.Equal("yuck", parameters["bar"]);

        }

        [Fact]
        public void OptionalPathParamWithMultipleValues()
        {
            var uri = new Uri("/foo/yuck/yob/bob", UriKind.RelativeOrAbsolute);

            var template = new UriTemplate("/foo{/bar,baz}/bob");

            var parameters = template.GetParameters(uri);
            Assert.Equal(2, parameters.Count); // This current fails
            Assert.Equal("yuck", parameters["bar"]);
            Assert.Equal("yob", parameters["baz"]);
        }


    }
}


================================================
FILE: src/UriTemplateTests/SpecTests.cs
================================================
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json.Linq;
using Tavis.UriTemplates;
using Xunit;
using Xunit.Extensions;

namespace UriTemplateTests
{
    public class UriTemplateTests2
    {

        [Theory, MemberData(nameof(SpecSamples))]
        public void SpecSamplesTest(string template, string[] results, TestSet.TestCase testCase)
        {
            var uriTemplate = new UriTemplate(template);

            foreach (var variable in testCase.TestSet.Variables)
            {
                uriTemplate.SetParameter(variable.Key, variable.Value);
            }

            string result = null;
            result = uriTemplate.Resolve();

            Assert.Contains(results, x => x == result);
        }


        [Theory, MemberData(nameof(ExtendedSamples))]
        public void ExtendedSamplesTest(string template, string[] results, TestSet.TestCase testCase)
        {
            var uriTemplate = new UriTemplate(template);

            foreach (var variable in testCase.TestSet.Variables)
            {
                uriTemplate.SetParameter(variable.Key, variable.Value);
            }

            string result = null;
            ArgumentException aex = null;

            try
            {
                result = uriTemplate.Resolve();

            }
            catch (ArgumentException ex)
            {
                aex = ex;
            }

            if (results[0] == "False")
            {
                Assert.NotNull(aex);
            }
            else
            {
                Assert.Contains(results, x => x == result);
            }

        }


        [Theory(Skip = "Disabled for the moment."), MemberData(nameof(FailureSamples))]
        public void FailureSamplesTest(string template, string[] results, TestSet.TestCase testCase)
        {
            var uriTemplate = new UriTemplate(template);

            foreach (var variable in testCase.TestSet.Variables)
            {
                uriTemplate.SetParameter(variable.Key, variable.Value);
            }

            string result = null;
            ArgumentException aex = null;

            try
            {
                result = uriTemplate.Resolve();

            }
            catch (ArgumentException ex)
            {
                aex = ex;
            }

            if (results[0] == "False")
            {
                Assert.NotNull(aex);
            }
            else
            {
                Assert.Contains(results, x => x == result);
            }


        }


        public static IEnumerable<object[]> SpecSamples
        {
            get
            {
                Stream stream = null;

                var suites = new List<Dictionary<string, TestSet>>();

                stream =
                    typeof(UriTemplateTests2).Assembly.GetManifestResourceStream("UriTemplateTests.spec-examples.json");
                suites.Add(CreateTestSuite(new StreamReader(stream).ReadToEnd()));

                stream = typeof(UriTemplateTests2).Assembly.GetManifestResourceStream("UriTemplateTests.spec-examples-by-section.json");
                suites.Add(CreateTestSuite(new StreamReader(stream).ReadToEnd()));


                foreach (var suite in suites)
                {

                    foreach (var testset in suite.Values)
                    {
                        foreach (var testCase in testset.TestCases)
                        {
                            yield return new object[] { testCase.Template, testCase.Result, testCase };

                        }
                    }

                }

            }
        }


        public static IEnumerable<object[]> ExtendedSamples
        {
            get
            {
                Stream stream = null;

                var suites = new List<Dictionary<string, TestSet>>();

                stream =
                    typeof(UriTemplateTests2).Assembly.GetManifestResourceStream("UriTemplateTests.extended-tests.json");
                suites.Add(CreateTestSuite(new StreamReader(stream).ReadToEnd()));

                foreach (var suite in suites)
                {

                    foreach (var testset in suite.Values)
                    {
                        foreach (var testCase in testset.TestCases)
                        {
                            yield return new object[] { testCase.Template, testCase.Result, testCase };

                        }
                    }

                }

            }
        }

        public static IEnumerable<object[]> FailureSamples
        {
            get
            {
                Stream stream = null;

                var suites = new List<Dictionary<string, TestSet>>();


                stream =
                    typeof(UriTemplateTests2).Assembly.GetManifestResourceStream("UriTemplateTests.negative-tests.json");
                suites.Add(CreateTestSuite(new StreamReader(stream).ReadToEnd()));


                foreach (var suite in suites)
                {

                    foreach (var testset in suite.Values)
                    {
                        foreach (var testCase in testset.TestCases)
                        {
                            yield return new object[] { testCase.Template, testCase.Result, testCase };

                        }
                    }

                }

            }
        }


        private static Dictionary<string, TestSet> CreateTestSuite(string json)
        {
            JObject token = JObject.Parse(json);

            var testSuite = new Dictionary<string, TestSet>();
            foreach (JProperty levelSet in token.Children())
            {
                testSuite.Add(levelSet.Name, CreateTestSet(levelSet.Name, levelSet.Value));

            }
            return testSuite;
        }

        private static TestSet CreateTestSet(string name, JToken token)
        {
            var testSet = new TestSet();
            testSet.Name = name;

            var variables = token["variables"];

            foreach (JProperty variable in variables)
            {
                ParseVariable(variable, testSet.Variables);
            }

            var testcases = token["testcases"];

            foreach (var testcase in testcases)
            {
                testSet.TestCases.Add(CreateTestCase(testSet, testcase));
            }

            return testSet;
        }

        private static void ParseVariable(JProperty variable, Dictionary<string, object> dictionary)
        {
            if (variable.Value.Type == JTokenType.Array)
            {
                var array = (JArray)variable.Value;
                if (array.Count == 0)
                {
                    dictionary.Add(variable.Name, new List<string>());
                }
                else
                {
                    dictionary.Add(variable.Name, array.Values<string>());
                }
            }
            else if (variable.Value.Type == JTokenType.Object)
            {
                var jvalue = (JObject)variable.Value;
                var dict = new Dictionary<string, string>();
                foreach (var prop in jvalue.Properties())
                {
                    dict[prop.Name] = prop.Value.ToString();
                }
                dictionary.Add(variable.Name, dict);
            }
            else
            {
                if (((JValue)variable.Value).Value == null)
                {
                    dictionary.Add(variable.Name, null);
                }
                else
                {
                    dictionary.Add(variable.Name, variable.Value.ToString());
                }

            }
        }

        private static TestSet.TestCase CreateTestCase(TestSet testSet, JToken testcase)
        {
            var testCase = new TestSet.TestCase(testSet);

            testCase.Template = testcase[0].Value<string>();

            if (testcase[1].Type == JTokenType.Array)
            {
                var results = (JArray)testcase[1];
                testCase.Result = results.Select(jv => jv.Value<string>()).ToArray();
            }
            else
            {
                testCase.Result = new string[1];
                testCase.Result[0] = testcase[1].Value<string>();
            }
            return testCase;
        }

        public class TestSet
        {
            public string Name { get; set; }
            public int level { get; set; }
            public Dictionary<string, object> Variables = new Dictionary<string, object>();
            public List<TestCase> TestCases = new List<TestCase>();

            public class TestCase
            {
                private readonly TestSet _testSet;

                public TestCase(TestSet testSet)
                {
                    _testSet = testSet;
                }

                public TestSet TestSet
                {
                    get { return _testSet; }
                }

                public string Template { get; set; }
                public string[] Result { get; set; }
            }


        }



    }
}


================================================
FILE: src/UriTemplateTests/UriExtensionTests.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tavis.UriTemplates;
using Xunit;

namespace UriTemplateTests
{
    public class UriExtensionTests
    {
        [Fact]
        public void Change_an_existing_parameter_within_multiple()
        {
            var target = new Uri("http://example/customer?view=False&foo=bar");

            var parameters = target.GetQueryStringParameters();
            parameters["view"] = true;

            var template = target.MakeTemplate(parameters);

            Assert.Equal("http://example/customer?view=True&foo=bar", template.Resolve());
        }

        [Fact]
        public void Change_an_existing_parameter()
        {
            var target = new Uri("http://example/customer?view=False&foo=bar");

            var template = target.MakeTemplate();
            template.SetParameter("view", true);

            Assert.Equal("http://example/customer?view=True&foo=bar", template.Resolve());
        }

        [Fact]
        public void Remove_an_existing_parameter()
        {
            var target = new Uri("http://example/customer?view=False&foo=bar");

            var template = target.MakeTemplate();
            template.ClearParameter("view");

            Assert.Equal("http://example/customer?foo=bar", template.Resolve());
        }

        [Fact]
        public void Remove_a_query_parameters2()
        {

            var target = new Uri("http://example.org/customer?format=xml&id=23");

            var template = target.MakeTemplate();
            template.ClearParameter("format");


            Assert.Equal("http://example.org/customer?id=23", template.Resolve());
        }

        [Fact]
        public void Add_multiple_parameters_to_uri()
        {
            var target = new Uri("http://example/customer");

            var template = target.MakeTemplate(new Dictionary<string, object>
            {
                {"id", 99},
                {"view", false}
            });

            Assert.Equal("http://example/customer?id=99&view=False", template.Resolve());
        }

        [Fact]
        public void Add_parameters_to_uri_with_query_string_ignoring_path_parameter()
        {
            var target = new Uri("http://example/customer/{id}?view=true");


            var template = target.MakeTemplate(target.GetQueryStringParameters()
                .Union(new Dictionary<string, object> { { "context", "detail" } })
                .ToDictionary(k => k.Key, v => v.Value));
            template.AddParameter("id", 99);

            Assert.Equal("http://example/customer/99?view=true&context=detail", template.Resolve());
        }
    }
}


================================================
FILE: src/UriTemplateTests/UriTemplateConverterTests.cs
================================================
using System.ComponentModel;
using Tavis.UriTemplates;
using Xunit;

namespace UriTemplateTests
{
    public class UriTemplateConverterTests
    {
        [Theory]
        [InlineData("http://example.org/{tenant}/customers")]
        [InlineData("http://example.org/{environment}/{version}/customers{?active,country}")]
        [InlineData("http://example.org/foo{?coords*}")]
        public void ConvertFromString(string rawTemplate)
        {
            var converter = TypeDescriptor.GetConverter(typeof(UriTemplate));
            var template = converter.ConvertFromString(rawTemplate);

            Assert.NotNull(template);
            Assert.Equal(rawTemplate, template.ToString());
        }
    }
}


================================================
FILE: src/UriTemplateTests/UriTemplateExtensionsTests.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tavis;
using Tavis.UriTemplates;
using Xunit;

namespace UriTemplateTests
{
    public class UriTemplateExtensionsTests
    {
        [Fact]
        public void UpdatePathParameter()
        {
            var url = new UriTemplate("http://example.org/{tenant}/customers")
                .AddParameter("tenant", "acmé")
                .Resolve();

            Assert.Equal("http://example.org/acm%C3%A9/customers", url);
        }


        [Fact]
        public void QueryParametersTheOldWay()
        {
            var url = new UriTemplate("http://example.org/customers?active={activeflag}")
                .AddParameter("activeflag", "true")
                .Resolve();

            Assert.Equal("http://example.org/customers?active=true", url);
        }

        [Fact]
        public void QueryParametersTheNewWay()
        {
            var url = new UriTemplate("http://example.org/customers{?active}")
                .AddParameter("active", "true")
                .Resolve();

            Assert.Equal("http://example.org/customers?active=true", url);
        }

        [Fact]
        public void QueryParametersTheNewWayWithoutValue()
        {

            var url = new UriTemplate("http://example.org/customers{?active}")
                .AddParameters(null)
                .Resolve();

            Assert.Equal("http://example.org/customers", url);
        }

        [Fact]
        public void ShouldResolveUriTemplateWithNonStringParameter()
        {
            var url = new UriTemplate("http://example.org/location{?lat,lng}")
                .AddParameters(new { lat = 31.464, lng = 74.386 })
                .Resolve();

            Assert.Equal("http://example.org/location?lat=31.464&lng=74.386", url);
        }


        [Fact]
        public void ParametersFromAnObject()
        {
            var url = new UriTemplate("http://example.org/{environment}/{version}/customers{?active,country}")
                .AddParameters(new
                {
                    environment = "dev",
                    version = "v2",
                    active = "true",
                    country = "CA"
                })
                .Resolve();

            Assert.Equal("http://example.org/dev/v2/customers?active=true&country=CA", url);
        }

        [Fact]
        public void SomeParametersFromAnObject()
        {
            var url = new UriTemplate("http://example.org{/environment}{/version}/customers{?active,country}")
                .AddParameters(new
                {
                    version = "v2",
                    active = "true"
                })
                .Resolve();

            Assert.Equal("http://example.org/v2/customers?active=true", url);
        }

        [Fact]
        public void ApplyDictionaryToQueryParameters()
        {
            var url = new UriTemplate("http://example.org/foo{?coords*}")
                .AddParameter("coords", new Dictionary<string, string>
                {
                    {"x", "1"},
                    {"y", "2"},
                })
                .Resolve();

            Assert.Equal("http://example.org/foo?x=1&y=2", url);
        }

        [Fact]
        public void ApplyParametersObjectToPathSegment()
        {
            var url = new UriTemplate("http://example.org/foo/{bar}/baz")
                .AddParameters(new { bar = "yo" })
                .Resolve();

            Assert.Equal("http://example.org/foo/yo/baz", url);
        }

        [Fact]
        public void ExtremeEncoding()
        {
            var url = new UriTemplate("http://example.org/sparql{?query}")
                    .AddParameter("query", "PREFIX dc: <http://purl.org/dc/elements/1.1/> SELECT ?book ?who WHERE { ?book dc:creator ?who }")
                    .Resolve();
            Assert.Equal("http://example.org/sparql?query=PREFIX%20dc%3A%20%3Chttp%3A%2F%2Fpurl.org%2Fdc%2Felements%2F1.1%2F%3E%20SELECT%20%3Fbook%20%3Fwho%20WHERE%20%7B%20%3Fbook%20dc%3Acreator%20%3Fwho%20%7D", url);
        }

        [Fact]
        public void ApplyParametersObjectWithAList()
        {
            var url = new UriTemplate("http://example.org/customers{?ids,order}")
                .AddParameters(new
                {
                    order = "up",
                    ids = new List<string> { "21", "75", "21" }
                }).Resolve();

            Assert.Equal("http://example.org/customers?ids=21,75,21&order=up", url);
        }

        [Fact]
        public void ApplyParametersObjectWithAListofInts()
        {
            var url = new UriTemplate("http://example.org/customers{?ids,order}")
                .AddParameters(new
                {
                    order = "up",
                    ids = new[] { 21, 75, 21 }
                })
                .Resolve();

            Assert.Equal("http://example.org/customers?ids=21,75,21&order=up", url);
        }

        [Fact]
        public void ApplyParametersObjectWithAListofIntsExploded()
        {
            var url = new UriTemplate("http://example.org/customers{?ids*,order}")
                .AddParameters(new
                {
                    order = "up",
                    ids = new[] { 21, 75, 21 }
                })
                .Resolve();

            Assert.Equal("http://example.org/customers?ids=21&ids=75&ids=21&order=up", url);
        }

        [Fact]
        public void ApplyFoldersToPath()
        {
            var url = new UriTemplate("http://example.org/files{/folders*}{?filename}")
                .AddParameters(new
                {
                    folders = new[] { "customer", "project" },
                    filename = "proposal.pdf"
                })
                .Resolve();

            Assert.Equal("http://example.org/files/customer/project?filename=proposal.pdf", url);
        }

        [Fact]
        public void ParametersFromAnObjectFromInvalidUrl()
        {

            var url = new UriTemplate("http://{environment}.example.org/{version}/customers{?active,country}")
            .AddParameters(new
            {
                environment = "dev",
                version = "v2",
                active = "true",
                country = "CA"
            })
            .Resolve();

            Assert.Equal("http://dev.example.org/v2/customers?active=true&country=CA", url);
        }


        [Fact]
        public void ApplyFoldersToPathFromStringNotUrl()
        {

            var url = new UriTemplate("http://example.org{/folders*}{?filename}")
                .AddParameters(new
                {
                    folders = new[] { "files", "customer", "project" },
                    filename = "proposal.pdf"
                })
                .Resolve();

            Assert.Equal("http://example.org/files/customer/project?filename=proposal.pdf", url);
        }


        [Fact]
        public void ReplaceBaseAddress()
        {

            var url = new UriTemplate("{+baseUrl}api/customer/{id}")
                .AddParameters(new
                {
                    baseUrl = "http://example.org/",
                    id = "22"
                })
                .Resolve();

            Assert.Equal("http://example.org/api/customer/22", url);
        }

        [Fact]
        public void ReplaceBaseAddressButNotId()
        {

            var url = new UriTemplate("{+baseUrl}api/customer/{id}", resolvePartially: true)
                .AddParameters(new
                {
                    baseUrl = "http://example.org/"
                })
                .Resolve();

            Assert.Equal("http://example.org/api/customer/{id}", url);
        }

        [Fact]
        public void PartiallyParametersFromAnObjectFromInvalidUrl()
        {

            var url = new UriTemplate("http://{environment}.example.org/{version}/customers{?active,country}", resolvePartially: true)
            .AddParameters(new
            {
                environment = "dev",
                version = "v2"
            })
            .Resolve();

            Assert.Equal("http://dev.example.org/v2/customers{?active,country}", url);
        }


        [Fact]
        public void PartiallyApplyFoldersToPathFromStringNotUrl()
        {

            var url = new UriTemplate("http://example.org{/folders*}{?filename}", true)
                .AddParameters(new
                {
                    filename = "proposal.pdf"
                })
                .Resolve();

            Assert.Equal("http://example.org{/folders*}?filename=proposal.pdf", url);
        }

        [Fact]
        public void UseArbitraryClassAsParameter()
        {


            var url = new UriTemplate("/{test}", true)
                .AddParameters(new
                {
                    test = new Something()
                })
                .Resolve();

            Assert.Equal("/something", url);
        }


        [Fact]
        public void AddMultipleParametersToLink()
        {
            var template = new UriTemplate("http://localhost/api/{dataset}/customer{?foo,bar,baz}");

            template.AddParameters(new Dictionary<string, object>
            {
                {"foo", "bar"},
                {"baz", "99"},
                {"dataset", "bob"}
            });

            var uri = template.Resolve();

            Assert.Equal("http://localhost/api/bob/customer?foo=bar&baz=99", uri);
        }

    }

    class Something
    {
        public override string ToString()
        {
            return "something";
        }
    }
}


================================================
FILE: src/UriTemplateTests/UriTemplateTableTests.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tavis.UriTemplates;
using Xunit;


namespace UriTemplateTests
{
    public class UriTemplateTableTests
    {





        [Theory,
        InlineData("/", "root"),
        InlineData("/baz/fod/burg", ""),
        InlineData("/baz/kit", "kit"),
        InlineData("/baz/fod", "baz"),
        InlineData("/baz/fod/blob", "blob"),
        InlineData("/glah/flid/blob", "goo")]
        public void FindPathTemplates(string url, string key)
        {
            var table = new UriTemplateTable();  // Shorter paths and literal path segments should be added to the table first.
            table.Add("root", new UriTemplate("/"));
            table.Add("foo", new UriTemplate("/foo/{bar}"));
            table.Add("kit", new UriTemplate("/baz/kit"));
            table.Add("baz", new UriTemplate("/baz/{bar}"));
            table.Add("blob", new UriTemplate("/baz/{bar}/blob"));
            table.Add("goo", new UriTemplate("/{goo}/{bar}/blob"));


            var result = table.Match(new Uri(url, UriKind.RelativeOrAbsolute));

            if (string.IsNullOrEmpty(key))
            {
                Assert.Null(result);
            }
            else
            {
                Assert.Equal(key, result.Key);
            }

            Assert.NotNull(table["goo"]);
            Assert.Null(table["goo1"]);
        }

        [Theory,
     InlineData("/games", "games"),
     InlineData("/games/monopoly/Setup/23", "gamessetup"),
     InlineData("/games/monopoly/Resources/foo/23", "resource"),
     InlineData("/games/monopoly/22/Chat/33", "chat"),
     InlineData("/games/monopoly/22/State/33", "state"),
    ]
        public void FindTemplatesInGamesApi(string url, string key)
        {
            var table = new UriTemplateTable();
            table.Add("games", new UriTemplate("/games"));
            table.Add("gamessetup", new UriTemplate("/games/{gametitle}/Setup/{gamesid}"));
            table.Add("resource", new UriTemplate("/games/{gametitle}/Resources/{resourcetype}/{resourceid}"));
            table.Add("chat", new UriTemplate("/games/{gametitle}/{gameid}/Chat/{chatid}"));
            table.Add("state", new UriTemplate("/games/{gametitle}/{gameid}/State/{stateid}"));


            var result = table.Match(new Uri(url, UriKind.RelativeOrAbsolute));

            if (string.IsNullOrEmpty(key))
            {
                Assert.Null(result);
            }
            else
            {
                Assert.Equal(key, result.Key);
            }
        }


        [Theory,
InlineData("/foo?x=1&y=2", "fooxy3"),
InlineData("/foo?x=1", "fooxy2"),
InlineData("/foo?x=a,b,c,d", "fooxy2"),
InlineData("/foo?y=2", "fooxy"),

InlineData("/foo", "fooxy"),
]
        public void FindTemplatesWithQueryStrings(string url, string key)
        {
            var table = new UriTemplateTable();   // More restrictive templates should have priority over less restrictive ones
            table.Add("fooxy3", new UriTemplate("/foo?x={x}&y={y}"));
            table.Add("fooxy2", new UriTemplate("/foo?x={x}{&y}"));
            table.Add("fooxy4", new UriTemplate("/foo?x={x}{&z}"));
            table.Add("fooxy", new UriTemplate("/foo{?x,y}"));
            table.Add("foo", new UriTemplate("/foo"));


            var result = table.Match(new Uri(url, UriKind.RelativeOrAbsolute));

            if (string.IsNullOrEmpty(key))
            {
                Assert.Null(result);
            }
            else
            {
                Assert.Equal(key, result.Key);
            }
        }

        [Fact]
        public void FindTemplatesWithArrayQueryParameters()
        {
            var table = new UriTemplateTable();   // More restrictive templates should have priority over less restrictive ones
            table.Add("fooxy3", new UriTemplate("/foo?x={x}&y={y}"));
            table.Add("fooxy2", new UriTemplate("/foo?x={x}{&y}"));
            table.Add("fooxy4", new UriTemplate("/foo?x={x}{&z}"));
            table.Add("fooxy", new UriTemplate("/foo{?x,y}"));
            table.Add("foo", new UriTemplate("/foo"));


            var result = table.Match(new Uri("/foo?x=a,b,c,d", UriKind.RelativeOrAbsolute));

            Assert.Equal("fooxy2", result.Key);

        }


        [Fact]
        public void MatchTemplateWithDifferentOrderOfQueryStringParameters()
        {
            var table = new UriTemplateTable();   // More restrictive templates should have priority over less restrictive ones
            table.Add("fooxy3", new UriTemplate("/foo?x={x}&y={y}"));

            var result = table.Match(new Uri("/foo?y=a&x=b", UriKind.RelativeOrAbsolute), QueryStringParameterOrder.Any);

            Assert.Equal("fooxy3", result.Key);
        }

    }

}





================================================
FILE: src/UriTemplateTests/UriTemplateTests.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>

    <IsPackable>false</IsPackable>
  </PropertyGroup>

  <ItemGroup>
    <EmbeddedResource Include="..\..\lib\uritemplate-test\extended-tests.json" Link="extended-tests.json" />
    <EmbeddedResource Include="..\..\lib\uritemplate-test\negative-tests.json" Link="negative-tests.json" />
    <EmbeddedResource Include="..\..\lib\uritemplate-test\spec-examples-by-section.json" Link="spec-examples-by-section.json" />
    <EmbeddedResource Include="..\..\lib\uritemplate-test\spec-examples.json" Link="spec-examples.json" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
    <PackageReference Include="xunit" Version="2.9.3" />
    <PackageReference Include="xunit.runner.visualstudio" Version="3.0.2" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\UriTemplates\UriTemplates.csproj" />
  </ItemGroup>

</Project>


================================================
FILE: src/UriTemplateTests/UsageTests.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using Tavis.UriTemplates;
using Xunit;

namespace UriTemplateTests
{
    public class UsageTests
    {
        [Fact]
        public void TestHexEscape()
        {
            for (int i = 20; i < 128; i++)
            {
                Assert.Equal(Uri.HexEscape((char)i), Result.HexEscape((char)i));
            }

        }


        [Fact]
        public void ShouldAllowUriTemplateWithPathSegmentParameter()
        {
            var template = new UriTemplate("http://example.org/foo/{bar}/baz");
            template.SetParameter("bar", "yo");
            var uriString = template.Resolve();
            Assert.Equal("http://example.org/foo/yo/baz", uriString);
        }


        [Fact]
        public void ShouldAllowUriTemplateWithMultiplePathSegmentParameter()
        {
            var template = new UriTemplate("http://example.org/foo/{bar}/baz/{blar}");
            template.SetParameter("bar", "yo");
            template.SetParameter("blar", "yuck");
            var uriString = template.Resolve();
            Assert.Equal("http://example.org/foo/yo/baz/yuck", uriString);
        }

        [Fact]
        public void ShouldResolveUriTemplateWithNonStringParameter()
        {
            var template = new UriTemplate("http://example.org/foo/{bar}/baz{?lat,lng}");

            double lat = 31.464, lng = 74.386;

            template.SetParameter("bar", "yo");
            template.SetParameter("lat", lat);
            template.SetParameter("lng", lng);

            var uriString = template.Resolve();
            Assert.Equal("http://example.org/foo/yo/baz?lat=31.464&lng=74.386", uriString);
        }


        [Fact]
        public void ShouldResolveMatrixParameter()
        {
            var template = new UriTemplate("http://example.org/foo{;lat,lng}");

            double lat = 31.464, lng = 74.386;

            template.SetParameter("lat", lat);
            template.SetParameter("lng", lng);

            var uriString = template.Resolve();
            Assert.Equal("http://example.org/foo;lat=31.464;lng=74.386", uriString);
        }


        [Fact]
        public void ShouldAllowUriTemplateWithQueryParamsButNoValues()
        {
            var template = new UriTemplate("http://example.org/foo{?bar,baz}");
            //template.SetParameter("bar", "yo");
            //template.SetParameter("blar", "yuck");
            var uriString = template.Resolve();
            Assert.Equal("http://example.org/foo", uriString);
        }

        [Fact]
        public void ShouldAllowPartialUriTemplateWithQueryParamsButNoValues()
        {
            var template = new UriTemplate("http://example.org/foo{?bar,baz}", resolvePartially: true);
            //template.SetParameter("bar", "yo");
            //template.SetParameter("blar", "yuck");
            var uriString = template.Resolve();
            Assert.Equal("http://example.org/foo{?bar,baz}", uriString);
        }

        [Fact]
        public void ShouldAllowUriTemplateToRemoveParameter()
        {
            var template = new UriTemplate("http://example.org/foo{?bar,baz}");
            template.SetParameter("bar", "yo");
            template.SetParameter("baz", "yuck");
            template.ClearParameter("bar");

            var uriString = template.Resolve();
            Assert.Equal("http://example.org/foo?baz=yuck", uriString);
        }


        [Fact]
        public void ShouldAllowUriTemplateWithQueryParamsWithOneValue()
        {
            var template = new UriTemplate("http://example.org/foo{?bar,baz}");
            template.SetParameter("baz", "yo");

            var uriString = template.Resolve();
            Assert.Equal("http://example.org/foo?baz=yo", uriString);
        }


        [Fact]
        public void LabelExpansionWithDotPrefixAndEmptyKeys()
        {
            var template = new UriTemplate("X{.empty_keys}");
            template.SetParameter("empty_keys", new Dictionary<string, string>());
            var uriString = template.Resolve();
            Assert.Equal("X", uriString);
        }

        [Fact]
        public void QueryParametersFromDictionary()
        {
            var template = new UriTemplate("http://example.org/customers{?query*}");
            template.SetParameter("query", new Dictionary<string, string>()
            {
                {"active","true"},
                {"Country","Brazil"}
            });
            var uriString = template.Resolve();
            Assert.Equal("http://example.org/customers?active=true&Country=Brazil", uriString);
        }

        [Fact]
        public void ShouldAllowListAndSingleValueInQueryParam()
        {
            var template = new UriTemplate("http://example.org{/id*}{?fields,token}");
            template.SetParameter("id", new List<string>() { "person", "albums" });
            template.SetParameter("fields", new List<string>() { "id", "name", "picture" });
            template.SetParameter("token", "12345");
            var uriString = template.Resolve();
            Assert.Equal("http://example.org/person/albums?fields=id,name,picture&token=12345", uriString);
        }


        [Fact]
        public void ShouldHandleUriEncoding()
        {
            var template = new UriTemplate("http://example.org/sparql{?query}");
            template.SetParameter("query", "PREFIX dc: <http://purl.org/dc/elements/1.1/> SELECT ?book ?who WHERE { ?book dc:creator ?who }");
            var uriString = template.Resolve();
            Assert.Equal("http://example.org/sparql?query=PREFIX%20dc%3A%20%3Chttp%3A%2F%2Fpurl.org%2Fdc%2Felements%2F1.1%2F%3E%20SELECT%20%3Fbook%20%3Fwho%20WHERE%20%7B%20%3Fbook%20dc%3Acreator%20%3Fwho%20%7D", uriString);
        }

        [Fact]
        public void ShouldHandleEncodingAParametersThatIsAUriWithAUriAsAParameter()
        {
            var template = new UriTemplate("http://example.org/go{?uri}");
            template.SetParameter("uri", "http://example.org/?uri=http%3A%2F%2Fexample.org%2F");
            var uriString = template.Resolve();
            Assert.Equal("http://example.org/go?uri=http%3A%2F%2Fexample.org%2F%3Furi%3Dhttp%253A%252F%252Fexample.org%252F", uriString);
        }


        [Fact]
        public void ShouldThrowWhenExpressionIsNotClosed()
        {
            var result = string.Empty;
            try
            {
                var template = new UriTemplate("http://example.org/foo/{bar/baz/");
                var uriString = template.Resolve();
            }
            catch (ArgumentException ex)
            {

                result = ex.Message;
            }

            Assert.Equal("Malformed template, missing } : http://example.org/foo/{bar/baz/", result);

        }

        [Fact]
        public void ShouldThrowWhenTemplateExpressionIsEmpty()
        {
            var result = string.Empty;
            try
            {
                var template = new UriTemplate("http://example.org/foo/{}/baz/");
                var uriString = template.Resolve();
            }
            catch (ArgumentException ex)
            {

                result = ex.Message;
            }

            Assert.Equal("Malformed template : http://example.org/foo/{}/baz/", result);

        }

        [Fact]
        public void Query_param_with_exploded_array()
        {
            UriTemplate template = new UriTemplate("/foo/{foo}/baz{?haz*}");
            template.SetParameter("foo", "1234");
            template.SetParameter("haz", new string[] { "foo", "bar" });

            string uri = template.Resolve();

            Assert.Equal("/foo/1234/baz?haz=foo&haz=bar", uri);
        }

        [Fact]
        public void Query_param_with_list_array()
        {
            UriTemplate template = new UriTemplate("/foo/{foo}/baz{?haz}");
            template.SetParameter("foo", "1234");
            template.SetParameter("haz", new string[] { "foo", "bar" });

            string uri = template.Resolve();

            Assert.Equal("/foo/1234/baz?haz=foo,bar", uri);
        }

        [Fact]
        public void Query_param_with_empty_array()
        {
            UriTemplate template = new UriTemplate("/foo/{foo}/baz{?haz*}");
            template.SetParameter("foo", "1234");
            template.SetParameter("haz", new string[] { });

            string uri = template.Resolve();

            Assert.Equal("/foo/1234/baz", uri);
        }

        [Fact]
        public void ResolveOptionalAndRequiredQueryParameters()
        {
            UriTemplate template = new UriTemplate("https://api.github.com/search/code?q={query}{&page,per_page,sort,order}");
            template.SetParameter("query", "1234");
            template.SetParameter("per_page", "19");
            var result = template.Resolve();

            Assert.Equal("https://api.github.com/search/code?q=1234&per_page=19", result);
        }


        [Fact]
        public void ReservedCharacterExpansion()
        {
            UriTemplate template = new UriTemplate("https://foo.com/{?format}");
            template.SetParameter("format", "application/vnd.foo+xml");

            var result = template.Resolve();

            Assert.Equal("https://foo.com/?format=application%2Fvnd.foo%2Bxml", result);

        }

        [Fact(Skip = "Unit tests should not require internet access!!")]
        public void PreserveReservedCharacterExpansion()
        {
            UriTemplate template = new UriTemplate("https://foo.com/?format={+format}");
            template.SetParameter("format", "application/vnd.foo+xml");

            var result = template.Resolve();

            Assert.Equal("https://foo.com/?format=application/vnd.foo+xml", result);

        }

        [Fact]
        public void ShouldSupportUnicodeCharacters()
        {
            UriTemplate template = new UriTemplate("/lookup{?Stra%C3%9Fe}");
            template.SetParameter("Stra%C3%9Fe", "Grüner Weg");

            var result = template.Resolve();


            Assert.Equal("/lookup?Stra%C3%9Fe=Gr%C3%BCner%20Weg", result);

        }
        // "assoc_special_chars"  : { "šö䟜ñꀣ¥‡ÑÒÓÔÕ" : "ÖרÙÚàáâãäåæçÿ" }
        [Fact]
        public void ShouldSupportUnicodeCharacters2()
        {
            UriTemplate template = new UriTemplate("{?assoc_special_chars*}");
            var dict = new Dictionary<string, string>
            {
                {"šö䟜ñꀣ¥‡ÑÒÓÔÕ", "ÖרÙÚàáâãäåæçÿ"}
            };
            template.SetParameter("assoc_special_chars", dict);

            var result = template.Resolve();


            Assert.Equal("?%C5%A1%C3%B6%C3%A4%C5%B8%C5%93%C3%B1%C3%AA%E2%82%AC%C2%A3%C2%A5%E2%80%A1%C3%91%C3%92%C3%93%C3%94%C3%95=%C3%96%C3%97%C3%98%C3%99%C3%9A%C3%A0%C3%A1%C3%A2%C3%A3%C3%A4%C3%A5%C3%A6%C3%A7%C3%BF", result);

        }

        [Fact]
        public void Remove_a_query_parameters2()
        {

            var target = new Uri("http://example.org/customer?format=xml&id=23");

            var template = target.MakeTemplate();
            template.ClearParameter("format");


            Assert.Equal("http://example.org/customer?id=23", template.Resolve());
        }


        [Fact]
        public void EncodingTest1()
        {

            var url = new UriTemplate("/1/search/auto/{folder}{?query}")
                .AddParameter("folder", "My Documents")
                .AddParameter("query", "draft 2013")
                .Resolve();

            Assert.Equal("/1/search/auto/My%20Documents?query=draft%202013", url);
        }

        [Fact]
        public void EncodingTest2()
        {

            // Parameter values get encoded but hyphen doesn't need to be encoded because it
            // is an "unreserved" character according to RFC 3986
            var url = new UriTemplate("{/greeting}")
                .AddParameter("greeting", "hello-world")
                .Resolve();

            Assert.Equal("/hello-world", url);

            // A slash does need to be encoded
            var url2 = new UriTemplate("{/greeting}")
                .AddParameter("greeting", "hello/world")
                .Resolve();

            Assert.Equal("/hello%2Fworld", url2);

            // If you truly want to make multiple path segments then do this
            var url3 = new UriTemplate("{/greeting*}")
                .AddParameter("greeting", new List<string> { "hello", "world" })
                .Resolve();

            Assert.Equal("/hello/world", url3);

        }

        //  /docs/salary.csv?columns=1,2
        //  /docs/salary.csv?column=1&column=2

        //   /emails?from[name]=Don&from[date]=1998-03-24&to[name]=Norm

        // : /log?a=b&c=4
        [Fact]
        public void EncodingTest3()
        {

            // There are different ways that lists can be included in query params
            // Just as a comma delimited list
            var url = new UriTemplate("/docs/salary.csv{?columns}")
                .AddParameter("columns", new List<int> { 1, 2 })
                .Resolve();

            Assert.Equal("/docs/salary.csv?columns=1,2", url);

            // or as a multiple parameter instances
            var url2 = new UriTemplate("/docs/salary.csv{?columns*}")
                .AddParameter("columns", new List<int> { 1, 2 })
                .Resolve();

            Assert.Equal("/docs/salary.csv?columns=1&columns=2", url2);
        }

        [Fact]
        public void EncodingTest4()
        {
            var url = new UriTemplate("/emails{?params*}")
                .AddParameter("params", new Dictionary<string, string>
                {
                    {"from[name]","Don"},
                    {"from[date]","1998-03-24"},
                    {"to[name]","Norm"}
                })
                .Resolve();

            Assert.Equal("/emails?from[name]=Don&from[date]=1998-03-24&to[name]=Norm", url);
        }

        [Fact]
        public void EncodingTest5()
        {
            ///log?a=b&c=4
            var url = new UriTemplate("/log?a={a}&c={c}")
                .AddParameter("a", "b")
                .AddParameter("c", "4")
                .Resolve();

            Assert.Equal("/log?a=b&c=4", url);
        }


        [Fact]
        public void InvalidSpace()
        {
            Exception ex = null;
            try
            {
                var url = new UriTemplate("/feeds/events{? fromId").Resolve();
            }
            catch (Exception e)
            {
                ex = e;
            }
            Assert.NotNull(ex);
        }


    }
}

================================================
FILE: src/UriTemplates/OperatorInfo.cs
================================================
namespace Tavis.UriTemplates
{
    public class OperatorInfo
    {
        public bool Default { get; set; }
        public string First { get; set; }
        public char Separator { get; set; }
        public bool Named { get; set; }
        public string IfEmpty { get; set; }
        public bool AllowReserved { get; set; }

    }
}

================================================
FILE: src/UriTemplates/QueryStringParameterOrder.cs
================================================
namespace Tavis.UriTemplates
{
    public enum QueryStringParameterOrder
    {
        Strict,
        Any
    }
}

================================================
FILE: src/UriTemplates/Result.cs
================================================
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Tavis.UriTemplates
{
    public class Result
    {
        public bool ErrorDetected { get; set; }
        public List<string> ParameterNames { get; set; }
        private const string _UriReservedSymbols = ":/?#[]@!$&'()*+,;=";
        private const string _UriUnreservedSymbols = "-._~";

        private StringBuilder _Result = new StringBuilder();

        public Result()
        {
            ParameterNames = new List<string>();
        }
        public StringBuilder Append(char value)
        {
            return _Result.Append(value);
        }
        public StringBuilder Append(string value)
        {

            return _Result.Append(value);
        }

        public override string ToString()
        {
            return _Result.ToString();
        }
        public void AppendName(string variable, OperatorInfo op, bool valueIsEmpty)
        {
            _Result.Append(variable);
            if (valueIsEmpty) { _Result.Append(op.IfEmpty); } else { _Result.Append("="); }
        }


        public void AppendList(OperatorInfo op, bool explode, string variable, IList list)
        {
            foreach (object item in list)
            {
                if (op.Named && explode)
                {
                    _Result.Append(variable);
                    _Result.Append("=");
                }
                AppendValue(item.ToString(), 0, op.AllowReserved);

                _Result.Append(explode ? op.Separator : ',');
            }
            if (list.Count > 0)
            {
                _Result.Remove(_Result.Length - 1, 1);
            }
        }

        public void AppendDictionary(OperatorInfo op, bool explode, IDictionary<string, string> dictionary)
        {
            foreach (string key in dictionary.Keys)
            {
                _Result.Append(Encode(key, op.AllowReserved));
                if (explode) _Result.Append('='); else _Result.Append(',');
                AppendValue(dictionary[key], 0, op.AllowReserved);

                if (explode)
                {
                    _Result.Append(op.Separator);
                }
                else
                {
                    _Result.Append(',');
                }
            }
            if (dictionary.Count() > 0)
            {
                _Result.Remove(_Result.Length - 1, 1);
            }
        }

        public void AppendValue(string value, int prefixLength, bool allowReserved)
        {

            if (prefixLength != 0)
            {
                if (prefixLength < value.Length)
                {
                    value = value.Substring(0, prefixLength);
                }
            }

            _Result.Append(Encode(value, allowReserved));

        }


        private static string Encode(string p, bool allowReserved)
        {

            var result = new StringBuilder();
            foreach (char c in p)
            {
                if ((c >= 'A' && c <= 'z')   //Alpha
                    || (c >= '0' && c <= '9')  // Digit
                    || _UriUnreservedSymbols.IndexOf(c) != -1  // Unreserved symbols  - These should never be percent encoded
                    || (allowReserved && _UriReservedSymbols.IndexOf(c) != -1))  // Reserved symbols - should be included if requested (+)
                {
                    result.Append(c);
                }
                else
                {
                    var bytes = Encoding.UTF8.GetBytes(new[] { c });
                    foreach (var abyte in bytes)
                    {
                        result.Append(HexEscape(abyte));
                    }

                }
            }

            return result.ToString();


        }

        public static string HexEscape(byte i)
        {
            var esc = new char[3];
            esc[0] = '%';
            esc[1] = HexDigits[((i & 240) >> 4)];
            esc[2] = HexDigits[(i & 15)];
            return new string(esc);
        }
        public static string HexEscape(char c)
        {
            var esc = new char[3];
            esc[0] = '%';
            esc[1] = HexDigits[(((int)c & 240) >> 4)];
            esc[2] = HexDigits[((int)c & 15)];
            return new string(esc);
        }
        private static readonly char[] HexDigits = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };



    }
}

================================================
FILE: src/UriTemplates/UriTemplate.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Tavis.UriTemplates
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
#if TYPE_CONVERTER
    using System.ComponentModel;
#endif
    using System.Linq;
    using System.Text;

#if TYPE_CONVERTER
    [TypeConverter(typeof(UriTemplateConverter))]
#endif
    public class UriTemplate
    {


        private static Dictionary<char, OperatorInfo> _Operators = new Dictionary<char, OperatorInfo>() {
                                        {'\0', new OperatorInfo {Default = true, First = "", Separator = ',', Named = false, IfEmpty = "",AllowReserved = false}},
                                        {'+', new OperatorInfo {Default = false, First = "", Separator = ',', Named = false, IfEmpty = "",AllowReserved = true}},
                                        {'.', new OperatorInfo {Default = false, First = ".", Separator = '.', Named = false, IfEmpty = "",AllowReserved = false}},
                                        {'/', new OperatorInfo {Default = false, First = "/", Separator = '/', Named = false, IfEmpty = "",AllowReserved = false}},
                                        {';', new OperatorInfo {Default = false, First = ";", Separator = ';', Named = true, IfEmpty = "",AllowReserved = false}},
                                        {'?', new OperatorInfo {Default = false, First = "?", Separator = '&', Named = true, IfEmpty = "=",AllowReserved = false}},
                                        {'&', new OperatorInfo {Default = false, First = "&", Separator = '&', Named = true, IfEmpty = "=",AllowReserved = false}},
                                        {'#', new OperatorInfo {Default = false, First = "#", Separator = ',', Named = false, IfEmpty = "",AllowReserved = true}}
                                        };

        private readonly string _template;
        private readonly Dictionary<string, object> _Parameters;
        private enum States { CopyingLiterals, ParsingExpression }


        private readonly bool _resolvePartially;

        public UriTemplate(string template, bool resolvePartially = false, bool caseInsensitiveParameterNames = false)
        {
            _resolvePartially = resolvePartially;
            _template = template;
            _Parameters = caseInsensitiveParameterNames
                ? new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
                : new Dictionary<string, object>();
        }

        public override string ToString()
        {
            return _template;
        }

        public void SetParameter(string name, object value)
        {
            _Parameters[name] = value;
        }

        public void ClearParameter(string name)
        {
            _Parameters.Remove(name);
        }

        public void SetParameter(string name, string value)
        {
            _Parameters[name] = value;
        }

        public void SetParameter(string name, IEnumerable<string> value)
        {
            _Parameters[name] = value;
        }

        public void SetParameter(string name, IDictionary<string, string> value)
        {
            _Parameters[name] = value;
        }

        public IEnumerable<string> GetParameterNames()
        {
            Result result = ResolveResult();
            return result.ParameterNames;
        }

        public string Resolve()
        {
            var result = ResolveResult();
            return result.ToString();
        }

        private Result ResolveResult()
        {
            var currentState = States.CopyingLiterals;
            var result = new Result();
            StringBuilder currentExpression = null;
            foreach (var character in _template.ToCharArray())
            {
                switch (currentState)
                {
                    case States.CopyingLiterals:
                        if (character == '{')
                        {
                            currentState = States.ParsingExpression;
                            currentExpression = new StringBuilder();
                        }
                        else if (character == '}')
                        {
                            throw new ArgumentException("Malformed template, unexpected } : " + result.ToString());
                        }
                        else
                        {
                            result.Append(character);
                        }
                        break;
                    case States.ParsingExpression:
                        if (character == '}')
                        {
                            ProcessExpression(currentExpression, result);

                            currentState = States.CopyingLiterals;
                        }
                        else
                        {
                            currentExpression.Append(character);
                        }

                        break;
                }
            }
            if (currentState == States.ParsingExpression)
            {
                result.Append("{");
                result.Append(currentExpression.ToString());

                throw new ArgumentException("Malformed template, missing } : " + result.ToString());
            }

            if (result.ErrorDetected)
            {
                throw new ArgumentException("Malformed template : " + result.ToString());
            }
            return result;
        }

        private void ProcessExpression(StringBuilder currentExpression, Result result)
        {

            if (currentExpression.Length == 0)
            {
                result.ErrorDetected = true;
                result.Append("{}");
                return;
            }

            OperatorInfo op = GetOperator(currentExpression[0]);

            var firstChar = op.Default ? 0 : 1;
            bool multivariableExpression = false;

            var varSpec = new VarSpec(op);
            for (int i = firstChar; i < currentExpression.Length; i++)
            {
                char currentChar = currentExpression[i];
                switch (currentChar)
                {
                    case '*':
                        varSpec.Explode = true;
                        break;

                    case ':':  // Parse Prefix Modifier
                        var prefixText = new StringBuilder();
                        currentChar = currentExpression[++i];
                        while (currentChar >= '0' && currentChar <= '9' && i < currentExpression.Length)
                        {
                            prefixText.Append(currentChar);
                            i++;
                            if (i < currentExpression.Length) currentChar = currentExpression[i];
                        }
                        varSpec.PrefixLength = int.Parse(prefixText.ToString());
                        i--;
                        break;

                    case ',':
                        multivariableExpression = true;
                        var success = ProcessVariable(varSpec, result, multivariableExpression);
                        bool isFirst = varSpec.First;
                        // Reset for new variable
                        varSpec = new VarSpec(op);
                        if (success || !isFirst || _resolvePartially) varSpec.First = false;
                        if (!success && _resolvePartially) { result.Append(","); }
                        break;


                    default:
                        if (IsVarNameChar(currentChar))
                        {
                            varSpec.VarName.Append(currentChar);
                        }
                        else
                        {
                            result.ErrorDetected = true;
                        }
                        break;
                }
            }

            ProcessVariable(varSpec, result, multivariableExpression);
            if (multivariableExpression && _resolvePartially) result.Append("}");
        }

        private bool ProcessVariable(VarSpec varSpec, Result result, bool multiVariableExpression = false)
        {
            var varname = varSpec.VarName.ToString();
            result.ParameterNames.Add(varname);

            if (!_Parameters.ContainsKey(varname)
                || _Parameters[varname] == null
                || (_Parameters[varname] is IList && ((IList)_Parameters[varname]).Count == 0)
                || (_Parameters[varname] is IDictionary && ((IDictionary)_Parameters[varname]).Count == 0))
            {
                if (_resolvePartially == true)
                {
                    if (multiVariableExpression)
                    {
                        if (varSpec.First)
                        {
                            result.Append("{");
                        }

                        result.Append(varSpec.ToString());
                    }
                    else
                    {
                        result.Append("{");
                        result.Append(varSpec.ToString());
                        result.Append("}");
                    }
                    return false;
                }
                return false;
            }

            if (varSpec.First)
            {
                result.Append(varSpec.OperatorInfo.First);
            }
            else
            {
                result.Append(varSpec.OperatorInfo.Separator);
            }

            object value = _Parameters[varname];

            // Handle Strings
            if (value is string)
            {
                var stringValue = (string)value;
                if (varSpec.OperatorInfo.Named)
                {
                    result.AppendName(varname, varSpec.OperatorInfo, string.IsNullOrEmpty(stringValue));
                }
                result.AppendValue(stringValue, varSpec.PrefixLength, varSpec.OperatorInfo.AllowReserved);
            }
            else
            {
                // Handle Lists
                var list = value as IList;
                if (list == null && value is IEnumerable<string>)
                {
                    list = ((IEnumerable<string>)value).ToList<string>();
                };
                if (list != null)
                {
                    if (varSpec.OperatorInfo.Named && !varSpec.Explode)  // exploding will prefix with list name
                    {
                        result.AppendName(varname, varSpec.OperatorInfo, list.Count == 0);
                    }

                    result.AppendList(varSpec.OperatorInfo, varSpec.Explode, varname, list);
                }
                else
                {

                    // Handle associative arrays
                    var dictionary = value as IDictionary<string, string>;
                    if (dictionary != null)
                    {
                        if (varSpec.OperatorInfo.Named && !varSpec.Explode)  // exploding will prefix with list name
                        {
                            result.AppendName(varname, varSpec.OperatorInfo, dictionary.Count() == 0);
                        }
                        result.AppendDictionary(varSpec.OperatorInfo, varSpec.Explode, dictionary);
                    }
                    else
                    {
                        // If above all fails, convert the object to string using the default object.ToString() implementation
                        var stringValue = value.ToString();
                        if (varSpec.OperatorInfo.Named)
                        {
                            result.AppendName(varname, varSpec.OperatorInfo, string.IsNullOrEmpty(stringValue));
                        }
                        result.AppendValue(stringValue, varSpec.PrefixLength, varSpec.OperatorInfo.AllowReserved);
                    }

                }

            }
            return true;
        }

        private static bool IsVarNameChar(char c)
        {
            return ((c >= 'A' && c <= 'z') //Alpha
                    || (c >= '0' && c <= '9') // Digit
                    || c == '_'
                    || c == '%'
                    || c == '.');
        }

        private static OperatorInfo GetOperator(char operatorIndicator)
        {
            OperatorInfo op;
            switch (operatorIndicator)
            {

                case '+':
                case ';':
                case '/':
                case '#':
                case '&':
                case '?':
                case '.':
                    op = _Operators[operatorIndicator];
                    break;

                default:
                    op = _Operators['\0'];
                    break;
            }
            return op;
        }

        private const string varname = "[a-zA-Z0-9_]*";
        private const string op = "(?<op>[+#./;?&]?)";
        private const string var = "(?<var>(?:(?<lvar>" + varname + ")[*]?,?)*)";
        private const string varspec = "(?<varspec>{" + op + var + "})";

        // (?<varspec>{(?<op>[+#./;?&]?)(?<var>[a-zA-Z0-9_]*[*]?|(?:(?<lvar>[a-zA-Z0-9_]*[*]?),?)*)})

        private Regex _ParameterRegex = null;

        private Uri _ComponentBaseUri = new Uri("https://localhost.com", UriKind.Absolute);

        public IDictionary<string, object> GetParameters(Uri uri, QueryStringParameterOrder order = QueryStringParameterOrder.Strict)
        {
            switch (order)
            {
                case QueryStringParameterOrder.Strict:
                    {
                        if (_ParameterRegex == null)
                        {
                            var matchingRegex = CreateMatchingRegex(_template);
                            lock (this)
                            {
                                _ParameterRegex = new Regex(matchingRegex);
                            }
                        }

                        var match = _ParameterRegex.Match(uri.OriginalString);
                        var parameters = new Dictionary<string, object>();

                        for (int x = 1; x < match.Groups.Count; x++)
                        {
                            if (match.Groups[x].Success)
                            {
                                var paramName = _ParameterRegex.GroupNameFromNumber(x);
                                if (!string.IsNullOrEmpty(paramName))
                                {
                                    parameters.Add(paramName, Uri.UnescapeDataString(match.Groups[x].Value));
                                }

                            }
                        }
                        return match.Success ? parameters : null;
                    }
                case QueryStringParameterOrder.Any:
                    {
                        if (!uri.IsAbsoluteUri)
                            uri = new Uri(_ComponentBaseUri, uri);

                        var uriString = uri.GetComponents(UriComponents.SchemeAndServer | UriComponents.Path | UriComponents.Fragment, UriFormat.UriEscaped);
                        var uriWithoutQuery = new Uri(uriString, UriKind.Absolute);

                        var pathParameters = GetParameters(uriWithoutQuery) ?? new Dictionary<string, object>(_Parameters.Comparer);

                        Result result = ResolveResult();

                        var parameterNames = result.ParameterNames;
                        foreach (var parameter in uri.GetQueryStringParameters())
                        {
                            if (!parameterNames.Contains(parameter.Key))
                                continue;
                            pathParameters.Add(parameter.Key, parameter.Value);
                        }

                        return pathParameters.Count == 0 ? null : pathParameters;
                    }
                default:
                    throw new ArgumentOutOfRangeException(nameof(order), order, null);
            }
        }

        public static string CreateMatchingRegex(string uriTemplate)
        {
            var findParam = new Regex(varspec);

            var template = new Regex(@"([^{]|^)\?").Replace(uriTemplate, @"$+\?"); ;//.Replace("?",@"\?");
            var regex = findParam.Replace(template, delegate (Match m)
            {
                var paramNames = m.Groups["lvar"].Captures.Cast<Capture>().Where(c => !string.IsNullOrEmpty(c.Value)).Select(c => c.Value).ToList();
                var op = m.Groups["op"].Value;
                switch (op)
                {
                    case "?":
                        return GetQueryExpression(paramNames, prefix: "?");
                    case "&":
                        return GetQueryExpression(paramNames, prefix: "&");
                    case "#":
                        return GetExpression(paramNames, prefix: "#");
                    case "/":
                        return GetExpression(paramNames, prefix: "/");

                    case "+":
                        return GetExpression(paramNames);
                    default:
                        return GetExpression(paramNames);
                }

            });

            return regex + "$";
        }

        public static string CreateMatchingRegex2(string uriTemplate)
        {
            var findParam = new Regex(varspec);
            //split by host/path/query/fragment

            var template = new Regex(@"([^{]|^)\?").Replace(uriTemplate, @"$+\?"); ;//.Replace("?",@"\?");
            var regex = findParam.Replace(template, delegate (Match m)
            {
                var paramNames = m.Groups["lvar"].Captures.Cast<Capture>().Where(c => !string.IsNullOrEmpty(c.Value)).Select(c => c.Value).ToList();
                var op = m.Groups["op"].Value;
                switch (op)
                {
                    case "?":
                        return GetQueryExpression(paramNames, prefix: "?");
                    case "&":
                        return GetQueryExpression(paramNames, prefix: "&");
                    case "#":
                        return GetExpression(paramNames, prefix: "#");
                    case "/":
                        return GetExpression(paramNames, prefix: "/");

                    case "+":
                        return GetExpression(paramNames);
                    default:
                        return GetExpression(paramNames);
                }

            });

            return regex + "$";
        }

        private static string GetQueryExpression(List<String> paramNames, string prefix)
        {
            StringBuilder sb = new StringBuilder();
            foreach (var paramname in paramNames)
            {

                sb.Append(@"\" + prefix + "?");
                if (prefix == "?") prefix = "&";

                sb.Append("(?:");
                sb.Append(paramname);
                sb.Append("=");

                sb.Append("(?<");
                sb.Append(paramname);
                sb.Append(">");
                sb.Append("[^/?&]+");
                sb.Append(")");
                sb.Append(")?");
            }

            return sb.ToString();
        }


        private static string GetExpression(List<String> paramNames, string prefix = null)
        {
            StringBuilder sb = new StringBuilder();

            string paramDelim;

            switch (prefix)
            {
                case "#":
                    paramDelim = "[^,]+";
                    break;
                case "/":
                    paramDelim = "[^/?]+";
                    break;
                case "?":
                case "&":
                    paramDelim = "[^&#]+";
                    break;
                case ";":
                    paramDelim = "[^;/?#]+";
                    break;
                case ".":
                    paramDelim = "[^./?#]+";
                    break;

                default:
                    paramDelim = "[^/?&]+";
                    break;

            }

            foreach (var paramname in paramNames)
            {
                if (string.IsNullOrEmpty(paramname)) continue;

                if (prefix != null)
                {
                    sb.Append(@"\" + prefix + "?");
                    if (prefix == "#") { prefix = ","; }
                }
                sb.Append("(?<");
                sb.Append(paramname);
                sb.Append(">");
                sb.Append(paramDelim); // Param Value
                sb.Append(")?");
            }

            return sb.ToString();
        }


    }


}


================================================
FILE: src/UriTemplates/UriTemplateConverter.cs
================================================
using System;
using System.ComponentModel;
using System.Globalization;

namespace Tavis.UriTemplates
{
#if TYPE_CONVERTER
    /// <summary>
    /// Converts to <see cref="UriTemplate"/> instances from other representations.
    /// </summary>
    public sealed class UriTemplateConverter
        : TypeConverter
    {
        /// <inheritdoc/>
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            return sourceType == typeof(string);
        }

        /// <inheritdoc/>
        public override object ConvertFrom(
            ITypeDescriptorContext context,
            CultureInfo culture,
            object value)
        {
            if (value == null) { return null; }

            var template = value as string;
            if (template != null)
            {
                if (template.Length == 0)
                {
                    // For TypeConverter purposes, an empty string is "no value."
                    return null;
                }

                return new UriTemplate(template);
            }

            throw (NotSupportedException)GetConvertFromException(value);
        }
    }
#endif
}


================================================
FILE: src/UriTemplates/UriTemplateExtensions.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;

namespace Tavis.UriTemplates
{
    public static class UriTemplateExtensions
    {
        public static UriTemplate AddParameter(this UriTemplate template, string name, object value)
        {
            template.SetParameter(name, value);

            return template;
        }

        public static UriTemplate AddParameters(this UriTemplate template, object parametersObject)
        {

            if (parametersObject != null)
            {
                IEnumerable<PropertyInfo> properties;
#if NETSTANDARD1_0
                var type = parametersObject.GetType().GetTypeInfo();
                properties = type.DeclaredProperties.Where(p=> p.CanRead);
#else
                properties = parametersObject.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
#endif

                foreach (var propinfo in properties)
                {
                    template.SetParameter(propinfo.Name, propinfo.GetValue(parametersObject, null));
                }
            }

            return template;
        }
        public static UriTemplate AddParameters(this UriTemplate uriTemplate, IDictionary<string, object> linkParameters)
        {
            if (linkParameters != null)
            {
                foreach (var parameter in linkParameters)
                {
                    uriTemplate.SetParameter(parameter.Key, parameter.Value);
                }
            }
            return uriTemplate;
        }
    }

    public static class UriExtensions
    {
        public static UriTemplate MakeTemplate(this Uri uri)
        {
            var parameters = uri.GetQueryStringParameters();
            return MakeTemplate(uri, parameters);

        }

        public static UriTemplate MakeTemplate(this Uri uri, IDictionary<string, object> parameters)
        {
            var target = uri.GetComponents(UriComponents.AbsoluteUri
                                                     & ~UriComponents.Query
                                                     & ~UriComponents.Fragment, UriFormat.Unescaped);
            var template = new UriTemplate(target + "{?" + string.Join(",", parameters.Keys.ToArray()) + "}");
            template.AddParameters(parameters);

            return template;
        }

        public static Dictionary<string, object> GetQueryStringParameters(this Uri target)
        {
            Uri uri = target;
            var parameters = new Dictionary<string, object>();

            var reg = new Regex(@"([-A-Za-z0-9._~]*)=([^&]*)&?");		// Unreserved characters: http://tools.ietf.org/html/rfc3986#section-2.3
            foreach (Match m in reg.Matches(uri.Query))
            {
                string key = m.Groups[1].Value.ToLowerInvariant();
                string value = m.Groups[2].Value;
                parameters.Add(key, value);
            }
            return parameters;
        }


    }
}


================================================
FILE: src/UriTemplates/UriTemplateTable.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Tavis.UriTemplates
{
    public class UriTemplateTable
    {
        private Dictionary<string, UriTemplate> _Templates = new Dictionary<string, UriTemplate>();

        public void Add(string key, UriTemplate template)
        {
            _Templates.Add(key, template);
        }

        public TemplateMatch Match(Uri url, QueryStringParameterOrder order = QueryStringParameterOrder.Strict)
        {
            foreach (var template in _Templates)
            {
                var parameters = template.Value.GetParameters(url, order);
                if (parameters != null)
                {
                    return new TemplateMatch() { Key = template.Key, Parameters = parameters, Template = template.Value };
                }
            }
            return null;
        }

        public UriTemplate this[string key]
        {
            get
            {
                UriTemplate value;
                if (_Templates.TryGetValue(key, out value))
                {
                    return value;
                }
                else
                {
                    return null;
                }
            }
        }

    }

    public class TemplateMatch
    {
        public string Key { get; set; }
        public UriTemplate Template { get; set; }
        public IDictionary<string, object> Parameters { get; set; }
    }
}


================================================
FILE: src/UriTemplates/UriTemplates.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>netstandard2.0;netstandard2.1;</TargetFrameworks>
    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
    <RootNamespace>Tavis</RootNamespace>
    <AssemblyName>Tavis.UriTemplates</AssemblyName>
    <Description>URI Template resolution library - Implementation of RFC 6570</Description>
    <Authors>Darrel Miller</Authors>
    <Version>2.0.0</Version>
    <PackageReleaseNotes><![CDATA[
            For full release notes see https://github.com/tavis-software/Tavis.UriTemplates/blob/main/CHANGELOG.md
            ]]></PackageReleaseNotes>
    <PackageProjectUrl>https://github.com/tavis-software/Tavis.UriTemplates</PackageProjectUrl>
    <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
    <PackageLicenseFile>LICENSE</PackageLicenseFile>
    <PackageReadmeFile>README.md</PackageReadmeFile>
    <RepositoryType>git</RepositoryType>
    <SignAssembly>true</SignAssembly>
    <RepositoryUrl>https://github.com/tavis-software/Tavis.UriTemplates</RepositoryUrl>
    <IncludeSymbols>true</IncludeSymbols>
    <SymbolPackageFormat>snupkg</SymbolPackageFormat>
    <PublishRepositoryUrl>true</PublishRepositoryUrl>
    <EmbedUntrackedSources>true</EmbedUntrackedSources>
    <AssemblyOriginatorKeyFile>../../UriTemplateKey.snk</AssemblyOriginatorKeyFile>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="all" />
  </ItemGroup>

  <PropertyGroup Condition="'$(TargetFramework)'!='netstandard1.0'">
    <DefineConstants>DEBUG;TRACE;TYPE_CONVERTER</DefineConstants>
  </PropertyGroup>

  <PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">
    <ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
  </PropertyGroup>
  <ItemGroup>
    <None Include="../../LICENSE" Pack="true" PackagePath="" />
    <None Include="../../README.md" Pack="true" PackagePath="" />
  </ItemGroup>
</Project>


================================================
FILE: src/UriTemplates/VarSpec.cs
================================================
using System.Text;

namespace Tavis.UriTemplates
{
    public class VarSpec
    {
        private readonly OperatorInfo _operatorInfo;
        public StringBuilder VarName = new StringBuilder();
        public bool Explode = false;
        public int PrefixLength = 0;
        public bool First = true;

        public VarSpec(OperatorInfo operatorInfo)
        {
            _operatorInfo = operatorInfo;
        }

        public OperatorInfo OperatorInfo
        {
            get { return _operatorInfo; }
        }

        public override string ToString()
        {
            return (First ? _operatorInfo.First : "") +
                   VarName.ToString()
                   + (Explode ? "*" : "")
                   + (PrefixLength > 0 ? ":" + PrefixLength : "");

        }
    }
}
Download .txt
gitextract_a58_z357/

├── .gitattributes
├── .github/
│   ├── CODEOWNERS
│   ├── dependabot.yml
│   └── workflows/
│       ├── auto-merge-dependabot.yml
│       ├── buildAndDeploy.yml
│       └── codeql-analysis.yml
├── .gitignore
├── .gitmodules
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── UriTemplateKey.snk
├── UriTemplates.sln
├── build.cmd
└── src/
    ├── UriTemplateTests/
    │   ├── App.config
    │   ├── ParameterMatchingTests.cs
    │   ├── SpecTests.cs
    │   ├── UriExtensionTests.cs
    │   ├── UriTemplateConverterTests.cs
    │   ├── UriTemplateExtensionsTests.cs
    │   ├── UriTemplateTableTests.cs
    │   ├── UriTemplateTests.csproj
    │   └── UsageTests.cs
    └── UriTemplates/
        ├── OperatorInfo.cs
        ├── QueryStringParameterOrder.cs
        ├── Result.cs
        ├── UriTemplate.cs
        ├── UriTemplateConverter.cs
        ├── UriTemplateExtensions.cs
        ├── UriTemplateTable.cs
        ├── UriTemplates.csproj
        └── VarSpec.cs
Download .txt
SYMBOL INDEX (155 symbols across 15 files)

FILE: src/UriTemplateTests/ParameterMatchingTests.cs
  class ParameterMatchingTests (line 12) | public class ParameterMatchingTests
    method MatchUriToTemplate (line 16) | [Fact]
    method GetParameters (line 29) | [Fact]
    method GetParametersWithOperators (line 45) | [Fact]
    method GetParametersFromQueryString (line 59) | [Fact]
    method GetParametersFromMultipleQueryString (line 75) | [Fact]
    method GetParametersFromMultipleQueryStringWithTwoParamValues (line 90) | [Fact]
    method GetParameterFromArrayParameter (line 107) | [Fact]
    method GetParametersFromMultipleQueryStringWithOptionalAndMandatoryParameters (line 121) | [Fact]
    method GetParametersFromMultipleQueryStringWithOptionalParameters (line 138) | [Fact]
    method TestGlimpseUrl (line 153) | [Fact]
    method TestUrlWithQuestionMarkAsFirstCharacter (line 169) | [Fact]
    method TestExactParameterCount (line 182) | [Fact]
    method SimplePerfTest (line 195) | [Fact]
    method Level1Decode (line 212) | [Fact]
    method FragmentParam (line 239) | [Fact]
    method FragmentParams (line 253) | [Fact]
    method OptionalPathParam (line 267) | [Fact]
    method OptionalPathParamWithMultipleValues (line 280) | [Fact]

FILE: src/UriTemplateTests/SpecTests.cs
  class UriTemplateTests2 (line 12) | public class UriTemplateTests2
    method SpecSamplesTest (line 15) | [Theory, MemberData(nameof(SpecSamples))]
    method ExtendedSamplesTest (line 32) | [Theory, MemberData(nameof(ExtendedSamples))]
    method FailureSamplesTest (line 67) | [Theory(Skip = "Disabled for the moment."), MemberData(nameof(FailureS...
    method CreateTestSuite (line 198) | private static Dictionary<string, TestSet> CreateTestSuite(string json)
    method CreateTestSet (line 211) | private static TestSet CreateTestSet(string name, JToken token)
    method ParseVariable (line 233) | private static void ParseVariable(JProperty variable, Dictionary<strin...
    method CreateTestCase (line 271) | private static TestSet.TestCase CreateTestCase(TestSet testSet, JToken...
    class TestSet (line 290) | public class TestSet
      class TestCase (line 297) | public class TestCase
        method TestCase (line 301) | public TestCase(TestSet testSet)

FILE: src/UriTemplateTests/UriExtensionTests.cs
  class UriExtensionTests (line 10) | public class UriExtensionTests
    method Change_an_existing_parameter_within_multiple (line 12) | [Fact]
    method Change_an_existing_parameter (line 25) | [Fact]
    method Remove_an_existing_parameter (line 36) | [Fact]
    method Remove_a_query_parameters2 (line 47) | [Fact]
    method Add_multiple_parameters_to_uri (line 60) | [Fact]
    method Add_parameters_to_uri_with_query_string_ignoring_path_parameter (line 74) | [Fact]

FILE: src/UriTemplateTests/UriTemplateConverterTests.cs
  class UriTemplateConverterTests (line 7) | public class UriTemplateConverterTests
    method ConvertFromString (line 9) | [Theory]

FILE: src/UriTemplateTests/UriTemplateExtensionsTests.cs
  class UriTemplateExtensionsTests (line 11) | public class UriTemplateExtensionsTests
    method UpdatePathParameter (line 13) | [Fact]
    method QueryParametersTheOldWay (line 24) | [Fact]
    method QueryParametersTheNewWay (line 34) | [Fact]
    method QueryParametersTheNewWayWithoutValue (line 44) | [Fact]
    method ShouldResolveUriTemplateWithNonStringParameter (line 55) | [Fact]
    method ParametersFromAnObject (line 66) | [Fact]
    method SomeParametersFromAnObject (line 82) | [Fact]
    method ApplyDictionaryToQueryParameters (line 96) | [Fact]
    method ApplyParametersObjectToPathSegment (line 110) | [Fact]
    method ExtremeEncoding (line 120) | [Fact]
    method ApplyParametersObjectWithAList (line 129) | [Fact]
    method ApplyParametersObjectWithAListofInts (line 142) | [Fact]
    method ApplyParametersObjectWithAListofIntsExploded (line 156) | [Fact]
    method ApplyFoldersToPath (line 170) | [Fact]
    method ParametersFromAnObjectFromInvalidUrl (line 184) | [Fact]
    method ApplyFoldersToPathFromStringNotUrl (line 202) | [Fact]
    method ReplaceBaseAddress (line 218) | [Fact]
    method ReplaceBaseAddressButNotId (line 233) | [Fact]
    method PartiallyParametersFromAnObjectFromInvalidUrl (line 247) | [Fact]
    method PartiallyApplyFoldersToPathFromStringNotUrl (line 263) | [Fact]
    method UseArbitraryClassAsParameter (line 277) | [Fact]
    method AddMultipleParametersToLink (line 293) | [Fact]
  class Something (line 312) | class Something
    method ToString (line 314) | public override string ToString()

FILE: src/UriTemplateTests/UriTemplateTableTests.cs
  class UriTemplateTableTests (line 11) | public class UriTemplateTableTests
    method FindPathTemplates (line 18) | [Theory,
    method FindTemplatesInGamesApi (line 51) | [Theory,
    method FindTemplatesWithQueryStrings (line 81) | [Theory,
    method FindTemplatesWithArrayQueryParameters (line 111) | [Fact]
    method MatchTemplateWithDifferentOrderOfQueryStringParameters (line 129) | [Fact]

FILE: src/UriTemplateTests/UsageTests.cs
  class UsageTests (line 11) | public class UsageTests
    method TestHexEscape (line 13) | [Fact]
    method ShouldAllowUriTemplateWithPathSegmentParameter (line 24) | [Fact]
    method ShouldAllowUriTemplateWithMultiplePathSegmentParameter (line 34) | [Fact]
    method ShouldResolveUriTemplateWithNonStringParameter (line 44) | [Fact]
    method ShouldResolveMatrixParameter (line 60) | [Fact]
    method ShouldAllowUriTemplateWithQueryParamsButNoValues (line 75) | [Fact]
    method ShouldAllowPartialUriTemplateWithQueryParamsButNoValues (line 85) | [Fact]
    method ShouldAllowUriTemplateToRemoveParameter (line 95) | [Fact]
    method ShouldAllowUriTemplateWithQueryParamsWithOneValue (line 108) | [Fact]
    method LabelExpansionWithDotPrefixAndEmptyKeys (line 119) | [Fact]
    method QueryParametersFromDictionary (line 128) | [Fact]
    method ShouldAllowListAndSingleValueInQueryParam (line 141) | [Fact]
    method ShouldHandleUriEncoding (line 153) | [Fact]
    method ShouldHandleEncodingAParametersThatIsAUriWithAUriAsAParameter (line 162) | [Fact]
    method ShouldThrowWhenExpressionIsNotClosed (line 172) | [Fact]
    method ShouldThrowWhenTemplateExpressionIsEmpty (line 191) | [Fact]
    method Query_param_with_exploded_array (line 210) | [Fact]
    method Query_param_with_list_array (line 222) | [Fact]
    method Query_param_with_empty_array (line 234) | [Fact]
    method ResolveOptionalAndRequiredQueryParameters (line 246) | [Fact]
    method ReservedCharacterExpansion (line 258) | [Fact]
    method PreserveReservedCharacterExpansion (line 270) | [Fact(Skip = "Unit tests should not require internet access!!")]
    method ShouldSupportUnicodeCharacters (line 282) | [Fact]
    method ShouldSupportUnicodeCharacters2 (line 295) | [Fact]
    method Remove_a_query_parameters2 (line 312) | [Fact]
    method EncodingTest1 (line 326) | [Fact]
    method EncodingTest2 (line 338) | [Fact]
    method EncodingTest3 (line 372) | [Fact]
    method EncodingTest4 (line 392) | [Fact]
    method EncodingTest5 (line 407) | [Fact]
    method InvalidSpace (line 420) | [Fact]

FILE: src/UriTemplates/OperatorInfo.cs
  class OperatorInfo (line 3) | public class OperatorInfo

FILE: src/UriTemplates/QueryStringParameterOrder.cs
  type QueryStringParameterOrder (line 3) | public enum QueryStringParameterOrder

FILE: src/UriTemplates/Result.cs
  class Result (line 8) | public class Result
    method Result (line 17) | public Result()
    method Append (line 21) | public StringBuilder Append(char value)
    method Append (line 25) | public StringBuilder Append(string value)
    method ToString (line 31) | public override string ToString()
    method AppendName (line 35) | public void AppendName(string variable, OperatorInfo op, bool valueIsE...
    method AppendList (line 42) | public void AppendList(OperatorInfo op, bool explode, string variable,...
    method AppendDictionary (line 61) | public void AppendDictionary(OperatorInfo op, bool explode, IDictionar...
    method AppendValue (line 84) | public void AppendValue(string value, int prefixLength, bool allowRese...
    method Encode (line 100) | private static string Encode(string p, bool allowReserved)
    method HexEscape (line 129) | public static string HexEscape(byte i)
    method HexEscape (line 137) | public static string HexEscape(char c)

FILE: src/UriTemplates/UriTemplate.cs
  class UriTemplate (line 18) | #if TYPE_CONVERTER
    type States (line 38) | private enum States { CopyingLiterals, ParsingExpression }
    method UriTemplate (line 43) | public UriTemplate(string template, bool resolvePartially = false, boo...
    method ToString (line 52) | public override string ToString()
    method SetParameter (line 57) | public void SetParameter(string name, object value)
    method ClearParameter (line 62) | public void ClearParameter(string name)
    method SetParameter (line 67) | public void SetParameter(string name, string value)
    method SetParameter (line 72) | public void SetParameter(string name, IEnumerable<string> value)
    method SetParameter (line 77) | public void SetParameter(string name, IDictionary<string, string> value)
    method GetParameterNames (line 82) | public IEnumerable<string> GetParameterNames()
    method Resolve (line 88) | public string Resolve()
    method ResolveResult (line 94) | private Result ResolveResult()
    method ProcessExpression (line 148) | private void ProcessExpression(StringBuilder currentExpression, Result...
    method ProcessVariable (line 214) | private bool ProcessVariable(VarSpec varSpec, Result result, bool mult...
    method IsVarNameChar (line 314) | private static bool IsVarNameChar(char c)
    method GetOperator (line 323) | private static OperatorInfo GetOperator(char operatorIndicator)
    method GetParameters (line 357) | public IDictionary<string, object> GetParameters(Uri uri, QueryStringP...
    method CreateMatchingRegex (line 416) | public static string CreateMatchingRegex(string uriTemplate)
    method CreateMatchingRegex2 (line 447) | public static string CreateMatchingRegex2(string uriTemplate)
    method GetQueryExpression (line 479) | private static string GetQueryExpression(List<String> paramNames, stri...
    method GetExpression (line 504) | private static string GetExpression(List<String> paramNames, string pr...

FILE: src/UriTemplates/UriTemplateConverter.cs
  class UriTemplateConverter (line 11) | public sealed class UriTemplateConverter
    method CanConvertFrom (line 15) | public override bool CanConvertFrom(ITypeDescriptorContext context, Ty...
    method ConvertFrom (line 21) | public override object ConvertFrom(

FILE: src/UriTemplates/UriTemplateExtensions.cs
  class UriTemplateExtensions (line 9) | public static class UriTemplateExtensions
    method AddParameter (line 11) | public static UriTemplate AddParameter(this UriTemplate template, stri...
    method AddParameters (line 18) | public static UriTemplate AddParameters(this UriTemplate template, obj...
    method AddParameters (line 39) | public static UriTemplate AddParameters(this UriTemplate uriTemplate, ...
  class UriExtensions (line 52) | public static class UriExtensions
    method MakeTemplate (line 54) | public static UriTemplate MakeTemplate(this Uri uri)
    method MakeTemplate (line 61) | public static UriTemplate MakeTemplate(this Uri uri, IDictionary<strin...
    method GetQueryStringParameters (line 72) | public static Dictionary<string, object> GetQueryStringParameters(this...

FILE: src/UriTemplates/UriTemplateTable.cs
  class UriTemplateTable (line 8) | public class UriTemplateTable
    method Add (line 12) | public void Add(string key, UriTemplate template)
    method Match (line 17) | public TemplateMatch Match(Uri url, QueryStringParameterOrder order = ...
  class TemplateMatch (line 48) | public class TemplateMatch

FILE: src/UriTemplates/VarSpec.cs
  class VarSpec (line 5) | public class VarSpec
    method VarSpec (line 13) | public VarSpec(OperatorInfo operatorInfo)
    method ToString (line 23) | public override string ToString()
Condensed preview — 33 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (121K chars).
[
  {
    "path": ".gitattributes",
    "chars": 780,
    "preview": "*.doc  diff=astextplain\r\n*.DOC\tdiff=astextplain\r\n*.docx\tdiff=astextplain\r\n*.DOCX\tdiff=astextplain\r\n*.dot\tdiff=astextplai"
  },
  {
    "path": ".github/CODEOWNERS",
    "chars": 16,
    "preview": "* @darrelmiller\n"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 357,
    "preview": "version: 2\nupdates:\n- package-ecosystem: nuget\n  directory: \"/\"\n  schedule:\n    interval: daily\n  open-pull-requests-lim"
  },
  {
    "path": ".github/workflows/auto-merge-dependabot.yml",
    "chars": 792,
    "preview": "name: Auto-merge dependabot updates\n\non:\n  pull_request:\n    branches: [ main ]\n\npermissions:\n  pull-requests: write\n  c"
  },
  {
    "path": ".github/workflows/buildAndDeploy.yml",
    "chars": 1956,
    "preview": "name: Build and Test\n\non:\n  workflow_dispatch:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n\njo"
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "chars": 2739,
    "preview": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# Y"
  },
  {
    "path": ".gitignore",
    "chars": 472,
    "preview": "\n#ignore thumbnails created by windows\nThumbs.db\n#Ignore files build by Visual Studio\n*.obj\n*.exe\n*.pdb\n*.user\n*.aps\n*.p"
  },
  {
    "path": ".gitmodules",
    "chars": 125,
    "preview": "[submodule \"lib/uritemplate-test\"]\n\tpath = lib/uritemplate-test\n\turl = https://github.com/uri-templates/uritemplate-test"
  },
  {
    "path": ".travis.yml",
    "chars": 199,
    "preview": "language: csharp\nsudo: false   # use the new container-based Travis infrastructure\ninstall:\n  - nuget restore UriTemplat"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 2454,
    "preview": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Change"
  },
  {
    "path": "LICENSE",
    "chars": 10765,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "README.md",
    "chars": 3859,
    "preview": "# Uri Templates # \n\n[![Build and deploy](https://github.com/tavis-software/Tavis.UriTemplates/actions/workflows/buildAnd"
  },
  {
    "path": "UriTemplates.sln",
    "chars": 3645,
    "preview": "\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio 15\r\nVisualStudioVersion = 15.0.28307.489"
  },
  {
    "path": "build.cmd",
    "chars": 257,
    "preview": "@echo Off\nset config=%1\nif \"%config%\" == \"\" (\n   set config=debug\n)\nmd artifacts\ndotnet build --configuration %config% -"
  },
  {
    "path": "src/UriTemplateTests/App.config",
    "chars": 164,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n<configuration>\r\n    <uri>\r\n      <idn enabled=\"All\" />\r\n      <iriParsing ena"
  },
  {
    "path": "src/UriTemplateTests/ParameterMatchingTests.cs",
    "chars": 8673,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Text.RegularExpressi"
  },
  {
    "path": "src/UriTemplateTests/SpecTests.cs",
    "chars": 9136,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing Newtonsoft.Json.Linq;\nusing T"
  },
  {
    "path": "src/UriTemplateTests/UriExtensionTests.cs",
    "chars": 2678,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Tavis.UriTemplates;\nusing X"
  },
  {
    "path": "src/UriTemplateTests/UriTemplateConverterTests.cs",
    "chars": 710,
    "preview": "using System.ComponentModel;\nusing Tavis.UriTemplates;\nusing Xunit;\n\nnamespace UriTemplateTests\n{\n    public class UriT"
  },
  {
    "path": "src/UriTemplateTests/UriTemplateExtensionsTests.cs",
    "chars": 9619,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Tavis;\nusing Tavis.UriTempl"
  },
  {
    "path": "src/UriTemplateTests/UriTemplateTableTests.cs",
    "chars": 4794,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Tavis.UriTemplates;\nusing X"
  },
  {
    "path": "src/UriTemplateTests/UriTemplateTests.csproj",
    "chars": 991,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net6.0</TargetFramework>\n\n    <IsPackable>fals"
  },
  {
    "path": "src/UriTemplateTests/UsageTests.cs",
    "chars": 14582,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Net.Http;\nusing System.Text;\nusing Tavi"
  },
  {
    "path": "src/UriTemplates/OperatorInfo.cs",
    "chars": 335,
    "preview": "namespace Tavis.UriTemplates\n{\n    public class OperatorInfo\n    {\n        public bool Default { get; set; }\n        pub"
  },
  {
    "path": "src/UriTemplates/QueryStringParameterOrder.cs",
    "chars": 114,
    "preview": "namespace Tavis.UriTemplates\n{\n    public enum QueryStringParameterOrder\n    {\n        Strict,\n        Any\n    }\n}"
  },
  {
    "path": "src/UriTemplates/Result.cs",
    "chars": 4477,
    "preview": "using System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace Tavis.UriTe"
  },
  {
    "path": "src/UriTemplates/UriTemplate.cs",
    "chars": 20716,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Text.RegularExpressi"
  },
  {
    "path": "src/UriTemplates/UriTemplateConverter.cs",
    "chars": 1189,
    "preview": "using System;\nusing System.ComponentModel;\nusing System.Globalization;\n\nnamespace Tavis.UriTemplates\n{\n#if TYPE_CONVERT"
  },
  {
    "path": "src/UriTemplates/UriTemplateExtensions.cs",
    "chars": 3021,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text.RegularEx"
  },
  {
    "path": "src/UriTemplates/UriTemplateTable.cs",
    "chars": 1465,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace Tavis.UriTemplates\n{\n "
  },
  {
    "path": "src/UriTemplates/UriTemplates.csproj",
    "chars": 2239,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <TargetFrameworks>netstandard2.0;netstandard2.1;</TargetFramewor"
  },
  {
    "path": "src/UriTemplates/VarSpec.cs",
    "chars": 794,
    "preview": "using System.Text;\n\nnamespace Tavis.UriTemplates\n{\n    public class VarSpec\n    {\n        private readonly OperatorInfo "
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the tavis-software/Tavis.UriTemplates GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 33 files (111.4 KB), approximately 25.6k tokens, and a symbol index with 155 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!