diff --git a/cwFlowTypes.h b/cwFlowTypes.h index 105d26d..e991fb9 100644 --- a/cwFlowTypes.h +++ b/cwFlowTypes.h @@ -106,6 +106,8 @@ namespace cw struct variable_str* src_var; // pointer to this input variables source link (or null if it uses the local_value) value_t* value; // pointer to the value associated with this variable + const list_t* value_list; // list of valid values for this variable or nullptr if not applicable + struct variable_str* var_link; // instance.varL list link struct variable_str* ch_link; // list of channels that share this variable (rooted on 'any' channel - in order by channel number) @@ -546,7 +548,6 @@ namespace cw // Disconnect an in_var from it's source void var_disconnect( variable_t* in_var ); - // Get the count of 'mult' vars associated with this var label. unsigned var_mult_count( proc_t* proc, const char* var_label ); diff --git a/cwFlowValue.cpp b/cwFlowValue.cpp index 51352e1..3312b6e 100644 --- a/cwFlowValue.cpp +++ b/cwFlowValue.cpp @@ -1814,6 +1814,215 @@ cw::rc_t cw::flow::recd_array_destroy( recd_array_t*& recd_array_ref ) return kOkRC; } +//------------------------------------------------------------------------------------------------------------------------ +// +// List +// + +namespace cw { + namespace flow { + rc_t _list_destroy( list_t*& list_ref ) + { + if( list_ref != nullptr ) + { + for(unsigned i=0; ieleN; ++i) + { + mem::release(list_ref->eleA[i].label); + value_release(&list_ref->eleA[i].value ); + } + mem::release(list_ref); + } + return kOkRC; + } + } +} + +cw::rc_t cw::flow::list_create( list_t*& list_ref, const object_t* cfg ) +{ + rc_t rc = kOkRC; + + bool labelOnlyListFl = false; + + if( !cfg->is_list() && !cfg->is_dict() ) + { + rc = cwLogError(kInvalidDataTypeRC,"The cfg. given to a flow list is not a JSON list or dictionary."); + goto errLabel; + } + + if( cfg->child_count() == 0 ) + cwLogWarning("The cfg. list used to form a flow list is empty."); + + if((rc = list_create(list_ref, cfg->child_count())) != kOkRC ) + goto errLabel; + + if( list_ref->eleAllocN == 0 ) + goto errLabel; + + if( cfg->is_list() ) + labelOnlyListFl = true; + + for(unsigned i=0; ieleAllocN; ++i) + { + const object_t* ele = cfg->child_ele(i); + const char* label = nullptr; + value_t value; + + // if this is a label-only list ... + if( labelOnlyListFl ) + { + // this is a label-only list and so all elements must be strings + if( ele->is_string()==false ) + { + rc = cwLogError(kSyntaxErrorRC,"The list element at index '%i' is not a string.",i); + goto errLabel; + } + + // get the element label + if( ele->value(label) != kOkRC ) + { + rc = cwLogError(kSyntaxErrorRC,"Could not parse the list element at index '%i'.",i); + goto errLabel; + } + + // ... then the value is the list element index + value_set(&value,i); + + } + else // ... otherwise this is (label,value) dictioanry + { + // verify that the list element is a (label,element) pair. + if( !ele->is_pair() ) + { + rc = cwLogError(kSyntaxErrorRC,"The list dictionary element at index '%i' is not a (label,value) pair.",i); + goto errLabel; + } + + // validate the dictionary label + if( ele->pair_label() == nullptr ) + { + rc = cwLogError(kSyntaxErrorRC,"The list dictionary element is missing it's label at index '%i'.",i); + goto errLabel; + } + + // convert the dict value to a flow value + if((rc = value_from_cfg(ele->pair_value(),value)) != kOkRC ) + { + rc = cwLogError(rc,"Unable to parse the dict. element value field for '%s' at index '%i'.",ele->pair_label(),i); + goto errLabel; + } + + // if the conversion did not result in a numeric or string data type + if( cwIsFlag(value.tflag,kCfgTFl) ) + { + rc = cwLogError(rc,"List element value field at index '%i' is not a numeric or string type.",i); + goto errLabel; + } + + label = ele->pair_label(); + } + + // add the element to the list + if((rc = list_append( list_ref, label, value )) != kOkRC ) + { + rc = cwLogError(rc,"List append failed at index '%i'.",i); + goto errLabel; + } + } + + + errLabel: + if( rc != kOkRC ) + _list_destroy(list_ref); + + return rc; +} + +cw::rc_t cw::flow::list_create( list_t*& list_ref, unsigned count ) +{ + rc_t rc = kOkRC; + if((rc = list_destroy(list_ref)) != kOkRC ) + return rc; + + + list_ref = mem::allocZ< list_t >(); + list_ref->eleA = mem::allocZ< list_ele_t >( count ); + list_ref->eleAllocN = count; + list_ref->eleN = 0; + + return rc; +} + +cw::rc_t cw::flow::list_destroy( list_t*& list_ref ) +{ + return _list_destroy(list_ref); +} + +cw::rc_t cw::flow::list_append( list_t* list, const char* label, const value_t& value ) +{ + rc_t rc = kOkRC; + + if( textLength(label) == 0 ) + { + rc = cwLogError(kInvalidArgRC,"List elements must have a valid label."); + goto errLabel; + } + + if( list->eleN >= list->eleAllocN ) + { + rc = cwLogError(kBufTooSmallRC,"Cannot append '%s' to a full list.",label); + goto errLabel; + } + + if( list->eleN == 0 ) + list->eleA[0].value.tflag = value.tflag; + else + list->eleA[list->eleN].value.tflag = list->eleA[0].value.tflag; + + if((rc = value_from_value( value, list->eleA[list->eleN].value )) != kOkRC ) + { + rc = cwLogError(rc,"Value conversion failed. All value types must be convertiable to type of the first list value."); + goto errLabel; + } + + list->eleA[list->eleN].label = mem::duplStr(label); + + list->eleN += 1; + +errLabel: + if( rc != kOkRC ) + rc = cwLogError(rc,"List append failed."); + + return rc; + +} + + +const char* cw::flow::list_ele_label( const list_t* list, unsigned index ) +{ + assert( list != nullptr ); + + if( index >= list->eleN ) + { + cwLogError(kInvalidArgRC,"The list index '%i' is invalid for a list of length '%i'.",index,list->eleN); + return nullptr; + } + + return list->eleA[ index ].label; +} + +unsigned cw::flow::list_ele_index( const list_t* list, const char* label ) +{ + assert( list != nullptr ); + + for(unsigned i=0; ieleN; ++i) + if( textIsEqual(list->eleA[i].label,label) ) + return i; + + return kInvalidIdx; +} + + +//------------------------------------------------------------------------------------------------------------------------ cw::rc_t cw::flow::value_test( const test::test_args_t& args ) { rc_t rc = kOkRC; diff --git a/cwFlowValue.h b/cwFlowValue.h index 6374346..42d105f 100644 --- a/cwFlowValue.h +++ b/cwFlowValue.h @@ -423,7 +423,82 @@ namespace cw // The source and destination record types should be the same, but this // function does very little to verify that the actually are. rc_t recd_copy( const recd_type_t* src_recd_type, const recd_t* src_recdA, unsigned src_recdN, recd_array_t* dest_recd_array ); + + + //------------------------------------------------------------------------------------------------------------------------ + // + // List + // + + typedef struct list_ele_str + { + char* label; + value_t value; + } list_ele_t; + typedef struct list_str + { + unsigned tflag; // all elements of the list share the same value type. + list_ele_t* eleA; + unsigned eleAllocN; + unsigned eleN; + + } list_t; + + + // Cfg: [ , ... ] (value is the same as the element index) + // or + // [ (, ... (,) ] + rc_t list_create( list_t*& list_ref, const object_t* cfg ); + rc_t list_create( list_t*& list_ref, unsigned count ); + + rc_t list_destroy( list_t*& list_ref ); + + rc_t list_append( list_t* list, const char* label, const value_t& value ); + + + + template< typename T > + rc_t list_append( list_t* list, const char* label, const T& v ) + { + rc_t rc; + value_t value; + value.tflag = kInvalidTFl; + if((rc = value_set(&value,v)) != kOkRC ) + goto errLabel; + + if((rc = list_append(list,label,value)) != kOkRC ) + goto errLabel; + + errLabel: + return rc; + } + + const char* list_ele_label( const list_t* list, unsigned index ); + unsigned list_ele_index( const list_t* list, const char* label ); + + template< typename T > + rc_t list_ele_value( const list_t* list, unsigned index, T& v ) + { + rc_t rc = kOkRC; + if( index >= list->eleN ) + { + rc = cwLogError(rc,"The list element index '%i' is invalid on a list of length '%i'.",index,list->eleN); + goto errLabel; + } + + if((rc = value_get(&list->eleA[index].value,v)) != kOkRC ) + { + rc = cwLogError(rc,"Read of list element value at index '%i' failed.",index,list->eleN); + goto errLabel; + } + + errLabel: + return rc; + } + + + //------------------------------------------------------------------------------------------------------------------------ rc_t value_test( const test::test_args_t& args );