LCOV - code coverage report
Current view: top level - src - thingset_common.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 216 238 90.8 %
Date: 2025-02-03 11:19:13 Functions: 10 10 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 135 157 86.0 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (c) The ThingSet Project Contributors
       3                 :            :  *
       4                 :            :  * SPDX-License-Identifier: Apache-2.0
       5                 :            :  */
       6                 :            : 
       7                 :            : #include <thingset.h>
       8                 :            : 
       9                 :            : #include "thingset_internal.h"
      10                 :            : 
      11                 :            : #include <errno.h>
      12                 :            : #include <inttypes.h>
      13                 :            : #include <math.h>
      14                 :            : #include <stdarg.h>
      15                 :            : #include <stdio.h>
      16                 :            : #include <stdlib.h>
      17                 :            : #include <string.h>
      18                 :            : 
      19                 :         13 : int thingset_common_serialize_group(struct thingset_context *ts,
      20                 :            :                                     const struct thingset_data_object *object)
      21                 :            : {
      22                 :            :     int err;
      23                 :            : 
      24                 :         13 :     err = ts->api->serialize_map_start(ts);
      25         [ -  + ]:         13 :     if (err != 0) {
      26                 :          0 :         return err;
      27                 :            :     }
      28                 :            : 
      29         [ +  + ]:         13 :     if (object->data.group_callback != NULL) {
      30                 :          2 :         object->data.group_callback(THINGSET_CALLBACK_PRE_READ);
      31                 :            :     }
      32                 :            : 
      33         [ +  + ]:        936 :     for (unsigned int i = 0; i < ts->num_objects; i++) {
      34         [ +  + ]:        923 :         if (ts->data_objects[i].parent_id == object->id
      35         [ +  - ]:         67 :             && (ts->data_objects[i].access & THINGSET_READ_MASK))
      36                 :            :         {
      37                 :         67 :             err = ts->api->serialize_key_value(ts, &ts->data_objects[i]);
      38         [ -  + ]:         67 :             if (err != 0) {
      39                 :          0 :                 return err;
      40                 :            :             }
      41                 :            :         }
      42                 :            :     }
      43                 :            : 
      44         [ +  + ]:         13 :     if (object->data.group_callback != NULL) {
      45                 :          2 :         object->data.group_callback(THINGSET_CALLBACK_POST_READ);
      46                 :            :     }
      47                 :            : 
      48                 :         13 :     return ts->api->serialize_map_end(ts);
      49                 :            : }
      50                 :            : 
      51                 :         95 : int thingset_common_prepare_record_element(struct thingset_context *ts,
      52                 :            :                                            const struct thingset_data_object *item,
      53                 :            :                                            uint8_t *record_ptr,
      54                 :            :                                            thingset_common_record_element_action callback)
      55                 :            : {
      56                 :         95 :     int err = -EINVAL;
      57                 :            : 
      58      [ +  +  + ]:         95 :     switch (item->type)
      59                 :            :     {
      60                 :          7 :         case THINGSET_TYPE_ARRAY:
      61                 :          7 :             struct thingset_array *arr = item->data.array;
      62                 :          7 :             struct thingset_array arr_offset = {
      63                 :          7 :                 { .u8 = record_ptr + arr->elements.offset },
      64                 :          7 :                 arr->element_type,
      65                 :          7 :                 arr->decimals,
      66                 :          7 :                 arr->max_elements,
      67                 :          7 :                 arr->num_elements,
      68                 :            :             };
      69                 :          7 :             struct thingset_data_object array_item_offset = {
      70                 :          7 :                 item->parent_id,          item->id,   item->name,
      71                 :          7 :                 { .array = &arr_offset }, item->type, item->detail,
      72                 :            :             };
      73                 :          7 :             err = callback(ts, &array_item_offset);
      74                 :          7 :             break;
      75                 :            : 
      76                 :            : 
      77                 :          6 :         case THINGSET_TYPE_RECORDS:
      78                 :          6 :             struct thingset_records *rec = item->data.records;
      79                 :          6 :             struct thingset_records rec_offset = {
      80                 :          6 :                 record_ptr + (size_t)rec->records,
      81                 :          6 :                 rec->record_size,
      82                 :          6 :                 rec->max_records,
      83                 :          6 :                 rec->num_records,
      84                 :          6 :                 rec->callback,
      85                 :            :             };
      86                 :          6 :             struct thingset_data_object record_item_offset = {
      87                 :          6 :                 item->parent_id, item->id,     item->name, { .records = &rec_offset },
      88                 :          6 :                 item->type,      item->detail,
      89                 :            :             };
      90                 :          6 :             err = callback(ts, &record_item_offset);
      91                 :          6 :             break;
      92                 :            : 
      93                 :            : 
      94                 :         82 :         default:
      95                 :         82 :             struct thingset_data_object default_item_offset = {
      96                 :         82 :                 item->parent_id, item->id,     item->name, { .u8 = record_ptr + item->data.offset },
      97                 :         82 :                 item->type,      item->detail,
      98                 :            :             };
      99                 :         82 :             err = callback(ts, &default_item_offset);
     100                 :         82 :             break;
     101                 :            : 
     102                 :            :     }
     103                 :            : 
     104                 :         95 :     return err;
     105                 :            : }
     106                 :            : 
     107                 :          8 : int thingset_common_serialize_record(struct thingset_context *ts,
     108                 :            :                                      const struct thingset_data_object *object, int record_index)
     109                 :            : {
     110                 :          8 :     struct thingset_records *records = object->data.records;
     111                 :            :     size_t record_offset;
     112                 :            :     int err;
     113                 :            : 
     114         [ -  + ]:          8 :     if (record_index >= records->num_records) {
     115                 :          0 :         return -THINGSET_ERR_NOT_FOUND;
     116                 :            :     }
     117                 :            : 
     118                 :          8 :     err = ts->api->serialize_map_start(ts);
     119         [ -  + ]:          8 :     if (err != 0) {
     120                 :          0 :         return err;
     121                 :            :     }
     122                 :            : 
     123         [ +  + ]:          8 :     if (object->detail == THINGSET_DETAIL_DYN_RECORDS) {
     124                 :          2 :         record_offset = 0;
     125                 :            :     }
     126                 :            :     else {
     127                 :          6 :         record_offset = record_index * records->record_size;
     128                 :            :     }
     129                 :            : 
     130         [ +  + ]:          8 :     if (records->callback != NULL) {
     131                 :          2 :         records->callback(THINGSET_CALLBACK_PRE_READ, record_index);
     132                 :            :     }
     133                 :            : 
     134                 :          8 :     const struct thingset_data_object *item = thingset_get_object_by_id(ts, object->id) + 1;
     135         [ +  + ]:        212 :     while (item < &ts->data_objects[ts->num_objects]) {
     136         [ +  + ]:        204 :         if (item->parent_id != object->id) {
     137                 :        112 :             item++;
     138                 :        112 :             continue;
     139                 :            :         }
     140                 :            : 
     141                 :            :         /* create new object with data pointer including offset */
     142                 :         92 :         uint8_t *record_ptr = (uint8_t *)records->records + record_offset;
     143                 :         92 :         err = thingset_common_prepare_record_element(ts, item, record_ptr,
     144                 :         92 :                                                      ts->api->serialize_key_value);
     145                 :            : 
     146         [ -  + ]:         92 :         if (err != 0) {
     147                 :          0 :             return err;
     148                 :            :         }
     149                 :            : 
     150                 :         92 :         item++;
     151                 :            :     }
     152                 :            : 
     153         [ +  + ]:          8 :     if (records->callback != NULL) {
     154                 :          2 :         records->callback(THINGSET_CALLBACK_POST_READ, record_index);
     155                 :            :     }
     156                 :            : 
     157                 :          8 :     return ts->api->serialize_map_end(ts);
     158                 :            : }
     159                 :            : 
     160                 :         30 : int thingset_common_get(struct thingset_context *ts)
     161                 :            : {
     162                 :            :     struct thingset_data_object *parent;
     163                 :            :     int err;
     164                 :            : 
     165                 :         30 :     ts->api->serialize_response(ts, THINGSET_STATUS_CONTENT, NULL);
     166                 :            : 
     167   [ +  -  +  + ]:         30 :     switch (ts->endpoint.object->type) {
     168                 :         11 :         case THINGSET_TYPE_GROUP:
     169                 :         11 :             err = thingset_common_serialize_group(ts, ts->endpoint.object);
     170                 :         11 :             break;
     171                 :          0 :         case THINGSET_TYPE_FN_VOID:
     172                 :            :         case THINGSET_TYPE_FN_I32:
     173                 :            :             /* bad request, as we can't read exec object's values */
     174                 :          0 :             err = -THINGSET_ERR_BAD_REQUEST;
     175                 :          0 :             break;
     176                 :          7 :         case THINGSET_TYPE_RECORDS:
     177         [ +  + ]:          7 :             if (ts->endpoint.index != THINGSET_ENDPOINT_INDEX_NONE) {
     178                 :          4 :                 err = thingset_common_serialize_record(ts, ts->endpoint.object, ts->endpoint.index);
     179                 :          4 :                 break;
     180                 :            :             }
     181                 :          3 :             err = ts->api->serialize_value(ts, ts->endpoint.object);
     182                 :          3 :             break;
     183                 :         12 :         default:
     184                 :         12 :             parent = thingset_get_object_by_id(ts, ts->endpoint.object->parent_id);
     185                 :            : 
     186   [ +  +  +  + ]:         12 :             if (parent != NULL && parent->data.group_callback != NULL) {
     187                 :          2 :                 parent->data.group_callback(THINGSET_CALLBACK_PRE_READ);
     188                 :            :             }
     189                 :            : 
     190                 :         12 :             err = ts->api->serialize_value(ts, ts->endpoint.object);
     191                 :            : 
     192   [ +  +  +  + ]:         12 :             if (parent != NULL && parent->data.group_callback != NULL) {
     193                 :          2 :                 parent->data.group_callback(THINGSET_CALLBACK_POST_READ);
     194                 :            :             }
     195                 :         12 :             break;
     196                 :            :     }
     197                 :            : 
     198         [ +  - ]:         30 :     if (err == 0) {
     199                 :         30 :         return ts->rsp_pos;
     200                 :            :     }
     201                 :            :     else {
     202                 :          0 :         return ts->api->serialize_response(ts, -err, NULL);
     203                 :            :     }
     204                 :            : }
     205                 :            : 
     206                 :         34 : int thingset_common_fetch(struct thingset_context *ts)
     207                 :            : {
     208                 :            :     int err;
     209                 :            : 
     210                 :            :     /* initialize response with success message */
     211                 :         34 :     ts->api->serialize_response(ts, THINGSET_STATUS_CONTENT, NULL);
     212                 :            : 
     213                 :         34 :     ts->api->serialize_list_start(ts);
     214                 :            : 
     215         [ +  + ]:         34 :     if (ts->api->deserialize_null(ts) == 0) {
     216                 :            :         /* fetch names */
     217         [ +  + ]:        432 :         for (unsigned int i = 0; i < ts->num_objects; i++) {
     218         [ +  - ]:        426 :             if ((ts->data_objects[i].access & THINGSET_READ_MASK)
     219         [ +  + ]:        426 :                 && (ts->data_objects[i].parent_id == ts->endpoint.object->id))
     220                 :            :             {
     221                 :         45 :                 err = ts->api->serialize_key(ts, &ts->data_objects[i]);
     222         [ -  + ]:         45 :                 if (err != 0) {
     223                 :          0 :                     return ts->api->serialize_response(ts, -err, NULL);
     224                 :            :                 }
     225                 :            :             }
     226                 :            :         }
     227                 :            :     }
     228         [ +  - ]:         28 :     else if (ts->api->deserialize_list_start(ts) == 0) {
     229         [ -  + ]:         28 :         if (ts->endpoint.object->type != THINGSET_TYPE_GROUP) {
     230                 :          6 :             return ts->api->serialize_response(ts, THINGSET_ERR_BAD_REQUEST, "%s is not a group",
     231                 :          0 :                                                ts->endpoint.object->name);
     232                 :            :         }
     233                 :            : 
     234                 :            :         /* fetch values */
     235         [ +  + ]:         28 :         if (ts->endpoint.object->data.group_callback != NULL) {
     236                 :          2 :             ts->endpoint.object->data.group_callback(THINGSET_CALLBACK_PRE_READ);
     237                 :            :         }
     238                 :            : 
     239                 :            :         const struct thingset_data_object *object;
     240                 :         60 :         while ((err = ts->api->deserialize_child(ts, &object))
     241         [ +  + ]:         60 :                != -THINGSET_ERR_DESERIALIZATION_FINISHED)
     242                 :            :         {
     243         [ +  + ]:         38 :             if (err != 0) {
     244                 :          4 :                 return ts->api->serialize_response(ts, -err, NULL);
     245                 :            :             }
     246                 :            : 
     247   [ +  +  +  + ]:         34 :             if (object->type == THINGSET_TYPE_GROUP && ts->endpoint.object->id != THINGSET_ID_PATHS
     248         [ +  + ]:          3 :                 && ts->endpoint.object->id != THINGSET_ID_METADATA)
     249                 :            :             {
     250                 :          2 :                 return ts->api->serialize_response(ts, THINGSET_ERR_BAD_REQUEST, "%s is a group",
     251                 :          2 :                                                    object->name);
     252                 :            :             }
     253                 :            : 
     254         [ -  + ]:         32 :             if ((object->access & THINGSET_READ_MASK & ts->auth_flags) == 0) {
     255         [ #  # ]:          0 :                 if (object->access & THINGSET_READ_MASK) {
     256                 :          0 :                     return ts->api->serialize_response(ts, THINGSET_ERR_UNAUTHORIZED,
     257                 :            :                                                        "Authentication required for %s",
     258                 :          0 :                                                        object->name);
     259                 :            :                 }
     260                 :            :                 else {
     261                 :          0 :                     return ts->api->serialize_response(ts, THINGSET_ERR_FORBIDDEN,
     262                 :          0 :                                                        "Reading %s forbidden", object->name);
     263                 :            :                 }
     264                 :            :             }
     265                 :            : 
     266         [ +  + ]:         32 :             if (ts->endpoint.object->id == THINGSET_ID_PATHS) {
     267                 :          4 :                 err = ts->api->serialize_path(ts, object);
     268                 :            :             }
     269                 :            : #ifdef CONFIG_THINGSET_METADATA_ENDPOINT
     270         [ +  + ]:         28 :             else if (ts->endpoint.object->id == THINGSET_ID_METADATA) {
     271                 :          8 :                 err = ts->api->serialize_metadata(ts, object);
     272                 :            :             }
     273                 :            : #endif
     274                 :            :             else {
     275                 :         20 :                 err = ts->api->serialize_value(ts, object);
     276                 :            :             }
     277                 :            : 
     278         [ -  + ]:         32 :             if (err != 0) {
     279                 :          0 :                 return ts->api->serialize_response(ts, -err, NULL);
     280                 :            :             }
     281                 :            :         }
     282                 :            : 
     283         [ +  + ]:         22 :         if (ts->endpoint.object->data.group_callback != NULL) {
     284                 :          2 :             ts->endpoint.object->data.group_callback(THINGSET_CALLBACK_POST_READ);
     285                 :            :         }
     286                 :            :     }
     287                 :            :     else {
     288                 :          0 :         return ts->api->serialize_response(ts, THINGSET_ERR_BAD_REQUEST, "Invalid payload");
     289                 :            :     }
     290                 :            : 
     291                 :         28 :     ts->api->serialize_list_end(ts);
     292                 :            : 
     293                 :         28 :     return 0;
     294                 :            : }
     295                 :            : 
     296                 :         23 : int thingset_common_update(struct thingset_context *ts)
     297                 :            : {
     298                 :            :     const struct thingset_data_object *object;
     299                 :         23 :     bool updated = false;
     300                 :            :     int err;
     301                 :            : 
     302                 :         23 :     err = ts->api->deserialize_map_start(ts);
     303         [ -  + ]:         23 :     if (err != 0) {
     304                 :          0 :         return ts->api->serialize_response(ts, THINGSET_ERR_BAD_REQUEST, "Map with data required");
     305                 :            :     }
     306                 :            : 
     307                 :            :     /* loop through all elements to check if request is valid */
     308                 :         41 :     while ((err = ts->api->deserialize_child(ts, &object))
     309         [ +  + ]:         41 :            != -THINGSET_ERR_DESERIALIZATION_FINISHED)
     310                 :            :     {
     311         [ +  + ]:         24 :         if (err != 0) {
     312                 :          6 :             return ts->api->serialize_response(ts, -err, NULL);
     313                 :            :         }
     314                 :            : 
     315         [ +  + ]:         21 :         if ((object->access & THINGSET_WRITE_MASK & ts->auth_flags) == 0) {
     316         [ +  + ]:          3 :             if (object->access & THINGSET_WRITE_MASK) {
     317                 :          1 :                 return ts->api->serialize_response(ts, THINGSET_ERR_UNAUTHORIZED,
     318                 :          1 :                                                    "Authentication required for %s", object->name);
     319                 :            :             }
     320                 :            :             else {
     321                 :          2 :                 return ts->api->serialize_response(ts, THINGSET_ERR_FORBIDDEN,
     322                 :          2 :                                                    "Item %s is read-only", object->name);
     323                 :            :             }
     324                 :            :         }
     325                 :            : 
     326                 :            :         /*
     327                 :            :          * Test format of simple data types (up to 64-bit) by deserializing the value into a dummy
     328                 :            :          * object of the same type. For string and byte buffers only the size of the buffers is
     329                 :            :          * checked.
     330                 :            :          */
     331                 :            :         uint8_t dummy_data[8];
     332         [ +  + ]:         16 :         uint8_t *data = object->type == THINGSET_TYPE_BYTES || object->type == THINGSET_TYPE_ARRAY
     333                 :          6 :                             ? object->data.u8
     334         [ +  + ]:         34 :                             : dummy_data;
     335                 :         18 :         struct thingset_data_object dummy_object = {
     336                 :         18 :             0, 0, "Dummy", { .u8 = data }, object->type, object->detail
     337                 :            :         };
     338                 :            : 
     339                 :         18 :         err = ts->api->deserialize_value(ts, &dummy_object, true);
     340         [ -  + ]:         18 :         if (err != 0) {
     341                 :          0 :             return ts->api->serialize_response(ts, -err, NULL);
     342                 :            :         }
     343                 :            :     }
     344                 :            : 
     345                 :         17 :     ts->api->deserialize_payload_reset(ts);
     346                 :         17 :     ts->api->deserialize_map_start(ts);
     347                 :            : 
     348         [ +  + ]:         17 :     if (ts->endpoint.object->data.group_callback != NULL) {
     349                 :          5 :         ts->endpoint.object->data.group_callback(THINGSET_CALLBACK_PRE_WRITE);
     350                 :            :     }
     351                 :            : 
     352                 :            :     /* actually write data */
     353                 :         35 :     while ((err = ts->api->deserialize_child(ts, &object))
     354         [ +  + ]:         35 :            != -THINGSET_ERR_DESERIALIZATION_FINISHED)
     355                 :            :     {
     356                 :         18 :         err = ts->api->deserialize_value(ts, object, false);
     357         [ -  + ]:         18 :         if (err != 0) {
     358                 :          0 :             return ts->api->serialize_response(ts, -err, NULL);
     359                 :            :         }
     360                 :            : 
     361         [ +  + ]:         18 :         if (ts->update_subsets & object->subsets) {
     362                 :          2 :             updated = true;
     363                 :            :         }
     364                 :            :     }
     365                 :            : 
     366         [ +  + ]:         17 :     if (ts->endpoint.object->data.group_callback != NULL) {
     367                 :          5 :         ts->endpoint.object->data.group_callback(THINGSET_CALLBACK_POST_WRITE);
     368                 :            :     }
     369                 :            : 
     370                 :            :     /*
     371                 :            :      * The update callback should be invoked after the group callback. This allows to use the group
     372                 :            :      * callback for data processing and the update callback for finally storing the data in NVM.
     373                 :            :      */
     374   [ +  +  +  + ]:         17 :     if (updated && ts->update_cb != NULL) {
     375                 :          1 :         ts->update_cb();
     376                 :            :     }
     377                 :            : 
     378                 :         17 :     return ts->api->serialize_response(ts, THINGSET_STATUS_CHANGED, NULL);
     379                 :            : }
     380                 :            : 
     381                 :         24 : int thingset_common_exec(struct thingset_context *ts)
     382                 :            : {
     383                 :            :     int err;
     384                 :            : 
     385                 :         24 :     err = ts->api->deserialize_list_start(ts);
     386         [ +  + ]:         24 :     if (err != 0) {
     387                 :          9 :         err = ts->api->deserialize_finish(ts);
     388         [ +  + ]:          9 :         if (err != 0) {
     389                 :          2 :             return ts->api->serialize_response(ts, THINGSET_ERR_BAD_REQUEST, "Invalid parameters");
     390                 :            :         }
     391                 :            :     }
     392                 :            : 
     393         [ +  + ]:         22 :     if ((ts->endpoint.object->access & THINGSET_WRITE_MASK)
     394         [ +  + ]:         20 :         && (ts->endpoint.object->type == THINGSET_TYPE_FN_VOID
     395         [ +  - ]:          3 :             || ts->endpoint.object->type == THINGSET_TYPE_FN_I32))
     396                 :            :     {
     397                 :            :         /* object is generally executable, but are we authorized? */
     398         [ +  + ]:         20 :         if ((ts->endpoint.object->access & THINGSET_WRITE_MASK & ts->auth_flags) == 0) {
     399                 :          4 :             return ts->api->serialize_response(ts, THINGSET_ERR_UNAUTHORIZED,
     400                 :            :                                                "Authentication required");
     401                 :            :         }
     402                 :            :     }
     403                 :            :     else {
     404                 :          2 :         return ts->api->serialize_response(ts, THINGSET_ERR_FORBIDDEN, "%s is not executable",
     405                 :          2 :                                            ts->endpoint.object->name);
     406                 :            :     }
     407                 :            : 
     408         [ +  + ]:        992 :     for (unsigned int i = 0; i < ts->num_objects; i++) {
     409         [ +  + ]:        980 :         if (ts->data_objects[i].parent_id == ts->endpoint.object->id) {
     410                 :         15 :             err = ts->api->deserialize_value(ts, &ts->data_objects[i], false);
     411         [ +  + ]:         15 :             if (err == -THINGSET_ERR_DESERIALIZATION_FINISHED) {
     412                 :            :                 /* more child objects found than parameters were passed */
     413                 :          2 :                 return ts->api->serialize_response(ts, THINGSET_ERR_BAD_REQUEST,
     414                 :            :                                                    "Not enough parameters");
     415                 :            :             }
     416         [ +  + ]:         13 :             else if (err != 0) {
     417                 :            :                 /* deserializing the value was not successful */
     418                 :          2 :                 return ts->api->serialize_response(ts, -err, NULL);
     419                 :            :             }
     420                 :            :         }
     421                 :            :     }
     422                 :            : 
     423                 :         12 :     err = ts->api->deserialize_finish(ts);
     424         [ +  + ]:         12 :     if (err != 0) {
     425                 :            :         /* more parameters passed than child objects found */
     426                 :          2 :         return ts->api->serialize_response(ts, THINGSET_ERR_BAD_REQUEST, "Too many parameters");
     427                 :            :     }
     428                 :            : 
     429                 :         10 :     ts->api->serialize_response(ts, THINGSET_STATUS_CHANGED, NULL);
     430                 :            : 
     431                 :            :     /* if we got here, finally create function pointer and call function */
     432         [ +  + ]:         10 :     if (ts->endpoint.object->type == THINGSET_TYPE_FN_I32) {
     433                 :          3 :         int32_t ret = ts->endpoint.object->data.i32_fn();
     434                 :          3 :         struct thingset_data_object ret_object = THINGSET_ITEM_INT32(0, 0, "", &ret, 0, 0);
     435                 :          3 :         err = ts->api->serialize_value(ts, &ret_object);
     436         [ -  + ]:          3 :         if (err != 0) {
     437                 :          0 :             return ts->api->serialize_response(ts, THINGSET_ERR_RESPONSE_TOO_LARGE, NULL);
     438                 :            :         }
     439                 :            :     }
     440                 :            :     else {
     441                 :          7 :         ts->endpoint.object->data.void_fn();
     442                 :            :     }
     443                 :            : 
     444                 :         10 :     return 0;
     445                 :            : }
     446                 :            : 
     447                 :         10 : int thingset_common_create_delete(struct thingset_context *ts, bool create)
     448                 :            : {
     449         [ +  + ]:         10 :     if (ts->endpoint.object->id == 0) {
     450                 :          1 :         return ts->api->serialize_response(ts, THINGSET_ERR_BAD_REQUEST, "Endpoint item required");
     451                 :            :     }
     452                 :            : 
     453         [ +  + ]:          9 :     if (ts->endpoint.object->type == THINGSET_TYPE_ARRAY) {
     454                 :          1 :         return ts->api->serialize_response(ts, THINGSET_ERR_NOT_IMPLEMENTED,
     455                 :            :                                            "Arrays not yet supported");
     456                 :            :     }
     457         [ +  + ]:          8 :     else if (ts->endpoint.object->type == THINGSET_TYPE_SUBSET) {
     458                 :            : #if CONFIG_THINGSET_IMMUTABLE_OBJECTS
     459                 :            :         return ts->api->serialize_response(ts, THINGSET_ERR_METHOD_NOT_ALLOWED,
     460                 :            :                                            "Subset is immutable");
     461                 :            : #else
     462                 :            :         const char *str_start;
     463                 :            :         size_t str_len;
     464                 :          7 :         int err = ts->api->deserialize_string(ts, &str_start, &str_len);
     465         [ +  + ]:          7 :         if (err != 0) {
     466                 :          2 :             return ts->api->serialize_response(ts, THINGSET_ERR_UNSUPPORTED_FORMAT, NULL);
     467                 :            :         }
     468                 :            : 
     469                 :            :         struct thingset_endpoint element;
     470                 :          5 :         int ret = thingset_endpoint_by_path(ts, &element, str_start, str_len);
     471   [ +  +  +  - ]:          5 :         if (ret >= 0 && element.index == THINGSET_ENDPOINT_INDEX_NONE) {
     472         [ +  + ]:          4 :             if (create) {
     473                 :          2 :                 element.object->subsets |= ts->endpoint.object->data.subset;
     474                 :          2 :                 return ts->api->serialize_response(ts, THINGSET_STATUS_CREATED, NULL);
     475                 :            :             }
     476                 :            :             else {
     477                 :          2 :                 element.object->subsets &= ~ts->endpoint.object->data.subset;
     478                 :          2 :                 return ts->api->serialize_response(ts, THINGSET_STATUS_DELETED, NULL);
     479                 :            :             }
     480                 :            :         }
     481                 :          1 :         return ts->api->serialize_response(ts, THINGSET_ERR_NOT_FOUND, NULL);
     482                 :            : #endif /* CONFIG_THINGSET_IMMUTABLE_OBJECTS */
     483                 :            :     }
     484                 :            : 
     485                 :          1 :     return ts->api->serialize_response(ts, THINGSET_ERR_METHOD_NOT_ALLOWED, NULL);
     486                 :            : }
     487                 :            : 
     488                 :          8 : int thingset_common_create(struct thingset_context *ts)
     489                 :            : {
     490                 :          8 :     return thingset_common_create_delete(ts, true);
     491                 :            : }
     492                 :            : 
     493                 :          2 : int thingset_common_delete(struct thingset_context *ts)
     494                 :            : {
     495                 :          2 :     return thingset_common_create_delete(ts, false);
     496                 :            : }

Generated by: LCOV version 1.14