1//////////////////////////////////////////////////////////////////////
  2// LibFile: structs.scad
  3//   This file provides manipulation of "structs".  A "struct" is a data structure that
  4//   associates arbitrary keys with values and allows you to get and set values
  5//   by key.  
  6// Includes:
  7//   include <BOSL2/std.scad>
  8//   include <BOSL2/structs.scad>
  9// FileGroup: Data Management
 10// FileSummary: Structure/Dictionary Manipulation
 11//////////////////////////////////////////////////////////////////////
 12
 13
 14// Section: struct operations
 15//
 16// A struct is a data structure that associates arbitrary keys (of any type) with values (of any type).
 17// Structures are implemented as lists of [key, value] pairs.
 18//
 19// An empty list `[]` is an empty structure and can be used wherever a structure input is required.
 20
 21// Function: struct_set()
 22// Usage:
 23//   struct_set(struct, key, value, [grow=])
 24//   struct_set(struct, [key1, value1, key2, value2, ...], [grow=])
 25// Description:
 26//   Sets the key(s) in the structure to the specified value(s), returning a new updated structure.  If a key
 27//   exists its value is changed, otherwise the key is added to the structure.  If grow is set to false then
 28//   it is an error to set a key not already defined in the structure.  If you specify the same key twice
 29//   that is also an error.  Note that key order will change when you change a key's value.   
 30// Arguments:
 31//   struct = input structure.
 32//   key = key to set or list of key,value pairs to set
 33//   value = value to set the key to (when giving a single key and value)
 34//   ---
 35//   grow = Set to true to allow structure to grow, or false for new keys to generate an error.  Default: true
 36function struct_set(struct, key, value, grow=true) =
 37  is_def(value) ? struct_set(struct,[key,value],grow=grow)
 38  :
 39  assert(is_list(key) && len(key)%2==0, "[key,value] pair list is not a list or has an odd length") 
 40  let(
 41      new_entries = [for(i=[0:1:len(key)/2-1]) [key[2*i], key[2*i+1]]],
 42      newkeys = column(new_entries,0),
 43      indlist = search(newkeys, struct,0,0),
 44      badkeys = grow ? (search([undef],new_entries,1,0)[0] != [] ? [undef] : [])
 45                     : [for(i=idx(indlist)) if (is_undef(newkeys[i]) || len(indlist[i])==0) newkeys[i]],
 46      ind = flatten(indlist),
 47      dupfind = search(newkeys, new_entries,0,0),
 48      dupkeys = [for(i=idx(dupfind)) if (len(dupfind[i])>1) newkeys[i]]
 49  )
 50  assert(badkeys==[], str("Unknown or bad key ",_format_key(badkeys[0])," in struct_set"))
 51  assert(dupkeys==[], str("Duplicate key ",_format_key(dupkeys[0])," for struct"))
 52  concat(list_remove(struct,ind), new_entries);
 53
 54function _format_key(key) = is_string(key) ? str("\"",key,"\""): key;
 55
 56// Function: struct_remove()
 57// Usage:
 58//   struct_remove(struct, key)
 59// Description:
 60//   Remove key or list of keys from a structure.  If you want to remove a single key which is a list
 61//   you must pass it as a singleton list, or struct_remove will attempt to remove the listed items as keys.
 62//   If you list the same item multiple times for removal it will be removed without error.  
 63// Arguments:
 64//   struct = input structure
 65//   key = a single key or list of keys to remove.  
 66function struct_remove(struct, key) =
 67    !is_list(key) ? struct_remove(struct, [key]) :
 68    let(ind = search(key, struct))
 69    list_remove(struct, ind);
 70
 71
 72// Function: struct_val()
 73// Usage:
 74//   struct_val(struct, key, default)
 75// Description:
 76//   Returns the value for the specified key in the structure, or default value if the key is not present
 77// Arguments:
 78//   struct = input structure
 79//   key = key whose value to return
 80//   default = default value to return if key is not present.  Default: undef
 81function struct_val(struct, key, default=undef) =
 82    assert(is_def(key),"key is missing")
 83    let(ind = search([key],struct)[0])
 84    ind == [] ? default : struct[ind][1];
 85
 86
 87// Function: struct_keys()
 88// Usage:
 89//   keys = struct_keys(struct)
 90// Description:
 91//   Returns a list of the keys in a structure
 92// Arguments:
 93//   struct = input structure
 94function struct_keys(struct) = column(struct,0);
 95
 96
 97// Function&Module: echo_struct()
 98// Usage:
 99//   echo_struct(struct, [name])
100// Description:
101//   Displays a list of structure keys and values, one pair per line, for easier reading.
102// Arguments:
103//   struct = input structure
104//   name = optional structure name to list at the top of the output.  Default: ""
105function echo_struct(struct,name="") =
106    let( keylist = [for(entry=struct) str("  ",entry[0],": ",entry[1],"\n")])
107    echo(str("\nStructure ",name,"\n",str_join(keylist)))
108    undef;
109
110module echo_struct(struct,name="") {
111    no_children($children);
112    dummy = echo_struct(struct,name);
113}
114
115
116// Function: is_struct()
117// Usage:
118//   is_struct(struct)
119// Description:
120//   Returns true if the input is a list of pairs, false otherwise.
121function is_struct(x) =
122    is_list(x) && [for (xx=x) if(!(is_list(xx) && len(xx)==2)) 1] == [];
123
124
125// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap