[cas] Simplify UploadResult.Digest() signature (#362)

* [cas] Simplify UploadResult.Digest() signature

Instead of returning a possibly nil digest, introduce a new error
ErrNoDigest and return that instead. It simplifies most callsites: now
they check only 2 cases instead of 3.
diff --git a/go/pkg/cas/upload.go b/go/pkg/cas/upload.go
index 8737659..ae79d81 100644
--- a/go/pkg/cas/upload.go
+++ b/go/pkg/cas/upload.go
@@ -107,13 +107,19 @@
 	digest   *repb.Digest  // may be nil, e.g. for dangling symlinks
 }
 
-// ErrSkip when returned by UploadOptions.Prelude, means the file/dir must be
-// not be uploaded.
-//
-// Note that if UploadOptions.PreserveSymlinks is true and the ErrSkip is
-// returned for a symlink target, but not the symlink itself, then it may
-// result in a dangling symlink.
-var ErrSkip = errors.New("skip file")
+var (
+	// ErrSkip when returned by UploadOptions.Prelude, means the file/dir must be
+	// not be uploaded.
+	//
+	// Note that if UploadOptions.PreserveSymlinks is true and the ErrSkip is
+	// returned for a symlink target, but not the symlink itself, then it may
+	// result in a dangling symlink.
+	ErrSkip = errors.New("skip file")
+
+	// ErrNoDigest indicates that the requested digest is uknown.
+	// Use errors.Is instead of direct equality check.
+	ErrNoDigest = errors.New("the requested digest is unknown")
+)
 
 // UploadResult is the result of a Client.Upload call.
 // It provides file/dir digests and statistics.
@@ -130,28 +136,28 @@
 // To retrieve a digest of a directory or a symlink, ps.Exclude must match one
 // of the PathSpecs passed to Client.Upload earlier.
 //
-// If the digest is unknown, returns (nil, nil).
+// If the digest is unknown, returns (nil, err), where err is ErrDigestUnknown
+// according to errors.Is.
 // If the file is a danging symlink, then its digest is unknown.
-func (r *UploadResult) Digest(ps *PathSpec) (*digest.Digest, error) {
+func (r *UploadResult) Digest(ps *PathSpec) (digest.Digest, error) {
 	if !filepath.IsAbs(ps.Path) {
-		return nil, errors.Errorf("%q is not absolute", ps.Path)
+		return digest.Digest{}, errors.Errorf("%q is not absolute", ps.Path)
 	}
 
 	// TODO(nodir): cache this syscall too.
 	info, err := os.Lstat(ps.Path)
 	if err != nil {
-		return nil, err
+		return digest.Digest{}, err
 	}
 
 	key := makeFSCacheKey(ps.Path, info.Mode().IsRegular(), ps.Exclude)
 	switch val, err, loaded := r.u.fsCache.Load(key); {
 	case !loaded:
-		return nil, nil
+		return digest.Digest{}, errors.Wrapf(ErrNoDigest, "digest not found for %#v", ps)
 	case err != nil:
-		return nil, err
+		return digest.Digest{}, err
 	default:
-		dig := digest.NewFromProtoUnvalidated(val.(*digested).digest)
-		return &dig, nil
+		return digest.NewFromProtoUnvalidated(val.(*digested).digest), nil
 	}
 }
 
diff --git a/go/pkg/cas/upload_test.go b/go/pkg/cas/upload_test.go
index fae5598..550c67f 100644
--- a/go/pkg/cas/upload_test.go
+++ b/go/pkg/cas/upload_test.go
@@ -108,11 +108,10 @@
 		},
 	})
 
-	digSlice := func(items ...*uploadItem) []*digest.Digest {
-		ret := make([]*digest.Digest, len(items))
+	digSlice := func(items ...*uploadItem) []digest.Digest {
+		ret := make([]digest.Digest, len(items))
 		for i, item := range items {
-			dig := digest.NewFromProtoUnvalidated(item.Digest)
-			ret[i] = &dig
+			ret[i] = digest.NewFromProtoUnvalidated(item.Digest)
 		}
 		return ret
 	}
@@ -120,7 +119,7 @@
 	tests := []struct {
 		desc                string
 		paths               []*PathSpec
-		wantDigests         []*digest.Digest
+		wantDigests         []digest.Digest
 		wantScheduledChecks []*uploadItem
 		wantErr             error
 		opt                 UploadOptions
@@ -275,7 +274,7 @@
 				t.Errorf("unexpected scheduled checks (-want +got):\n%s", diff)
 			}
 
-			gotDigests := make([]*digest.Digest, 0, len(tc.paths))
+			gotDigests := make([]digest.Digest, 0, len(tc.paths))
 			for _, ps := range tc.paths {
 				dig, err := res.Digest(ps)
 				if err != nil {