130 lines
3.7 KiB
Go
130 lines
3.7 KiB
Go
package entity
|
|
|
|
import "fmt"
|
|
import "strings"
|
|
import "unicode"
|
|
import "unicode/utf8"
|
|
import "path/filepath"
|
|
import "github.com/google/uuid"
|
|
import "git.tebibyte.media/fspl/fspl/errors"
|
|
|
|
// Metadata represents a module metadata file.
|
|
type Metadata struct {
|
|
Pos errors.Position
|
|
UUID uuid.UUID
|
|
Dependencies []*Dependency
|
|
}
|
|
func (this *Metadata) Position () errors.Position { return this.Pos }
|
|
func (this *Metadata) String () string {
|
|
out := fmt.Sprint(Quote(this.UUID.String()))
|
|
for _, dependency := range this.Dependencies {
|
|
out += fmt.Sprint("\n", dependency)
|
|
}
|
|
return out
|
|
}
|
|
|
|
// Directive is a declaration within a module metadata file.
|
|
type Directive interface {
|
|
fmt.Stringer
|
|
|
|
// Position returns the position of the directive within its metadata
|
|
// file.
|
|
Position () errors.Position
|
|
|
|
directive()
|
|
}
|
|
|
|
var _ Directive = &Dependency { }
|
|
// Dependency is a metadata dependency listing.
|
|
type Dependency struct {
|
|
Pos errors.Position
|
|
Address Address
|
|
Nickname string
|
|
}
|
|
func (*Dependency) directive () { }
|
|
func (this *Dependency) Position () errors.Position { return this.Pos }
|
|
func (this *Dependency) String () string {
|
|
out := fmt.Sprint("+ ", this.Address)
|
|
if this.Nickname != "" {
|
|
out += fmt.Sprint(" ", this.Nickname)
|
|
}
|
|
return out
|
|
}
|
|
|
|
// Address is the address of a unit.
|
|
type Address string
|
|
func (addr Address) String () string {
|
|
return Quote(string(addr))
|
|
}
|
|
// SourceFile returns the FSPL source file associated with the address. If the
|
|
// address does not point to an FSPL source file, it returns "", false.
|
|
func (addr Address) SourceFile () (string, bool) {
|
|
ext := filepath.Ext(string(addr))
|
|
if ext == ".fspl" {
|
|
return filepath.Clean(string(addr)), true
|
|
} else {
|
|
return "", false
|
|
}
|
|
}
|
|
// Module returns the module associated with the address. If the address does
|
|
// does not point to a module, it returns "", false.
|
|
func (addr Address) Module () (string, bool) {
|
|
path := string(addr)
|
|
switch {
|
|
case filepath.Base(path) == "fspl.mod":
|
|
return filepath.Dir(path), true
|
|
case filepath.Ext(path) == "" || filepath.Ext(path) == ".":
|
|
return filepath.Clean(path), true
|
|
default:
|
|
return "", false
|
|
}
|
|
}
|
|
// Nickname automatically generates a nickname from the address, which is a
|
|
// valid Ident token. On failure "", false is returned. The nickname is
|
|
// generated according to the folowing rules:
|
|
//
|
|
// - If the name contains at least one dot, the last dot and everything after
|
|
// it is removed
|
|
// - All non-letter, non-numeric characters are removed, and any letters that
|
|
// were directly after them are converted to uppercase
|
|
// - All numeric digits at the start of the string are removed
|
|
// - The first character is converted to lowercase
|
|
func (addr Address) Nickname () (string, bool) {
|
|
// get the normalized basename with no extension
|
|
base := filepath.Base(string(addr))
|
|
base = strings.TrimSuffix(base, filepath.Ext(base))
|
|
|
|
// remove all non-letter, non-digit characters and convert to camel case
|
|
nickname := ""
|
|
uppercase := false
|
|
for _, char := range base {
|
|
if unicode.IsLetter(char) || unicode.IsDigit(char) {
|
|
if uppercase {
|
|
uppercase = false
|
|
char = unicode.ToUpper(char)
|
|
}
|
|
nickname += string(char)
|
|
} else {
|
|
uppercase = true
|
|
}
|
|
}
|
|
|
|
// remove numeric digits
|
|
for len(nickname) > 0 {
|
|
char, size := utf8.DecodeRuneInString(nickname)
|
|
nickname = nickname[size:]
|
|
if unicode.IsLetter(char) {
|
|
// lowercase the first letter
|
|
nickname = string(unicode.ToLower(char)) + nickname
|
|
break
|
|
}
|
|
}
|
|
|
|
return nickname, nickname != ""
|
|
}
|
|
// UUID automatically generates a UUID from the address. It is the MD5 hash of
|
|
// the basename, with a space of the zero UUID.
|
|
func (addr Address) UUID () uuid.UUID {
|
|
return uuid.NewMD5(uuid.UUID { }, []byte(filepath.Base(string(addr))))
|
|
}
|