// cache.go package main import ( "crypto/md5" "encoding/hex" "os" "path/filepath" "sync" ) var ( memoryCache = make(map[string][]byte) cacheMutex sync.RWMutex cacheDir = "cache" // TestMode disables caching when set to true TestMode bool ) // InitCache ensures the cache directory exists func InitCache() { if _, err := os.Stat(cacheDir); os.IsNotExist(err) { os.Mkdir(cacheDir, 0755) } } // ClearCache wipes the cache directory (used for Test Mode) func ClearCache() { os.RemoveAll(cacheDir) InitCache() } // getCacheFilename generates the hashed filename preserving the extension func getCacheFilename(key string) string { hash := md5.Sum([]byte(key)) ext := filepath.Ext(key) // Default to .html if no extension (e.g. for processed markdown) if ext == "" || ext == ".md" { ext = ".html" } return filepath.Join(cacheDir, hex.EncodeToString(hash[:])+ext) } // CheckCache looks for content in memory, then on disk. func CheckCache(key string) ([]byte, bool) { if TestMode { return nil, false } cacheMutex.RLock() // 1. Check Memory if data, found := memoryCache[key]; found { cacheMutex.RUnlock() return data, true } cacheMutex.RUnlock() // 2. Check Disk filePath := getCacheFilename(key) data, err := os.ReadFile(filePath) if err == nil { // Populate memory for next time cacheMutex.Lock() memoryCache[key] = data cacheMutex.Unlock() return data, true } return nil, false } // StoreCache saves content to memory and disk. func StoreCache(key string, data []byte) error { if TestMode { return nil } cacheMutex.Lock() defer cacheMutex.Unlock() // 1. Save to memory memoryCache[key] = data // 2. Save to disk filePath := getCacheFilename(key) return os.WriteFile(filePath, data, 0644) }