Matter SDK Coverage Report
Current view: top level - data-model-providers/codegen - CodegenDataModelProvider.cpp (source / functions) Coverage Total Hit
Test: SHA:f84fe08d06f240e801b5d923f8a938a9938ca110 Lines: 98.0 % 202 198
Test Date: 2025-02-22 08:08:07 Functions: 100.0 % 20 20

            Line data    Source code
       1              : /*
       2              :  *    Copyright (c) 2024 Project CHIP Authors
       3              :  *    All rights reserved.
       4              :  *
       5              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       6              :  *    you may not use this file except in compliance with the License.
       7              :  *    You may obtain a copy of the License at
       8              :  *
       9              :  *        http://www.apache.org/licenses/LICENSE-2.0
      10              :  *
      11              :  *    Unless required by applicable law or agreed to in writing, software
      12              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      13              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14              :  *    See the License for the specific language governing permissions and
      15              :  *    limitations under the License.
      16              :  */
      17              : #include <data-model-providers/codegen/CodegenDataModelProvider.h>
      18              : 
      19              : #include <access/AccessControl.h>
      20              : #include <access/Privilege.h>
      21              : #include <app-common/zap-generated/attribute-type.h>
      22              : #include <app/CommandHandlerInterface.h>
      23              : #include <app/CommandHandlerInterfaceRegistry.h>
      24              : #include <app/ConcreteAttributePath.h>
      25              : #include <app/ConcreteClusterPath.h>
      26              : #include <app/ConcreteCommandPath.h>
      27              : #include <app/EventPathParams.h>
      28              : #include <app/GlobalAttributes.h>
      29              : #include <app/RequiredPrivilege.h>
      30              : #include <app/data-model-provider/MetadataList.h>
      31              : #include <app/data-model-provider/MetadataTypes.h>
      32              : #include <app/data-model-provider/Provider.h>
      33              : #include <app/util/DataModelHandler.h>
      34              : #include <app/util/IMClusterCommandHandler.h>
      35              : #include <app/util/af-types.h>
      36              : #include <app/util/attribute-metadata.h>
      37              : #include <app/util/attribute-storage.h>
      38              : #include <app/util/endpoint-config-api.h>
      39              : #include <app/util/persistence/AttributePersistenceProvider.h>
      40              : #include <app/util/persistence/DefaultAttributePersistenceProvider.h>
      41              : #include <data-model-providers/codegen/EmberMetadata.h>
      42              : #include <lib/core/CHIPError.h>
      43              : #include <lib/core/DataModelTypes.h>
      44              : #include <lib/support/CodeUtils.h>
      45              : #include <lib/support/SpanSearchValue.h>
      46              : 
      47              : #include <cstdint>
      48              : #include <optional>
      49              : 
      50              : namespace chip {
      51              : namespace app {
      52              : namespace {
      53              : 
      54           79 : DataModel::AcceptedCommandEntry AcceptedCommandEntryFor(const ConcreteCommandPath & path)
      55              : {
      56           79 :     const CommandId commandId = path.mCommandId;
      57              : 
      58           79 :     DataModel::AcceptedCommandEntry entry;
      59              : 
      60           79 :     entry.commandId       = path.mCommandId;
      61           79 :     entry.invokePrivilege = RequiredPrivilege::ForInvokeCommand(path);
      62           79 :     entry.flags.Set(DataModel::CommandQualityFlags::kTimed, CommandNeedsTimedInvoke(path.mClusterId, commandId));
      63           79 :     entry.flags.Set(DataModel::CommandQualityFlags::kFabricScoped, CommandIsFabricScoped(path.mClusterId, commandId));
      64           79 :     entry.flags.Set(DataModel::CommandQualityFlags::kLargeMessage, CommandHasLargePayload(path.mClusterId, commandId));
      65              : 
      66           79 :     return entry;
      67              : }
      68              : 
      69        11438 : DataModel::ServerClusterEntry ServerClusterEntryFrom(EndpointId endpointId, const EmberAfCluster & cluster)
      70              : {
      71        11438 :     DataModel::ServerClusterEntry entry;
      72              : 
      73        11438 :     entry.clusterId = cluster.clusterId;
      74              : 
      75        11438 :     DataVersion * versionPtr = emberAfDataVersionStorage(ConcreteClusterPath(endpointId, cluster.clusterId));
      76        11438 :     if (versionPtr == nullptr)
      77              :     {
      78              : #if CHIP_CONFIG_DATA_MODEL_EXTRA_LOGGING
      79            0 :         ChipLogError(AppServer, "Failed to get data version for %d/" ChipLogFormatMEI, endpointId,
      80              :                      ChipLogValueMEI(cluster.clusterId));
      81              : #endif
      82            0 :         entry.dataVersion = 0;
      83              :     }
      84              :     else
      85              :     {
      86        11438 :         entry.dataVersion = *versionPtr;
      87              :     }
      88              : 
      89              :     // TODO: set entry flags:
      90              :     //   entry.flags.Set(ClusterQualityFlags::kDiagnosticsData)
      91              : 
      92        11438 :     return entry;
      93              : }
      94              : 
      95        56265 : DataModel::AttributeEntry AttributeEntryFrom(const ConcreteClusterPath & clusterPath, const EmberAfAttributeMetadata & attribute)
      96              : {
      97        56265 :     DataModel::AttributeEntry entry;
      98              : 
      99        56265 :     const ConcreteAttributePath attributePath(clusterPath.mEndpointId, clusterPath.mClusterId, attribute.attributeId);
     100              : 
     101        56265 :     entry.attributeId   = attribute.attributeId;
     102        56265 :     entry.readPrivilege = RequiredPrivilege::ForReadAttribute(attributePath);
     103        56265 :     if (!attribute.IsReadOnly())
     104              :     {
     105        31147 :         entry.writePrivilege = RequiredPrivilege::ForWriteAttribute(attributePath);
     106              :     }
     107              : 
     108        56265 :     entry.flags.Set(DataModel::AttributeQualityFlags::kListAttribute, (attribute.attributeType == ZCL_ARRAY_ATTRIBUTE_TYPE));
     109        56265 :     entry.flags.Set(DataModel::AttributeQualityFlags::kTimed, attribute.MustUseTimedWrite());
     110              : 
     111              :     // NOTE: we do NOT provide additional info for:
     112              :     //    - IsExternal/IsSingleton/IsAutomaticallyPersisted is not used by IM handling
     113              :     //    - IsSingleton spec defines it for CLUSTERS where as we have it for ATTRIBUTES
     114              :     //    - Several specification flags are not available (reportable, quieter reporting,
     115              :     //      fixed, source attribution)
     116              : 
     117              :     // TODO: Set additional flags:
     118              :     // entry.flags.Set(DataModel::AttributeQualityFlags::kFabricScoped)
     119              :     // entry.flags.Set(DataModel::AttributeQualityFlags::kFabricSensitive)
     120              :     // entry.flags.Set(DataModel::AttributeQualityFlags::kChangesOmitted)
     121        56265 :     return entry;
     122              : }
     123              : 
     124              : const ConcreteCommandPath kInvalidCommandPath(kInvalidEndpointId, kInvalidClusterId, kInvalidCommandId);
     125              : 
     126              : DefaultAttributePersistenceProvider gDefaultAttributePersistence;
     127              : 
     128              : } // namespace
     129              : 
     130          387 : CHIP_ERROR CodegenDataModelProvider::Startup(DataModel::InteractionModelContext context)
     131              : {
     132          387 :     ReturnErrorOnFailure(DataModel::Provider::Startup(context));
     133              : 
     134              :     // Ember NVM requires have a data model provider. attempt to create one if one is not available
     135              :     //
     136              :     // It is not a critical failure to not have one, however if one is not set up, ember NVM operations
     137              :     // will error out with a `persistence not available`.
     138          387 :     if (GetAttributePersistenceProvider() == nullptr)
     139              :     {
     140              : #if CHIP_CONFIG_DATA_MODEL_EXTRA_LOGGING
     141          387 :         ChipLogProgress(DataManagement, "Ember attribute persistence requires setting up");
     142              : #endif
     143          387 :         if (mPersistentStorageDelegate != nullptr)
     144              :         {
     145            1 :             ReturnErrorOnFailure(gDefaultAttributePersistence.Init(mPersistentStorageDelegate));
     146            1 :             SetAttributePersistenceProvider(&gDefaultAttributePersistence);
     147              : #if CHIP_CONFIG_DATA_MODEL_EXTRA_LOGGING
     148              :         }
     149              :         else
     150              :         {
     151          386 :             ChipLogError(DataManagement, "No storage delegate available, will not set up attribute persistence.");
     152              : #endif
     153              :         }
     154              :     }
     155              : 
     156          387 :     InitDataModelForTesting();
     157              : 
     158          387 :     return CHIP_NO_ERROR;
     159              : }
     160              : 
     161            5 : std::optional<DataModel::ActionReturnStatus> CodegenDataModelProvider::Invoke(const DataModel::InvokeRequest & request,
     162              :                                                                               TLV::TLVReader & input_arguments,
     163              :                                                                               CommandHandler * handler)
     164              : {
     165              :     CommandHandlerInterface * handler_interface =
     166            5 :         CommandHandlerInterfaceRegistry::Instance().GetCommandHandler(request.path.mEndpointId, request.path.mClusterId);
     167              : 
     168            5 :     if (handler_interface)
     169              :     {
     170            3 :         CommandHandlerInterface::HandlerContext context(*handler, request.path, input_arguments);
     171            3 :         handler_interface->InvokeCommand(context);
     172              : 
     173              :         // If the command was handled, don't proceed any further and return successfully.
     174            3 :         if (context.mCommandHandled)
     175              :         {
     176            3 :             return std::nullopt;
     177              :         }
     178              :     }
     179              : 
     180              :     // Ember always sets the return in the handler
     181            2 :     DispatchSingleClusterCommand(request.path, input_arguments, handler);
     182            2 :     return std::nullopt;
     183              : }
     184              : 
     185         2924 : CHIP_ERROR CodegenDataModelProvider::Endpoints(DataModel::ListBuilder<DataModel::EndpointEntry> & builder)
     186              : {
     187         2924 :     const uint16_t endpointCount = emberAfEndpointCount();
     188              : 
     189         2924 :     ReturnErrorOnFailure(builder.EnsureAppendCapacity(endpointCount));
     190              : 
     191        17113 :     for (uint16_t endpointIndex = 0; endpointIndex < endpointCount; endpointIndex++)
     192              :     {
     193        14189 :         if (!emberAfEndpointIndexIsEnabled(endpointIndex))
     194              :         {
     195         4333 :             continue;
     196              :         }
     197              : 
     198              :         DataModel::EndpointEntry entry;
     199         9856 :         entry.id       = emberAfEndpointFromIndex(endpointIndex);
     200         9856 :         entry.parentId = emberAfParentEndpointFromIndex(endpointIndex);
     201              : 
     202         9856 :         switch (GetCompositionForEndpointIndex(endpointIndex))
     203              :         {
     204         8376 :         case EndpointComposition::kFullFamily:
     205         8376 :             entry.compositionPattern = DataModel::EndpointCompositionPattern::kFullFamily;
     206         8376 :             break;
     207         1480 :         case EndpointComposition::kTree:
     208              :         case EndpointComposition::kInvalid: // should NOT happen, but force compiler to check we validate all versions
     209         1480 :             entry.compositionPattern = DataModel::EndpointCompositionPattern::kTree;
     210         1480 :             break;
     211              :         }
     212         9856 :         ReturnErrorOnFailure(builder.Append(entry));
     213              :     }
     214              : 
     215         2924 :     return CHIP_NO_ERROR;
     216              : }
     217              : 
     218            3 : std::optional<unsigned> CodegenDataModelProvider::TryFindEndpointIndex(EndpointId id) const
     219              : {
     220            3 :     const uint16_t lastEndpointIndex = emberAfEndpointCount();
     221              : 
     222            6 :     if ((mEndpointIterationHint < lastEndpointIndex) && emberAfEndpointIndexIsEnabled(mEndpointIterationHint) &&
     223            3 :         (id == emberAfEndpointFromIndex(mEndpointIterationHint)))
     224              :     {
     225            1 :         return std::make_optional(mEndpointIterationHint);
     226              :     }
     227              : 
     228              :     // Linear search, this may be slow
     229            2 :     uint16_t idx = emberAfIndexFromEndpoint(id);
     230            2 :     if (idx == kEmberInvalidEndpointIndex)
     231              :     {
     232            0 :         return std::nullopt;
     233              :     }
     234              : 
     235            2 :     return std::make_optional<unsigned>(idx);
     236              : }
     237              : 
     238         8442 : CHIP_ERROR CodegenDataModelProvider::ServerClusters(EndpointId endpointId,
     239              :                                                     DataModel::ListBuilder<DataModel::ServerClusterEntry> & builder)
     240              : {
     241         8442 :     const EmberAfEndpointType * endpoint = emberAfFindEndpointType(endpointId);
     242              : 
     243         8442 :     VerifyOrReturnValue(endpoint != nullptr, CHIP_ERROR_NOT_FOUND);
     244         8431 :     VerifyOrReturnValue(endpoint->clusterCount > 0, CHIP_NO_ERROR);
     245         8431 :     VerifyOrReturnValue(endpoint->cluster != nullptr, CHIP_NO_ERROR);
     246              : 
     247         8431 :     ReturnErrorOnFailure(builder.EnsureAppendCapacity(emberAfClusterCountForEndpointType(endpoint, /* server = */ true)));
     248              : 
     249         8431 :     const EmberAfCluster * begin = endpoint->cluster;
     250         8431 :     const EmberAfCluster * end   = endpoint->cluster + endpoint->clusterCount;
     251        21264 :     for (const EmberAfCluster * cluster = begin; cluster != end; cluster++)
     252              :     {
     253        12833 :         if (!cluster->IsServer())
     254              :         {
     255         1395 :             continue;
     256              :         }
     257        11438 :         ReturnErrorOnFailure(builder.Append(ServerClusterEntryFrom(endpointId, *cluster)));
     258              :     }
     259              : 
     260         8431 :     return CHIP_NO_ERROR;
     261              : }
     262              : 
     263        13356 : CHIP_ERROR CodegenDataModelProvider::Attributes(const ConcreteClusterPath & path,
     264              :                                                 DataModel::ListBuilder<DataModel::AttributeEntry> & builder)
     265              : {
     266        13356 :     const EmberAfCluster * cluster = FindServerCluster(path);
     267              : 
     268        13356 :     VerifyOrReturnValue(cluster != nullptr, CHIP_ERROR_NOT_FOUND);
     269        12655 :     VerifyOrReturnValue(cluster->attributeCount > 0, CHIP_NO_ERROR);
     270        12655 :     VerifyOrReturnValue(cluster->attributes != nullptr, CHIP_NO_ERROR);
     271              : 
     272              :     // TODO: if ember would encode data in AttributeEntry form, we could reference things directly (shorter code,
     273              :     //       although still allocation overhead due to global attributes not in metadata)
     274              :     //
     275              :     // We have Attributes from ember + global attributes that are NOT in ember metadata.
     276              :     // We have to report them all
     277        12655 :     constexpr size_t kGlobalAttributeNotInMetadataCount = MATTER_ARRAY_SIZE(GlobalAttributesNotInMetadata);
     278              : 
     279        12655 :     ReturnErrorOnFailure(builder.EnsureAppendCapacity(cluster->attributeCount + kGlobalAttributeNotInMetadataCount));
     280              : 
     281        12655 :     Span<const EmberAfAttributeMetadata> attributeSpan(cluster->attributes, cluster->attributeCount);
     282              : 
     283        68920 :     for (auto & attribute : attributeSpan)
     284              :     {
     285        56265 :         ReturnErrorOnFailure(builder.Append(AttributeEntryFrom(path, attribute)));
     286              :     }
     287              : 
     288              :     // This "GlobalListEntry" is specific for metadata that ember does not include
     289              :     // in its attribute list metadata.
     290              :     //
     291              :     // By spec these Attribute/AcceptedCommands/GeneratedCommants lists are:
     292              :     //   - lists of elements
     293              :     //   - read-only, with read privilege view
     294              :     //   - fixed value (no such flag exists, so this is not a quality flag we set/track)
     295        12655 :     DataModel::AttributeEntry globalListEntry;
     296              : 
     297        12655 :     globalListEntry.readPrivilege = Access::Privilege::kView;
     298        12655 :     globalListEntry.flags.Set(DataModel::AttributeQualityFlags::kListAttribute);
     299              : 
     300        50620 :     for (auto & attribute : GlobalAttributesNotInMetadata)
     301              :     {
     302        37965 :         globalListEntry.attributeId = attribute;
     303        37965 :         ReturnErrorOnFailure(builder.Append(globalListEntry));
     304              :     }
     305              : 
     306        12655 :     return CHIP_NO_ERROR;
     307              : }
     308              : 
     309            4 : CHIP_ERROR CodegenDataModelProvider::ClientClusters(EndpointId endpointId, DataModel::ListBuilder<ClusterId> & builder)
     310              : {
     311            4 :     const EmberAfEndpointType * endpoint = emberAfFindEndpointType(endpointId);
     312              : 
     313            4 :     VerifyOrReturnValue(endpoint != nullptr, CHIP_ERROR_NOT_FOUND);
     314            2 :     VerifyOrReturnValue(endpoint->clusterCount > 0, CHIP_NO_ERROR);
     315            2 :     VerifyOrReturnValue(endpoint->cluster != nullptr, CHIP_NO_ERROR);
     316              : 
     317            2 :     ReturnErrorOnFailure(builder.EnsureAppendCapacity(emberAfClusterCountForEndpointType(endpoint, /* server = */ false)));
     318              : 
     319            2 :     const EmberAfCluster * begin = endpoint->cluster;
     320            2 :     const EmberAfCluster * end   = endpoint->cluster + endpoint->clusterCount;
     321           10 :     for (const EmberAfCluster * cluster = begin; cluster != end; cluster++)
     322              :     {
     323            8 :         if (!cluster->IsClient())
     324              :         {
     325            4 :             continue;
     326              :         }
     327            4 :         ReturnErrorOnFailure(builder.Append(cluster->clusterId));
     328              :     }
     329              : 
     330            2 :     return CHIP_NO_ERROR;
     331              : }
     332              : 
     333        14161 : const EmberAfCluster * CodegenDataModelProvider::FindServerCluster(const ConcreteClusterPath & path)
     334              : {
     335        27278 :     if (mPreviouslyFoundCluster.has_value() && (mPreviouslyFoundCluster->path == path) &&
     336        13117 :         (mEmberMetadataStructureGeneration == emberAfMetadataStructureGeneration()))
     337              : 
     338              :     {
     339        13008 :         return mPreviouslyFoundCluster->cluster;
     340              :     }
     341              : 
     342         1153 :     const EmberAfCluster * cluster = emberAfFindServerCluster(path.mEndpointId, path.mClusterId);
     343         1153 :     if (cluster != nullptr)
     344              :     {
     345          439 :         mPreviouslyFoundCluster           = std::make_optional<ClusterReference>(path, cluster);
     346          439 :         mEmberMetadataStructureGeneration = emberAfMetadataStructureGeneration();
     347              :     }
     348         1153 :     return cluster;
     349              : }
     350              : 
     351          428 : CHIP_ERROR CodegenDataModelProvider::AcceptedCommands(const ConcreteClusterPath & path,
     352              :                                                       DataModel::ListBuilder<DataModel::AcceptedCommandEntry> & builder)
     353              : {
     354              :     // Some CommandHandlerInterface instances are registered of ALL endpoints, so make sure first that
     355              :     // the cluster actually exists on this endpoint before asking the CommandHandlerInterface what commands
     356              :     // it claims to support.
     357          428 :     const EmberAfCluster * serverCluster = FindServerCluster(path);
     358          428 :     VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_NOT_FOUND);
     359              : 
     360              :     CommandHandlerInterface * interface =
     361          421 :         CommandHandlerInterfaceRegistry::Instance().GetCommandHandler(path.mEndpointId, path.mClusterId);
     362          421 :     if (interface != nullptr)
     363              :     {
     364           16 :         size_t commandCount = 0;
     365              : 
     366           16 :         CHIP_ERROR err = interface->EnumerateAcceptedCommands(
     367              :             path,
     368            6 :             [](CommandId id, void * context) -> Loop {
     369            6 :                 *reinterpret_cast<size_t *>(context) += 1;
     370            6 :                 return Loop::Continue;
     371              :             },
     372              :             reinterpret_cast<void *>(&commandCount));
     373              : 
     374           16 :         if (err == CHIP_NO_ERROR)
     375              :         {
     376              :             using EnumerationData = struct
     377              :             {
     378              :                 ConcreteCommandPath commandPath;
     379              :                 DataModel::ListBuilder<DataModel::AcceptedCommandEntry> * acceptedCommandList;
     380              :                 CHIP_ERROR processingError;
     381              :             };
     382              : 
     383            8 :             EnumerationData enumerationData;
     384            8 :             enumerationData.commandPath         = ConcreteCommandPath(path.mEndpointId, path.mClusterId, kInvalidCommandId);
     385            8 :             enumerationData.processingError     = CHIP_NO_ERROR;
     386            8 :             enumerationData.acceptedCommandList = &builder;
     387              : 
     388            8 :             ReturnErrorOnFailure(builder.EnsureAppendCapacity(commandCount));
     389              : 
     390           14 :             ReturnErrorOnFailure(interface->EnumerateAcceptedCommands(
     391              :                 path,
     392              :                 [](CommandId commandId, void * context) -> Loop {
     393              :                     auto input                    = reinterpret_cast<EnumerationData *>(context);
     394              :                     input->commandPath.mCommandId = commandId;
     395              :                     CHIP_ERROR appendError        = input->acceptedCommandList->Append(AcceptedCommandEntryFor(input->commandPath));
     396              :                     if (appendError != CHIP_NO_ERROR)
     397              :                     {
     398              :                         input->processingError = appendError;
     399              :                         return Loop::Break;
     400              :                     }
     401              :                     return Loop::Continue;
     402              :                 },
     403              :                 reinterpret_cast<void *>(&enumerationData)));
     404            8 :             ReturnErrorOnFailure(enumerationData.processingError);
     405              : 
     406              :             // the two invocations MUST return the same sizes.
     407            8 :             VerifyOrReturnError(builder.Size() == commandCount, CHIP_ERROR_INTERNAL);
     408            8 :             return CHIP_NO_ERROR;
     409              :         }
     410            8 :         VerifyOrReturnError(err == CHIP_ERROR_NOT_IMPLEMENTED, err);
     411              :     }
     412              : 
     413          413 :     VerifyOrReturnError(serverCluster->acceptedCommandList != nullptr, CHIP_NO_ERROR);
     414              : 
     415           28 :     const chip::CommandId * endOfList = serverCluster->acceptedCommandList;
     416          101 :     while (*endOfList != kInvalidCommandId)
     417              :     {
     418           73 :         endOfList++;
     419              :     }
     420           28 :     const auto commandCount = static_cast<size_t>(endOfList - serverCluster->acceptedCommandList);
     421              : 
     422              :     // TODO: if ember would store command entries, we could simplify this code to use static data
     423           28 :     ReturnErrorOnFailure(builder.EnsureAppendCapacity(commandCount));
     424              : 
     425           28 :     ConcreteCommandPath commandPath = ConcreteCommandPath(path.mEndpointId, path.mClusterId, kInvalidCommandId);
     426          101 :     for (const chip::CommandId * p = serverCluster->acceptedCommandList; p != endOfList; p++)
     427              :     {
     428           73 :         commandPath.mCommandId = *p;
     429           73 :         ReturnErrorOnFailure(builder.Append(AcceptedCommandEntryFor(commandPath)));
     430              :     }
     431              : 
     432           28 :     return CHIP_NO_ERROR;
     433              : }
     434              : 
     435          377 : CHIP_ERROR CodegenDataModelProvider::GeneratedCommands(const ConcreteClusterPath & path,
     436              :                                                        DataModel::ListBuilder<CommandId> & builder)
     437              : {
     438              :     // Some CommandHandlerInterface instances are registered of ALL endpoints, so make sure first that
     439              :     // the cluster actually exists on this endpoint before asking the CommandHandlerInterface what commands
     440              :     // it claims to support.
     441          377 :     const EmberAfCluster * serverCluster = FindServerCluster(path);
     442          377 :     VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_NOT_FOUND);
     443              : 
     444              :     CommandHandlerInterface * interface =
     445          371 :         CommandHandlerInterfaceRegistry::Instance().GetCommandHandler(path.mEndpointId, path.mClusterId);
     446          371 :     if (interface != nullptr)
     447              :     {
     448            4 :         size_t commandCount = 0;
     449              : 
     450            4 :         CHIP_ERROR err = interface->EnumerateGeneratedCommands(
     451              :             path,
     452            1 :             [](CommandId id, void * context) -> Loop {
     453            1 :                 *reinterpret_cast<size_t *>(context) += 1;
     454            1 :                 return Loop::Continue;
     455              :             },
     456              :             reinterpret_cast<void *>(&commandCount));
     457              : 
     458            4 :         if (err == CHIP_NO_ERROR)
     459              :         {
     460            2 :             ReturnErrorOnFailure(builder.EnsureAppendCapacity(commandCount));
     461              : 
     462              :             using EnumerationData = struct
     463              :             {
     464              :                 DataModel::ListBuilder<CommandId> * generatedCommandList;
     465              :                 CHIP_ERROR processingError;
     466              :             };
     467              :             EnumerationData enumerationData;
     468            2 :             enumerationData.processingError      = CHIP_NO_ERROR;
     469            2 :             enumerationData.generatedCommandList = &builder;
     470              : 
     471            3 :             ReturnErrorOnFailure(interface->EnumerateGeneratedCommands(
     472              :                 path,
     473              :                 [](CommandId id, void * context) -> Loop {
     474              :                     auto input = reinterpret_cast<EnumerationData *>(context);
     475              : 
     476              :                     CHIP_ERROR appendError = input->generatedCommandList->Append(id);
     477              :                     if (appendError != CHIP_NO_ERROR)
     478              :                     {
     479              :                         input->processingError = appendError;
     480              :                         return Loop::Break;
     481              :                     }
     482              :                     return Loop::Continue;
     483              :                 },
     484              :                 reinterpret_cast<void *>(&enumerationData)));
     485            2 :             ReturnErrorOnFailure(enumerationData.processingError);
     486              : 
     487              :             // the two invocations MUST return the same sizes.
     488            2 :             VerifyOrReturnError(builder.Size() == commandCount, CHIP_ERROR_INTERNAL);
     489            2 :             return CHIP_NO_ERROR;
     490              :         }
     491            2 :         VerifyOrReturnError(err == CHIP_ERROR_NOT_IMPLEMENTED, err);
     492              :     }
     493              : 
     494          369 :     VerifyOrReturnError(serverCluster->generatedCommandList != nullptr, CHIP_NO_ERROR);
     495              : 
     496            2 :     const chip::CommandId * endOfList = serverCluster->generatedCommandList;
     497            6 :     while (*endOfList != kInvalidCommandId)
     498              :     {
     499            4 :         endOfList++;
     500              :     }
     501            2 :     const auto commandCount = static_cast<size_t>(endOfList - serverCluster->generatedCommandList);
     502            2 :     return builder.ReferenceExisting({ serverCluster->generatedCommandList, commandCount });
     503              : }
     504              : 
     505          380 : void CodegenDataModelProvider::InitDataModelForTesting()
     506              : {
     507              :     // Call the Ember-specific InitDataModelHandler
     508          380 :     InitDataModelHandler();
     509          380 : }
     510              : 
     511            3 : CHIP_ERROR CodegenDataModelProvider::DeviceTypes(EndpointId endpointId,
     512              :                                                  DataModel::ListBuilder<DataModel::DeviceTypeEntry> & builder)
     513              : {
     514            3 :     std::optional<unsigned> endpoint_index = TryFindEndpointIndex(endpointId);
     515            3 :     if (!endpoint_index.has_value())
     516              :     {
     517            0 :         return {};
     518              :     }
     519              : 
     520            3 :     CHIP_ERROR err = CHIP_NO_ERROR;
     521              : 
     522            3 :     builder.ReferenceExisting(emberAfDeviceTypeListFromEndpointIndex(*endpoint_index, err));
     523            3 :     return CHIP_NO_ERROR;
     524              : }
     525              : 
     526            2 : CHIP_ERROR CodegenDataModelProvider::SemanticTags(EndpointId endpointId, DataModel::ListBuilder<SemanticTag> & builder)
     527              : {
     528            2 :     DataModel::Provider::SemanticTag semanticTag;
     529            2 :     size_t count = 0;
     530              : 
     531            5 :     while (GetSemanticTagForEndpointAtIndex(endpointId, count, semanticTag) == CHIP_NO_ERROR)
     532              :     {
     533            3 :         count++;
     534              :     }
     535              : 
     536            2 :     ReturnErrorOnFailure(builder.EnsureAppendCapacity(count));
     537              : 
     538            5 :     for (size_t idx = 0; idx < count; idx++)
     539              :     {
     540            3 :         ReturnErrorOnFailure(GetSemanticTagForEndpointAtIndex(endpointId, idx, semanticTag));
     541            3 :         ReturnErrorOnFailure(builder.Append(semanticTag));
     542              :     }
     543              : 
     544            2 :     return CHIP_NO_ERROR;
     545              : }
     546              : 
     547              : } // namespace app
     548              : } // namespace chip
        

Generated by: LCOV version 2.0-1