Skip to content

Commit ccdad29

Browse files
committed
feat: add DataToPoint
1 parent 060d69e commit ccdad29

File tree

2 files changed

+84
-0
lines changed

2 files changed

+84
-0
lines changed

api/write.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ package api
66

77
import (
88
"context"
9+
"fmt"
10+
"reflect"
911
"strings"
1012
"sync"
1113
"sync/atomic"
@@ -266,3 +268,79 @@ func (w *WriteAPIImpl) WritePoint(point *write.Point) {
266268
func buffer(lines []string) string {
267269
return strings.Join(lines, "")
268270
}
271+
272+
// DataToPoint converts custom point structures into a Point.
273+
// Each visible field of the point on input must be annotated with
274+
// 'lp' prefix and values measurement,tag, field or timestamp.
275+
// Valid point must contain measurement and at least one field.
276+
//
277+
// A field with timestamp must be of a type time.Time
278+
//
279+
// type TemperatureSensor struct {
280+
// Measurement string `lp:"measurement"`
281+
// Sensor string `lp:"tag,sensor"`
282+
// ID string `lp:"tag,device_id"`
283+
// Temp float64 `lp:"field,temperature"`
284+
// Hum int `lp:"field,humidity"`
285+
// Time time.Time `lp:"timestamp,temperature"`
286+
// Description string `lp:"-"`
287+
// }
288+
func DataToPoint(x interface{}) (*write.Point, error) {
289+
if err := checkContainerType(x, false, "point"); err != nil {
290+
return nil, err
291+
}
292+
t := reflect.TypeOf(x)
293+
v := reflect.ValueOf(x)
294+
if t.Kind() == reflect.Ptr {
295+
t = t.Elem()
296+
v = v.Elem()
297+
}
298+
fields := reflect.VisibleFields(t)
299+
300+
var measurement string = ""
301+
var lpTags = make(map[string]string)
302+
var lpFields = make(map[string]interface{})
303+
var lpTime time.Time
304+
305+
for _, f := range fields {
306+
name := f.Name
307+
if tag, ok := f.Tag.Lookup("lp"); ok {
308+
if tag == "-" {
309+
continue
310+
}
311+
parts := strings.Split(tag, ",")
312+
if len(parts) > 2 {
313+
return nil, fmt.Errorf("multiple tag attributes are not supported")
314+
}
315+
typ := parts[0]
316+
if len(parts) == 2 {
317+
name = parts[1]
318+
}
319+
switch typ {
320+
case "measurement":
321+
if measurement != "" {
322+
return nil, fmt.Errorf("multiple measurement fields")
323+
}
324+
measurement = v.FieldByIndex(f.Index).String()
325+
case "tag":
326+
lpTags[name] = v.FieldByIndex(f.Index).String()
327+
case "field":
328+
lpFields[name] = v.FieldByIndex(f.Index).Interface()
329+
case "timestamp":
330+
if f.Type != timeType {
331+
return nil, fmt.Errorf("cannot use field '%s' as a timestamp", f.Name)
332+
}
333+
lpTime = v.FieldByIndex(f.Index).Interface().(time.Time)
334+
default:
335+
return nil, fmt.Errorf("invalid tag %s", typ)
336+
}
337+
}
338+
}
339+
if measurement == "" {
340+
return nil, fmt.Errorf("no struct field with tag 'measurement'")
341+
}
342+
if len(lpFields) == 0 {
343+
return nil, fmt.Errorf("no struct field with tag 'field'")
344+
}
345+
return write.NewPoint(measurement, lpTags, lpFields, lpTime), nil
346+
}

api/write/point.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ func (m *Point) Name() string {
8484
return m.measurement
8585
}
8686

87+
// Name returns the name of measurement of a point.
88+
func (m *Point) SetName(name string) *Point {
89+
m.measurement = name
90+
return m
91+
}
92+
8793
// NewPointWithMeasurement creates a empty Point
8894
// Use AddTag and AddField to fill point with data
8995
func NewPointWithMeasurement(measurement string) *Point {

0 commit comments

Comments
 (0)