commit a439d275a8384994f6408e51de38553275fba918 from: Oliver Lowe date: Sun Feb 04 11:43:33 2024 UTC lemmy/fs: KISS. Keep posts and comments as files in the same dir Kinda like NNTP. commit - d4b74d7a68368284eca18e00ef626542ae8bb20c commit + a439d275a8384994f6408e51de38553275fba918 blob - 0e77af598acb419e017ac16d27b15bf86ec8c861 blob + 3501d481db3d7c8fc4ae0c37bf21e1787f61ba69 --- fs/file.go +++ fs/file.go @@ -2,11 +2,9 @@ package fs import ( "bytes" - "errors" "fmt" "io" "io/fs" - "strconv" "strings" "time" @@ -116,99 +114,55 @@ func (f *comFile) ReadDir(n int) ([]fs.DirEntry, error if err != nil { return nil, &fs.PathError{"readdir", f.name, err} } - for i := range posts { - f.dirinfo.entries = append(f.dirinfo.entries, fs.FileInfoToDirEntry(&posts[i])) + for _, post := range posts { + post := post + f.dirinfo.entries = append(f.dirinfo.entries, fs.FileInfoToDirEntry(&post)) + comments, err := f.client.Comments(post.ID, lemmy.ListAll) + if err != nil { + return nil, &fs.PathError{"readdir", f.name, err} + } + for i := range comments { + f.dirinfo.entries = append(f.dirinfo.entries, fs.FileInfoToDirEntry(&comments[i])) + } } } return f.dirinfo.ReadDir(n) } - -type postFile struct { - post lemmy.Post - dirinfo *dirInfo - client *lemmy.Client - buf io.ReadCloser -} - -func (f *postFile) Stat() (fs.FileInfo, error) { - name := strconv.Itoa(f.post.ID) - var err error - f.post, err = f.client.LookupPost(f.post.ID) - if errors.Is(err, lemmy.ErrNotFound) { - return nil, &fs.PathError{"stat", name, fs.ErrNotExist} - } else if err != nil { - return nil, &fs.PathError{"stat", name, err} +func postFile(p *lemmy.Post) *dummy { + buf := &bytes.Buffer{} + fmt.Fprintln(buf, "From:", p.CreatorID) + fmt.Fprintf(buf, "Message-Id: <%d>\n", p.ID) + fmt.Fprintf(buf, "List-Archive: <%s>\n", p.ActivityURL) + fmt.Fprintln(buf, "Date:", p.ModTime().Format(time.RFC822)) + fmt.Fprintln(buf, "Subject:", p.Title) + fmt.Fprintln(buf) + if p.URL != "" { + fmt.Fprintln(buf, p.URL) } - return &f.post, nil -} - -func (f *postFile) Read(p []byte) (int, error) { - if f.buf == nil { - return 0, io.EOF + fmt.Fprintln(buf, p.Body) + return &dummy{ + name: p.Name(), + mode: p.Mode(), + mtime: p.ModTime(), + buf: io.NopCloser(buf), } - return f.buf.Read(p) } -func (f *postFile) Close() error { - if f.buf == nil || f.dirinfo == nil { - return fs.ErrClosed - } - f.dirinfo = nil - err := f.buf.Close() - f.buf = nil - return err -} +func commentFile(c *lemmy.Comment, person lemmy.Person, post lemmy.Post) *dummy { + buf := &bytes.Buffer{} + fmt.Fprintln(buf, "From:", person.String()) + fmt.Fprintln(buf, "Date:", c.ModTime().Format(time.RFC822)) + fmt.Fprintf(buf, "Message-Id: <%d>\n", c.ID) + fmt.Fprintf(buf, "List-Archive: <%s>\n", c.ActivityURL) + fmt.Fprintln(buf, "Subject: Re:", post.Title) -func (f *postFile) ReadDir(n int) ([]fs.DirEntry, error) { - name := strconv.Itoa(f.post.ID) - if f.dirinfo == nil { - f.dirinfo = new(dirInfo) - for _, v := range postFiles(f.post) { - f.dirinfo.entries = append(f.dirinfo.entries, v) - } - comments, err := f.client.Comments(f.post.ID, lemmy.ListAll) - if err != nil { - return nil, &fs.PathError{"readdir", name, err} - } - for i := range comments { - f.dirinfo.entries = append(f.dirinfo.entries, fs.FileInfoToDirEntry(&comments[i])) - } + refs, err := c.References() + if err != nil || len(refs) <= 2 { + fmt.Fprintf(buf, "References: <%d>\n", c.PostID) + } else { + fmt.Fprintf(buf, "References: <%d>\n", refs[len(refs)-2]) } - return f.dirinfo.ReadDir(n) -} -func postFiles(post lemmy.Post) map[string]*dummy { - m := make(map[string]*dummy) - m["body"] = &dummy{ - name: "body", - mode: 0444, - contents: []byte(post.Body), - mtime: post.ModTime(), - } - m["creator"] = &dummy{ - name: "creator", - mode: 0444, - contents: []byte(strconv.Itoa(post.CreatorID)), - mtime: post.ModTime(), - } - m["title"] = &dummy{ - name: "title", - mode: 0444, - contents: []byte(post.Title), - mtime: post.ModTime(), - } - m["url"] = &dummy{ - name: "url", - mode: 0444, - contents: []byte(post.URL), - mtime: post.ModTime(), - } - return m -} - -func commentFile(c *lemmy.Comment) *dummy { - buf := &bytes.Buffer{} - fmt.Fprintf(buf, "From: %d\n", c.CreatorID) fmt.Fprintln(buf) fmt.Fprintln(buf, c.Content) return &dummy{ blob - 798eb37e257ab1e4e90922d31f86ab832ec5688f blob + 0c8f335984284995fb3d46ebb73e9eb9a761bdd9 --- fs/fs.go +++ fs/fs.go @@ -60,12 +60,16 @@ func (fsys *FS) Open(name string) (fs.File, error) { return nil, fmt.Errorf("start fs: %w", err) } } - if name == "." { return fsys.openRoot() } elems := strings.Split(name, "/") + // We've only got communities, then posts/comments. + // Anything deeper cannot exist. + if len(elems) >= 3 { + return nil, &fs.PathError{"open", name, fs.ErrNotExist} + } community, ok := fsys.Communities[elems[0]] if !ok { @@ -88,54 +92,23 @@ func (fsys *FS) Open(name string) (fs.File, error) { id, err := strconv.Atoi(elems[1]) if err != nil { - return nil, &fs.PathError{"open", name, fmt.Errorf("bad post id")} + return nil, &fs.PathError{"open", name, fmt.Errorf("bad post/comment id")} } - post, ok := fsys.Posts[id] - if !ok || len(elems) == 2 { - post, err = fsys.Client.LookupPost(id) - if errors.Is(err, lemmy.ErrNotFound) { - delete(fsys.Posts, id) - return nil, &fs.PathError{"open", name, fs.ErrNotExist} - } else if err != nil { - return nil, &fs.PathError{"open", name, err} - } - fsys.Posts[id] = post - } - if len(elems) == 2 { - return &postFile{ - post: post, - buf: io.NopCloser(strings.NewReader("")), - client: fsys.Client, - }, nil + comment, creator, post, err := fsys.Client.LookupComment(id) + if err == nil { + return commentFile(&comment, creator, post), nil + } else if !errors.Is(err, lemmy.ErrNotFound) { + return nil, &fs.PathError{"open", name, err} } - // At the bottom of the tree. If there's more than one element left then - // it cannot exist. - if len(elems) > 3 { - return nil, &fs.PathError{"open", name, fs.ErrNotExist} - } - - // if it's not a numbered directory (comment) it must be a regular file. - if !strings.ContainsAny(elems[2], "0123456789") { - files := postFiles(post) - if f, ok := files[elems[2]]; ok { - return f, nil - } - return nil, &fs.PathError{"open", name, fs.ErrNotExist} - } - - id, err = strconv.Atoi(elems[2]) - if err != nil { - return nil, &fs.PathError{"open", name, fmt.Errorf("bad comment id")} - } - comment, err := fsys.Client.LookupComment(id) + post, err = fsys.Client.LookupPost(id) if errors.Is(err, lemmy.ErrNotFound) { return nil, &fs.PathError{"open", name, fs.ErrNotExist} } else if err != nil { return nil, &fs.PathError{"open", name, err} } - return commentFile(&comment), nil + return postFile(&post), nil } func (fsys *FS) openRoot() (fs.File, error) {