github.com/grafana/pyroscope@v1.18.0/public/app/models/query.ts (about) 1 import { Maybe } from '@pyroscope/util/fp'; 2 3 // ParseQuery parses a string of $app_name<{<$tag_matchers>}> form. 4 // It assumes the query is well formed 5 export function parse(query: Query): 6 | { 7 profileId: string; 8 tags?: Record<string, string>; 9 } 10 | undefined { 11 const regex = /(.+){(.*)}/; 12 const match = query?.match?.(regex); 13 14 if (!match) { 15 // TODO: return a Nothing() ? 16 return undefined; 17 } 18 19 const [_original, head, tail] = match; 20 const tags = parseTags(tail); 21 22 if (!Object.keys(tags).length) { 23 return { profileId: head }; 24 } 25 return { profileId: head, tags }; 26 } 27 28 function parseTags(s: string) { 29 const pattern = /(\w+)="([^"]+)/g; 30 31 let match; 32 const matches: Record<string, string> = {}; 33 34 while ((match = pattern.exec(s)) !== null) { 35 const key = match[1]; 36 const value = match[2]; 37 matches[key] = value; 38 } 39 40 return matches; 41 } 42 43 // Nominal typing 44 // https://basarat.gitbook.io/typescript/main-1/nominaltyping 45 enum QueryBrand { 46 _ = '', 47 } 48 export type Query = QueryBrand & string; 49 50 export function brandQuery(query: string) { 51 return query as unknown as Query; 52 } 53 54 export function queryFromAppName(appName: string): Query { 55 return `${appName}{}` as unknown as Query; 56 } 57 58 export function queryToAppName(q: Query): Maybe<string> { 59 const query: string = q; 60 61 if (!query || !query.length) { 62 return Maybe.nothing(); 63 } 64 65 const rep = query.replace(/\{.*/g, ''); 66 67 if (!rep.length) { 68 return Maybe.nothing(); 69 } 70 71 return Maybe.just(rep); 72 }