Support `CppUtilities::DateTimeExpression` in `TagValue`

This commit is contained in:
Martchus 2022-08-13 14:42:51 +02:00
parent d6a2903749
commit 4aff37b788
4 changed files with 90 additions and 7 deletions

View File

@ -185,7 +185,7 @@ set(RES_FILES "${LANGUAGE_HEADER_ISO_639_2}")
set(CONFIGURATION_PACKAGE_SUFFIX
""
CACHE STRING "sets the suffix for find_package() calls to packages configured via c++utilities")
find_package(c++utilities${CONFIGURATION_PACKAGE_SUFFIX} 5.13.0 REQUIRED)
find_package(c++utilities${CONFIGURATION_PACKAGE_SUFFIX} 5.19.0 REQUIRED)
use_cpp_utilities(VISIBILITY PUBLIC)
# link against a possibly required extra library for std::filesystem

View File

@ -48,6 +48,8 @@ std::string_view tagDataTypeString(TagDataType dataType)
return "popularity";
case TagDataType::UnsignedInteger:
return "unsigned integer";
case TagDataType::DateTimeExpression:
return "date time expression";
default:
return "undefined";
}
@ -203,8 +205,6 @@ TagTextEncoding pickUtfEncoding(TagTextEncoding encoding1, TagTextEncoding encod
* - If any of the differently typed values can not be converted to a string (eg. it is binary data) the values
* are *not* considered equal. So the text "foo" and the binary value "foo" are not considered equal although
* the raw data is identical.
* - In fact, values of the types TagDataType::DateTime, TagDataType::TimeSpan, TagDataType::Picture, TagDataType::Binary
* and TagDataType::Unspecified will never be considered equal with a value of another type.
* - If the type is TagDataType::Text and the encoding differs values might still be considered equal if they
* represent the same characters. The same counts for the description.
* - This might be a costly operation due to possible conversions.
@ -303,6 +303,8 @@ bool TagValue::compareTo(const TagValue &other, TagValueComparisionFlags options
return toTimeSpan() == other.toTimeSpan();
case TagDataType::DateTime:
return toDateTime() == other.toDateTime();
case TagDataType::DateTimeExpression:
return toDateTimeExpression() == other.toDateTimeExpression();
case TagDataType::Picture:
case TagDataType::Binary:
case TagDataType::Undefined:
@ -318,6 +320,7 @@ bool TagValue::compareTo(const TagValue &other, TagValueComparisionFlags options
switch (dataType) {
case TagDataType::TimeSpan:
case TagDataType::DateTime:
case TagDataType::DateTimeExpression:
case TagDataType::Picture:
case TagDataType::Binary:
case TagDataType::Undefined:
@ -595,7 +598,7 @@ TimeSpan TagValue::toTimeSpan() const
case sizeof(std::int64_t):
return TimeSpan(*(reinterpret_cast<std::int64_t *>(m_ptr.get())));
default:
throw ConversionException("The size of the assigned integer is not appropriate for conversion to time span.");
throw ConversionException("The size of the assigned data is not appropriate for conversion to time span.");
}
case TagDataType::UnsignedInteger:
switch (m_size) {
@ -615,7 +618,7 @@ TimeSpan TagValue::toTimeSpan() const
/*!
* \brief Converts the value of the current TagValue object to its equivalent
* DateTime representation.
* DateTime representation (using the UTC timezone).
* \throws Throws ConversionException on failure.
*/
DateTime TagValue::toDateTime() const
@ -640,7 +643,43 @@ DateTime TagValue::toDateTime() const
} else if (m_size == sizeof(std::uint64_t)) {
return DateTime(*(reinterpret_cast<std::uint64_t *>(m_ptr.get())));
} else {
throw ConversionException("The size of the assigned integer is not appropriate for conversion to date time.");
throw ConversionException("The size of the assigned data is not appropriate for conversion to date time.");
}
case TagDataType::DateTimeExpression:
return toDateTimeExpression().gmt();
default:
throw ConversionException(argsToString("Can not convert ", tagDataTypeString(m_type), " to date time."));
}
}
/*!
* \brief Converts the value of the current TagValue object to its equivalent
* DateTimeExpression representation.
* \throws Throws ConversionException on failure.
*/
CppUtilities::DateTimeExpression TagParser::TagValue::toDateTimeExpression() const
{
if (isEmpty()) {
return DateTimeExpression();
}
switch (m_type) {
case TagDataType::Text: {
const auto str = toString(m_encoding == TagTextEncoding::Utf8 ? TagTextEncoding::Utf8 : TagTextEncoding::Latin1);
try {
return DateTimeExpression::fromIsoString(str.data());
} catch (const ConversionException &) {
return DateTimeExpression::fromString(str.data());
}
}
case TagDataType::Integer:
case TagDataType::DateTime:
case TagDataType::UnsignedInteger:
return DateTimeExpression{ .value = toDateTime(), .delta = TimeSpan(), .parts = DateTimeParts::DateTime };
case TagDataType::DateTimeExpression:
if (m_size == sizeof(DateTimeExpression)) {
return *reinterpret_cast<DateTimeExpression *>(m_ptr.get());
} else {
throw ConversionException("The size of the assigned data is not appropriate for conversion to date time expression.");
}
default:
throw ConversionException(argsToString("Can not convert ", tagDataTypeString(m_type), " to date time."));
@ -908,6 +947,9 @@ void TagValue::toString(string &result, TagTextEncoding encoding) const
case TagDataType::UnsignedInteger:
result = numberToString(toUnsignedInteger());
break;
case TagDataType::DateTimeExpression:
result = toDateTimeExpression().toIsoString();
break;
default:
throw ConversionException(argsToString("Can not convert ", tagDataTypeString(m_type), " to string."));
}
@ -998,6 +1040,9 @@ void TagValue::toWString(std::u16string &result, TagTextEncoding encoding) const
case TagDataType::UnsignedInteger:
regularStrRes = numberToString(toUnsignedInteger());
break;
case TagDataType::DateTimeExpression:
regularStrRes = toDateTimeExpression().toIsoString();
break;
default:
throw ConversionException(argsToString("Can not convert ", tagDataTypeString(m_type), " to string."));
}

View File

@ -128,6 +128,7 @@ enum class TagDataType : unsigned int {
Undefined, /**< undefined/invalid data type */
Popularity, /**< rating with user info and play counter (as in ID3v2's "Popularimeter") */
UnsignedInteger, /**< unsigned integer */
DateTimeExpression, /**< date time expression, see CppUtilities::DateTimeExpression */
};
/*!
@ -159,6 +160,7 @@ public:
TagTextEncoding encoding = TagTextEncoding::Latin1);
explicit TagValue(PositionInSet value);
explicit TagValue(CppUtilities::DateTime value);
explicit TagValue(const CppUtilities::DateTimeExpression &value);
explicit TagValue(CppUtilities::TimeSpan value);
explicit TagValue(const Popularity &value);
TagValue(const TagValue &other);
@ -190,6 +192,7 @@ public:
PositionInSet toPositionInSet() const;
CppUtilities::TimeSpan toTimeSpan() const;
CppUtilities::DateTime toDateTime() const;
CppUtilities::DateTimeExpression toDateTimeExpression() const;
Popularity toPopularity() const;
Popularity toScaledPopularity(TagType scale = TagType::Unspecified) const;
std::size_t dataSize() const;
@ -231,6 +234,7 @@ public:
void assignPosition(PositionInSet value);
void assignTimeSpan(CppUtilities::TimeSpan value);
void assignDateTime(CppUtilities::DateTime value);
void assignDateTimeExpression(const CppUtilities::DateTimeExpression &value);
void assignPopularity(const Popularity &value);
static void stripBom(const char *&text, std::size_t &length, TagTextEncoding encoding);
@ -422,6 +426,14 @@ inline TagValue::TagValue(CppUtilities::DateTime value)
{
}
/*!
* \brief Constructs a new TagValue holding a copy of the given DateTimeExpression \a value.
*/
inline TagValue::TagValue(const CppUtilities::DateTimeExpression &value)
: TagValue(reinterpret_cast<const char *>(&value), sizeof(value), TagDataType::DateTimeExpression)
{
}
/*!
* \brief Constructs a new TagValue holding a copy of the given TimeSpan \a value.
*/
@ -523,6 +535,14 @@ inline void TagValue::assignDateTime(CppUtilities::DateTime value)
assignData(reinterpret_cast<const char *>(&value), sizeof(value), TagDataType::DateTime);
}
/*!
* \brief Assigns the given DateTimeExpression \a value.
*/
inline void TagParser::TagValue::assignDateTimeExpression(const CppUtilities::DateTimeExpression &value)
{
assignData(reinterpret_cast<const char *>(&value), sizeof(value), TagDataType::DateTimeExpression);
}
/*!
* \brief Assigns the given standard genre \a index to be assigned.
* \param index Specifies the index to be assigned.

View File

@ -28,6 +28,7 @@ class TagValueTests : public TestFixture {
CPPUNIT_TEST(testPositionInSet);
CPPUNIT_TEST(testTimeSpan);
CPPUNIT_TEST(testDateTime);
CPPUNIT_TEST(testDateTimeExpression);
CPPUNIT_TEST(testPopularity);
CPPUNIT_TEST(testString);
CPPUNIT_TEST(testEqualityOperator);
@ -45,6 +46,7 @@ public:
void testPositionInSet();
void testTimeSpan();
void testDateTime();
void testDateTimeExpression();
void testPopularity();
void testString();
void testEqualityOperator();
@ -179,6 +181,20 @@ void TagValueTests::testDateTime()
CPPUNIT_ASSERT_THROW(dateTime.toPositionInSet(), ConversionException);
}
void TagValueTests::testDateTimeExpression()
{
auto expr = DateTimeExpression::fromIsoString("2007");
auto value = TagValue();
value.assignDateTimeExpression(expr);
CPPUNIT_ASSERT_EQUAL(value, TagValue(expr));
CPPUNIT_ASSERT_EQUAL(expr.value, value.toDateTime());
CPPUNIT_ASSERT_EQUAL(expr, value.toDateTimeExpression());
CPPUNIT_ASSERT_EQUAL(expr.toIsoString(), value.toString());
CPPUNIT_ASSERT_THROW(value.toInteger(), ConversionException);
CPPUNIT_ASSERT_THROW(value.toTimeSpan(), ConversionException);
CPPUNIT_ASSERT_THROW(value.toPositionInSet(), ConversionException);
}
void TagValueTests::testPopularity()
{
const auto tagValue = TagValue(Popularity{ .user = "foo", .rating = 40.0, .playCounter = 123, .scale = TagType::VorbisComment });
@ -228,7 +244,9 @@ void TagValueTests::testString()
"conversion to pos", PositionInSet(15), TagValue("\0\x31\0\x35", 4, TagTextEncoding::Utf16BigEndian).toPositionInSet());
CPPUNIT_ASSERT_THROW_MESSAGE("failing conversion pos", TagValue("a4 / 15", 7, TagTextEncoding::Utf8).toPositionInSet(), ConversionException);
CPPUNIT_ASSERT_EQUAL_MESSAGE(
"conversion to date", DateTime::fromDate(2004, 4, 15), TagValue("2004-04-15", 10, TagTextEncoding::Utf8).toDateTime());
"conversion to date time", DateTime::fromDate(2004, 4, 15), TagValue("2004-04-15", 10, TagTextEncoding::Utf8).toDateTime());
CPPUNIT_ASSERT_EQUAL_MESSAGE("conversion to date time expression", DateTimeExpression::fromIsoString("2004-04"),
TagValue("2004-04-15", 7, TagTextEncoding::Utf8).toDateTimeExpression());
CPPUNIT_ASSERT_EQUAL_MESSAGE("conversion to date from UTF-16", DateTime::fromDate(2015, 4, 15),
TagValue("\0\x32\0\x30\0\x31\0\x35\0\x2d\0\x30\0\x34\0\x2d\0\x31\0\x35", 20, TagTextEncoding::Utf16BigEndian).toDateTime());
CPPUNIT_ASSERT_THROW_MESSAGE("failing conversion to date", TagValue("_", 1, TagTextEncoding::Utf8).toDateTime(), ConversionException);