github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/lang/define_index_objects.go (about)

     1  package lang
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/lmorg/murex/lang/types"
    10  )
    11  
    12  // IndexTemplateObject is a handy standard indexer you can use in your custom data types for structured object types.
    13  // The point of this is to minimize code rewriting and standardising the behavior of the indexer.
    14  func IndexTemplateObject(p *Process, params []string, object *interface{}, marshaller func(interface{}) ([]byte, error)) error {
    15  	if p.IsNot {
    16  		return itoNot(p, params, object, marshaller)
    17  	}
    18  	return itoIndex(p, params, object, marshaller)
    19  }
    20  
    21  // itoIndex allow
    22  func itoIndex(p *Process, params []string, object *interface{}, marshaller func(interface{}) ([]byte, error)) error {
    23  	var objArray []interface{}
    24  	switch v := (*object).(type) {
    25  	case []interface{}:
    26  		for _, key := range params {
    27  			i, err := strconv.Atoi(key)
    28  			if err != nil {
    29  				return err
    30  			}
    31  			if i < 0 {
    32  				//i = len(v) + i
    33  				i += len(v)
    34  			}
    35  			if i >= len(v) {
    36  				return errors.New("key '" + key + "' greater than number of items in array")
    37  			}
    38  
    39  			if len(params) > 1 {
    40  				objArray = append(objArray, v[i])
    41  
    42  			} else {
    43  				switch v[i].(type) {
    44  				case nil:
    45  					p.Stdout.SetDataType(types.Null)
    46  				case bool:
    47  					p.Stdout.SetDataType(types.Boolean)
    48  					if v[i].(bool) {
    49  						p.Stdout.Write(types.TrueByte)
    50  					} else {
    51  						p.Stdout.Write(types.FalseByte)
    52  					}
    53  				case int:
    54  					p.Stdout.SetDataType(types.Integer)
    55  					s := strconv.Itoa(v[i].(int))
    56  					p.Stdout.Write([]byte(s))
    57  				case float64:
    58  					p.Stdout.SetDataType(types.Number)
    59  					s := types.FloatToString(v[i].(float64))
    60  					p.Stdout.Write([]byte(s))
    61  				case string:
    62  					p.Stdout.SetDataType(types.String)
    63  					p.Stdout.Write([]byte(v[i].(string)))
    64  				default:
    65  					b, err := marshaller(v[i])
    66  					if err != nil {
    67  						return err
    68  					}
    69  					p.Stdout.Writeln(b)
    70  				}
    71  			}
    72  		}
    73  		if len(objArray) > 0 {
    74  			b, err := marshaller(objArray)
    75  			if err != nil {
    76  				return err
    77  			}
    78  			p.Stdout.Writeln(b)
    79  		}
    80  		return nil
    81  
    82  	case map[string]interface{}:
    83  		var (
    84  			obj interface{}
    85  			err error
    86  		)
    87  
    88  		for i := range params {
    89  			if len(params[i]) > 2 && params[i][0] == '[' && params[i][len(params[i])-1] == ']' {
    90  				obj, err = ElementLookup(v, params[i][1:len(params[i])-1])
    91  				if err != nil {
    92  					return err
    93  				}
    94  
    95  			} else {
    96  
    97  				switch {
    98  				case v[params[i]] != nil:
    99  					obj = v[params[i]]
   100  				case v[strings.Title(params[i])] != nil:
   101  					obj = v[strings.Title(params[i])]
   102  				case v[strings.ToLower(params[i])] != nil:
   103  					obj = v[strings.ToLower(params[i])]
   104  				case v[strings.ToUpper(params[i])] != nil:
   105  					obj = v[strings.ToUpper(params[i])]
   106  				default:
   107  					return errors.New("key '" + params[i] + "' not found")
   108  				}
   109  			}
   110  
   111  			if len(params) > 1 {
   112  				objArray = append(objArray, obj)
   113  
   114  			} else {
   115  				switch obj := obj.(type) {
   116  				case nil:
   117  					p.Stdout.SetDataType(types.Null)
   118  				case bool:
   119  					p.Stdout.SetDataType(types.Boolean)
   120  					if obj {
   121  						p.Stdout.Write(types.TrueByte)
   122  					} else {
   123  						p.Stdout.Write(types.FalseByte)
   124  					}
   125  				case int:
   126  					p.Stdout.SetDataType(types.Integer)
   127  					s := strconv.Itoa(obj)
   128  					p.Stdout.Write([]byte(s))
   129  				case float64:
   130  					p.Stdout.SetDataType(types.Number)
   131  					s := types.FloatToString(obj)
   132  					p.Stdout.Write([]byte(s))
   133  				case string:
   134  					p.Stdout.SetDataType(types.String)
   135  					p.Stdout.Write([]byte(obj))
   136  				default:
   137  					b, err := marshaller(obj)
   138  					if err != nil {
   139  						return err
   140  					}
   141  					p.Stdout.Writeln(b)
   142  				}
   143  			}
   144  		}
   145  		if len(objArray) > 0 {
   146  			b, err := marshaller(objArray)
   147  			if err != nil {
   148  				return err
   149  			}
   150  			p.Stdout.Writeln(b)
   151  		}
   152  		return nil
   153  
   154  	case map[interface{}]interface{}:
   155  		for i := range params {
   156  			//if v[key] == nil {
   157  			//	return errors.New("key '" + key + "' not found.")
   158  			//}
   159  			switch {
   160  			case v[params[i]] != nil:
   161  			case v[strings.Title(params[i])] != nil:
   162  				params[i] = strings.Title(params[i])
   163  			case v[strings.ToLower(params[i])] != nil:
   164  				params[i] = strings.ToLower(params[i])
   165  			case v[strings.ToUpper(params[i])] != nil:
   166  				params[i] = strings.ToUpper(params[i])
   167  			//case v[strings.ToTitle(params[i])] != nil:
   168  			//	params[i] = strings.ToTitle(params[i])
   169  			default:
   170  				return errors.New("key '" + params[i] + "' not found")
   171  			}
   172  
   173  			if len(params) > 1 {
   174  				objArray = append(objArray, v[params[i]])
   175  
   176  			} else {
   177  				switch v[params[i]].(type) {
   178  				case nil:
   179  					p.Stdout.SetDataType(types.Null)
   180  				case bool:
   181  					p.Stdout.SetDataType(types.Boolean)
   182  					if v[params[i]].(bool) {
   183  						p.Stdout.Write(types.TrueByte)
   184  					} else {
   185  						p.Stdout.Write(types.FalseByte)
   186  					}
   187  				case int:
   188  					p.Stdout.SetDataType(types.Integer)
   189  					s := strconv.Itoa(v[params[i]].(int))
   190  					p.Stdout.Write([]byte(s))
   191  				case float64:
   192  					p.Stdout.SetDataType(types.Number)
   193  					s := types.FloatToString(v[params[i]].(float64))
   194  					p.Stdout.Write([]byte(s))
   195  				case string:
   196  					p.Stdout.SetDataType(types.String)
   197  					p.Stdout.Write([]byte(v[params[i]].(string)))
   198  				default:
   199  					b, err := marshaller(v[params[i]])
   200  					if err != nil {
   201  						return err
   202  					}
   203  					p.Stdout.Writeln(b)
   204  				}
   205  			}
   206  		}
   207  		if len(objArray) > 0 {
   208  			b, err := marshaller(objArray)
   209  			if err != nil {
   210  				return err
   211  			}
   212  			p.Stdout.Writeln(b)
   213  		}
   214  		return nil
   215  
   216  	default:
   217  		return errors.New("object cannot be indexed")
   218  	}
   219  }
   220  
   221  // itoNot requires the indexes to be explicit
   222  func itoNot(p *Process, params []string, object *interface{}, marshaller func(interface{}) ([]byte, error)) error {
   223  	switch v := (*object).(type) {
   224  	case []interface{}:
   225  		var objArray []interface{}
   226  		not := make(map[int]bool)
   227  		for _, key := range params {
   228  			i, err := strconv.Atoi(key)
   229  			if err != nil {
   230  				return err
   231  			}
   232  			if i < 0 {
   233  				return errors.New("cannot have negative keys in array")
   234  			}
   235  			if i >= len(v) {
   236  				return errors.New("Key '" + key + "' greater than number of items in array")
   237  			}
   238  
   239  			not[i] = true
   240  		}
   241  
   242  		for i := range v {
   243  			if !not[i] {
   244  				objArray = append(objArray, v[i])
   245  			}
   246  		}
   247  
   248  		//if len(objArray) > 0 {
   249  		b, err := marshaller(objArray)
   250  		if err != nil {
   251  			return err
   252  		}
   253  		_, err = p.Stdout.Writeln(b)
   254  		//}
   255  		return err
   256  
   257  	case map[string]interface{}:
   258  		objMap := make(map[string]interface{})
   259  		not := make(map[string]bool)
   260  		for _, key := range params {
   261  			not[key] = true
   262  			not[strings.Title(key)] = true
   263  			not[strings.ToLower(key)] = true
   264  			not[strings.ToUpper(key)] = true
   265  			//not[strings.ToTitle(key)] = true
   266  		}
   267  
   268  		for s := range v {
   269  			if !not[s] {
   270  				objMap[s] = v[s]
   271  			}
   272  		}
   273  
   274  		//if len(objMap) > 0 {
   275  		b, err := marshaller(objMap)
   276  		if err != nil {
   277  			return err
   278  		}
   279  		p.Stdout.Writeln(b)
   280  		//}
   281  		return nil
   282  
   283  	case map[interface{}]interface{}:
   284  		objMap := make(map[interface{}]interface{})
   285  		not := make(map[string]bool)
   286  		for _, key := range params {
   287  			not[key] = true
   288  			not[strings.Title(key)] = true
   289  			not[strings.ToLower(key)] = true
   290  			not[strings.ToUpper(key)] = true
   291  			//not[strings.ToTitle(key)] = true
   292  		}
   293  
   294  		for iface := range v {
   295  			s := fmt.Sprint(iface)
   296  			if !not[s] {
   297  				objMap[iface] = v[iface]
   298  			}
   299  		}
   300  
   301  		//if len(objMap) > 0 {
   302  		b, err := marshaller(objMap)
   303  		if err != nil {
   304  			return err
   305  		}
   306  		_, err = p.Stdout.Writeln(b)
   307  		//}
   308  		return err
   309  
   310  	default:
   311  		return errors.New("object cannot be !indexed")
   312  	}
   313  }