package gott import ( "crypto/sha256" "encoding/gob" "encoding/hex" "os" "path/filepath" ) func init() { // Register all AST types with gob for serialization. // Gob requires concrete types to be registered when encoding/decoding interfaces. // Root and text nodes gob.Register(&Template{}) gob.Register(&TextNode{}) // Expression nodes gob.Register(&IdentExpr{}) gob.Register(&LiteralExpr{}) gob.Register(&BinaryExpr{}) gob.Register(&UnaryExpr{}) gob.Register(&CallExpr{}) gob.Register(&FilterExpr{}) gob.Register(&DefaultExpr{}) // Statement nodes gob.Register(&OutputStmt{}) gob.Register(&IfStmt{}) gob.Register(&UnlessStmt{}) gob.Register(&ForeachStmt{}) gob.Register(&BlockStmt{}) gob.Register(&IncludeStmt{}) gob.Register(&WrapperStmt{}) gob.Register(&SetStmt{}) gob.Register(&TryStmt{}) // Supporting types gob.Register(&ElsIfClause{}) gob.Register(PathPart{}) } // diskCache persists compiled templates to disk using gob encoding. type diskCache struct { path string // directory for cache files } // newDiskCache creates a new disk cache at the specified path. // Creates the directory if it doesn't exist. func newDiskCache(path string) (*diskCache, error) { if err := os.MkdirAll(path, 0755); err != nil { return nil, err } return &diskCache{path: path}, nil } // hashKey creates a safe filename from a cache key using SHA256. func hashKey(key string) string { h := sha256.Sum256([]byte(key)) return hex.EncodeToString(h[:]) + ".gob" } // Get retrieves a cached template from disk. // Returns nil and no error if the file doesn't exist. func (d *diskCache) Get(key string) (*Template, error) { filename := filepath.Join(d.path, hashKey(key)) file, err := os.Open(filename) if err != nil { if os.IsNotExist(err) { return nil, nil } return nil, err } defer file.Close() var tmpl Template decoder := gob.NewDecoder(file) if err := decoder.Decode(&tmpl); err != nil { // Corrupted cache file - remove it os.Remove(filename) return nil, nil } return &tmpl, nil } // Put stores a template to disk. func (d *diskCache) Put(key string, tmpl *Template) error { filename := filepath.Join(d.path, hashKey(key)) file, err := os.Create(filename) if err != nil { return err } defer file.Close() encoder := gob.NewEncoder(file) return encoder.Encode(tmpl) } // Clear removes all cached templates from disk. func (d *diskCache) Clear() error { entries, err := os.ReadDir(d.path) if err != nil { return err } for _, entry := range entries { if entry.IsDir() { continue } // Only remove .gob files to avoid deleting unrelated files if filepath.Ext(entry.Name()) == ".gob" { os.Remove(filepath.Join(d.path, entry.Name())) } } return nil }