diff --git a/var.go b/var.go index 2c3dc32..8dff7ed 100644 --- a/var.go +++ b/var.go @@ -38,7 +38,7 @@ const ( // Example: "mem:memstats.Alloc" => []string{"memstats", "Alloc"} func (v VarName) ToSlice() []string { start := strings.IndexRune(string(v), ':') + 1 - slice := strings.FieldsFunc(string(v)[start:], func(r rune) bool { return r == '.' }) + slice := DottedFieldsToSliceEscaped(string(v)[start:]) return slice } @@ -128,3 +128,47 @@ func roundDuration(d time.Duration) time.Duration { } return d } + +func DottedFieldsToSliceEscaped(s string) []string { + rv := make([]string, 0) + lastSlash := false + curr := "" + for _, r := range s { + // base case, dot not after slash + if !lastSlash && r == '.' { + if len(curr) > 0 { + rv = append(rv, curr) + curr = "" + } + continue + } else if !lastSlash { + // any character not after slash + curr += string(r) + if r == '\\' { + lastSlash = true + } else { + lastSlash = false + } + continue + } else if r == '\\' { + // last was slash, and so is this + lastSlash = false // 2 slashes = 0 + // we already appended a single slash on first + continue + } else if r == '.' { + // we see \. but already appended \ last time + // replace it with . + curr = curr[:len(curr)-1] + "." + lastSlash = false + } else { + // \ and any other character, ignore + curr += string(r) + lastSlash = false + continue + } + } + if len(curr) > 0 { + rv = append(rv, curr) + } + return rv +} diff --git a/var_test.go b/var_test.go index ddc084d..3d56bf1 100644 --- a/var_test.go +++ b/var_test.go @@ -44,4 +44,49 @@ func TestVarName(t *testing.T) { if kind != KindDuration { t.Fatalf("Expecting kind to be %v, but got: %v", KindDuration, kind) } + + // single \. escapes the dot + v = VarName(`bleve.indexes.bench\.bleve.index.lookup_queue_len`) + + slice = v.ToSlice() + if len(slice) != 5 || slice[0] != "bleve" || slice[1] != "indexes" || slice[2] != "bench.bleve" || + slice[3] != "index" || slice[4] != "lookup_queue_len" { + t.Fatalf("ToSlice failed: %v", slice) + } + + // double \\. escapes backslash, not dot + v = VarName(`bleve.indexes.bench\\.bleve.index.lookup_queue_len`) + + slice = v.ToSlice() + if len(slice) != 6 || slice[0] != "bleve" || slice[1] != "indexes" || slice[2] != "bench\\" || + slice[3] != "bleve" || slice[4] != "index" || slice[5] != "lookup_queue_len" { + t.Fatalf("ToSlice failed: %v", slice) + } + + // triple \\\. escapes backslash then dot + v = VarName(`bleve.indexes.bench\\\.bleve.index.lookup_queue_len`) + + slice = v.ToSlice() + if len(slice) != 5 || slice[0] != "bleve" || slice[1] != "indexes" || slice[2] != "bench\\.bleve" || + slice[3] != "index" || slice[4] != "lookup_queue_len" { + t.Fatalf("ToSlice failed: %v", slice) + } + + // quadruple \\\\. escapes two backslashes, not dot + v = VarName(`bleve.indexes.bench\\\\.bleve.index.lookup_queue_len`) + + slice = v.ToSlice() + if len(slice) != 6 || slice[0] != "bleve" || slice[1] != "indexes" || slice[2] != "bench\\\\" || + slice[3] != "bleve" || slice[4] != "index" || slice[5] != "lookup_queue_len" { + t.Fatalf("ToSlice failed: %v", slice) + } + + // unsupported \x passes through unaltered + v = VarName(`bleve.indexes.bench\xbleve.index.lookup_queue_len`) + + slice = v.ToSlice() + if len(slice) != 5 || slice[0] != "bleve" || slice[1] != "indexes" || slice[2] != "bench\\xbleve" || + slice[3] != "index" || slice[4] != "lookup_queue_len" { + t.Fatalf("ToSlice failed: %v", slice) + } }