* @file
*
* This file declares the APILevelVersion type used for @!APILevel and @IfAvailable checks.
*
* API level version encoding for runtime comparison:
* encoded = major * 1_000_000 + minor * 1_000 + patch
*
* Strict validation used by APILevel/@IfAvailable parsing:
* major : 1 – 99
* minor : 0 – 99
* patch : 0 – 99
*/
#ifndef CANGJIE_BASIC_APILEVELVERSION_H
#define CANGJIE_BASIC_APILEVELVERSION_H
#include <cstdint>
#include <optional>
#include <string>
#ifdef major
#undef major
#endif
#ifdef minor
#undef minor
#endif
namespace Cangjie {
* @brief API level version triple (major.minor.patch).
*
* Used for @!APILevel since values and --cfg APILevel_level option.
* The sentinel value 0.0.0 means "not set" (check IsZero()).
*/
struct APILevelVersion {
enum class ParseRule {
MAJOR_ONLY,
MAJOR_OR_TRIPLE,
TRIPLE_ONLY,
};
uint32_t major{0};
uint32_t minor{0};
uint32_t patch{0};
APILevelVersion() = default;
explicit APILevelVersion(uint32_t maj, uint32_t min = 0, uint32_t pat = 0) : major(maj), minor(min), patch(pat)
{
}
bool IsZero() const
{
return major == 0 && minor == 0 && patch == 0;
}
std::string ToString() const
{
return std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(patch);
}
* @brief Returns a compact display string, omitting trailing zero components.
*
* This preserves backward compatibility with integer-style API levels:
* {10, 0, 0} -> "10"
* {10, 1, 0} -> "10.1"
* {10, 1, 5} -> "10.1.5"
* Used for diagnostic messages so existing test golden output is unchanged.
*/
std::string ToDisplayString() const;
* @brief Encodes the version as a single uint64_t suitable for runtime comparison.
*
* Encoding: major * 1_000_000 + minor * 1_000 + patch
* This ordering guarantees that encoded(v1) < encoded(v2) iff v1 < v2,
* provided minor and patch are each in [0, 999].
*/
uint64_t ToEncoded() const
{
return static_cast<uint64_t>(major) * 1000000ULL + static_cast<uint64_t>(minor) * 1000ULL +
static_cast<uint64_t>(patch);
}
* @brief Parses a version string of the form "major[.minor[.patch]]".
*
* Accepts "20", "20.1", "20.1.5". Non-numeric components default to 0.
* Returns the zero version APILevelVersion{0,0,0} for empty or entirely
* invalid input.
*/
static APILevelVersion Parse(const std::string& s);
static std::optional<APILevelVersion> ParseChecked(const std::string& s, ParseRule rule);
* @brief Validates that @p s is a well-formed version string.
*
* A valid string contains 1–3 dot-separated components, each consisting
* solely of decimal digits.
*/
static bool IsValidFormat(const std::string& s);
static bool IsValidFormat(const std::string& s, ParseRule rule);
bool operator<(const APILevelVersion& other) const
{
if (major != other.major) {
return major < other.major;
}
if (minor != other.minor) {
return minor < other.minor;
}
return patch < other.patch;
}
bool operator<=(const APILevelVersion& other) const
{
return *this < other || *this == other;
}
bool operator>(const APILevelVersion& other) const
{
return other < *this;
}
bool operator>=(const APILevelVersion& other) const
{
return !(*this < other);
}
bool operator==(const APILevelVersion& other) const
{
return major == other.major && minor == other.minor && patch == other.patch;
}
bool operator!=(const APILevelVersion& other) const
{
return !(*this == other);
}
};
}
#endif