diff --git a/CHANGELOG.md b/CHANGELOG.md index fbbabe3..439a6c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,23 @@ +## 0.4.0 + +- **Breaking (dependency resolution):** `hooks` ^1.0.2 → ^2.1.0 and + `code_assets` ^1.0.0 → ^1.2.1. Consumers pinned to `hooks` 1.x will no + longer resolve. The build hook API is unchanged between `hooks` 1.x and + 2.x, so `hook/build.dart` needed no edits and the public Dart API is + untouched; the bump also unpins `native_toolchain_c` and `record_use` + from their 1.x-era versions. The SDK constraint stays `^3.10.0`. +- Repository moved to `github.com/flatpak-minimal/appstream_dart`; + `repository` and `issue_tracker` updated to match. +- Fix `scripts/test.sh` passing `-DBUILD_TESTING=ON`, which the CMake build + ignores — the gate has been `-DAPPSTREAM_BUILD_TESTS=ON` since 0.2.2. The + C++ suite was therefore never configured or rebuilt, and `ctest` silently + ran whatever stale binary was left in the build directory. CI already + passed the correct flag, so only local runs were affected. +- clang-tidy cleanups in `AppStreamParser` and `XmlScanner`: explicit + parentheses in mixed `*`/`+` accumulator arithmetic, `contains()` in place + of a `find() != npos` membership test, and consistent braces across the + `provides` if/else chain. + ## 0.3.0 - Licensing: adopt SPDX license headers (`SPDX-License-Identifier` / diff --git a/pubspec.yaml b/pubspec.yaml index b2f2097..9542132 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,9 +2,9 @@ name: appstream_dart description: >- High-performance AppStream XML parser with C++23 FFI bridge. Streams catalog metadata into SQLite with Drift ORM and FTS5 search. -version: 0.3.0 -repository: https://github.com/meta-flutter/appstream -issue_tracker: https://github.com/meta-flutter/appstream/issues +version: 0.4.0 +repository: https://github.com/flatpak-minimal/appstream_dart +issue_tracker: https://github.com/flatpak-minimal/appstream_dart/issues topics: - appstream - flathub @@ -27,9 +27,9 @@ environment: dependencies: drift: ^2.22.0 - code_assets: ^1.0.0 + code_assets: ^1.2.1 ffi: ^2.1.0 - hooks: ^1.0.2 + hooks: ^2.1.0 http: ^1.2.0 path: ^1.9.0 sqlite3: ^3.3.1 diff --git a/scripts/test.sh b/scripts/test.sh index 446b0fd..dc4b81d 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -36,7 +36,7 @@ if [[ -z "${SKIP_CXX:-}" ]]; then cmake -S . -B "$BUILD_DIR" \ "${GEN_ARGS[@]}" \ -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \ - -DBUILD_TESTING=ON \ + -DAPPSTREAM_BUILD_TESTS=ON \ -DENABLE_SANITIZER="$SANITIZER" \ -DENABLE_COVERAGE="$COVERAGE" \ -DENABLE_BENCHMARKS="$BENCHMARKS" diff --git a/src/AppStreamParser.cpp b/src/AppStreamParser.cpp index ae4c139..25e4562 100644 --- a/src/AppStreamParser.cpp +++ b/src/AppStreamParser.cpp @@ -34,7 +34,7 @@ static int convertToInt(const std::string_view sv) { for (; i < sv.size(); ++i) { if (sv[i] < '0' || sv[i] > '9') break; - result = result * 10 + (sv[i] - '0'); + result = (result * 10) + (sv[i] - '0'); } return neg ? -result : result; } @@ -44,7 +44,7 @@ static size_t convertToSizeT(const std::string_view sv) { for (const char c : sv) { if (c < '0' || c > '9') break; - result = result * 10 + static_cast(c - '0'); + result = (result * 10) + static_cast(c - '0'); } return result; } @@ -54,7 +54,7 @@ static std::string unixEpochToISO8601(const std::string_view epochStr) { for (char c : epochStr) { if (c < '0' || c > '9') break; - epoch = epoch * 10 + (c - '0'); + epoch = (epoch * 10) + (c - '0'); } const auto t = static_cast(epoch); std::tm tm{}; @@ -751,20 +751,21 @@ AppStreamParser::doParse(XmlScanner &scanner, const std::string &language, Compo break; } if (insideProvides) { - if (tag == "binary"sv) + if (tag == "binary"sv) { currentComponent.provides.binaries.push_back(std::move(textAccum)); - else if (tag == "library"sv) + } else if (tag == "library"sv) { currentComponent.provides.libraries.push_back(std::move(textAccum)); - else if (tag == "mediatype"sv) + } else if (tag == "mediatype"sv) { currentComponent.provides.mediatypes.push_back(std::move(textAccum)); - else if (tag == "id"sv) + } else if (tag == "id"sv) { currentComponent.provides.ids.push_back(std::move(textAccum)); - else if (tag == "dbus"sv) { + } else if (tag == "dbus"sv) { currentComponent.provides.dbus.emplace_back(std::move(currentDbusType), std::move(textAccum)); currentDbusType.clear(); - } else if (tag == "firmware"sv) + } else if (tag == "firmware"sv) { currentComponent.provides.firmware.push_back(std::move(textAccum)); + } currentElement.clear(); textAccum.clear(); break; diff --git a/src/XmlScanner.cpp b/src/XmlScanner.cpp index db214ae..6186885 100644 --- a/src/XmlScanner.cpp +++ b/src/XmlScanner.cpp @@ -113,7 +113,7 @@ void XmlScanner::skipComment() { } bool XmlScanner::containsEntity(std::string_view sv) noexcept { - return sv.find('&') != std::string_view::npos; + return sv.contains('&'); } void XmlScanner::decodeEntities(std::string_view src) { @@ -157,11 +157,11 @@ void XmlScanner::decodeEntities(std::string_view src) { for (size_t i = 2; i < entity.size() && valid; ++i) { char c = entity[i]; if (c >= '0' && c <= '9') - cp = cp * 16 + static_cast(c - '0'); + cp = (cp * 16) + static_cast(c - '0'); else if (c >= 'a' && c <= 'f') - cp = cp * 16 + static_cast(c - 'a' + 10); + cp = (cp * 16) + static_cast(c - 'a' + 10); else if (c >= 'A' && c <= 'F') - cp = cp * 16 + static_cast(c - 'A' + 10); + cp = (cp * 16) + static_cast(c - 'A' + 10); else valid = false; if (cp > 0x10FFFF) @@ -170,7 +170,7 @@ void XmlScanner::decodeEntities(std::string_view src) { } else { for (size_t i = 1; i < entity.size() && valid; ++i) { if (entity[i] >= '0' && entity[i] <= '9') - cp = cp * 10 + static_cast(entity[i] - '0'); + cp = (cp * 10) + static_cast(entity[i] - '0'); else valid = false; if (cp > 0x10FFFF) diff --git a/test/appstream_parse_fallback_test.dart b/test/appstream_parse_fallback_test.dart index d9ddb9f..db0a0c0 100644 --- a/test/appstream_parse_fallback_test.dart +++ b/test/appstream_parse_fallback_test.dart @@ -21,14 +21,12 @@ bool _initNative() { final _nativeReady = _initNative(); void main() { - test( - 'parseToSqlite falls back when isolate spawn fails', - () async { - final tempDir = await Directory.systemTemp.createTemp('appstream_test_'); - final xmlPath = '${tempDir.path}/appstream.xml'; - final dbPath = '${tempDir.path}/catalog.db'; + test('parseToSqlite falls back when isolate spawn fails', () async { + final tempDir = await Directory.systemTemp.createTemp('appstream_test_'); + final xmlPath = '${tempDir.path}/appstream.xml'; + final dbPath = '${tempDir.path}/catalog.db'; - const xml = ''' + const xml = ''' com.example.Test @@ -38,42 +36,40 @@ void main() { '''; - try { - await File(xmlPath).writeAsString(xml); + try { + await File(xmlPath).writeAsString(xml); - final events = await Appstream.parseToSqlite( - xmlPath: xmlPath, - dbPath: dbPath, - workerSpawner: - ( - void Function(Map) _, - Map _, - ) async { - throw StateError('forced spawn failure'); - }, - ).toList().timeout(const Duration(seconds: 30)); + final events = await Appstream.parseToSqlite( + xmlPath: xmlPath, + dbPath: dbPath, + workerSpawner: + ( + void Function(Map) _, + Map _, + ) async { + throw StateError('forced spawn failure'); + }, + ).toList().timeout(const Duration(seconds: 30)); - final failures = events.whereType().toList(); - expect( - failures, - isEmpty, - reason: 'Fallback worker should complete parse without failures', - ); + final failures = events.whereType().toList(); + expect( + failures, + isEmpty, + reason: 'Fallback worker should complete parse without failures', + ); - final done = events.whereType().toList(); - expect(done, hasLength(1)); - expect(done.single.count, greaterThanOrEqualTo(1)); + final done = events.whereType().toList(); + expect(done, hasLength(1)); + expect(done.single.count, greaterThanOrEqualTo(1)); - expect(File(dbPath).existsSync(), isTrue); - expect(File(dbPath).lengthSync(), greaterThan(0)); - } finally { - if (tempDir.existsSync()) { - await tempDir.delete(recursive: true); - } + expect(File(dbPath).existsSync(), isTrue); + expect(File(dbPath).lengthSync(), greaterThan(0)); + } finally { + if (tempDir.existsSync()) { + await tempDir.delete(recursive: true); } - }, - skip: _nativeReady ? null : 'native library not available', - ); + } + }, skip: _nativeReady ? null : 'native library not available'); test( 'parseToSqlite emits ParseFailed or empty ParseDone on loosely-malformed XML',