github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/examples/gno.land/p/demo/microblog/microblog.gno (about)

     1  package microblog
     2  
     3  import (
     4  	"errors"
     5  	"sort"
     6  	"std"
     7  	"strings"
     8  	"time"
     9  
    10  	"gno.land/p/demo/avl"
    11  	"gno.land/p/demo/ufmt"
    12  )
    13  
    14  var (
    15  	ErrNotFound    = errors.New("not found")
    16  	StatusNotFound = "404"
    17  )
    18  
    19  type Microblog struct {
    20  	Title  string
    21  	Prefix string   // i.e. r/gnoland/blog:
    22  	Pages  avl.Tree // author (string) -> Page
    23  }
    24  
    25  func NewMicroblog(title string, prefix string) (m *Microblog) {
    26  	return &Microblog{
    27  		Title:  title,
    28  		Prefix: prefix,
    29  		Pages:  avl.Tree{},
    30  	}
    31  }
    32  
    33  func (m *Microblog) GetPages() []*Page {
    34  	var (
    35  		pages = make([]*Page, m.Pages.Size())
    36  		index = 0
    37  	)
    38  
    39  	m.Pages.Iterate("", "", func(key string, value interface{}) bool {
    40  		pages[index] = value.(*Page)
    41  		index++
    42  		return false
    43  	})
    44  
    45  	sort.Sort(byLastPosted(pages))
    46  
    47  	return pages
    48  }
    49  
    50  func (m *Microblog) NewPost(text string) error {
    51  	author := std.GetOrigCaller()
    52  	_, found := m.Pages.Get(author.String())
    53  	if !found {
    54  		// make a new page for the new author
    55  		m.Pages.Set(author.String(), &Page{
    56  			Author:    author,
    57  			CreatedAt: time.Now(),
    58  		})
    59  	}
    60  
    61  	page, err := m.GetPage(author.String())
    62  	if err != nil {
    63  		return err
    64  	}
    65  	return page.NewPost(text)
    66  }
    67  
    68  func (m *Microblog) GetPage(author string) (*Page, error) {
    69  	silo, found := m.Pages.Get(author)
    70  	if !found {
    71  		return nil, ErrNotFound
    72  	}
    73  	return silo.(*Page), nil
    74  }
    75  
    76  type Page struct {
    77  	ID         int
    78  	Author     std.Address
    79  	CreatedAt  time.Time
    80  	LastPosted time.Time
    81  	Posts      avl.Tree // time -> Post
    82  }
    83  
    84  // byLastPosted implements sort.Interface for []Page based on
    85  // the LastPosted field.
    86  type byLastPosted []*Page
    87  
    88  func (a byLastPosted) Len() int           { return len(a) }
    89  func (a byLastPosted) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
    90  func (a byLastPosted) Less(i, j int) bool { return a[i].LastPosted.After(a[j].LastPosted) }
    91  
    92  func (p *Page) NewPost(text string) error {
    93  	now := time.Now()
    94  	p.LastPosted = now
    95  	p.Posts.Set(ufmt.Sprintf("%s%d", now.Format(time.RFC3339), p.Posts.Size()), &Post{
    96  		ID:        p.Posts.Size(),
    97  		Text:      text,
    98  		CreatedAt: now,
    99  	})
   100  	return nil
   101  }
   102  
   103  func (p *Page) GetPosts() []*Post {
   104  	posts := make([]*Post, p.Posts.Size())
   105  	i := 0
   106  	p.Posts.ReverseIterate("", "", func(key string, value interface{}) bool {
   107  		postParsed := value.(*Post)
   108  		posts[i] = postParsed
   109  		i++
   110  		return false
   111  	})
   112  	return posts
   113  }
   114  
   115  // Post lists the specific update
   116  type Post struct {
   117  	ID        int
   118  	CreatedAt time.Time
   119  	Text      string
   120  }
   121  
   122  func (p *Post) String() string {
   123  	return "> " + strings.ReplaceAll(p.Text, "\n", "\n>\n>") + "\n>\n> *" + p.CreatedAt.Format(time.RFC1123) + "*"
   124  }