mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-04-29 12:05:12 +00:00
replace semver with home grown simplier alternative
This commit is contained in:
@@ -1,22 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015 Marko Živanović
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
# About
|
|
||||||
|
|
||||||
This project is MIT-licensed, C++14 implementation of [semantic versioning](http://semver.org) parser and comparator with support for modifying parsed version strings. Semantic versioning 2.0.0 specification is supported out-of-the-box and the code should be flexible-enough to support future revisions or other similar versioning schemes.
|
|
||||||
|
|
||||||
Copyright (c) 2015 Marko Zivanovic
|
|
||||||
|
|
||||||
Based on https://github.com/zmarko/semver
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
/*
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015 Marko Zivanovic
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <functional>
|
|
||||||
#include <map>
|
|
||||||
#include "semver200.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace version {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// Compare normal version identifiers.
|
|
||||||
int compare_normal(const Version_data & l, const Version_data & r) {
|
|
||||||
if (l.major > r.major)
|
|
||||||
return 1;
|
|
||||||
if (l.major < r.major)
|
|
||||||
return -1;
|
|
||||||
if (l.minor > r.minor)
|
|
||||||
return 1;
|
|
||||||
if (l.minor < r.minor)
|
|
||||||
return -1;
|
|
||||||
if (l.patch > r.patch)
|
|
||||||
return 1;
|
|
||||||
if (l.patch < r.patch)
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare alphanumeric prerelease identifiers.
|
|
||||||
inline int cmp_alnum_prerel_ids(const string & l, const string & r) {
|
|
||||||
auto cmp = l.compare(r);
|
|
||||||
if (cmp == 0) {
|
|
||||||
return cmp;
|
|
||||||
} else {
|
|
||||||
return cmp > 0 ? 1 : -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare numeric prerelease identifiers.
|
|
||||||
inline int cmp_num_prerel_ids(const string & l, const string & r) {
|
|
||||||
long long li = stoll(l);
|
|
||||||
long long ri = stoll(r);
|
|
||||||
if (li == ri)
|
|
||||||
return 0;
|
|
||||||
return li > ri ? 1 : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
using Prerel_type_pair = pair<Id_type, Id_type>;
|
|
||||||
using Prerel_id_comparator = function<int(const string &, const string &)>;
|
|
||||||
const map<Prerel_type_pair, Prerel_id_comparator> comparators = {{{Id_type::alnum, Id_type::alnum}, cmp_alnum_prerel_ids},
|
|
||||||
{{Id_type::alnum, Id_type::num}, [](const string &, const string &) { return 1; }},
|
|
||||||
{{Id_type::num, Id_type::alnum}, [](const string &, const string &) { return -1; }},
|
|
||||||
{{Id_type::num, Id_type::num}, cmp_num_prerel_ids}};
|
|
||||||
|
|
||||||
// Compare prerelease identifiers based on their types.
|
|
||||||
inline int compare_prerel_identifiers(const Prerelease_identifier & l, const Prerelease_identifier & r) {
|
|
||||||
auto cmp = comparators.at({l.second, r.second});
|
|
||||||
return cmp(l.first, r.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int cmp_rel_prerel(const Prerelease_identifiers & l, const Prerelease_identifiers & r) {
|
|
||||||
if (l.empty() && !r.empty())
|
|
||||||
return 1;
|
|
||||||
if (r.empty() && !l.empty())
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
int Semver200_comparator::compare(const Version_data & l, const Version_data & r) const {
|
|
||||||
// Compare normal version components.
|
|
||||||
int cmp = compare_normal(l, r);
|
|
||||||
if (cmp != 0)
|
|
||||||
return cmp;
|
|
||||||
|
|
||||||
// Compare if one version is release and the other prerelease - release is always higher.
|
|
||||||
cmp = cmp_rel_prerel(l.prerelease_ids, r.prerelease_ids);
|
|
||||||
if (cmp != 0)
|
|
||||||
return cmp;
|
|
||||||
|
|
||||||
// Compare prerelease by looking at each identifier: numeric ones are compared as numbers,
|
|
||||||
// alphanum as ASCII strings.
|
|
||||||
auto shorter = min(l.prerelease_ids.size(), r.prerelease_ids.size());
|
|
||||||
for (size_t i = 0; i < shorter; i++) {
|
|
||||||
cmp = compare_prerel_identifiers(l.prerelease_ids[i], r.prerelease_ids[i]);
|
|
||||||
if (cmp != 0)
|
|
||||||
return cmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prerelease identifiers are the same, to the length of the shorter version string;
|
|
||||||
// if they are the same length, then versions are equal, otherwise, longer one wins.
|
|
||||||
if (l.prerelease_ids.size() == r.prerelease_ids.size())
|
|
||||||
return 0;
|
|
||||||
return l.prerelease_ids.size() > r.prerelease_ids.size() ? 1 : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace version
|
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
/*
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015 Marko Zivanovic
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <map>
|
|
||||||
#include "semver200.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace version {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
enum class Parser_state { major, minor, patch, prerelease, build };
|
|
||||||
|
|
||||||
using Validator = function<void(const string &, const char)>;
|
|
||||||
using State_transition_hook = function<void(string &)>;
|
|
||||||
/// State transition is described by a character that triggers it, a state to transition to and
|
|
||||||
/// optional hook to be invoked on transition.
|
|
||||||
using Transition = tuple<const char, Parser_state, State_transition_hook>;
|
|
||||||
using Transitions = vector<Transition>;
|
|
||||||
using State = tuple<Transitions, string &, Validator>;
|
|
||||||
using State_machine = std::map<Parser_state, State>;
|
|
||||||
|
|
||||||
// Ranges of characters allowed in prerelease and build identifiers.
|
|
||||||
const vector<pair<char, char>> allowed_prerel_id_chars = {{'0', '9'}, {'A', 'Z'}, {'a', 'z'}, {'-', '-'}};
|
|
||||||
|
|
||||||
inline Transition mkx(const char c, Parser_state p, State_transition_hook pth) {
|
|
||||||
return make_tuple(c, p, pth);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void Parse_error(const std::string & s) {
|
|
||||||
// EMSESP::logger().err("parse error: %s", s.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Advance parser state machine by a single step.
|
|
||||||
/**
|
|
||||||
Perform single step of parser state machine: if character matches one from transition tables -
|
|
||||||
trigger transition to next state; otherwise, validate if current token is in legal state
|
|
||||||
(throw Parse_error if not) and then add character to current token; State transition includes
|
|
||||||
preparing various vars for next state and invoking state transition hook (if specified) which is
|
|
||||||
where whole tokens are validated.
|
|
||||||
*/
|
|
||||||
inline void process_char(const char c, Parser_state & cstate, Parser_state & pstate, const Transitions & transitions, string & target, Validator validate) {
|
|
||||||
for (const auto & transition : transitions) {
|
|
||||||
if (c == get<0>(transition)) {
|
|
||||||
if (get<2>(transition))
|
|
||||||
get<2>(transition)(target);
|
|
||||||
pstate = cstate;
|
|
||||||
cstate = get<1>(transition);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
validate(target, c);
|
|
||||||
target.push_back(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Validate normal (major, minor, patch) version components.
|
|
||||||
inline void normal_version_validator(const string & tgt, const char c) {
|
|
||||||
if (c < '0' || c > '9')
|
|
||||||
Parse_error("invalid character encountered: " + string(1, c));
|
|
||||||
if (tgt.compare(0, 1, "0") == 0)
|
|
||||||
Parse_error("leading 0 not allowed");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Validate that prerelease and build version identifiers are comprised of allowed chars only.
|
|
||||||
inline void prerelease_version_validator(const string &, const char c) {
|
|
||||||
bool res = false;
|
|
||||||
for (const auto & r : allowed_prerel_id_chars) {
|
|
||||||
res |= (c >= r.first && c <= r.second);
|
|
||||||
}
|
|
||||||
if (!res)
|
|
||||||
Parse_error("invalid character encountered: " + string(1, c));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool is_identifier_numeric(const string & id) {
|
|
||||||
return id.find_first_not_of("0123456789") == string::npos;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool check_for_leading_0(const string & str) {
|
|
||||||
return str.length() > 1 && str[0] == '0';
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Validate every individual prerelease identifier, determine it's type and add it to collection.
|
|
||||||
void prerelease_hook_impl(string & id, Prerelease_identifiers & prerelease) {
|
|
||||||
if (id.empty())
|
|
||||||
Parse_error("version identifier cannot be empty");
|
|
||||||
Id_type t = Id_type::alnum;
|
|
||||||
if (is_identifier_numeric(id)) {
|
|
||||||
t = Id_type::num;
|
|
||||||
if (check_for_leading_0(id)) {
|
|
||||||
Parse_error("numeric identifiers cannot have leading 0");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
prerelease.push_back(Prerelease_identifier(id, t));
|
|
||||||
id.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Validate every individual build identifier and add it to collection.
|
|
||||||
void build_hook_impl(string & id, Parser_state & pstate, Build_identifiers & build, std::string & prerelease_id, Prerelease_identifiers & prerelease) {
|
|
||||||
// process last token left from parsing prerelease data
|
|
||||||
if (pstate == Parser_state::prerelease)
|
|
||||||
prerelease_hook_impl(prerelease_id, prerelease);
|
|
||||||
if (id.empty())
|
|
||||||
Parse_error("version identifier cannot be empty");
|
|
||||||
build.push_back(id);
|
|
||||||
id.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
/// Parse semver 2.0.0-compatible string to Version_data structure.
|
|
||||||
/**
|
|
||||||
Version text parser is implemented as a state machine. In each step one successive character from version
|
|
||||||
string is consumed and is either added to current token or triggers state transition. Hooks can be
|
|
||||||
injected into state transitions for validation/customization purposes.
|
|
||||||
*/
|
|
||||||
Version_data Semver200_parser::parse(const string & s) const {
|
|
||||||
string major;
|
|
||||||
string minor;
|
|
||||||
string patch;
|
|
||||||
string prerelease_id;
|
|
||||||
string build_id;
|
|
||||||
Prerelease_identifiers prerelease;
|
|
||||||
Build_identifiers build;
|
|
||||||
Parser_state cstate{Parser_state::major};
|
|
||||||
Parser_state pstate;
|
|
||||||
|
|
||||||
auto prerelease_hook = [&](string & id) { prerelease_hook_impl(id, prerelease); };
|
|
||||||
|
|
||||||
auto build_hook = [&](string & id) { build_hook_impl(id, pstate, build, prerelease_id, prerelease); };
|
|
||||||
|
|
||||||
// State transition tables
|
|
||||||
auto major_trans = {mkx('.', Parser_state::minor, {})};
|
|
||||||
auto minor_trans = {mkx('.', Parser_state::patch, {})};
|
|
||||||
auto patch_trans = {mkx('-', Parser_state::prerelease, {}), mkx('+', Parser_state::build, {})};
|
|
||||||
auto prerelease_trans = {// When identifier separator (.) is found, stay in the same state but invoke hook
|
|
||||||
// in order to process each individual identifier separately.
|
|
||||||
mkx('.', Parser_state::prerelease, prerelease_hook),
|
|
||||||
mkx('+', Parser_state::build, {})};
|
|
||||||
auto build_trans = {// Same stay-in-the-same-state-but-invoke-hook trick from above.
|
|
||||||
mkx('.', Parser_state::build, build_hook)};
|
|
||||||
|
|
||||||
State_machine state_machine = {{Parser_state::major, State{major_trans, major, normal_version_validator}},
|
|
||||||
{Parser_state::minor, State{minor_trans, minor, normal_version_validator}},
|
|
||||||
{Parser_state::patch, State{patch_trans, patch, normal_version_validator}},
|
|
||||||
{Parser_state::prerelease, State{prerelease_trans, prerelease_id, prerelease_version_validator}},
|
|
||||||
{Parser_state::build, State{build_trans, build_id, prerelease_version_validator}}};
|
|
||||||
|
|
||||||
// Main loop.
|
|
||||||
for (const auto & c : s) {
|
|
||||||
auto state = state_machine.at(cstate);
|
|
||||||
process_char(c, cstate, pstate, get<0>(state), get<1>(state), get<2>(state));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trigger appropriate hooks in order to process last token, because no state transition was
|
|
||||||
// triggered for it.
|
|
||||||
if (cstate == Parser_state::prerelease) {
|
|
||||||
prerelease_hook(prerelease_id);
|
|
||||||
} else if (cstate == Parser_state::build) {
|
|
||||||
build_hook(build_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Version_data{stoi(major), stoi(minor), stoi(patch), prerelease, build};
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace version
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015 Marko Zivanovic
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "version.h"
|
|
||||||
|
|
||||||
namespace version {
|
|
||||||
|
|
||||||
/// Parse string into Version_data structure according to semantic versioning 2.0.0 rules.
|
|
||||||
struct Semver200_parser {
|
|
||||||
Version_data parse(const std::string &) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Compare Version_data to another using semantic versioning 2.0.0 rules.
|
|
||||||
struct Semver200_comparator {
|
|
||||||
int compare(const Version_data &, const Version_data &) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Concrete version class that binds all semver 2.0.0 functionality together.
|
|
||||||
class Semver200_version : public Basic_version<Semver200_parser, Semver200_comparator> {
|
|
||||||
public:
|
|
||||||
Semver200_version()
|
|
||||||
: Basic_version{Semver200_parser(), Semver200_comparator()} {
|
|
||||||
}
|
|
||||||
|
|
||||||
Semver200_version(const std::string & v)
|
|
||||||
: Basic_version{v, Semver200_parser(), Semver200_comparator()} {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace version
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
/*
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015 Marko Zivanovic
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace version {
|
|
||||||
|
|
||||||
/// Type of prerelease identifier: alphanumeric or numeric.
|
|
||||||
/**
|
|
||||||
Type of identifier affects comparison: alphanumeric identifiers are compared as ASCII strings, while
|
|
||||||
numeric identifiers are compared as numbers.
|
|
||||||
*/
|
|
||||||
enum class Id_type {
|
|
||||||
alnum, ///< Identifier is alphanumerical
|
|
||||||
num ///< Identifier is numeric
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Container for prerelease identifier value and it's type.
|
|
||||||
/**
|
|
||||||
Prerelease version string consist of an optional series of dot-separated identifiers.
|
|
||||||
These identifiers can be either numerical or alphanumerical.
|
|
||||||
This structure describes one such identifier.
|
|
||||||
*/
|
|
||||||
using Prerelease_identifier = std::pair<std::string, Id_type>;
|
|
||||||
|
|
||||||
/// Container for all prerelease identifiers for a given version string.
|
|
||||||
using Prerelease_identifiers = std::vector<Prerelease_identifier>;
|
|
||||||
|
|
||||||
/// Build identifier is arbitrary string with no special meaning with regards to version precedence.
|
|
||||||
using Build_identifier = std::string;
|
|
||||||
|
|
||||||
/// Container for all build identifiers of a given version string.
|
|
||||||
using Build_identifiers = std::vector<Build_identifier>;
|
|
||||||
|
|
||||||
/// Description of version broken into parts, as per semantic versioning specification.
|
|
||||||
struct Version_data {
|
|
||||||
Version_data(const int M, const int m, const int p, const Prerelease_identifiers & pr, const Build_identifiers & b)
|
|
||||||
: major{M}
|
|
||||||
, minor{m}
|
|
||||||
, patch{p}
|
|
||||||
, prerelease_ids{pr}
|
|
||||||
, build_ids{b} {
|
|
||||||
}
|
|
||||||
|
|
||||||
int major; ///< Major version, change only on incompatible API modifications.
|
|
||||||
int minor; ///< Minor version, change on backwards-compatible API modifications.
|
|
||||||
int patch; ///< Patch version, change only on bugfixes.
|
|
||||||
|
|
||||||
/// Optional series of prerelease identifiers.
|
|
||||||
Prerelease_identifiers prerelease_ids;
|
|
||||||
|
|
||||||
/// Optional series of build identifiers.
|
|
||||||
Build_identifiers build_ids;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Forward declaration required for operators' template declarations.
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
class Basic_version;
|
|
||||||
|
|
||||||
/// Test if left-hand version operand is of lower precedence than the right-hand version.
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
bool operator<(const Basic_version<Parser, Comparator> &, const Basic_version<Parser, Comparator> &);
|
|
||||||
|
|
||||||
/// Test if left-hand version operand if of equal precedence as the right-hand version.
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
bool operator==(const Basic_version<Parser, Comparator> &, const Basic_version<Parser, Comparator> &);
|
|
||||||
|
|
||||||
/// Test if left-hand version and right-hand version are of different precedence.
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
bool operator!=(const Basic_version<Parser, Comparator> &, const Basic_version<Parser, Comparator> &);
|
|
||||||
|
|
||||||
/// Test if left-hand version operand is of higher precedence than the right-hand version.
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
bool operator>(const Basic_version<Parser, Comparator> &, const Basic_version<Parser, Comparator> &);
|
|
||||||
|
|
||||||
/// Test if left-hand version operand is of higher or equal precedence as the right-hand version.
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
bool operator>=(const Basic_version<Parser, Comparator> &, const Basic_version<Parser, Comparator> &);
|
|
||||||
|
|
||||||
/// Test if left-hand version operand is of lower or equal precedence as the right-hand version.
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
bool operator<=(const Basic_version<Parser, Comparator> &, const Basic_version<Parser, Comparator> &);
|
|
||||||
|
|
||||||
/// Base class for various version parsing, precedence ordering and data manipulation schemes.
|
|
||||||
/**
|
|
||||||
Basic_version class describes general version object without prescribing parsing,
|
|
||||||
validation, comparison and modification rules. These rules are implemented by supplied Parser, Comparator
|
|
||||||
and Modifier objects.
|
|
||||||
*/
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
class Basic_version {
|
|
||||||
public:
|
|
||||||
/// Construct Basic_version object using Parser object to parse default ("0.0.0") version string, Comparator for comparison and Modifier for modification.
|
|
||||||
Basic_version(Parser, Comparator);
|
|
||||||
|
|
||||||
/// Construct Basic_version object using Parser to parse supplied version string, Comparator for comparison and Modifier for modification.
|
|
||||||
Basic_version(const std::string &, Parser, Comparator);
|
|
||||||
|
|
||||||
/// Construct Basic_version object using supplied Version_data, Parser, Comparator and Modifier objects.
|
|
||||||
Basic_version(const Version_data &, Parser, Comparator);
|
|
||||||
|
|
||||||
/// Construct Basic_version by copying data from another one.
|
|
||||||
Basic_version(const Basic_version &);
|
|
||||||
|
|
||||||
/// Copy version data from another Basic_version to this one.
|
|
||||||
Basic_version & operator=(const Basic_version &);
|
|
||||||
|
|
||||||
int major() const; ///< Get major version.
|
|
||||||
int minor() const; ///< Get minor version.
|
|
||||||
int patch() const; ///< Get patch version.
|
|
||||||
const std::string prerelease() const; ///< Get prerelease version string.
|
|
||||||
const std::string build() const; ///< Get build version string.
|
|
||||||
|
|
||||||
friend bool operator< <>(const Basic_version &, const Basic_version &);
|
|
||||||
friend bool operator==<>(const Basic_version &, const Basic_version &);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Parser parser_;
|
|
||||||
Comparator comparator_;
|
|
||||||
Version_data ver_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace version
|
|
||||||
|
|
||||||
#include "version.inl"
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
/*
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015 Marko Zivanovic
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "version.h"
|
|
||||||
|
|
||||||
namespace version {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
/// Utility function to splice all vector elements to output stream, using designated separator
|
|
||||||
/// between elements and function object for getting values from vector elements.
|
|
||||||
template <typename T, typename F>
|
|
||||||
std::string & splice(std::string & ss, const std::vector<T> & vec, const std::string & sep, F read) {
|
|
||||||
if (!vec.empty()) {
|
|
||||||
for (auto it = vec.cbegin(); it < vec.cend() - 1; ++it) {
|
|
||||||
ss += read(*it) + sep;
|
|
||||||
}
|
|
||||||
ss += read(*vec.crbegin());
|
|
||||||
}
|
|
||||||
return ss;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
Basic_version<Parser, Comparator>::Basic_version(Parser p, Comparator c)
|
|
||||||
: parser_(p)
|
|
||||||
, comparator_(c)
|
|
||||||
, ver_(parser_.parse("0.0.0")) {
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
Basic_version<Parser, Comparator>::Basic_version(const std::string & v, Parser p, Comparator c)
|
|
||||||
: parser_(p)
|
|
||||||
, comparator_(c)
|
|
||||||
, ver_(parser_.parse(v)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
Basic_version<Parser, Comparator>::Basic_version(const Version_data & v, Parser p, Comparator c)
|
|
||||||
: parser_(p)
|
|
||||||
, comparator_(c)
|
|
||||||
, ver_(v) {
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
Basic_version<Parser, Comparator>::Basic_version(const Basic_version<Parser, Comparator> &) = default;
|
|
||||||
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
Basic_version<Parser, Comparator> & Basic_version<Parser, Comparator>::operator=(const Basic_version<Parser, Comparator> &) = default;
|
|
||||||
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
int Basic_version<Parser, Comparator>::major() const {
|
|
||||||
return ver_.major;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
int Basic_version<Parser, Comparator>::minor() const {
|
|
||||||
return ver_.minor;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
int Basic_version<Parser, Comparator>::patch() const {
|
|
||||||
return ver_.patch;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
const std::string Basic_version<Parser, Comparator>::prerelease() const {
|
|
||||||
std::string ss;
|
|
||||||
return splice(ss, ver_.prerelease_ids, ".", [](const Prerelease_identifier & id) { return id.first; });
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
const std::string Basic_version<Parser, Comparator>::build() const {
|
|
||||||
std::string ss;
|
|
||||||
return splice(ss, ver_.build_ids, ".", [](const std::string & id) { return id; });
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
bool operator<(const Basic_version<Parser, Comparator> & l, const Basic_version<Parser, Comparator> & r) {
|
|
||||||
return l.comparator_.compare(l.ver_, r.ver_) == -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
bool operator==(const Basic_version<Parser, Comparator> & l, const Basic_version<Parser, Comparator> & r) {
|
|
||||||
return l.comparator_.compare(l.ver_, r.ver_) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
inline bool operator!=(const Basic_version<Parser, Comparator> & l, const Basic_version<Parser, Comparator> & r) {
|
|
||||||
return !(l == r);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
inline bool operator>(const Basic_version<Parser, Comparator> & l, const Basic_version<Parser, Comparator> & r) {
|
|
||||||
return r < l;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
inline bool operator>=(const Basic_version<Parser, Comparator> & l, const Basic_version<Parser, Comparator> & r) {
|
|
||||||
return !(l < r);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Parser, typename Comparator>
|
|
||||||
inline bool operator<=(const Basic_version<Parser, Comparator> & l, const Basic_version<Parser, Comparator> & r) {
|
|
||||||
return !(l > r);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace version
|
|
||||||
124
src/core/EMSESP_Version.h
Normal file
124
src/core/EMSESP_Version.h
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||||
|
* Copyright 2020-2026 emsesp.org
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EMSESP_Version_H
|
||||||
|
#define EMSESP_Version_H
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// Drop-in lightweight replacement for the subset of the semver library actually used by EMS-ESP.
|
||||||
|
// The previous semver library (lib/semver) builds a std::map + std::function-based state machine on
|
||||||
|
// every parse, which fragments the internal heap on the ESP32. This replacement does no heap
|
||||||
|
// allocation beyond the std::string member for the prerelease tag, and matches the API surface
|
||||||
|
// we consume: construction from string, major()/minor()/patch()/prerelease(), and operator>/</==.
|
||||||
|
//
|
||||||
|
// Only strict numeric precedence (major.minor.patch) is used for comparison in EMS-ESP, so we
|
||||||
|
// intentionally ignore prerelease tags during comparison rather than implement the full semver
|
||||||
|
// ordering rules. This is consistent with how the old code was used (callers only check major/
|
||||||
|
// minor/patch numerically; prerelease() is only read for logging).
|
||||||
|
|
||||||
|
namespace version {
|
||||||
|
|
||||||
|
class EMSESP_Version {
|
||||||
|
public:
|
||||||
|
EMSESP_Version() = default;
|
||||||
|
|
||||||
|
// Construct from a version string like "3.9.0-dev.14" or "3.9.0".
|
||||||
|
// Anything past a '-' or '+' is kept as the prerelease string and not interpreted.
|
||||||
|
explicit EMSESP_Version(const std::string & s) {
|
||||||
|
parse(s.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit EMSESP_Version(const char * s) {
|
||||||
|
parse(s ? s : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
int major() const {
|
||||||
|
return major_;
|
||||||
|
}
|
||||||
|
int minor() const {
|
||||||
|
return minor_;
|
||||||
|
}
|
||||||
|
int patch() const {
|
||||||
|
return patch_;
|
||||||
|
}
|
||||||
|
const std::string & prerelease() const {
|
||||||
|
return prerelease_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Numeric-only comparison (major.minor.patch). Prerelease tags are ignored on purpose.
|
||||||
|
friend bool operator<(const EMSESP_Version & a, const EMSESP_Version & b) {
|
||||||
|
if (a.major_ != b.major_)
|
||||||
|
return a.major_ < b.major_;
|
||||||
|
if (a.minor_ != b.minor_)
|
||||||
|
return a.minor_ < b.minor_;
|
||||||
|
return a.patch_ < b.patch_;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator>(const EMSESP_Version & a, const EMSESP_Version & b) {
|
||||||
|
return b < a;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator==(const EMSESP_Version & a, const EMSESP_Version & b) {
|
||||||
|
return a.major_ == b.major_ && a.minor_ == b.minor_ && a.patch_ == b.patch_;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(const EMSESP_Version & a, const EMSESP_Version & b) {
|
||||||
|
return !(a == b);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator>=(const EMSESP_Version & a, const EMSESP_Version & b) {
|
||||||
|
return !(a < b);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator<=(const EMSESP_Version & a, const EMSESP_Version & b) {
|
||||||
|
return !(b < a);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int major_ = 0;
|
||||||
|
int minor_ = 0;
|
||||||
|
int patch_ = 0;
|
||||||
|
std::string prerelease_;
|
||||||
|
|
||||||
|
void parse(const char * s) {
|
||||||
|
major_ = minor_ = patch_ = 0;
|
||||||
|
prerelease_.clear();
|
||||||
|
if (s == nullptr || *s == '\0') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// parse numeric major.minor.patch; accept partial ("3", "3.9", "3.9.0")
|
||||||
|
sscanf(s, "%d.%d.%d", &major_, &minor_, &patch_);
|
||||||
|
// capture prerelease tag after '-' if present (stop at '+' which is build metadata)
|
||||||
|
const char * dash = strchr(s, '-');
|
||||||
|
if (dash != nullptr) {
|
||||||
|
const char * plus = strchr(dash, '+');
|
||||||
|
if (plus != nullptr) {
|
||||||
|
prerelease_.assign(dash + 1, plus - dash - 1);
|
||||||
|
} else {
|
||||||
|
prerelease_.assign(dash + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace version
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user