2021-05-11 10:14:00 +00:00
|
|
|
// Package hyphae is for the Hypha type, hypha storage and stuff like that. It shall not depend on mycorrhiza modules other than util.
|
2021-02-04 15:47:09 +00:00
|
|
|
package hyphae
|
|
|
|
|
|
|
|
import (
|
2021-02-17 18:41:35 +00:00
|
|
|
"log"
|
2021-07-30 14:16:58 +00:00
|
|
|
"path/filepath"
|
2021-02-17 18:41:35 +00:00
|
|
|
"regexp"
|
2021-10-27 06:43:01 +00:00
|
|
|
"strings"
|
2021-02-04 15:47:09 +00:00
|
|
|
"sync"
|
2021-10-27 06:43:01 +00:00
|
|
|
|
|
|
|
"github.com/bouncepaw/mycorrhiza/files"
|
2021-02-04 15:47:09 +00:00
|
|
|
)
|
|
|
|
|
2021-10-27 06:43:01 +00:00
|
|
|
// HyphaPattern is a pattern which all hyphae names must match.
|
2021-05-11 10:14:00 +00:00
|
|
|
var HyphaPattern = regexp.MustCompile(`[^?!:#@><*|"'&%{}]+`)
|
2021-02-17 18:41:35 +00:00
|
|
|
|
2021-10-27 06:43:01 +00:00
|
|
|
// IsValidName checks for invalid characters and path traversals.
|
|
|
|
func IsValidName(hyphaName string) bool {
|
|
|
|
if !HyphaPattern.MatchString(hyphaName) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
for _, segment := range strings.Split(hyphaName, "/") {
|
|
|
|
if segment == ".git" || segment == ".." {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2021-10-01 17:12:16 +00:00
|
|
|
// Hypha keeps vital information about a hypha
|
2021-02-04 15:47:09 +00:00
|
|
|
type Hypha struct {
|
|
|
|
sync.RWMutex
|
|
|
|
|
2021-03-14 13:16:30 +00:00
|
|
|
Name string // Canonical name
|
2021-02-04 15:47:09 +00:00
|
|
|
Exists bool
|
2021-03-14 13:16:30 +00:00
|
|
|
TextPath string // == "" => no text part
|
|
|
|
BinaryPath string // == "" => no attachment
|
2021-02-17 18:41:35 +00:00
|
|
|
}
|
|
|
|
|
2021-07-30 14:16:58 +00:00
|
|
|
// TextPartPath returns rooted path to the file where the text part should be.
|
|
|
|
func (h *Hypha) TextPartPath() string {
|
|
|
|
if h.TextPath == "" {
|
|
|
|
return filepath.Join(files.HyphaeDir(), h.Name+".myco")
|
|
|
|
}
|
|
|
|
return h.TextPath
|
|
|
|
}
|
|
|
|
|
2021-08-08 19:19:55 +00:00
|
|
|
// HasAttachment is true if the hypha has an attachment.
|
|
|
|
func (h *Hypha) HasAttachment() bool {
|
|
|
|
return h.BinaryPath != ""
|
|
|
|
}
|
|
|
|
|
2021-02-17 18:41:35 +00:00
|
|
|
var byNames = make(map[string]*Hypha)
|
|
|
|
var byNamesMutex = sync.Mutex{}
|
|
|
|
|
|
|
|
// EmptyHypha returns an empty hypha struct with given name.
|
|
|
|
func EmptyHypha(hyphaName string) *Hypha {
|
|
|
|
return &Hypha{
|
|
|
|
Name: hyphaName,
|
|
|
|
Exists: false,
|
|
|
|
TextPath: "",
|
|
|
|
BinaryPath: "",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-14 13:16:30 +00:00
|
|
|
// ByName returns a hypha by name. It may have been recorded to the storage.
|
2021-02-17 18:41:35 +00:00
|
|
|
func ByName(hyphaName string) (h *Hypha) {
|
2021-03-14 13:16:30 +00:00
|
|
|
h, recorded := byNames[hyphaName]
|
|
|
|
if recorded {
|
2021-02-17 18:41:35 +00:00
|
|
|
return h
|
|
|
|
}
|
|
|
|
return EmptyHypha(hyphaName)
|
2021-02-04 15:47:09 +00:00
|
|
|
}
|
|
|
|
|
2021-03-14 13:16:30 +00:00
|
|
|
func storeHypha(h *Hypha) {
|
2021-02-17 18:41:35 +00:00
|
|
|
byNamesMutex.Lock()
|
2021-03-14 13:16:30 +00:00
|
|
|
byNames[h.Name] = h
|
|
|
|
byNamesMutex.Unlock()
|
2021-03-14 15:20:02 +00:00
|
|
|
|
|
|
|
h.Lock()
|
|
|
|
h.Exists = true
|
|
|
|
h.Unlock()
|
2021-03-14 13:16:30 +00:00
|
|
|
}
|
|
|
|
|
2021-12-20 20:59:23 +00:00
|
|
|
// insert inserts the hypha into the storage. A previous record is used if possible. Count incrementation is done if needed.
|
|
|
|
func (h *Hypha) insert() (justRecorded bool) {
|
2021-03-14 13:16:30 +00:00
|
|
|
hp, recorded := byNames[h.Name]
|
|
|
|
if recorded {
|
2021-12-20 20:59:23 +00:00
|
|
|
hp.mergeIn(h)
|
2021-02-17 18:41:35 +00:00
|
|
|
} else {
|
2021-03-14 13:16:30 +00:00
|
|
|
storeHypha(h)
|
2021-12-20 20:59:23 +00:00
|
|
|
incrementCount()
|
2021-02-04 15:47:09 +00:00
|
|
|
}
|
|
|
|
|
2021-03-14 13:16:30 +00:00
|
|
|
return !recorded
|
2021-02-17 18:41:35 +00:00
|
|
|
}
|
2021-02-04 15:47:09 +00:00
|
|
|
|
2021-10-01 17:12:16 +00:00
|
|
|
// InsertIfNew checks whether hypha exists and returns `true` if it didn't and has been created.
|
2021-03-14 13:16:30 +00:00
|
|
|
func (h *Hypha) InsertIfNew() (justRecorded bool) {
|
2021-02-18 07:12:57 +00:00
|
|
|
if !h.Exists {
|
2021-12-20 20:59:23 +00:00
|
|
|
return h.insert()
|
2021-02-17 18:41:35 +00:00
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2021-10-01 17:12:16 +00:00
|
|
|
// Delete removes a hypha from the storage.
|
2021-02-20 14:03:54 +00:00
|
|
|
func (h *Hypha) Delete() {
|
2021-02-17 18:41:35 +00:00
|
|
|
byNamesMutex.Lock()
|
|
|
|
h.Lock()
|
|
|
|
delete(byNames, h.Name)
|
2021-12-20 20:59:23 +00:00
|
|
|
decrementCount()
|
2021-02-17 18:41:35 +00:00
|
|
|
byNamesMutex.Unlock()
|
|
|
|
h.Unlock()
|
|
|
|
}
|
|
|
|
|
2021-10-01 17:12:16 +00:00
|
|
|
// RenameTo renames a hypha and performs respective changes in the storage.
|
2021-02-20 14:03:54 +00:00
|
|
|
func (h *Hypha) RenameTo(newName string) {
|
2021-02-17 18:41:35 +00:00
|
|
|
byNamesMutex.Lock()
|
2021-02-04 15:47:09 +00:00
|
|
|
h.Lock()
|
2021-02-17 18:41:35 +00:00
|
|
|
delete(byNames, h.Name)
|
|
|
|
h.Name = newName
|
|
|
|
byNames[h.Name] = h
|
|
|
|
byNamesMutex.Unlock()
|
2021-02-04 15:47:09 +00:00
|
|
|
h.Unlock()
|
|
|
|
}
|
2021-02-17 18:41:35 +00:00
|
|
|
|
2021-12-20 20:59:23 +00:00
|
|
|
// mergeIn merges in content file paths from a different hypha object. Prints warnings sometimes.
|
|
|
|
func (h *Hypha) mergeIn(oh *Hypha) {
|
2021-03-14 13:16:30 +00:00
|
|
|
if h == oh {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
h.Lock()
|
2021-02-17 18:41:35 +00:00
|
|
|
if h.TextPath == "" && oh.TextPath != "" {
|
|
|
|
h.TextPath = oh.TextPath
|
|
|
|
}
|
|
|
|
if oh.BinaryPath != "" {
|
|
|
|
if h.BinaryPath != "" {
|
2021-07-25 13:08:59 +00:00
|
|
|
log.Println("There is a file collision for attachment of a hypha:", h.BinaryPath, "and", oh.BinaryPath, "-- going on with the latter")
|
2021-02-17 18:41:35 +00:00
|
|
|
}
|
|
|
|
h.BinaryPath = oh.BinaryPath
|
|
|
|
}
|
2021-03-14 13:16:30 +00:00
|
|
|
h.Unlock()
|
2021-03-02 16:36:57 +00:00
|
|
|
}
|