| 
									
										
										
										
											2024-01-26 19:40:38 -06:00
										 |  |  | package models | 
					
						
							| 
									
										
										
										
											2024-01-26 20:00:03 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2024-01-27 16:07:27 -06:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2024-01-26 20:00:03 -06:00
										 |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"regexp" | 
					
						
							|  |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2024-01-27 16:07:27 -06:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2024-01-26 20:00:03 -06:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type Meta struct { | 
					
						
							|  |  |  | 	Key   string | 
					
						
							|  |  |  | 	Value any | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (m Meta) MarshalText() ([]byte, error) { | 
					
						
							|  |  |  | 	if regexp.MustCompile(`\s`).MatchString(m.Key) { | 
					
						
							| 
									
										
										
										
											2024-01-27 16:07:27 -06:00
										 |  |  | 		return []byte{}, fmt.Errorf("whitespace is not allowed in key: %s", m.Key) | 
					
						
							| 
									
										
										
										
											2024-01-26 20:00:03 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	buff := &bytes.Buffer{} | 
					
						
							|  |  |  | 	buff.WriteRune('@') | 
					
						
							|  |  |  | 	buff.WriteString(m.Key) | 
					
						
							|  |  |  | 	buff.WriteRune(' ') | 
					
						
							|  |  |  | 	switch v := m.Value.(type) { | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("Unknown type %T", v) | 
					
						
							|  |  |  | 	case nil: | 
					
						
							|  |  |  | 		return []byte{}, nil | 
					
						
							|  |  |  | 	case string: | 
					
						
							|  |  |  | 		buff.WriteString(v) | 
					
						
							|  |  |  | 	case int: | 
					
						
							|  |  |  | 		buff.WriteString(strconv.Itoa(v)) | 
					
						
							| 
									
										
										
										
											2024-01-30 23:40:47 -06:00
										 |  |  | 	case int64: | 
					
						
							|  |  |  | 		buff.WriteString(strconv.FormatInt(v, 10)) | 
					
						
							| 
									
										
										
										
											2024-01-26 20:00:03 -06:00
										 |  |  | 	case float64: | 
					
						
							|  |  |  | 		buff.WriteString(strconv.FormatFloat(v, 'f', -1, 64)) | 
					
						
							|  |  |  | 	case json.Number: | 
					
						
							|  |  |  | 		buff.WriteString(v.String()) | 
					
						
							|  |  |  | 	case json.RawMessage: | 
					
						
							|  |  |  | 		buff.Write(v) | 
					
						
							|  |  |  | 	case []byte: | 
					
						
							|  |  |  | 		buff.Write(v) | 
					
						
							|  |  |  | 	case byte: | 
					
						
							|  |  |  | 		buff.WriteByte(v) | 
					
						
							|  |  |  | 	case rune: | 
					
						
							|  |  |  | 		buff.WriteString(string(v)) | 
					
						
							|  |  |  | 	case bool: | 
					
						
							|  |  |  | 		buff.WriteString(strconv.FormatBool(v)) | 
					
						
							|  |  |  | 	case time.Time: | 
					
						
							|  |  |  | 		buff.WriteString(v.Format(time.RFC3339)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return buff.Bytes(), nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2024-01-27 16:07:27 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | func (m *Meta) UnmarshalText(in []byte) error { | 
					
						
							|  |  |  | 	if len(in) == 0 { | 
					
						
							| 
									
										
										
										
											2024-01-28 12:41:55 -06:00
										 |  |  | 		return newParsingError(errors.New("Unable to Unmarshal empty string")) | 
					
						
							| 
									
										
										
										
											2024-01-27 16:07:27 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	re := regexp.MustCompile("(?s)^@([^ ]+) (.*)( @end)?$") | 
					
						
							|  |  |  | 	match := re.FindSubmatch(in) | 
					
						
							|  |  |  | 	if len(match) == 0 { | 
					
						
							| 
									
										
										
										
											2024-01-28 12:41:55 -06:00
										 |  |  | 		return newParsingError(fmt.Errorf("Failed to match %s", in)) | 
					
						
							| 
									
										
										
										
											2024-01-27 16:07:27 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	m.Key = string(match[1]) | 
					
						
							|  |  |  | 	return m.processMeta(match[2]) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (m *Meta) processMeta(in []byte) error { | 
					
						
							|  |  |  | 	if len(in) == 0 { | 
					
						
							| 
									
										
										
										
											2024-01-28 12:41:55 -06:00
										 |  |  | 		return newParsingError(errors.New("No value found")) | 
					
						
							| 
									
										
										
										
											2024-01-27 16:07:27 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	s := strings.TrimSpace(string(in)) | 
					
						
							|  |  |  | 	if len(s) == 0 { | 
					
						
							| 
									
										
										
										
											2024-01-28 12:41:55 -06:00
										 |  |  | 		return newParsingError(errors.New("No value found")) | 
					
						
							| 
									
										
										
										
											2024-01-27 16:07:27 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	yesno := regexp.MustCompile("^(y|Y|yes|Yes|YES|n|N|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF)$") | 
					
						
							|  |  |  | 	yes := regexp.MustCompile("^(y|Y|yes|Yes|YES|true|True|TRUE|on|On|ON)$") | 
					
						
							|  |  |  | 	null := regexp.MustCompile("^(~|null|Null|NULL|none|None|NONE|nil|Nil|NIL)$") | 
					
						
							|  |  |  | 	var j json.RawMessage | 
					
						
							|  |  |  | 	if null.MatchString(s) { | 
					
						
							|  |  |  | 		m.Value = nil | 
					
						
							|  |  |  | 	} else if yesno.MatchString(s) { | 
					
						
							|  |  |  | 		if yes.MatchString(s) { | 
					
						
							|  |  |  | 			m.Value = true | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			m.Value = false | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else if i, err := strconv.Atoi(s); err == nil { | 
					
						
							|  |  |  | 		m.Value = i | 
					
						
							|  |  |  | 	} else if f, err := strconv.ParseFloat(s, 64); err == nil { | 
					
						
							|  |  |  | 		m.Value = f | 
					
						
							|  |  |  | 	} else if t, err := time.Parse(time.RFC3339, s); err == nil { | 
					
						
							|  |  |  | 		m.Value = t | 
					
						
							|  |  |  | 	} else if err := json.Unmarshal(in, &j); err == nil { | 
					
						
							|  |  |  | 		m.Value = j | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		m.Value = s | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } |