Example #1
0
File: api.go Project: benjyw/kythe
// 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)
	} 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, true}
	} else {
		conn, err := grpc.Dial(apiSpec, grpc.WithInsecure())
		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), gpb.NewGraphServiceClient(conn))
		api.ft = filetree.GRPC(ftpb.NewFileTreeServiceClient(conn))
	}
	return api, nil
}
Example #2
0
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)
	}
}
Example #3
0
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)
			}
		}
	}
}