package stylemap import ( "fmt" "io" "log" "os" "prose/watcher" "strings" "sync" "github.com/bep/godartsass/v2" ) type styleMap struct { mu sync.RWMutex stylesheets map[string]string transpiler *godartsass.Transpiler } // New returns a new styleMap. func New() (watcher.AutoMap[string, string], error) { folder, err := os.ReadDir("styles/") if err != nil { return nil, fmt.Errorf("could not load styles directory: %w", err) } sassTranspiler, err := godartsass.Start(godartsass.Options{}) if err != nil { return nil, fmt.Errorf("could not start sass transpiler: %w", err) } sm := &styleMap{ stylesheets: make(map[string]string), transpiler: sassTranspiler, } for _, s := range folder { err := sm.fetchLocked(s.Name()) if err != nil { return nil, fmt.Errorf("could not generate styles for %s: %w", s.Name(), err) } log.Printf("loaded stylesheet %q", s.Name()) } go watcher.Watch("styles/", sm) return sm, nil } func (sm *styleMap) Get(filename string) (string, bool) { sm.mu.RLock() defer sm.mu.RUnlock() contents, ok := sm.stylesheets[filename] return contents, ok } func (sm *styleMap) Delete(filename string) error { sm.mu.Lock() defer sm.mu.Unlock() if s, ok := strings.CutSuffix(filename, ".scss"); ok { filename = s + ".css" } delete(sm.stylesheets, filename) return nil } func (sm *styleMap) Fetch(filename string) (err error) { sm.mu.Lock() defer sm.mu.Unlock() return sm.fetchLocked(filename) } func (sm *styleMap) fetchLocked(filename string) (err error) { var outfile, contents string if strings.HasSuffix(filename, ".scss") { outfile, contents, err = sm.loadSCSS(filename) } else { outfile, contents, err = sm.loadCSS(filename) } if err != nil { return } sm.stylesheets[outfile] = contents return } func (sm *styleMap) loadSCSS(filename string) (string, string, error) { in, err := os.Open("styles/" + filename) if err != nil { return "", "", fmt.Errorf("could not open stylesheet %s: %w", filename, err) } stylesheet, err := io.ReadAll(in) if err != nil { return "", "", fmt.Errorf("could not read stylesheet %s: %w", filename, err) } res, err := sm.transpiler.Execute(godartsass.Args{ Source: string(stylesheet), }) if err != nil { return "", "", fmt.Errorf("could not generate stylesheet %s: %w", filename, err) } return strings.TrimSuffix(filename, ".scss") + ".css", res.CSS, nil } func (sm *styleMap) loadCSS(filename string) (string, string, error) { in, err := os.Open("styles/" + filename) if err != nil { return "", "", fmt.Errorf("could not open style infile %s: %w", filename, err) } var buf strings.Builder _, err = io.Copy(&buf, in) if err != nil { return "", "", fmt.Errorf("could not copy stylesheet %s: %w", filename, err) } return filename, buf.String(), nil }