package engine import ( "io/fs" "path" "path/filepath" "strings" ) func contextsForAcquiredSources(source Source, context *FilesystemContext, baseSource *SourceInfo, sources []SourceInfo) (map[string]ResourceContext, *Diagnostic) { if len(sources) == 0 { return nil, nil } var rootPath, basePath string var filesystem fs.FS if source.kind == sourceFilesystem { filesystem = source.namespace.filesystem rootPath, basePath = ".", path.Dir(source.path) if context != nil && context.Root != "" { rootPath = context.Root } } else if context != nil && context.FS != nil { filesystem = context.FS.namespace.filesystem rootPath, basePath = context.Root, context.BaseDir } else if source.kind == sourceFile { filePath := sources[0].Path if baseSource != nil { filePath = baseSource.Path } basePath = filepath.Dir(filePath) rootPath = basePath if context != nil && context.Root != "" { canonical, err := canonicalExistingPath(context.Root) if err != nil { value := importDiagnostic(CodeImportInvalid, "configuration root could not be canonicalized", nil) return nil, &value } rootPath = canonical } } else if context != nil { var err error rootPath, err = canonicalExistingPath(context.Root) if err != nil { value := importDiagnostic(CodeImportInvalid, "configuration root could not be canonicalized", nil) return nil, &value } basePath, err = canonicalExistingPath(context.BaseDir) if err != nil { value := importDiagnostic(CodeImportInvalid, "import base directory could not be canonicalized", nil) return nil, &value } } else { return nil, nil } var rooted fs.FS if filesystem != nil { rooted = rootedNativeFS{filesystem: filesystem, root: rootPath} } else { rooted = rootedHostFS{root: rootPath} } result := make(map[string]ResourceContext, len(sources)) for _, acquired := range sources { base := basePath if acquired.Path != "" { if filesystem != nil { base = path.Dir(acquired.Path) } else { base = filepath.Dir(acquired.Path) } } var relative string if filesystem != nil { if !fs.ValidPath(rootPath) || !fs.ValidPath(base) || rootPath != "." && base != rootPath && !strings.HasPrefix(base, rootPath+"/") { value := importDiagnostic(CodeImportOutsideRoot, "source base is outside the configuration root", nil) return nil, &value } relative = base if rootPath != "." { relative = strings.TrimPrefix(strings.TrimPrefix(base, rootPath), "/") if relative == "" { relative = "." } } } else { if !containedPath(rootPath, base) { value := importDiagnostic(CodeImportOutsideRoot, "source base is outside the configuration root", nil) return nil, &value } var err error relative, err = filepath.Rel(rootPath, base) if err != nil { value := importDiagnostic(CodeImportInvalid, "source base could not be addressed in the configuration root", nil) return nil, &value } relative = filepath.ToSlash(relative) } result[acquired.ID] = ResourceContext{FS: rooted, BaseDir: relative} } return result, nil } func mergeResourceContexts(target *map[string]ResourceContext, incoming map[string]ResourceContext) { if len(incoming) == 0 { return } if *target == nil { *target = make(map[string]ResourceContext, len(incoming)) } for id, context := range incoming { (*target)[id] = context } }