From 8a095504c4ab1e385b3689318261c8808d57dd7d Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 5 Nov 2024 14:50:20 +0100 Subject: [PATCH] [libcalamares] Add nested (dotted) key lookup for GS --- src/libcalamares/GlobalStorage.cpp | 51 ++++++++++++++++++++++++ src/libcalamares/GlobalStorage.h | 20 ++++++++++ src/libcalamares/Tests.cpp | 33 +++++++++++++++ src/libcalamares/testdata/yaml-list.conf | 6 +++ 4 files changed, 110 insertions(+) diff --git a/src/libcalamares/GlobalStorage.cpp b/src/libcalamares/GlobalStorage.cpp index 0c78201d3..e78a1b04a 100644 --- a/src/libcalamares/GlobalStorage.cpp +++ b/src/libcalamares/GlobalStorage.cpp @@ -190,4 +190,55 @@ GlobalStorage::loadYaml( const QString& filename ) return false; } +///@brief Implementation for recursively looking up dotted selector parts. +static QVariant +lookup( const QStringList& nestedKey, int index, const QVariant& v, bool& ok ) +{ + if ( !v.canConvert< QVariantMap >() ) + { + // Mismatch: we're still looking for keys, but v is not a submap + ok = false; + return {}; + } + if ( index >= nestedKey.length() ) + { + cError() << "Recursion error looking at index" << index << "of" << nestedKey; + ok = false; + return {}; + } + + const QVariantMap map = v.toMap(); + const QString& key = nestedKey.at( index ); + if ( index == nestedKey.length() - 1 ) + { + ok = map.contains( key ); + return ok ? map.value( key ) : QVariant(); + } + else + { + return lookup( nestedKey, index + 1, map.value( key ), ok ); + } +} + +QVariant +lookup( const GlobalStorage* storage, const QString& nestedKey, bool& ok ) +{ + ok = false; + if ( !storage ) + { + return {}; + } + + if ( nestedKey.contains( '.' ) ) + { + QStringList steps = nestedKey.split( '.' ); + return lookup( steps, 1, storage->value( steps.first() ), ok ); + } + else + { + ok = storage->contains( nestedKey ); + return ok ? storage->value( nestedKey ) : QVariant(); + } +} + } // namespace Calamares diff --git a/src/libcalamares/GlobalStorage.h b/src/libcalamares/GlobalStorage.h index 5bb6d4e97..37ea332d2 100644 --- a/src/libcalamares/GlobalStorage.h +++ b/src/libcalamares/GlobalStorage.h @@ -167,6 +167,26 @@ private: mutable QMutex m_mutex; }; + +/** @brief Gets a value from the store + * + * When @p nestedKey contains no '.' characters, equivalent + * to `gs->value(nestedKey)`. Otherwise recursively looks up + * the '.'-separated parts of @p nestedKey in successive sub-maps + * of the store, returning the value in the innermost one. + * + * Example: `lookup(gs, "branding.name")` finds the value of the + * 'name' key in the 'branding' submap of the store. + * + * Sets @p ok to @c true if a value was found. Returns the value + * as a variant. If no value is found (e.g. the key is missing + * or some prefix submap is missing) sets @p ok to @c false + * and returns an invalid QVariant. + * + * @see GlobalStorage::value + */ +DLLEXPORT QVariant lookup( const GlobalStorage* gs, const QString& nestedKey, bool& ok ); + } // namespace Calamares #endif // CALAMARES_GLOBALSTORAGE_H diff --git a/src/libcalamares/Tests.cpp b/src/libcalamares/Tests.cpp index 42bdbbe38..956fda14f 100644 --- a/src/libcalamares/Tests.cpp +++ b/src/libcalamares/Tests.cpp @@ -32,6 +32,7 @@ private Q_SLOTS: void testGSLoadSave(); void testGSLoadSave2(); void testGSLoadSaveYAMLStringList(); + void testGSNestedLookup(); void testInstanceKey(); void testInstanceDescription(); @@ -177,6 +178,38 @@ TestLibCalamares::testGSLoadSaveYAMLStringList() QCOMPARE( gs2.value( "dwarfs" ).toString(), QStringLiteral( "" ) ); // .. they're gone } +void +TestLibCalamares::testGSNestedLookup() +{ + Logger::setupLogLevel( Logger::LOGDEBUG ); + + const QString filename( BUILD_AS_TEST "/testdata/yaml-list.conf" ); + QVERIFY2( QFile::exists( filename ), qPrintable( filename ) ); + + Calamares::GlobalStorage gs2; + QVERIFY( gs2.loadYaml( filename ) ); + + bool ok = false; + const auto v0 = Calamares::lookup( &gs2, "horse.colors.neck", ok ); + QVERIFY( ok ); + QVERIFY( v0.canConvert< QString >() ); + QCOMPARE( v0.toString(), QStringLiteral( "roan" ) ); + const auto v1 = Calamares::lookup( &gs2, "horse.colors.nose", ok ); + QVERIFY( !ok ); + QVERIFY( !v1.isValid() ); + const auto v2 = Calamares::lookup( &gs2, "cow.colors.nose", ok ); + QVERIFY( !ok ); + QVERIFY( !v2.isValid() ); + const auto v3 = Calamares::lookup( &gs2, "dwarfs", ok ); + QVERIFY( ok ); + QVERIFY( v3.canConvert< QVariantList >() ); // because it's a list-valued thing + const auto v4 = Calamares::lookup( &gs2, "dwarfs.sleepy", ok ); + QVERIFY( !ok ); // Sleepy is a value in the list of dwarfs, not a key + const auto v5 = Calamares::lookup( &gs2, "derp", ok ); + QVERIFY( ok ); + QCOMPARE( v5.toInt(), 17 ); +} + void TestLibCalamares::testInstanceKey() { diff --git a/src/libcalamares/testdata/yaml-list.conf b/src/libcalamares/testdata/yaml-list.conf index d8d2178d1..fca1c4c2d 100644 --- a/src/libcalamares/testdata/yaml-list.conf +++ b/src/libcalamares/testdata/yaml-list.conf @@ -9,3 +9,9 @@ - "sleepy" - "sneezy" - "doc" +horse: + hoofs: 4 + colors: + mane: black + neck: roan + tail: white