// Copyright 2021 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import { SyntaxNode } from '@lezer/common'; import { EqlRegex, EqlSingle, LabelName, MatchOp, Neq, NeqRegex, StringLiteral, UnquotedLabelMatcher, QuotedLabelMatcher, QuotedLabelName, } from '@prometheus-io/lezer-promql'; import { EditorState } from '@codemirror/state'; import { Matcher } from '../types'; function createMatcher(labelMatcher: SyntaxNode, state: EditorState): Matcher { const matcher = new Matcher(0, '', ''); const cursor = labelMatcher.cursor(); switch (cursor.type.id) { case QuotedLabelMatcher: if (!cursor.next()) { // weird case, that would mean the QuotedLabelMatcher doesn't have any child. return matcher; } do { switch (cursor.type.id) { case QuotedLabelName: matcher.name = state.sliceDoc(cursor.from, cursor.to).slice(1, -1); break; case MatchOp: const ope = cursor.node.firstChild; if (ope) { matcher.type = ope.type.id; } break; case StringLiteral: matcher.value = state.sliceDoc(cursor.from, cursor.to).slice(1, -1); break; } } while (cursor.nextSibling()); break; case UnquotedLabelMatcher: if (!cursor.next()) { // weird case, that would mean the UnquotedLabelMatcher doesn't have any child. return matcher; } do { switch (cursor.type.id) { case LabelName: matcher.name = state.sliceDoc(cursor.from, cursor.to); break; case MatchOp: const ope = cursor.node.firstChild; if (ope) { matcher.type = ope.type.id; } break; case StringLiteral: matcher.value = state.sliceDoc(cursor.from, cursor.to).slice(1, -1); break; } } while (cursor.nextSibling()); break; case QuotedLabelName: matcher.name = '__name__'; matcher.value = state.sliceDoc(cursor.from, cursor.to).slice(1, -1); matcher.type = EqlSingle; break; } return matcher; } export function buildLabelMatchers(labelMatchers: SyntaxNode[], state: EditorState): Matcher[] { const matchers: Matcher[] = []; labelMatchers.forEach((value) => { matchers.push(createMatcher(value, state)); }); return matchers; } export function labelMatchersToString(metricName: string, matchers?: Matcher[], labelName?: string): string { if (!matchers || matchers.length === 0) { return metricName; } let matchersAsString = ''; for (const matcher of matchers) { if (matcher.name === labelName || matcher.value === '') { continue; } let type = ''; switch (matcher.type) { case EqlSingle: type = '='; break; case Neq: type = '!='; break; case NeqRegex: type = '!~'; break; case EqlRegex: type = '=~'; break; default: type = '='; } const m = `${matcher.name}${type}"${matcher.value}"`; if (matchersAsString === '') { matchersAsString = m; } else { matchersAsString = `${matchersAsString},${m}`; } } return `${metricName}{${matchersAsString}}`; }