// 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) } } } }