123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- package pub
- import (
- "net/url"
- "mimicry/style"
- "errors"
- "mimicry/object"
- "time"
- "mimicry/client"
- "golang.org/x/exp/slices"
- "fmt"
- "strings"
- "mimicry/ansi"
- "mimicry/mime"
- "mimicry/render"
- )
- type Actor struct {
- kind string
- name string; nameErr error
- handle string; handleErr error
- id *url.URL
- bio string; bioErr error
- mediaType *mime.MediaType; mediaTypeErr error
- joined time.Time; joinedErr error
- pfp *Link; pfpErr error
- banner *Link; bannerErr error
- posts *Collection; postsErr error
- }
- func NewActor(input any, source *url.URL) (*Actor, error) {
- o, id, err := client.FetchUnknown(input, source)
- if err != nil { return nil, err }
- return NewActorFromObject(o, id)
- }
- func NewActorFromObject(o object.Object, id *url.URL) (*Actor, error) {
- a := &Actor{}
- a.id = id
- var err error
- if a.kind, err = o.GetString("type"); err != nil {
- return nil, err
- }
- if !slices.Contains([]string{
- "Application", "Group", "Organization", "Person", "Service",
- }, a.kind) {
- return nil, fmt.Errorf("%w: %s is not an Actor", ErrWrongType, a.kind)
- }
- a.name, a.nameErr = o.GetNatural("name", "en")
- a.handle, a.handleErr = o.GetString("preferredUsername")
- a.bio, a.bioErr = o.GetNatural("summary", "en")
- if a.bio == "" {
- a.bioErr = object.ErrKeyNotPresent
- }
- a.mediaType, a.mediaTypeErr = o.GetMediaType("mediaType")
- a.joined, a.joinedErr = o.GetTime("published")
- a.pfp, a.pfpErr = getBestLink(o, "icon", "image")
- a.banner, a.bannerErr = getBestLink(o, "image", "image")
-
- a.posts, a.postsErr = getCollection(o, "outbox", a.id)
- return a, nil
- }
- func (a *Actor) Kind() string {
- return a.kind
- }
- func (a *Actor) Parents(quantity uint) []Tangible {
- return []Tangible{}
- }
- func (a *Actor) Children(quantity uint) ([]Tangible, Container, uint) {
- if errors.Is(a.postsErr, object.ErrKeyNotPresent) {
- return []Tangible{}, nil, 0
- }
- if a.postsErr != nil {
- return []Tangible{
- NewFailure(a.postsErr),
- }, nil, 0
- }
- return a.posts.Harvest(quantity, 0)
- }
- func (a *Actor) Name() string {
- var output string
- if a.nameErr == nil {
- output = a.name
- } else if !errors.Is(a.nameErr, object.ErrKeyNotPresent) {
- output = style.Problem(a.nameErr)
- }
- if a.id != nil && !errors.Is(a.handleErr, object.ErrKeyNotPresent) {
- if output != "" { output += " " }
- if a.handleErr != nil {
- output += style.Problem(a.handleErr)
- } else {
- output += style.Italic("@" + a.handle + "@" + a.id.Host)
- }
- }
- if a.kind != "Person" {
- if output != "" { output += " " }
- output += "(" + strings.ToLower(a.kind) + ")"
- } else if output == "" {
- output = strings.ToLower(a.kind)
- }
- return style.Color(output)
- }
- func (a *Actor) header(width int) string {
- output := a.Name()
- if errors.Is(a.joinedErr, object.ErrKeyNotPresent) {
-
- } else if a.joinedErr != nil {
- output += "\njoined " + style.Problem(a.joinedErr)
- } else {
- output += "\njoined " + style.Color(a.joined.Format("2 Jan 2006"))
- }
- return ansi.Wrap(output, width)
- }
- func (a *Actor) center(width int) (string, bool) {
- if errors.Is(a.bioErr, object.ErrKeyNotPresent) {
- return "", false
- }
- if a.bioErr != nil {
- return ansi.Wrap(style.Problem(a.bioErr), width), true
- }
- mediaType := a.mediaType
- if errors.Is(a.mediaTypeErr, object.ErrKeyNotPresent) {
- mediaType = mime.Default()
- } else if a.mediaTypeErr != nil {
- return ansi.Wrap(style.Problem(a.mediaTypeErr), width), true
- }
- rendered, err := render.Render(a.bio, mediaType.Essence, width)
- if err != nil {
- return style.Problem(err), true
- }
- return rendered, true
- }
- func (a *Actor) footer(width int) (string, bool) {
- if errors.Is(a.postsErr, object.ErrKeyNotPresent) {
- return style.Problem(a.postsErr), true
- } else if a.postsErr != nil {
- return "", false
- } else if quantity, err := a.posts.Size(); errors.Is(err, object.ErrKeyNotPresent) {
- return "", false
- } else if err != nil {
- return style.Problem(err), true
- } else if quantity == 1 {
- return style.Color(fmt.Sprintf("%d post", quantity)), true
- } else {
- return style.Color(fmt.Sprintf("%d posts", quantity)), true
- }
- }
- func (a *Actor) String(width int) string {
- output := a.header(width)
- if body, present := a.center(width - 4); present {
- output += "\n\n" + ansi.Indent(body, " ", true) + "\n"
- }
- if footer, present := a.footer(width); present {
- output += "\n" + footer
- }
- return output
- }
- func (a Actor) Preview(width int) string {
- output := a.header(width)
-
- if body, present := a.center(width); present {
- output += "\n" + ansi.Snip(body, width, 4, style.Color("\u2026"))
- }
- if footer, present := a.footer(width); present {
- output += "\n" + footer
- }
- return output
- }
|