kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/serving/filetree/filetree.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 filetree implements a lookup table for files in a tree structure.
    18  //
    19  // Table format:
    20  //
    21  //	dirs:<corpus>\n<root>\n<path> -> srvpb.FileDirectory
    22  //	dirs:corpusRoots              -> srvpb.CorpusRoots
    23  package filetree // import "kythe.io/kythe/go/serving/filetree"
    24  
    25  import (
    26  	"context"
    27  	"errors"
    28  	"fmt"
    29  	"path/filepath"
    30  	"strings"
    31  
    32  	"kythe.io/kythe/go/storage/table"
    33  	"kythe.io/kythe/go/util/kytheuri"
    34  	"kythe.io/kythe/go/util/log"
    35  
    36  	ftpb "kythe.io/kythe/proto/filetree_go_proto"
    37  	srvpb "kythe.io/kythe/proto/serving_go_proto"
    38  )
    39  
    40  const (
    41  	// DirTablePrefix is used as the prefix of the keys of a combined serving
    42  	// table.  CorpusRootsPrefixedKey and PrefixedDirKey use this prefix to
    43  	// construct their keys.  Table uses this prefix when PrefixedKeys is true.
    44  	DirTablePrefix = "dirs:"
    45  
    46  	dirKeySep = "\n"
    47  )
    48  
    49  // CorpusRootsKey is the filetree lookup key for the tree's srvpb.CorpusRoots.
    50  var CorpusRootsKey = []byte("corpusRoots")
    51  
    52  // CorpusRootsPrefixedKey is the filetree lookup key for the tree's
    53  // srvpb.CorpusRoots when using PrefixedKeys.
    54  var CorpusRootsPrefixedKey = []byte(DirTablePrefix + "corpusRoots")
    55  
    56  // Table implements the FileTree interface using a static lookup table.
    57  type Table struct {
    58  	table.Proto
    59  
    60  	// PrefixedKeys indicates whether all keys are prefixed by DirTablePrefix
    61  	// (i.e. when using a combined serving table).
    62  	PrefixedKeys bool
    63  }
    64  
    65  // Directory implements part of the filetree Service interface.
    66  func (t *Table) Directory(ctx context.Context, req *ftpb.DirectoryRequest) (*ftpb.DirectoryReply, error) {
    67  	var key []byte
    68  	if t.PrefixedKeys {
    69  		key = PrefixedDirKey(req.Corpus, req.Root, req.Path)
    70  	} else {
    71  		key = DirKey(req.Corpus, req.Root, req.Path)
    72  	}
    73  	var d srvpb.FileDirectory
    74  	if err := t.Lookup(ctx, key, &d); err == table.ErrNoSuchKey {
    75  		return &ftpb.DirectoryReply{}, nil
    76  	} else if err != nil {
    77  		return nil, fmt.Errorf("lookup error: %v", err)
    78  	}
    79  	entries := make([]*ftpb.DirectoryReply_Entry, 0, len(d.Entry))
    80  	for _, e := range d.Entry {
    81  		if !req.GetIncludeFilesMissingText() && e.GetMissingText() {
    82  			continue
    83  		}
    84  		re := &ftpb.DirectoryReply_Entry{
    85  			Name:        e.Name,
    86  			BuildConfig: e.BuildConfig,
    87  			Generated:   e.GetGenerated(),
    88  			MissingText: e.GetMissingText(),
    89  		}
    90  		switch e.Kind {
    91  		case srvpb.FileDirectory_FILE:
    92  			re.Kind = ftpb.DirectoryReply_FILE
    93  		case srvpb.FileDirectory_DIRECTORY:
    94  			re.Kind = ftpb.DirectoryReply_DIRECTORY
    95  		default:
    96  			log.WarningContextf(ctx, "unknown directory entry type: %T", e)
    97  			continue
    98  		}
    99  		entries = append(entries, re)
   100  	}
   101  	entries, err := parseLegacyEntries(entries, ftpb.DirectoryReply_FILE, d.FileTicket)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  	entries, err = parseLegacyEntries(entries, ftpb.DirectoryReply_DIRECTORY, d.Subdirectory)
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  	return &ftpb.DirectoryReply{
   110  		Corpus: req.Corpus,
   111  		Root:   req.Root,
   112  		Path:   req.Path,
   113  		Entry:  entries,
   114  	}, nil
   115  }
   116  
   117  func parseLegacyEntries(entries []*ftpb.DirectoryReply_Entry, kind ftpb.DirectoryReply_Kind, tickets []string) ([]*ftpb.DirectoryReply_Entry, error) {
   118  	for _, ticket := range tickets {
   119  		uri, err := kytheuri.Parse(ticket)
   120  		if err != nil {
   121  			return nil, fmt.Errorf("invalid serving data: %v", err)
   122  		}
   123  		entries = append(entries, &ftpb.DirectoryReply_Entry{
   124  			Kind:      kind,
   125  			Name:      filepath.Base(uri.Path),
   126  			Generated: uri.Root != "",
   127  		})
   128  	}
   129  	return entries, nil
   130  }
   131  
   132  // CorpusRoots implements part of the filetree Service interface.
   133  func (t *Table) CorpusRoots(ctx context.Context, req *ftpb.CorpusRootsRequest) (*ftpb.CorpusRootsReply, error) {
   134  	key := CorpusRootsKey
   135  	if t.PrefixedKeys {
   136  		key = CorpusRootsPrefixedKey
   137  	}
   138  	var cr srvpb.CorpusRoots
   139  	if err := t.Lookup(ctx, key, &cr); err == table.ErrNoSuchKey {
   140  		return nil, errors.New("internal error: missing corpusRoots in table")
   141  	} else if err != nil {
   142  		return nil, fmt.Errorf("corpusRoots lookup error: %v", err)
   143  	}
   144  
   145  	reply := &ftpb.CorpusRootsReply{
   146  		Corpus: make([]*ftpb.CorpusRootsReply_Corpus, len(cr.Corpus)),
   147  	}
   148  
   149  	for i, corpus := range cr.Corpus {
   150  		reply.Corpus[i] = &ftpb.CorpusRootsReply_Corpus{
   151  			Name:        corpus.Corpus,
   152  			Root:        corpus.Root,
   153  			BuildConfig: corpus.BuildConfig,
   154  		}
   155  	}
   156  
   157  	return reply, nil
   158  }
   159  
   160  // DirKey returns the filetree lookup table key for the given corpus path.
   161  func DirKey(corpus, root, path string) []byte {
   162  	return []byte(strings.Join([]string{corpus, root, path}, dirKeySep))
   163  }
   164  
   165  // PrefixedDirKey returns the filetree lookup table key for the given corpus
   166  // path, prefixed by DirTablePrefix.
   167  func PrefixedDirKey(corpus, root, path string) []byte {
   168  	return []byte(DirTablePrefix + strings.Join([]string{corpus, root, path}, dirKeySep))
   169  }