// ParseSpec parses the given specification and returns an opened handle to an // API Interface. The following formats are currently supported: // - http:// URL pointed at a JSON web API // - https:// URL pointed at a JSON web API // - host:port pointed at a GRPC API // - local path to a LevelDB serving table func ParseSpec(apiSpec string) (Interface, error) { api := &apiCloser{} if strings.HasPrefix(apiSpec, "http://") || strings.HasPrefix(apiSpec, "https://") { api.xs = xrefs.WebClient(apiSpec) api.ft = filetree.WebClient(apiSpec) api.idx = search.WebClient(apiSpec) } else if _, err := os.Stat(apiSpec); err == nil { db, err := leveldb.Open(apiSpec, nil) if err != nil { return nil, fmt.Errorf("error opening local DB at %q: %v", apiSpec, err) } api.closer = func() error { return db.Close() } tbl := table.ProtoBatchParallel{&table.KVProto{db}} api.xs = xsrv.NewCombinedTable(tbl) api.ft = &ftsrv.Table{tbl} api.idx = &srchsrv.Table{&table.KVInverted{db}} } else { conn, err := grpc.Dial(apiSpec) if err != nil { return nil, fmt.Errorf("error connecting to remote API %q: %v", apiSpec, err) } api.closer = func() error { conn.Close(); return nil } api.xs = xrefs.GRPC(xpb.NewXRefServiceClient(conn)) api.ft = filetree.GRPC(ftpb.NewFileTreeServiceClient(conn)) api.idx = search.GRPC(spb.NewSearchServiceClient(conn)) } return api, nil }
func main() { flag.Parse() if len(flag.Args()) == 0 { flag.Usage() os.Exit(0) } else if *servingTable == "" && *remoteAPI == "" { log.Fatal("One of --serving_table or --api is required") } if *servingTable == "" { if strings.HasPrefix(*remoteAPI, "http://") || strings.HasPrefix(*remoteAPI, "https://") { xs = xrefs.WebClient(*remoteAPI) ft = filetree.WebClient(*remoteAPI) idx = search.WebClient(*remoteAPI) } else { conn, err := grpc.Dial(*remoteAPI) if err != nil { log.Fatalf("Error connecting to remote API %q: %v", *remoteAPI, err) } defer conn.Close() xs = xrefs.GRPC(xpb.NewXRefServiceClient(conn)) ft = filetree.GRPC(ftpb.NewFileTreeServiceClient(conn)) idx = search.GRPC(spb.NewSearchServiceClient(conn)) } } else { db, err := leveldb.Open(*servingTable, nil) if err != nil { log.Fatalf("Error opening db at %q: %v", *servingTable, err) } defer db.Close() tbl := &table.KVProto{db} xs = &xsrv.Table{tbl} ft = &ftsrv.Table{tbl} idx = &srchsrv.Table{&table.KVInverted{db}} } if err := getCommand(flag.Arg(0)).run(); err != nil { log.Fatal("ERROR: ", err) } }
func main() { flag.Parse() if *offset < 0 { flagutil.UsageError("non-negative --offset required") } else if *signature == "" && *path == "" { flagutil.UsageError("must provide at least --path or --signature") } if strings.HasPrefix(*remoteAPI, "http://") || strings.HasPrefix(*remoteAPI, "https://") { xs = xrefs.WebClient(*remoteAPI) idx = search.WebClient(*remoteAPI) } else { conn, err := grpc.Dial(*remoteAPI) if err != nil { log.Fatalf("Error connecting to remote API %q: %v", *remoteAPI, err) } defer conn.Close() xs = xrefs.GRPC(xpb.NewXRefServiceClient(conn)) idx = search.GRPC(spb.NewSearchServiceClient(conn)) } relPath := *path if !*ignoreLocalRepo { if _, err := os.Stat(relPath); err == nil { absPath, err := filepath.Abs(relPath) if err != nil { log.Fatal(err) } kytheRoot := findKytheRoot(filepath.Dir(absPath)) if kytheRoot != "" { relPath, err = filepath.Rel(filepath.Join(kytheRoot, *root), absPath) if err != nil { log.Fatal(err) } } } } partialFile := &spb.VName{ Signature: *signature, Corpus: *corpus, Root: *root, Path: relPath, Language: *language, } reply, err := idx.Search(ctx, &spb.SearchRequest{ Partial: partialFile, Fact: fileFacts, }) if err != nil { log.Fatalf("Error locating file {%v}: %v", partialFile, err) } if len(reply.Ticket) == 0 { log.Fatalf("Could not locate file {%v}", partialFile) } else if len(reply.Ticket) > 1 { log.Fatalf("Ambiguous file {%v}; multiple results: %v", partialFile, reply.Ticket) } fileTicket := reply.Ticket[0] decor, err := xs.Decorations(ctx, &xpb.DecorationsRequest{ // TODO(schroederc): limit Location to a SPAN around *offset Location: &xpb.Location{Ticket: fileTicket}, References: true, SourceText: true, DirtyBuffer: readDirtyBuffer(ctx), }) if err != nil { log.Fatal(err) } nodes := xrefs.NodesMap(decor.Node) en := json.NewEncoder(os.Stdout) for _, ref := range decor.Reference { start, end := parseAnchorSpan(nodes[ref.SourceTicket]) if start <= *offset && *offset < end { var r reference r.Span.Start = start r.Span.End = end r.Span.Text = string(decor.SourceText[start:end]) r.Kind = strings.TrimPrefix(ref.Kind, schema.EdgePrefix) r.Node.Ticket = ref.TargetTicket node := nodes[ref.TargetTicket] r.Node.Kind = string(node[schema.NodeKindFact]) r.Node.Subkind = string(node[schema.SubkindFact]) if eReply, err := xs.Edges(ctx, &xpb.EdgesRequest{ Ticket: []string{ref.TargetTicket}, Kind: []string{schema.NamedEdge, definedAtEdge}, }); err != nil { log.Printf("WARNING: error getting edges for %q: %v", ref.TargetTicket, err) } else { edges := xrefs.EdgesMap(eReply.EdgeSet)[ref.TargetTicket] for _, name := range edges[schema.NamedEdge] { if uri, err := kytheuri.Parse(name); err != nil { log.Printf("WARNING: named node ticket (%q) could not be parsed: %v", name, err) } else { r.Node.Names = append(r.Node.Names, uri.Signature) } } if !*skipDefinitions { for _, defAnchor := range edges[definedAtEdge] { def, err := completeDefinition(defAnchor) if err != nil { log.Printf("WARNING: failed to complete definition for %q: %v", defAnchor, err) } else { r.Node.Definitions = append(r.Node.Definitions, def) } } } } if err := en.Encode(r); err != nil { log.Fatal(err) } } } }
func main() { flag.Parse() if len(flag.Args()) == 0 { flagutil.UsageError("not given any files") } xs := xrefs.WebClient(*remoteAPI) idx := search.WebClient(*remoteAPI) for _, file := range flag.Args() { results, err := idx.Search(ctx, &spb.SearchRequest{ Partial: &spb.VName{Path: file}, Fact: []*spb.SearchRequest_Fact{{ Name: schema.NodeKindFact, Value: []byte(schema.FileKind), }}, }) if err != nil { log.Fatalf("Error searching for ticket of file %q", file) } else if len(results.Ticket) == 0 { log.Printf("Could not find ticket for file %q", file) continue } else if len(results.Ticket) != 1 { log.Printf("Multiple tickets found for file %q; choosing first from %v", file, results.Ticket) } ticket := results.Ticket[0] decor, err := xs.Decorations(ctx, &xpb.DecorationsRequest{ Location: &xpb.Location{Ticket: ticket}, SourceText: true, References: true, }) if err != nil { log.Fatalf("Failed to get decorations for file %q", file) } nodes := xrefs.NodesMap(decor.Node) emitted := stringset.New() for _, r := range decor.Reference { if r.Kind != schema.DefinesBindingEdge || emitted.Contains(r.TargetTicket) { continue } ident := string(nodes[r.TargetTicket][identifierFact]) if ident == "" { continue } offset, err := strconv.Atoi(string(nodes[r.SourceTicket][schema.AnchorStartFact])) if err != nil { log.Printf("Invalid start offset for anchor %q", r.SourceTicket) continue } fields, err := getTagFields(xs, r.TargetTicket) if err != nil { log.Printf("Failed to get tagfields for %q: %v", r.TargetTicket, err) } fmt.Printf("%s\t%s\t%d;\"\t%s\n", ident, file, offsetLine(decor.SourceText, offset), strings.Join(fields, "\t")) emitted.Add(r.TargetTicket) } } }