xref: /aosp_15_r20/external/bazelbuild-rules_go/go/runfiles/fs.go (revision 9bb1b549b6a84214c53be0924760be030e66b93a)
1// Copyright 2021, 2022 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//go:build go1.16
16// +build go1.16
17
18package runfiles
19
20import (
21	"errors"
22	"io"
23	"io/fs"
24	"os"
25	"time"
26)
27
28// Open implements fs.FS.Open.
29func (r *Runfiles) Open(name string) (fs.File, error) {
30	if !fs.ValidPath(name) {
31		return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrInvalid}
32	}
33	p, err := r.Rlocation(name)
34	if errors.Is(err, ErrEmpty) {
35		return emptyFile(name), nil
36	}
37	if err != nil {
38		return nil, pathError("open", name, err)
39	}
40	return os.Open(p)
41}
42
43// Stat implements fs.StatFS.Stat.
44func (r *Runfiles) Stat(name string) (fs.FileInfo, error) {
45	if !fs.ValidPath(name) {
46		return nil, &fs.PathError{Op: "stat", Path: name, Err: fs.ErrInvalid}
47	}
48	p, err := r.Rlocation(name)
49	if errors.Is(err, ErrEmpty) {
50		return emptyFileInfo(name), nil
51	}
52	if err != nil {
53		return nil, pathError("stat", name, err)
54	}
55	return os.Stat(p)
56}
57
58// ReadFile implements fs.ReadFileFS.ReadFile.
59func (r *Runfiles) ReadFile(name string) ([]byte, error) {
60	if !fs.ValidPath(name) {
61		return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrInvalid}
62	}
63	p, err := r.Rlocation(name)
64	if errors.Is(err, ErrEmpty) {
65		return nil, nil
66	}
67	if err != nil {
68		return nil, pathError("open", name, err)
69	}
70	return os.ReadFile(p)
71}
72
73type emptyFile string
74
75func (f emptyFile) Stat() (fs.FileInfo, error) { return emptyFileInfo(f), nil }
76func (f emptyFile) Read([]byte) (int, error)   { return 0, io.EOF }
77func (emptyFile) Close() error                 { return nil }
78
79type emptyFileInfo string
80
81func (i emptyFileInfo) Name() string     { return string(i) }
82func (emptyFileInfo) Size() int64        { return 0 }
83func (emptyFileInfo) Mode() fs.FileMode  { return 0444 }
84func (emptyFileInfo) ModTime() time.Time { return time.Time{} }
85func (emptyFileInfo) IsDir() bool        { return false }
86func (emptyFileInfo) Sys() interface{}   { return nil }
87
88func pathError(op, name string, err error) error {
89	if err == nil {
90		return nil
91	}
92	var rerr Error
93	if errors.As(err, &rerr) {
94		// Unwrap the error because we don’t need the failing name
95		// twice.
96		return &fs.PathError{Op: op, Path: rerr.Name, Err: rerr.Err}
97	}
98	return &fs.PathError{Op: op, Path: name, Err: err}
99}
100