blob: ec45e3be6b7802e161946ec9f592932f24ae9c48 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package bufimage
import (
"errors"
"fmt"
)
var _ Image = &image{}
type image struct {
files []ImageFile
pathToImageFile map[string]ImageFile
}
func newImage(files []ImageFile, reorder bool) (*image, error) {
if len(files) == 0 {
return nil, errors.New("image contains no files")
}
pathToImageFile := make(map[string]ImageFile, len(files))
identityStringToCommit := make(map[string]string)
for _, file := range files {
path := file.Path()
if _, ok := pathToImageFile[path]; ok {
return nil, fmt.Errorf("duplicate file: %s", path)
}
pathToImageFile[path] = file
if moduleIdentity := file.ModuleIdentity(); moduleIdentity != nil {
identityString := moduleIdentity.IdentityString()
existingCommit, ok := identityStringToCommit[identityString]
if ok {
if existingCommit != file.Commit() {
return nil, fmt.Errorf("image had two different commits for the same module: %q and %q", existingCommit, file.Commit())
}
} else {
identityStringToCommit[identityString] = file.Commit()
}
}
}
if reorder {
files = orderImageFiles(files, pathToImageFile)
}
return &image{
files: files,
pathToImageFile: pathToImageFile,
}, nil
}
func newImageNoValidate(files []ImageFile) *image {
pathToImageFile := make(map[string]ImageFile, len(files))
for _, file := range files {
path := file.Path()
pathToImageFile[path] = file
}
return &image{
files: files,
pathToImageFile: pathToImageFile,
}
}
func (i *image) Files() []ImageFile {
return i.files
}
func (i *image) GetFile(path string) ImageFile {
return i.pathToImageFile[path]
}
func (*image) isImage() {}
// orderImageFiles re-orders the ImageFiles in DAG order.
func orderImageFiles(
inputImageFiles []ImageFile,
pathToImageFile map[string]ImageFile,
) []ImageFile {
outputImageFiles := make([]ImageFile, 0, len(inputImageFiles))
alreadySeen := map[string]struct{}{}
for _, inputImageFile := range inputImageFiles {
outputImageFiles = orderImageFilesRec(
inputImageFile,
outputImageFiles,
pathToImageFile,
alreadySeen,
)
}
return outputImageFiles
}
func orderImageFilesRec(
inputImageFile ImageFile,
outputImageFiles []ImageFile,
pathToImageFile map[string]ImageFile,
alreadySeen map[string]struct{},
) []ImageFile {
path := inputImageFile.Path()
if _, ok := alreadySeen[path]; ok {
return outputImageFiles
}
alreadySeen[path] = struct{}{}
for _, dependency := range inputImageFile.FileDescriptor().GetDependency() {
dependencyImageFile, ok := pathToImageFile[dependency]
if ok {
outputImageFiles = orderImageFilesRec(
dependencyImageFile,
outputImageFiles,
pathToImageFile,
alreadySeen,
)
}
}
return append(outputImageFiles, inputImageFile)
}