2022-03-19 20:57:33 +00:00
package categories
import (
"encoding/json"
"github.com/bouncepaw/mycorrhiza/files"
"github.com/bouncepaw/mycorrhiza/util"
2022-04-17 13:07:33 +00:00
"golang.org/x/exp/slices"
2022-03-19 20:57:33 +00:00
"log"
"os"
2022-04-17 13:07:33 +00:00
"sort"
2022-03-20 11:48:16 +00:00
"sync"
2022-03-19 20:57:33 +00:00
)
var categoryToHyphae = map [ string ] * categoryNode { }
var hyphaToCategories = map [ string ] * hyphaNode { }
2022-04-01 22:01:54 +00:00
// Init initializes the category system. Call it after the Structure is initialized. This function might terminate the program in case of a bad mood or filesystem faults.
func Init ( ) {
2022-03-19 20:57:33 +00:00
var (
record , err = readCategoriesFromDisk ( )
)
if err != nil {
log . Fatalln ( err )
}
for _ , cat := range record . Categories {
if len ( cat . Hyphae ) == 0 {
continue
}
cat . Name = util . CanonicalName ( cat . Name )
for i , hyphaName := range cat . Hyphae {
cat . Hyphae [ i ] = util . CanonicalName ( hyphaName )
}
2022-04-17 13:07:33 +00:00
sort . Strings ( cat . Hyphae )
2022-03-19 20:57:33 +00:00
categoryToHyphae [ cat . Name ] = & categoryNode { hyphaList : cat . Hyphae }
}
for cat , hyphaeInCat := range categoryToHyphae {
for _ , hyphaName := range hyphaeInCat . hyphaList {
if node , ok := hyphaToCategories [ hyphaName ] ; ok {
node . storeCategory ( cat )
} else {
hyphaToCategories [ hyphaName ] = & hyphaNode { categoryList : [ ] string { cat } }
}
}
}
log . Println ( "Found" , len ( categoryToHyphae ) , "categories" )
}
type categoryNode struct {
hyphaList [ ] string
}
func ( cn * categoryNode ) storeHypha ( hypname string ) {
2022-04-17 13:07:33 +00:00
i , found := slices . BinarySearch ( cn . hyphaList , hypname )
if found {
return
2022-03-19 20:57:33 +00:00
}
2022-04-17 13:07:33 +00:00
cn . hyphaList = slices . Insert ( cn . hyphaList , i , hypname )
2022-03-19 20:57:33 +00:00
}
2022-03-20 09:28:51 +00:00
func ( cn * categoryNode ) removeHypha ( hypname string ) {
2022-04-17 13:07:33 +00:00
i , found := slices . BinarySearch ( cn . hyphaList , hypname )
if ! found {
return
2022-03-20 09:28:51 +00:00
}
2022-04-17 13:07:33 +00:00
cn . hyphaList = slices . Delete ( cn . hyphaList , i , i + 1 )
2022-03-20 09:28:51 +00:00
}
2022-03-19 20:57:33 +00:00
type hyphaNode struct {
categoryList [ ] string
}
2022-04-17 13:07:33 +00:00
// inserts sorted
2022-03-19 20:57:33 +00:00
func ( hn * hyphaNode ) storeCategory ( cat string ) {
2022-04-17 13:07:33 +00:00
i , found := slices . BinarySearch ( hn . categoryList , cat )
if found {
return
2022-03-19 20:57:33 +00:00
}
2022-04-17 13:07:33 +00:00
hn . categoryList = slices . Insert ( hn . categoryList , i , cat )
2022-03-19 20:57:33 +00:00
}
2022-03-20 09:28:51 +00:00
func ( hn * hyphaNode ) removeCategory ( cat string ) {
2022-04-17 13:07:33 +00:00
i , found := slices . BinarySearch ( hn . categoryList , cat )
if ! found {
return
2022-03-20 09:28:51 +00:00
}
2022-04-17 13:07:33 +00:00
hn . categoryList = slices . Delete ( hn . categoryList , i , i + 1 )
2022-03-20 09:28:51 +00:00
}
2022-03-19 20:57:33 +00:00
type catFileRecord struct {
Categories [ ] catRecord ` json:"categories" `
}
type catRecord struct {
Name string ` json:"name" `
Hyphae [ ] string ` json:"hyphae" `
}
func readCategoriesFromDisk ( ) ( catFileRecord , error ) {
var (
record catFileRecord
categoriesFile = files . CategoriesJSON ( )
fileContents , err = os . ReadFile ( categoriesFile )
)
if os . IsNotExist ( err ) {
return record , nil
}
if err != nil {
return record , err
}
err = json . Unmarshal ( fileContents , & record )
if err != nil {
return record , err
}
return record , nil
}
2022-03-20 11:48:16 +00:00
var fileMutex sync . Mutex
func saveToDisk ( ) {
var (
record catFileRecord
)
for name , node := range categoryToHyphae {
record . Categories = append ( record . Categories , catRecord {
Name : name ,
Hyphae : node . hyphaList ,
} )
}
data , err := json . MarshalIndent ( record , "" , "\t" )
if err != nil {
log . Fatalln ( err ) // Better fail now, than later
}
// TODO: make the data safer somehow?? Back it up before overwriting?
fileMutex . Lock ( )
err = os . WriteFile ( files . CategoriesJSON ( ) , data , 0666 )
if err != nil {
log . Fatalln ( err )
}
fileMutex . Unlock ( )
}