kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/indexer/facts.go (about) 1 /* 2 * Copyright 2015 The Kythe Authors. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package indexer 18 19 import ( 20 "context" 21 "crypto/sha256" 22 "encoding/base64" 23 "fmt" 24 "strconv" 25 26 "kythe.io/kythe/go/extractors/govname" 27 "kythe.io/kythe/go/util/schema/edges" 28 "kythe.io/kythe/go/util/schema/facts" 29 "kythe.io/kythe/go/util/schema/nodes" 30 31 spb "kythe.io/kythe/proto/storage_go_proto" 32 ) 33 34 // A Sink is a callback invoked by the indexer to deliver entries. 35 type Sink func(context.Context, *spb.Entry) error 36 37 // writeFact writes a single fact with the given name and value for src to s. 38 func (s Sink) writeFact(ctx context.Context, src *spb.VName, name, value string) error { 39 return s(ctx, &spb.Entry{ 40 Source: src, 41 FactName: name, 42 FactValue: []byte(value), 43 }) 44 } 45 46 // writeEdge writes an edge with the specified kind between src and tgt to s. 47 func (s Sink) writeEdge(ctx context.Context, src, tgt *spb.VName, kind string) error { 48 return s(ctx, &spb.Entry{ 49 Source: src, 50 Target: tgt, 51 EdgeKind: kind, 52 FactName: "/", 53 }) 54 } 55 56 // writeAnchor emits an anchor with the given offsets to s. 57 func (s Sink) writeAnchor(ctx context.Context, src *spb.VName, start, end int) error { 58 if err := s.writeFact(ctx, src, facts.NodeKind, nodes.Anchor); err != nil { 59 return err 60 } 61 if err := s.writeFact(ctx, src, facts.AnchorStart, strconv.Itoa(start)); err != nil { 62 return err 63 } 64 return s.writeFact(ctx, src, facts.AnchorEnd, strconv.Itoa(end)) 65 } 66 67 // A diagnostic represents a diagnostic message attached to some node in the 68 // graph by the indexer. 69 type diagnostic struct { 70 Message string // One-line human-readable summary 71 Details string // Detailed description or content 72 URL string // Optional context URL 73 } 74 75 func (d diagnostic) vname(src *spb.VName) *spb.VName { 76 return &spb.VName{ 77 Language: govname.Language, 78 Corpus: src.Corpus, 79 Path: src.Path, 80 Root: src.Root, 81 Signature: "diag " + hashSignature(d.Message, d.Details, d.URL), 82 } 83 } 84 85 // writeDiagnostic attaches d as a diagnostic on src. 86 func (s Sink) writeDiagnostic(ctx context.Context, src *spb.VName, d diagnostic) error { 87 dname := d.vname(src) 88 facts := [...]struct{ name, value string }{ 89 {facts.NodeKind, nodes.Diagnostic}, 90 {facts.Message, d.Message}, 91 {facts.Details, d.Details}, 92 {facts.ContextURL, d.URL}, 93 } 94 for _, fact := range facts { 95 if fact.value == "" { 96 continue 97 } else if err := s.writeFact(ctx, dname, fact.name, fact.value); err != nil { 98 return err 99 } 100 } 101 return s.writeEdge(ctx, src, dname, edges.Tagged) 102 } 103 104 func hashSignature(s ...any) string { 105 hash := sha256.New() 106 fmt.Fprintln(hash, s...) 107 return base64.URLEncoding.EncodeToString(hash.Sum(nil)[:]) 108 }