Files
o3de/Code/Framework/AzFramework/AzFramework/Dependency/Version.h
T
2021-03-08 14:30:57 -08:00

210 lines
7.0 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Outcome/Outcome.h>
#include <AzCore/std/numeric.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/std/hash.h>
#include <AzCore/std/algorithm.h>
#include <AzCore/std/containers/array.h>
#include <initializer_list>
#include <sstream>
namespace AzFramework
{
#define VERSION_SEPARATOR_CHAR '.'
#define VERSION_SEPARATOR_STR "."
template <size_t N>
struct Version
{
/// The type each component is stored as.
using ComponentType = AZ::u64;
/// Store size of Version object
enum
{
parts_count = N
};
Version()
{
static_assert(N > 0, "Size for Version must be more than 0.");
m_parts.fill(0);
}
Version(const Version& other)
: m_parts(other.m_parts)
{
}
Version(const AZStd::array<ComponentType, N>& parts)
: m_parts(parts) { }
Version(const std::initializer_list<ComponentType>& values)
{
AZ_Assert(values.size() == N,
"Initializer size does not matches Version size. "
"Expected: %u, got: %u",
N, values.size());
AZStd::transform(values.begin(), values.end(), m_parts.begin(), [](const ComponentType& c) { return c; });
}
/**
* Parses a version from a string in the format "[part].[part].[part] ...".
*
* \param[in] versionStr The string to parse for a version.
* \returns On success, the parsed Version; on failure, a message describing the error.
*/
static AZ::Outcome<Version, AZStd::string> ParseFromString(const AZStd::string& versionStr)
{
// there is 1 more part than there are dots in the string. (1.2.3 has 3 parts but 2 dots)
size_t partCount = AZStd::accumulate(versionStr.begin(), versionStr.end(), size_t(1),
[](size_t currentValue, const char elem) -> size_t
{
return currentValue + (elem == VERSION_SEPARATOR_CHAR ? 1 : 0);
});
if (N != partCount)
{
return AZ::Failure(AZStd::string::format(
"Failed to parse invalid version string \"%s\". "
"Number of parts in the string doesn't match the size. "
"Expected: %zu, got: %zu"
, versionStr.c_str(), N, partCount));
}
Version<N> result;
std::istringstream iss(versionStr.c_str());
for (int i = 0; i < partCount; ++i)
{
iss >> result.m_parts[i];
// remove the dot before the next iteration
char throwaway;
iss >> throwaway;
if (throwaway != VERSION_SEPARATOR_CHAR)
{
return AZ::Failure(AZStd::string::format(
"Failed to parse invalid version string \"%s\". "
"Unexpected separator character encountered. "
"Expected: \"%d\", got: \"%d\""
, versionStr.c_str(), VERSION_SEPARATOR_CHAR, throwaway));
}
}
if (!iss.eof())
{
return AZ::Failure(AZStd::string::format(
"Failed to parse invalid version string \"%s\". "
, versionStr.c_str()));
}
return AZ::Success(result);
}
/**
* Returns the version in string form.
*
* \returns The version as a string formatted as "[major].[minor].[patch]".
*/
AZStd::string ToString() const
{
std::stringstream ss;
const char* separator = "";
for (int i = 0; i < N; ++i)
{
ss << separator << m_parts[i];
separator = VERSION_SEPARATOR_STR;
}
return ss.str().c_str();
}
/**
* Compare two versions.
*
* Returns: 0 if a == b, <0 if a < b, and >0 if a > b.
*/
static int Compare(const Version& a, const Version& b)
{
for (int i = 0; i < N; ++i)
{
if (a.m_parts[i] != b.m_parts[i])
{
return static_cast<int>(a.m_parts[i]) - static_cast<int>(b.m_parts[i]);
}
}
return 0;
}
/**
* Check if current version is all zero-ed out
*
* Returns: True if the current version is all zero-ed out, else false
*/
bool IsZero() const
{
for (int i = 0; i < N; ++i)
{
if (m_parts[i] != 0)
{
return false;
}
}
return true;
}
AZStd::array<ComponentType, N> m_parts;
};
/**
* Represents a version conforming to the Semantic Versioning standard (http://semver.org/)
*/
struct SemanticVersion
: public Version<3>
{
SemanticVersion()
: Version<3>() { }
SemanticVersion(const Version<3>& other)
: Version<3>(other) { }
SemanticVersion(ComponentType major, ComponentType minor, ComponentType patch)
{
m_parts[0] = major;
m_parts[1] = minor;
m_parts[2] = patch;
}
ComponentType GetMajor() const { return m_parts[0]; }
ComponentType GetMinor() const { return m_parts[1]; }
ComponentType GetPatch() const { return m_parts[2]; }
};
template <size_t N>
inline bool operator< (const Version<N>& a, const Version<N>& b) { return Version<N>::Compare(a, b) < 0; }
template <size_t N>
inline bool operator> (const Version<N>& a, const Version<N>& b) { return Version<N>::Compare(a, b) > 0; }
template <size_t N>
inline bool operator<=(const Version<N>& a, const Version<N>& b) { return Version<N>::Compare(a, b) <= 0; }
template <size_t N>
inline bool operator>=(const Version<N>& a, const Version<N>& b) { return Version<N>::Compare(a, b) >= 0; }
template <size_t N>
inline bool operator==(const Version<N>& a, const Version<N>& b) { return Version<N>::Compare(a, b) == 0; }
template <size_t N>
inline bool operator!=(const Version<N>& a, const Version<N>& b) { return Version<N>::Compare(a, b) != 0; }
} // namespace AzFramework