package storage import ( "errors" "os" "path/filepath" "strings" ) // Store manages AsciiDoc files on disk. type Store struct { root string } func New(root string) (*Store, error) { if err := os.MkdirAll(root, 0755); err != nil { return nil, err } return &Store{root: root}, nil } // Read returns the content of a document by its slug path (e.g. "guides/install"). func (s *Store) Read(slug string) (string, error) { data, err := os.ReadFile(s.filePath(slug)) if err != nil { if errors.Is(err, os.ErrNotExist) { return "", ErrNotFound } return "", err } return string(data), nil } // Write persists content to disk, creating parent directories as needed. func (s *Store) Write(slug, content string) error { path := s.filePath(slug) if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { return err } return os.WriteFile(path, []byte(content), 0644) } // Delete removes a document from disk. func (s *Store) Delete(slug string) error { return os.Remove(s.filePath(slug)) } // List returns all document slugs under an optional prefix. func (s *Store) List(prefix string) ([]string, error) { base := filepath.Join(s.root, filepath.FromSlash(prefix)) var slugs []string err := filepath.WalkDir(base, func(path string, d os.DirEntry, err error) error { if err != nil { return err } if !d.IsDir() && strings.HasSuffix(path, ".adoc") { rel, _ := filepath.Rel(s.root, path) slug := strings.TrimSuffix(filepath.ToSlash(rel), ".adoc") slugs = append(slugs, slug) } return nil }) return slugs, err } func (s *Store) filePath(slug string) string { return filepath.Join(s.root, filepath.FromSlash(slug)+".adoc") } var ErrNotFound = errors.New("document not found")