150 lines
5.6 KiB
Go
150 lines
5.6 KiB
Go
package engine
|
|
|
|
import (
|
|
"io/fs"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
type resolvedFilesystemContext struct {
|
|
native *nativeNamespace
|
|
hostToken *namespaceToken
|
|
rootPath string
|
|
basePath string
|
|
mainPath string
|
|
}
|
|
|
|
func resolveFilesystemContext(source Source, context *FilesystemContext, main parsedSource, hostToken *namespaceToken) (resolvedFilesystemContext, *Diagnostic) {
|
|
if nativeContext(source, context) {
|
|
return resolveNativeFilesystemContext(source, context)
|
|
}
|
|
return resolveHostFilesystemContext(source, context, main, hostToken)
|
|
}
|
|
|
|
func resolveNativeFilesystemContext(source Source, context *FilesystemContext) (resolvedFilesystemContext, *Diagnostic) {
|
|
var namespace *nativeNamespace
|
|
resolved := resolvedFilesystemContext{rootPath: ".", basePath: "."}
|
|
if source.kind == sourceFilesystem {
|
|
namespace = source.namespace
|
|
resolved.mainPath = source.path
|
|
resolved.basePath = path.Dir(source.path)
|
|
if context != nil && context.Root != "" {
|
|
resolved.rootPath = context.Root
|
|
}
|
|
} else if context != nil && context.FS != nil {
|
|
namespace = context.FS.namespace
|
|
resolved.rootPath, resolved.basePath = context.Root, context.BaseDir
|
|
}
|
|
if namespace == nil || nilFilesystem(namespace.filesystem) || !fs.ValidPath(resolved.rootPath) || !fs.ValidPath(resolved.basePath) {
|
|
value := optionDiagnostic(CodeOptionsInvalid, "filesystem context is invalid")
|
|
return resolvedFilesystemContext{}, &value
|
|
}
|
|
resolved.native = namespace
|
|
if !containsNativePath(resolved.rootPath, resolved.basePath) || resolved.mainPath != "" && !containsNativePath(resolved.rootPath, resolved.mainPath) {
|
|
value := importDiagnostic(CodeImportOutsideRoot, "selected source and import base must stay inside the configuration root", nil)
|
|
return resolvedFilesystemContext{}, &value
|
|
}
|
|
return resolved, nil
|
|
}
|
|
|
|
func resolveHostFilesystemContext(source Source, context *FilesystemContext, main parsedSource, hostToken *namespaceToken) (resolvedFilesystemContext, *Diagnostic) {
|
|
if main.source == nil || main.document == nil {
|
|
value := importDiagnostic(CodeImportInvalid, "imports require a selected configuration source", nil)
|
|
return resolvedFilesystemContext{}, &value
|
|
}
|
|
resolved := resolvedFilesystemContext{hostToken: hostToken}
|
|
if source.kind == sourceFile {
|
|
if context != nil && context.BaseDir != "" {
|
|
value := optionDiagnostic(CodeOptionsInvalid, "file sources determine their own import base directory")
|
|
return resolvedFilesystemContext{}, &value
|
|
}
|
|
canonical := main.source.Path
|
|
if canonical == "" {
|
|
var err error
|
|
canonical, err = canonicalExistingPath(source.path)
|
|
if err != nil {
|
|
value := importDiagnostic(CodeImportInvalid, "selected source path could not be canonicalized", nil)
|
|
return resolvedFilesystemContext{}, &value
|
|
}
|
|
}
|
|
resolved.mainPath = canonical
|
|
resolved.basePath = filepath.Dir(canonical)
|
|
resolved.rootPath = resolved.basePath
|
|
if context != nil && context.Root != "" {
|
|
rootPath, err := canonicalExistingPath(context.Root)
|
|
if err != nil {
|
|
value := importDiagnostic(CodeImportInvalid, "configuration root could not be canonicalized", nil)
|
|
return resolvedFilesystemContext{}, &value
|
|
}
|
|
resolved.rootPath = rootPath
|
|
}
|
|
} else {
|
|
if context == nil || context.BaseDir == "" || context.Root == "" {
|
|
value := optionDiagnostic(CodeOptionsInvalid, "byte and reader imports require absolute base and root directories")
|
|
return resolvedFilesystemContext{}, &value
|
|
}
|
|
if !filepath.IsAbs(context.BaseDir) || !filepath.IsAbs(context.Root) {
|
|
value := optionDiagnostic(CodeOptionsInvalid, "byte and reader import base and root directories must be absolute")
|
|
return resolvedFilesystemContext{}, &value
|
|
}
|
|
var err error
|
|
resolved.basePath, err = canonicalExistingPath(context.BaseDir)
|
|
if err != nil {
|
|
value := importDiagnostic(CodeImportInvalid, "import base directory could not be canonicalized", nil)
|
|
return resolvedFilesystemContext{}, &value
|
|
}
|
|
resolved.rootPath, err = canonicalExistingPath(context.Root)
|
|
if err != nil {
|
|
value := importDiagnostic(CodeImportInvalid, "configuration root could not be canonicalized", nil)
|
|
return resolvedFilesystemContext{}, &value
|
|
}
|
|
}
|
|
if !containedPath(resolved.rootPath, resolved.basePath) || resolved.mainPath != "" && !containedPath(resolved.rootPath, resolved.mainPath) {
|
|
value := importDiagnostic(CodeImportOutsideRoot, "selected source and import base must stay inside the configuration root", nil)
|
|
return resolvedFilesystemContext{}, &value
|
|
}
|
|
return resolved, nil
|
|
}
|
|
|
|
func containsNativePath(root, name string) bool {
|
|
if !fs.ValidPath(name) || !fs.ValidPath(root) {
|
|
return false
|
|
}
|
|
return root == "." || name == root || strings.HasPrefix(name, root+"/")
|
|
}
|
|
|
|
func validateResolvedFilesystemDirectories(resolved resolvedFilesystemContext) *Diagnostic {
|
|
for _, directory := range []string{resolved.rootPath, resolved.basePath} {
|
|
var info fs.FileInfo
|
|
var err error
|
|
if resolved.native != nil {
|
|
info, err = fs.Stat(resolved.native.filesystem, directory)
|
|
} else {
|
|
info, err = os.Stat(directory)
|
|
}
|
|
if err != nil || !info.IsDir() {
|
|
value := importDiagnostic(CodeImportInvalid, "configuration root and import base must be directories", nil)
|
|
return &value
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func openResolvedFilesystemAccess(resolved resolvedFilesystemContext) (*filesystemAccess, error) {
|
|
if resolved.native != nil {
|
|
access := newNativeAccess(resolved.native.filesystem, resolved.rootPath, resolved.native.token)
|
|
access.label = resolved.native.label
|
|
return access, nil
|
|
}
|
|
access, err := openHostAccess(resolved.rootPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if resolved.hostToken != nil {
|
|
access.token = resolved.hostToken
|
|
}
|
|
return access, nil
|
|
}
|