🔀 Merge branch 'feature/types' into develop
This commit is contained in:
		
				commit
				
					
						8d238d0d76
					
				
			
		
					 8 changed files with 127 additions and 59 deletions
				
			
		|  | @ -42,18 +42,17 @@ var dropCmd = &cobra.Command{ | ||||||
| 	RunE: func(cmd *cobra.Command, args []string) error { | 	RunE: func(cmd *cobra.Command, args []string) error { | ||||||
| 		log := args[0] | 		log := args[0] | ||||||
| 		title := args[1] | 		title := args[1] | ||||||
| 		e := models.PartialEntry() | 		ms := models.Metas{} | ||||||
| 		if len(j.RawMessage) > 8 { | 		if len(j.RawMessage) > 8 { | ||||||
| 			err := json.Unmarshal([]byte(j.RawMessage), &e) | 			err := json.Unmarshal([]byte(j.RawMessage), &ms) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		for k, v := range fields { | 		for k, v := range fields { | ||||||
| 			e.Fields = append(e.Fields, models.Meta{k, tools.ParseString(v)}) | 			ms = append(ms, models.Meta{k, tools.ParseString(v)}) | ||||||
| 		} | 		} | ||||||
| 		e.Title = title | 		e := models.Entry{title, d.t, ms} | ||||||
| 		e.Date = d.t |  | ||||||
| 		l := models.Log{log, []models.Entry{e}} | 		l := models.Log{log, []models.Entry{e}} | ||||||
| 		err := files.Append(l) | 		err := files.Append(l) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
|  |  | ||||||
|  | @ -16,14 +16,9 @@ import ( | ||||||
| const DateFormat = tools.DateFormat | const DateFormat = tools.DateFormat | ||||||
| 
 | 
 | ||||||
| type Entry struct { | type Entry struct { | ||||||
| 	Title       string | 	Title  string | ||||||
| 	Date        time.Time | 	Date   time.Time | ||||||
| 	Fields      []Meta | 	Fields Metas | ||||||
| 	skipMissing bool |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func PartialEntry() Entry { |  | ||||||
| 	return Entry{skipMissing: true} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type metaRes struct { | type metaRes struct { | ||||||
|  | @ -42,9 +37,9 @@ func (e Entry) getFieldMarshalChan() chan metaRes { | ||||||
| 			defer wg.Done() | 			defer wg.Done() | ||||||
| 			if m.Key == "json" { | 			if m.Key == "json" { | ||||||
| 				if j, ok := m.Value.(json.RawMessage); ok { | 				if j, ok := m.Value.(json.RawMessage); ok { | ||||||
| 					sub := Entry{skipMissing: true} | 					sub := Metas{} | ||||||
| 					json.Unmarshal(j, &sub) | 					json.Unmarshal(j, &sub) | ||||||
| 					for _, subM := range sub.Fields { | 					for _, subM := range sub { | ||||||
| 						o, er := subM.MarshalText() | 						o, er := subM.MarshalText() | ||||||
| 						ch <- metaRes{o, er} | 						ch <- metaRes{o, er} | ||||||
| 					} | 					} | ||||||
|  | @ -160,9 +155,9 @@ func (e *Entry) getFieldUnarshalChan(in []byte) chan Meta { | ||||||
| 			if err == nil { | 			if err == nil { | ||||||
| 				if m.Key == "json" { | 				if m.Key == "json" { | ||||||
| 					if j, ok := m.Value.(json.RawMessage); ok { | 					if j, ok := m.Value.(json.RawMessage); ok { | ||||||
| 						sub := Entry{skipMissing: true} | 						ms := Metas{} | ||||||
| 						json.Unmarshal(j, &sub) | 						json.Unmarshal(j, &ms) | ||||||
| 						for _, subM := range sub.Fields { | 						for _, subM := range ms { | ||||||
| 							ch <- subM | 							ch <- subM | ||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
|  | @ -191,26 +186,8 @@ func (e Entry) MarshalJSON() ([]byte, error) { | ||||||
| 	out := map[string]any{} | 	out := map[string]any{} | ||||||
| 	out["title"] = e.Title | 	out["title"] = e.Title | ||||||
| 	out["date"] = e.Date.Format(time.RFC3339) | 	out["date"] = e.Date.Format(time.RFC3339) | ||||||
| 	for _, f := range e.Fields { | 	for k, v := range e.Fields.toMap() { | ||||||
| 		if _, ok := out[f.Key]; !ok { | 		out[k] = v | ||||||
| 			if f.Key == "json" { |  | ||||||
| 				ob := map[string]any{} |  | ||||||
| 				if j, ok := f.Value.(json.RawMessage); ok { |  | ||||||
| 					json.Unmarshal(j, &ob) |  | ||||||
| 				} |  | ||||||
| 				// If we couldn't get valid data from there, this will just be empty |  | ||||||
| 				for k, v := range ob { |  | ||||||
| 					if k != "title" && k != "date" { |  | ||||||
| 						out[k] = v |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} else { |  | ||||||
| 				out[f.Key] = f.Value |  | ||||||
| 				if vt, ok := f.Value.(time.Time); ok { |  | ||||||
| 					out[f.Key] = vt.Format(time.RFC3339) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 	return json.Marshal(out) | 	return json.Marshal(out) | ||||||
| } | } | ||||||
|  | @ -259,16 +236,16 @@ func (e *Entry) UnmarshalJSON(in []byte) error { | ||||||
| 		return newParsingError(err) | 		return newParsingError(err) | ||||||
| 	} | 	} | ||||||
| 	title, ok := out["title"].(string) | 	title, ok := out["title"].(string) | ||||||
| 	if (!ok || title == "") && !e.skipMissing { | 	if !ok || title == "" { | ||||||
| 		return ErrorMissingTitle | 		return ErrorMissingTitle | ||||||
| 	} | 	} | ||||||
| 	e.Title = title | 	e.Title = title | ||||||
| 	dates, ok := out["date"].(string) | 	dates, ok := out["date"].(string) | ||||||
| 	if (!ok || dates == "") && !e.skipMissing { | 	if !ok || dates == "" { | ||||||
| 		return ErrorMissingDate | 		return ErrorMissingDate | ||||||
| 	} | 	} | ||||||
| 	date, err := tools.ParseDate(dates) | 	date, err := tools.ParseDate(dates) | ||||||
| 	if err != nil && !e.skipMissing { | 	if err != nil { | ||||||
| 		return newParsingError(err) | 		return newParsingError(err) | ||||||
| 	} | 	} | ||||||
| 	e.Date = date | 	e.Date = date | ||||||
|  |  | ||||||
|  | @ -9,7 +9,6 @@ import ( | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| 	"github.com/stretchr/testify/require" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Type assertions | // Type assertions | ||||||
|  | @ -18,19 +17,6 @@ var _ encoding.TextUnmarshaler = new(Entry) | ||||||
| var _ json.Marshaler = Entry{} | var _ json.Marshaler = Entry{} | ||||||
| var _ json.Unmarshaler = new(Entry) | var _ json.Unmarshaler = new(Entry) | ||||||
| 
 | 
 | ||||||
| func TestPartialEntry(t *testing.T) { |  | ||||||
| 	e := PartialEntry() |  | ||||||
| 	assert.True(t, e.skipMissing) |  | ||||||
| 	err := json.Unmarshal([]byte(`{"a":42}`), &e) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.Equal(t, "", e.Title) |  | ||||||
| 	assert.Equal(t, time.Time{}, e.Date) |  | ||||||
| 	require.Len(t, e.Fields, 1) |  | ||||||
| 	f := e.Fields[0] |  | ||||||
| 	assert.Equal(t, "a", f.Key) |  | ||||||
| 	assert.Equal(t, int64(42), f.Value) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func TestEntryMarshal(t *testing.T) { | func TestEntryMarshal(t *testing.T) { | ||||||
| 	when := time.Now() | 	when := time.Now() | ||||||
| 	whens := when.Format(DateFormat) | 	whens := when.Format(DateFormat) | ||||||
|  | @ -84,7 +70,7 @@ func TestEntryMarshal(t *testing.T) { | ||||||
| 
 | 
 | ||||||
| func getEntryMarshalTestRunner(title string, date time.Time, fields []Meta, first string, lines []string, err error) func(*testing.T) { | func getEntryMarshalTestRunner(title string, date time.Time, fields []Meta, first string, lines []string, err error) func(*testing.T) { | ||||||
| 	return func(t *testing.T) { | 	return func(t *testing.T) { | ||||||
| 		en := Entry{title, date, fields, false} | 		en := Entry{title, date, fields} | ||||||
| 		o, er := en.MarshalText() | 		o, er := en.MarshalText() | ||||||
| 		assert.Equal(t, err, er) | 		assert.Equal(t, err, er) | ||||||
| 		if first == "" { | 		if first == "" { | ||||||
|  | @ -232,7 +218,7 @@ func TestEntryJsonMarshal(t *testing.T) { | ||||||
| 
 | 
 | ||||||
| func getEntryJsonMarshalTestRunner(title string, date time.Time, fields []Meta, out string, err error) func(t *testing.T) { | func getEntryJsonMarshalTestRunner(title string, date time.Time, fields []Meta, out string, err error) func(t *testing.T) { | ||||||
| 	return func(t *testing.T) { | 	return func(t *testing.T) { | ||||||
| 		e := Entry{title, date, fields, false} | 		e := Entry{title, date, fields} | ||||||
| 		o, er := json.Marshal(e) | 		o, er := json.Marshal(e) | ||||||
| 		if err == nil { | 		if err == nil { | ||||||
| 			assert.JSONEq(t, out, string(o)) | 			assert.JSONEq(t, out, string(o)) | ||||||
|  |  | ||||||
|  | @ -97,3 +97,31 @@ func getMetaTestRunner(key string, value any, out string, err error, newVal any) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func TestMetasJson(t *testing.T) { | ||||||
|  | 	ms := Metas{{"me", 41}, {"you", false}} | ||||||
|  | 	exp := `{"me":41,"you":false}` | ||||||
|  | 	o, err := json.Marshal(ms) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.JSONEq(t, exp, string(o)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestMetasJsonUnmarshal(t *testing.T) { | ||||||
|  | 	ms := Metas{} | ||||||
|  | 	in := `{"me":"cool","you":false}` | ||||||
|  | 	err := json.Unmarshal([]byte(in), &ms) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.Len(t, ms, 2) | ||||||
|  | 	assert.ElementsMatch(t, Metas{ | ||||||
|  | 		{"me", "cool"}, | ||||||
|  | 		{"you", false}, | ||||||
|  | 	}, ms) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestMetasJsonError(t *testing.T) { | ||||||
|  | 	ms := Metas{} | ||||||
|  | 	in := "not json" | ||||||
|  | 	err := (&ms).UnmarshalJSON([]byte(in)) | ||||||
|  | 	assert.Error(t, err) | ||||||
|  | 	assert.Len(t, ms, 0) | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										64
									
								
								models/metas.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								models/metas.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,64 @@ | ||||||
|  | package models | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	//"bufio" | ||||||
|  | 	//"bytes" | ||||||
|  | 	"encoding/json" | ||||||
|  | 	//"fmt" | ||||||
|  | 	//"errors" | ||||||
|  | 	//"regexp" | ||||||
|  | 	//"strings" | ||||||
|  | 	//"sync" | ||||||
|  | 	"time" | ||||||
|  | 	//"codeberg.org/danjones000/my-log/tools" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type Metas []Meta | ||||||
|  | 
 | ||||||
|  | func (ms Metas) toMap() map[string]any { | ||||||
|  | 	out := map[string]any{} | ||||||
|  | 	for _, f := range ms { | ||||||
|  | 		if _, found := out[f.Key]; found || f.Key == "title" || f.Key == "date" { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if f.Key == "json" { | ||||||
|  | 			ob := map[string]any{} | ||||||
|  | 			if j, ok := f.Value.(json.RawMessage); ok { | ||||||
|  | 				json.Unmarshal(j, &ob) | ||||||
|  | 			} | ||||||
|  | 			// If we couldn't get valid data from there, this will just be empty | ||||||
|  | 			for k, v := range ob { | ||||||
|  | 				if k != "title" && k != "date" { | ||||||
|  | 					out[k] = v | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			out[f.Key] = f.Value | ||||||
|  | 			if vt, ok := f.Value.(time.Time); ok { | ||||||
|  | 				out[f.Key] = vt.Format(time.RFC3339) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return out | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (ms Metas) MarshalJSON() ([]byte, error) { | ||||||
|  | 	return json.Marshal(ms.toMap()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (ms *Metas) UnmarshalJSON(in []byte) error { | ||||||
|  | 	old := (*ms).toMap() | ||||||
|  | 	out := map[string]any{} | ||||||
|  | 	err := json.Unmarshal(in, &out) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	ret := *ms | ||||||
|  | 	for k, v := range out { | ||||||
|  | 		if _, found := old[k]; k != "title" && k != "date" && !found { | ||||||
|  | 			ret = append(ret, Meta{k, v}) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	*ms = ret | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | @ -16,8 +16,8 @@ func TestParseDate(t *testing.T) { | ||||||
| 	y, mon, d, h, loc := now.Year(), now.Month(), now.Day(), now.Hour(), now.Location() | 	y, mon, d, h, loc := now.Year(), now.Month(), now.Day(), now.Hour(), now.Location() | ||||||
| 	sec := now.Truncate(time.Second) | 	sec := now.Truncate(time.Second) | ||||||
| 	today := time.Date(y, mon, d, 0, 0, 0, 0, loc) | 	today := time.Date(y, mon, d, 0, 0, 0, 0, loc) | ||||||
| 	tomorrow := today.Add(day) | 	tomorrow := time.Date(y, mon, d+1, 0, 0, 0, 0, loc) | ||||||
| 	yesterday := today.Add(-day) | 	yesterday := time.Date(y, mon, d-1, 0, 0, 0, 0, loc) | ||||||
| 	twoMin := now.Add(2 * time.Minute).Truncate(time.Minute) | 	twoMin := now.Add(2 * time.Minute).Truncate(time.Minute) | ||||||
| 	twoHour := time.Date(y, mon, d, h+2, 0, 0, 0, loc) | 	twoHour := time.Date(y, mon, d, h+2, 0, 0, 0, loc) | ||||||
| 	firstMonth := time.Date(y, mon, 1, 0, 0, 0, 0, loc) | 	firstMonth := time.Date(y, mon, 1, 0, 0, 0, 0, loc) | ||||||
|  |  | ||||||
|  | @ -14,6 +14,18 @@ func WriteValue(buff *bytes.Buffer, val any) (n int, err error) { | ||||||
| 		err = fmt.Errorf("Unsupported type %T", v) | 		err = fmt.Errorf("Unsupported type %T", v) | ||||||
| 	case nil: | 	case nil: | ||||||
| 		return | 		return | ||||||
|  | 	case []any: | ||||||
|  | 		var o []byte | ||||||
|  | 		o, err = json.Marshal(v) | ||||||
|  | 		if err == nil { | ||||||
|  | 			return buff.Write(o) | ||||||
|  | 		} | ||||||
|  | 	case map[string]any: | ||||||
|  | 		var o []byte | ||||||
|  | 		o, err = json.Marshal(v) | ||||||
|  | 		if err == nil { | ||||||
|  | 			return buff.Write(o) | ||||||
|  | 		} | ||||||
| 	case string: | 	case string: | ||||||
| 		return buff.WriteString(v) | 		return buff.WriteString(v) | ||||||
| 	case int: | 	case int: | ||||||
|  |  | ||||||
|  | @ -30,6 +30,8 @@ func TestWriteBuffer(t *testing.T) { | ||||||
| 		{"json.Number", json.Number("42.13"), "42.13", nil}, | 		{"json.Number", json.Number("42.13"), "42.13", nil}, | ||||||
| 		{"json.RawMessage", json.RawMessage("{}"), "{}", nil}, | 		{"json.RawMessage", json.RawMessage("{}"), "{}", nil}, | ||||||
| 		{"time", when, when.Format(time.RFC3339), nil}, | 		{"time", when, when.Format(time.RFC3339), nil}, | ||||||
|  | 		{"slice", []any{1, 2, "foo"}, `[1,2,"foo"]`, nil}, | ||||||
|  | 		{"map", map[string]any{"baz": 42, "foo": "bar"}, `{"baz":42,"foo":"bar"}`, nil}, | ||||||
| 		{"struct", struct{}{}, "", errors.New("Unsupported type struct {}")}, | 		{"struct", struct{}{}, "", errors.New("Unsupported type struct {}")}, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue