blob: 667661b57e4f7e727def2de948baec99e9b9a0cb [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package checklicenses
import (
"encoding/json"
"fmt"
"path/filepath"
"sync"
)
type File struct {
Name string
Path string `json:"-"`
Symlink string `json:"-"`
Parent *FileTree `json:"-"`
Licenses []*License `json:"licenses"`
}
func NewFile(path string, parent *FileTree) (*File, error) {
if f := globalFileMap.retrieveFileIfExists(path); f != nil {
return f, nil
}
symlink, err := filepath.EvalSymlinks(path)
if err != nil {
return nil, fmt.Errorf("error creating file %v: %v\n", path, err)
}
file := &File{
Name: filepath.Base(path),
Path: path,
Symlink: symlink,
Parent: parent,
}
return globalFileMap.insertFileIfMissing(file), nil
}
// Use a custom Marshal function to make Files easier to read in JSON:
// reduce associated license information down to a string list.
func (f *File) MarshalJSON() ([]byte, error) {
type Alias File
licenseList := []string{}
for _, l := range f.Licenses {
licenseList = append(licenseList, l.Category)
}
return json.Marshal(&struct {
*Alias
Licenses []string `json:"licenses"`
}{
Alias: (*Alias)(f),
Licenses: licenseList,
})
}
// ============================================================================
// Maintain a global map of File objects to ensure we don't accidentally
// process the same file multiple times, which may happen during dependency
// traversal.
type fileMap struct {
internal map[string]*File
sync.RWMutex
}
var globalFileMap = fileMap{internal: make(map[string]*File)}
func (f *fileMap) retrieveFileIfExists(path string) *File {
f.RLock()
defer f.RUnlock()
return f.internal[path]
}
func (f *fileMap) insertFileIfMissing(file *File) *File {
f.Lock()
defer f.Unlock()
if val, ok := f.internal[file.Path]; ok {
// TODO: merge file info into val before returning.
return val
}
f.internal[file.Path] = file
return file
}