Skip to content

Commit

Permalink
Merge branch 'release/1.1.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
i.s.vovk committed Mar 2, 2021
2 parents eebdbb2 + 8524319 commit ff6f3e6
Show file tree
Hide file tree
Showing 10 changed files with 1,074 additions and 63 deletions.
29 changes: 28 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
# 1.0.0 (2019-12-11)
## 1.1.1 (2021-03-02)

### Features

* Add doxygen notations throughout the lib

### Misc

* Update README

# 1.1.0 (2021-02-26)

### Features

* Implement template expansion

### Misc

* Style-related fixes

## 1.0.1 (2020-11-06)

### Features

* Implement operator==/!= for Literal, Expression, Variable
* Implement unit tests

# 1.0.0 (2020-10-07)

- initial release
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0 FATAL_ERROR)

set(URITEMPLATE_VERSION_MAJOR "1")
set(URITEMPLATE_VERSION_MINOR "1")
set(URITEMPLATE_VERSION_RELEASE "0")
set(URITEMPLATE_VERSION_RELEASE "1")
set(URITEMPLATE_VERSION_STRING "${URITEMPLATE_VERSION_MAJOR}.${URITEMPLATE_VERSION_MINOR}.${URITEMPLATE_VERSION_RELEASE}")
set(URITEMPLATE_LIB_VERSION ${URITEMPLATE_VERSION_STRING})
set(URITEMPLATE_LIB_SOVERSION "${URITEMPLATE_VERSION_MAJOR}")
Expand Down
95 changes: 84 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,86 @@
[![Language C++](https://img.shields.io/badge/language-c++-blue.svg)](https://isocpp.org)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)

TODO
This library implements [URI Template](https://tools.ietf.org/html/rfc6570) with full support up to Level 4 providing **expansion** and **match** capabilities. It requires c++17 compiler support and have no dependencies.

## Why
## What?

TODO
URI templates are convenient way to describe a range of possible URI by making some parts of it variable. For example, variety of resources:
* `http://example.com/~fred/`
* `http://example.com/~mark/`
* `http://example.com/~admin/`
* `http://example.com/~guest/`
* `http://example.com/~elephant/`

## How to use
Could be described by simple template – `http://example.com/~{username}/`.

TODO
Or, resources:
* `http://example.com/search?q=cat&lang=en`
* `http://example.com/search?q=chien&lang=fr`

Could be described by – `http://example.com/search{?q,lang}`.

A template is transformed into a URI by replacing each delimited expression with its value as defined by expansion rules and the values of variables.

URI Templates can also be used in reverse for the purpose of variable matching: comparing the template to a fully formed URI in order to extract the variables. It only works well if the template expressions are delimited by the beginning or end of the URI or by characters that cannot be part of the expansion, otherwise some ambiguity is present. For example, a template `http://example.com/{foo}{bar}` matches `http://example.com/foobar`, but is is impossible to distinguish if:
* `foo='foobar'` and `bar` is undefined; or
* `foo` is undefined and `bar='foobar'`

Although, if you only interested if a template matches some URI with at least one possible set of values and does not care about values itself, then it is ok.

There is more to it. For better understanding please refer to [RFC 6570](https://tools.ietf.org/html/rfc6570).

## Quickstart

URI Templates are presented as instances of `URI::Template::Template` class. It is basically a vector of parts, which can be either `URI::Template::Literal` or `URI::Template::Expression`. To make one you can use `URI::Template::ParseTemplate()` function or construct it by hand using:
* `URI::Template::Operator`
* `URI::Template::Variable`
* `URI::Template::Modifier`

classes.

From there you can provide values for template variables with `URI::Template::VarValue` objects and expand it, or use `URI::Template::MatchURI()` to test if some URI matches a template, i.e. if it can be expanded from a template if correct values are provided.

### Example

Here is basic example how to parse, match and expand URI template:
```c++
#include <uri-template/uri-template.h>
#include <iostream>

int main() {
const std::string uri = "http://example.com/search?q=cat&lang=en";
// Parse the template
const URI::Template::Template uri_template = URI::Template::ParseTemplate("http://example.com/search{?q,lang}");

// Match it to the URI
// &matched_values can be nullptr if you don't care about values.
std::unordered_map<std::string, URI::Template::VarValue> matched_values;
bool matched = URI::Template::MatchURI(uri_template, uri, &matched_values);

// Print results
std::cout << std::boolalpha;
std::cout << "Template matched: " << matched << std::endl;
for (const auto& [name, value] : matched_values) {
std::cout << name << "=" << value << std::endl;
}

// Expand
const std::string expanded_uri = URI::Template::ExpandTemplate(uri_template, matched_values);
std::cout << "Template expanded: " << expanded_uri << std::endl;
}
```

```bash
g++ -std=c++17 example.cpp -luri-template
```
Output:
```
Template matched: true
lang=en
q=cat
Template expanded: http://example.com/search?q=cat&lang=en
```

## How to build

Expand Down Expand Up @@ -42,25 +113,27 @@ make -j$(nproc)
make install
```

You may also test it with:
You can install it (sudo may be required):
```bash
make test
make install
```
Or install (sudo may be required):

Or test:
```bash
make install
cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=ON ..
make test
```

### Cmake options

* **CMAKE_BUILD_TYPE** - [build type](https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html). `RelWithDebInfo` by default.
* **BUILD_SHARED_LIBS** - [build shared or static library](https://cmake.org/cmake/help/v3.0/variable/BUILD_SHARED_LIBS.html). `ON` by default.
* **BUILD_TESTING** - [build tests or not](https://cmake.org/cmake/help/latest/module/CTest.html). `ON` by default.
* **BUILD_TESTING** - [build tests or not](https://cmake.org/cmake/help/latest/module/CTest.html). `OFF` by default.
* **URITEMPLATE_BUNDLED_MODE** - if the library is being built as a part of another project. If this options is set then *BUILD_SHARED_LIBS* forces to be OFF. Defined by `"${PROJECT_SOURCE_DIR}" == "${CMAKE_SOURCE_DIR}"` by default.

## License

Developed at **Tinkoff.ru** in 2020.\
Developed at **Tinkoff.ru** in 2021.\
Distibuted under **Apache License 2.0** [LICENSE](./LICENSE). You may also obtain this license at https://www.apache.org/licenses/LICENSE-2.0.

## Contacts
Expand Down
34 changes: 34 additions & 0 deletions include/uri-template/Expander.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,43 @@
namespace URI {
namespace Template {

/**
* Performs percent-encoding of the string.
* Will percent-encode incoming @p value. If @p allow_reserved is true then the characters from reserved
* (as per https://tools.ietf.org/html/rfc6570#section-1.5) are not encoded.
* This function is idempotent, means that already encoded triplets in @p value will not be encoded twice.
*
* @param[in] value A value to encode.
* @param[in] allow_reserved If reserved characters are allowed in the result.
* @param[in] max_len Maximum length of the result. Encoded triplets are counted as single character.
*
* @returns A percent-encoded representation of the @p value.
*/
std::string PctEncode(const std::string& value, bool allow_reserved = false,
std::size_t max_len = std::numeric_limits<size_t>::max());

/**
* Expands a single template expression.
* Expands an @p expression into a string according to the rules from https://tools.ietf.org/html/rfc6570#section-3.2.
* Uses @p values to locate variables values that are expanded. Variables which are not in the map treated as undefined.
*
* @param[in] expression A template expression to expand.
* @param[in] values Variables values to use for expansion.
*
* @returns Expansion result.
*/
std::string ExpandExpression(const Expression& expression, const std::unordered_map<std::string, VarValue>& values);

/**
* Expands uri-template into a string.
* Expands an @p uri_template into a string according to the rules from https://tools.ietf.org/html/rfc6570#section-3
* Uses @p values to locate variables values that are expanded. Variables which are not in the map treated as undefined.
*
* @param[in] uri_template A template expression to expand.
* @param[in] values Variables values to use for expansion.
*
* @returns Expansion result.
*/
std::string ExpandTemplate(const Template& uri_template, const std::unordered_map<std::string, VarValue>& values);

} // namespace Template
Expand Down
70 changes: 66 additions & 4 deletions include/uri-template/Matcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,89 @@
namespace URI {
namespace Template {

/**
* Container to hold match borders.
*/
class Match
{
public:
/**
* Parametrized constructor.
* Creates a match with known start and end positions. Match considered to be in [start, end)
*/
Match(std::size_t start, std::size_t end);

/// Destructor.
~Match() = default;

/// Get the starting position of the match.
std::size_t Start() const;
/// Get the ending position of the match.
std::size_t End() const;

private:
std::size_t start_;
std::size_t end_;
std::size_t start_; ///< Start of the match.
std::size_t end_; ///< End of the match.
};

/**
* Lookup for a literal.
* Returns result of searching for a match of @p literal in @p where, starting from position @p start.
*
* @param[in] literal A literal to match.
* @param[in] where A string where to lookup for a match.
* @param[in] start Position to start lookup at.
* @param[in] exact_start If true then .
*
* @returns Match instance wrapped in std::optional or std::nullopt.
*/
std::optional<Match> MatchLiteral(const Literal& literal, const std::string& where, std::size_t start,
bool exact_start);
std::optional<VarValue> MatchVarValue(const Variable& var, const Operator& oper,
std::optional<std::string>&& raw_value);

/**
* Lookup for variable value.
* Returns result of searching for a value of @p var in @p where consistent with the operator @p oper.
*
* @param[in] var Variable definition to lookup value for.
* @param[in] oper Template operator for @p var.
* @param[in] where A string where to lookup for a match.
*
* @returns VarValue instance wrapped in std::optional or std::nullopt.
*/
std::optional<VarValue> MatchVarValue(const Variable& var, const Operator& oper, std::optional<std::string>&& where);

/**
* Lookup for template expression.
* Returns result of searching for a expansion of an @p expression in @p where.
* Expression considered matched if its' expansion does not contradict definition
*
* @param[in] expression Template expression to lookup for.
* @param[in] where A string where to lookup for a match.
* @param[in] start Position to start lookup at.
* @param[in] end Position to end lookup at.
* @param[in] terminator If this character met matching stops.
* @param[out] values Map of template variables values found in the string.
* Not expanded variables will be filled with VarType::UNDEFINED.
*
* @returns Match instance wrapped in std::optional or std::nullopt.
*/
std::optional<Match> MatchExpression(const Expression& expression, const std::string& where, std::size_t start,
std::size_t end, char terminator,
std::unordered_map<std::string, VarValue>* values = nullptr);

/**
* Lookup for URI-template.
* Returns result of searching for a expansion of an @p uri_template in @p uri.
* Template considered matched if its' expansion does not contradict definition,
* i.e. all literals are in place and all expressions doesn't have any violation in their expansions.
*
* @param[in] uri_template Template to lookup for.
* @param[in] uri An URI where to lookup for a match.
* @param[out] values Map of template variables values found in the string.
* Not expanded variables will be filled with VarType::UNDEFINED.
*
* @returns true if template matched, false – if not.
*/
bool MatchURI(const Template& uri_template, const std::string& uri,
std::unordered_map<std::string, VarValue>* values = nullptr);

Expand Down
Loading

0 comments on commit ff6f3e6

Please sign in to comment.