git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/toml/internal/tag/rm.go (about)

     1  package tag
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"time"
     7  
     8  	"git.sr.ht/~pingoo/stdx/toml/internal"
     9  )
    10  
    11  // Remove JSON tags to a data structure as returned by toml-test.
    12  func Remove(typedJson interface{}) (interface{}, error) {
    13  	// Switch on the data type.
    14  	switch v := typedJson.(type) {
    15  
    16  	// Object: this can either be a TOML table or a primitive with tags.
    17  	case map[string]interface{}:
    18  		// This value represents a primitive: remove the tags and return just
    19  		// the primitive value.
    20  		if len(v) == 2 && in("type", v) && in("value", v) {
    21  			ut, err := untag(v)
    22  			if err != nil {
    23  				return ut, fmt.Errorf("tag.Remove: %w", err)
    24  			}
    25  			return ut, nil
    26  		}
    27  
    28  		// Table: remove tags on all children.
    29  		m := make(map[string]interface{}, len(v))
    30  		for k, v2 := range v {
    31  			var err error
    32  			m[k], err = Remove(v2)
    33  			if err != nil {
    34  				return nil, err
    35  			}
    36  		}
    37  		return m, nil
    38  
    39  	// Array: remove tags from all items.
    40  	case []interface{}:
    41  		a := make([]interface{}, len(v))
    42  		for i := range v {
    43  			var err error
    44  			a[i], err = Remove(v[i])
    45  			if err != nil {
    46  				return nil, err
    47  			}
    48  		}
    49  		return a, nil
    50  	}
    51  
    52  	// The top level must be an object or array.
    53  	return nil, fmt.Errorf("tag.Remove: unrecognized JSON format '%T'", typedJson)
    54  }
    55  
    56  // Check if key is in the table m.
    57  func in(key string, m map[string]interface{}) bool {
    58  	_, ok := m[key]
    59  	return ok
    60  }
    61  
    62  // Return a primitive: read the "type" and convert the "value" to that.
    63  func untag(typed map[string]interface{}) (interface{}, error) {
    64  	t := typed["type"].(string)
    65  	v := typed["value"].(string)
    66  	switch t {
    67  	case "string":
    68  		return v, nil
    69  	case "integer":
    70  		n, err := strconv.ParseInt(v, 10, 64)
    71  		if err != nil {
    72  			return nil, fmt.Errorf("untag: %w", err)
    73  		}
    74  		return n, nil
    75  	case "float":
    76  		f, err := strconv.ParseFloat(v, 64)
    77  		if err != nil {
    78  			return nil, fmt.Errorf("untag: %w", err)
    79  		}
    80  		return f, nil
    81  	case "datetime":
    82  		return parseTime(v, "2006-01-02T15:04:05.999999999Z07:00", nil)
    83  	case "datetime-local":
    84  		return parseTime(v, "2006-01-02T15:04:05.999999999", internal.LocalDatetime)
    85  	case "date-local":
    86  		return parseTime(v, "2006-01-02", internal.LocalDate)
    87  	case "time-local":
    88  		return parseTime(v, "15:04:05.999999999", internal.LocalTime)
    89  	case "bool":
    90  		switch v {
    91  		case "true":
    92  			return true, nil
    93  		case "false":
    94  			return false, nil
    95  		}
    96  		return nil, fmt.Errorf("untag: could not parse %q as a boolean", v)
    97  	}
    98  
    99  	return nil, fmt.Errorf("untag: unrecognized tag type %q", t)
   100  }
   101  
   102  func parseTime(v, format string, l *time.Location) (time.Time, error) {
   103  	t, err := time.Parse(format, v)
   104  	if err != nil {
   105  		return time.Time{}, fmt.Errorf("could not parse %q as a datetime: %w", v, err)
   106  	}
   107  	if l != nil {
   108  		t = t.In(l)
   109  	}
   110  	return t, nil
   111  }