2021-12-11 15:05:15 -08:00
|
|
|
package slack
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
)
|
|
|
|
|
|
|
|
// RichTextBlock defines a new block of type rich_text.
|
|
|
|
// More Information: https://api.slack.com/changelog/2019-09-what-they-see-is-what-you-get-and-more-and-less
|
|
|
|
type RichTextBlock struct {
|
|
|
|
Type MessageBlockType `json:"type"`
|
|
|
|
BlockID string `json:"block_id,omitempty"`
|
|
|
|
Elements []RichTextElement `json:"elements"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b RichTextBlock) BlockType() MessageBlockType {
|
|
|
|
return b.Type
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *RichTextBlock) UnmarshalJSON(b []byte) error {
|
|
|
|
var raw struct {
|
|
|
|
Type MessageBlockType `json:"type"`
|
|
|
|
BlockID string `json:"block_id"`
|
|
|
|
RawElements []json.RawMessage `json:"elements"`
|
|
|
|
}
|
|
|
|
if string(b) == "{}" {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if err := json.Unmarshal(b, &raw); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
elems := make([]RichTextElement, 0, len(raw.RawElements))
|
|
|
|
for _, r := range raw.RawElements {
|
|
|
|
var s struct {
|
|
|
|
Type RichTextElementType `json:"type"`
|
|
|
|
}
|
|
|
|
if err := json.Unmarshal(r, &s); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
var elem RichTextElement
|
|
|
|
switch s.Type {
|
|
|
|
case RTESection:
|
|
|
|
elem = &RichTextSection{}
|
2024-05-23 14:44:31 -07:00
|
|
|
case RTEList:
|
|
|
|
elem = &RichTextList{}
|
|
|
|
case RTEQuote:
|
|
|
|
elem = &RichTextQuote{}
|
|
|
|
case RTEPreformatted:
|
|
|
|
elem = &RichTextPreformatted{}
|
2021-12-11 15:05:15 -08:00
|
|
|
default:
|
|
|
|
elems = append(elems, &RichTextUnknown{
|
|
|
|
Type: s.Type,
|
|
|
|
Raw: string(r),
|
|
|
|
})
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if err := json.Unmarshal(r, &elem); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
elems = append(elems, elem)
|
|
|
|
}
|
|
|
|
*e = RichTextBlock{
|
|
|
|
Type: raw.Type,
|
|
|
|
BlockID: raw.BlockID,
|
|
|
|
Elements: elems,
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewRichTextBlock returns a new instance of RichText Block.
|
|
|
|
func NewRichTextBlock(blockID string, elements ...RichTextElement) *RichTextBlock {
|
|
|
|
return &RichTextBlock{
|
|
|
|
Type: MBTRichText,
|
|
|
|
BlockID: blockID,
|
|
|
|
Elements: elements,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type RichTextElementType string
|
|
|
|
|
|
|
|
type RichTextElement interface {
|
|
|
|
RichTextElementType() RichTextElementType
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
RTEList RichTextElementType = "rich_text_list"
|
|
|
|
RTEPreformatted RichTextElementType = "rich_text_preformatted"
|
|
|
|
RTEQuote RichTextElementType = "rich_text_quote"
|
|
|
|
RTESection RichTextElementType = "rich_text_section"
|
|
|
|
RTEUnknown RichTextElementType = "rich_text_unknown"
|
|
|
|
)
|
|
|
|
|
|
|
|
type RichTextUnknown struct {
|
|
|
|
Type RichTextElementType
|
|
|
|
Raw string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u RichTextUnknown) RichTextElementType() RichTextElementType {
|
|
|
|
return u.Type
|
|
|
|
}
|
|
|
|
|
2024-05-23 14:44:31 -07:00
|
|
|
type RichTextListElementType string
|
|
|
|
|
|
|
|
const (
|
|
|
|
RTEListOrdered RichTextListElementType = "ordered"
|
|
|
|
RTEListBullet RichTextListElementType = "bullet"
|
|
|
|
)
|
|
|
|
|
|
|
|
type RichTextList struct {
|
|
|
|
Type RichTextElementType `json:"type"`
|
|
|
|
Elements []RichTextElement `json:"elements"`
|
|
|
|
Style RichTextListElementType `json:"style"`
|
|
|
|
Indent int `json:"indent"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewRichTextList returns a new rich text list element.
|
|
|
|
func NewRichTextList(style RichTextListElementType, indent int, elements ...RichTextElement) *RichTextList {
|
|
|
|
return &RichTextList{
|
|
|
|
Type: RTEList,
|
|
|
|
Elements: elements,
|
|
|
|
Style: style,
|
|
|
|
Indent: indent,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ElementType returns the type of the Element
|
|
|
|
func (s RichTextList) RichTextElementType() RichTextElementType {
|
|
|
|
return s.Type
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *RichTextList) UnmarshalJSON(b []byte) error {
|
|
|
|
var raw struct {
|
|
|
|
RawElements []json.RawMessage `json:"elements"`
|
|
|
|
Style RichTextListElementType `json:"style"`
|
|
|
|
Indent int `json:"indent"`
|
|
|
|
}
|
|
|
|
if string(b) == "{}" {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if err := json.Unmarshal(b, &raw); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
elems := make([]RichTextElement, 0, len(raw.RawElements))
|
|
|
|
for _, r := range raw.RawElements {
|
|
|
|
var s struct {
|
|
|
|
Type RichTextElementType `json:"type"`
|
|
|
|
}
|
|
|
|
if err := json.Unmarshal(r, &s); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
var elem RichTextElement
|
|
|
|
switch s.Type {
|
|
|
|
case RTESection:
|
|
|
|
elem = &RichTextSection{}
|
|
|
|
case RTEList:
|
|
|
|
elem = &RichTextList{}
|
|
|
|
case RTEQuote:
|
|
|
|
elem = &RichTextQuote{}
|
|
|
|
case RTEPreformatted:
|
|
|
|
elem = &RichTextPreformatted{}
|
|
|
|
default:
|
|
|
|
elems = append(elems, &RichTextUnknown{
|
|
|
|
Type: s.Type,
|
|
|
|
Raw: string(r),
|
|
|
|
})
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if err := json.Unmarshal(r, elem); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
elems = append(elems, elem)
|
|
|
|
}
|
|
|
|
*e = RichTextList{
|
|
|
|
Type: RTEList,
|
|
|
|
Elements: elems,
|
|
|
|
Style: raw.Style,
|
|
|
|
Indent: raw.Indent,
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-12-11 15:05:15 -08:00
|
|
|
type RichTextSection struct {
|
|
|
|
Type RichTextElementType `json:"type"`
|
|
|
|
Elements []RichTextSectionElement `json:"elements"`
|
|
|
|
}
|
|
|
|
|
2024-05-23 14:44:31 -07:00
|
|
|
// RichTextElementType returns the type of the Element
|
2021-12-11 15:05:15 -08:00
|
|
|
func (s RichTextSection) RichTextElementType() RichTextElementType {
|
|
|
|
return s.Type
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *RichTextSection) UnmarshalJSON(b []byte) error {
|
|
|
|
var raw struct {
|
|
|
|
RawElements []json.RawMessage `json:"elements"`
|
|
|
|
}
|
|
|
|
if string(b) == "{}" {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if err := json.Unmarshal(b, &raw); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
elems := make([]RichTextSectionElement, 0, len(raw.RawElements))
|
|
|
|
for _, r := range raw.RawElements {
|
|
|
|
var s struct {
|
|
|
|
Type RichTextSectionElementType `json:"type"`
|
|
|
|
}
|
|
|
|
if err := json.Unmarshal(r, &s); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
var elem RichTextSectionElement
|
|
|
|
switch s.Type {
|
|
|
|
case RTSEText:
|
|
|
|
elem = &RichTextSectionTextElement{}
|
|
|
|
case RTSEChannel:
|
|
|
|
elem = &RichTextSectionChannelElement{}
|
|
|
|
case RTSEUser:
|
|
|
|
elem = &RichTextSectionUserElement{}
|
|
|
|
case RTSEEmoji:
|
|
|
|
elem = &RichTextSectionEmojiElement{}
|
|
|
|
case RTSELink:
|
|
|
|
elem = &RichTextSectionLinkElement{}
|
|
|
|
case RTSETeam:
|
|
|
|
elem = &RichTextSectionTeamElement{}
|
|
|
|
case RTSEUserGroup:
|
|
|
|
elem = &RichTextSectionUserGroupElement{}
|
|
|
|
case RTSEDate:
|
|
|
|
elem = &RichTextSectionDateElement{}
|
|
|
|
case RTSEBroadcast:
|
|
|
|
elem = &RichTextSectionBroadcastElement{}
|
|
|
|
case RTSEColor:
|
|
|
|
elem = &RichTextSectionColorElement{}
|
|
|
|
default:
|
|
|
|
elems = append(elems, &RichTextSectionUnknownElement{
|
|
|
|
Type: s.Type,
|
|
|
|
Raw: string(r),
|
|
|
|
})
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if err := json.Unmarshal(r, elem); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
elems = append(elems, elem)
|
|
|
|
}
|
|
|
|
*e = RichTextSection{
|
|
|
|
Type: RTESection,
|
|
|
|
Elements: elems,
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewRichTextSectionBlockElement .
|
|
|
|
func NewRichTextSection(elements ...RichTextSectionElement) *RichTextSection {
|
|
|
|
return &RichTextSection{
|
|
|
|
Type: RTESection,
|
|
|
|
Elements: elements,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type RichTextSectionElementType string
|
|
|
|
|
|
|
|
const (
|
|
|
|
RTSEBroadcast RichTextSectionElementType = "broadcast"
|
|
|
|
RTSEChannel RichTextSectionElementType = "channel"
|
|
|
|
RTSEColor RichTextSectionElementType = "color"
|
|
|
|
RTSEDate RichTextSectionElementType = "date"
|
|
|
|
RTSEEmoji RichTextSectionElementType = "emoji"
|
|
|
|
RTSELink RichTextSectionElementType = "link"
|
|
|
|
RTSETeam RichTextSectionElementType = "team"
|
|
|
|
RTSEText RichTextSectionElementType = "text"
|
|
|
|
RTSEUser RichTextSectionElementType = "user"
|
|
|
|
RTSEUserGroup RichTextSectionElementType = "usergroup"
|
|
|
|
|
|
|
|
RTSEUnknown RichTextSectionElementType = "unknown"
|
|
|
|
)
|
|
|
|
|
|
|
|
type RichTextSectionElement interface {
|
|
|
|
RichTextSectionElementType() RichTextSectionElementType
|
|
|
|
}
|
|
|
|
|
|
|
|
type RichTextSectionTextStyle struct {
|
|
|
|
Bold bool `json:"bold,omitempty"`
|
|
|
|
Italic bool `json:"italic,omitempty"`
|
|
|
|
Strike bool `json:"strike,omitempty"`
|
|
|
|
Code bool `json:"code,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type RichTextSectionTextElement struct {
|
|
|
|
Type RichTextSectionElementType `json:"type"`
|
|
|
|
Text string `json:"text"`
|
|
|
|
Style *RichTextSectionTextStyle `json:"style,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r RichTextSectionTextElement) RichTextSectionElementType() RichTextSectionElementType {
|
|
|
|
return r.Type
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewRichTextSectionTextElement(text string, style *RichTextSectionTextStyle) *RichTextSectionTextElement {
|
|
|
|
return &RichTextSectionTextElement{
|
|
|
|
Type: RTSEText,
|
|
|
|
Text: text,
|
|
|
|
Style: style,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type RichTextSectionChannelElement struct {
|
|
|
|
Type RichTextSectionElementType `json:"type"`
|
|
|
|
ChannelID string `json:"channel_id"`
|
|
|
|
Style *RichTextSectionTextStyle `json:"style,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r RichTextSectionChannelElement) RichTextSectionElementType() RichTextSectionElementType {
|
|
|
|
return r.Type
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewRichTextSectionChannelElement(channelID string, style *RichTextSectionTextStyle) *RichTextSectionChannelElement {
|
|
|
|
return &RichTextSectionChannelElement{
|
|
|
|
Type: RTSEText,
|
|
|
|
ChannelID: channelID,
|
|
|
|
Style: style,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type RichTextSectionUserElement struct {
|
|
|
|
Type RichTextSectionElementType `json:"type"`
|
|
|
|
UserID string `json:"user_id"`
|
|
|
|
Style *RichTextSectionTextStyle `json:"style,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r RichTextSectionUserElement) RichTextSectionElementType() RichTextSectionElementType {
|
|
|
|
return r.Type
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewRichTextSectionUserElement(userID string, style *RichTextSectionTextStyle) *RichTextSectionUserElement {
|
|
|
|
return &RichTextSectionUserElement{
|
|
|
|
Type: RTSEUser,
|
|
|
|
UserID: userID,
|
|
|
|
Style: style,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type RichTextSectionEmojiElement struct {
|
|
|
|
Type RichTextSectionElementType `json:"type"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
SkinTone int `json:"skin_tone"`
|
|
|
|
Style *RichTextSectionTextStyle `json:"style,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r RichTextSectionEmojiElement) RichTextSectionElementType() RichTextSectionElementType {
|
|
|
|
return r.Type
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewRichTextSectionEmojiElement(name string, skinTone int, style *RichTextSectionTextStyle) *RichTextSectionEmojiElement {
|
|
|
|
return &RichTextSectionEmojiElement{
|
|
|
|
Type: RTSEEmoji,
|
|
|
|
Name: name,
|
|
|
|
SkinTone: skinTone,
|
|
|
|
Style: style,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type RichTextSectionLinkElement struct {
|
|
|
|
Type RichTextSectionElementType `json:"type"`
|
|
|
|
URL string `json:"url"`
|
|
|
|
Text string `json:"text"`
|
|
|
|
Style *RichTextSectionTextStyle `json:"style,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r RichTextSectionLinkElement) RichTextSectionElementType() RichTextSectionElementType {
|
|
|
|
return r.Type
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewRichTextSectionLinkElement(url, text string, style *RichTextSectionTextStyle) *RichTextSectionLinkElement {
|
|
|
|
return &RichTextSectionLinkElement{
|
|
|
|
Type: RTSELink,
|
|
|
|
URL: url,
|
|
|
|
Text: text,
|
|
|
|
Style: style,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type RichTextSectionTeamElement struct {
|
|
|
|
Type RichTextSectionElementType `json:"type"`
|
|
|
|
TeamID string `json:"team_id"`
|
2023-08-05 11:43:19 -07:00
|
|
|
Style *RichTextSectionTextStyle `json:"style,omitempty"`
|
2021-12-11 15:05:15 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r RichTextSectionTeamElement) RichTextSectionElementType() RichTextSectionElementType {
|
|
|
|
return r.Type
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewRichTextSectionTeamElement(teamID string, style *RichTextSectionTextStyle) *RichTextSectionTeamElement {
|
|
|
|
return &RichTextSectionTeamElement{
|
|
|
|
Type: RTSETeam,
|
|
|
|
TeamID: teamID,
|
|
|
|
Style: style,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type RichTextSectionUserGroupElement struct {
|
|
|
|
Type RichTextSectionElementType `json:"type"`
|
|
|
|
UsergroupID string `json:"usergroup_id"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r RichTextSectionUserGroupElement) RichTextSectionElementType() RichTextSectionElementType {
|
|
|
|
return r.Type
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewRichTextSectionUserGroupElement(usergroupID string) *RichTextSectionUserGroupElement {
|
|
|
|
return &RichTextSectionUserGroupElement{
|
|
|
|
Type: RTSEUserGroup,
|
|
|
|
UsergroupID: usergroupID,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type RichTextSectionDateElement struct {
|
|
|
|
Type RichTextSectionElementType `json:"type"`
|
2023-01-28 13:57:53 -08:00
|
|
|
Timestamp JSONTime `json:"timestamp"`
|
2021-12-11 15:05:15 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r RichTextSectionDateElement) RichTextSectionElementType() RichTextSectionElementType {
|
|
|
|
return r.Type
|
|
|
|
}
|
|
|
|
|
2023-01-28 13:57:53 -08:00
|
|
|
func NewRichTextSectionDateElement(timestamp int64) *RichTextSectionDateElement {
|
2021-12-11 15:05:15 -08:00
|
|
|
return &RichTextSectionDateElement{
|
|
|
|
Type: RTSEDate,
|
2023-01-28 13:57:53 -08:00
|
|
|
Timestamp: JSONTime(timestamp),
|
2021-12-11 15:05:15 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type RichTextSectionBroadcastElement struct {
|
|
|
|
Type RichTextSectionElementType `json:"type"`
|
|
|
|
Range string `json:"range"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r RichTextSectionBroadcastElement) RichTextSectionElementType() RichTextSectionElementType {
|
|
|
|
return r.Type
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewRichTextSectionBroadcastElement(rangeStr string) *RichTextSectionBroadcastElement {
|
|
|
|
return &RichTextSectionBroadcastElement{
|
|
|
|
Type: RTSEBroadcast,
|
|
|
|
Range: rangeStr,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type RichTextSectionColorElement struct {
|
|
|
|
Type RichTextSectionElementType `json:"type"`
|
|
|
|
Value string `json:"value"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r RichTextSectionColorElement) RichTextSectionElementType() RichTextSectionElementType {
|
|
|
|
return r.Type
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewRichTextSectionColorElement(value string) *RichTextSectionColorElement {
|
|
|
|
return &RichTextSectionColorElement{
|
|
|
|
Type: RTSEColor,
|
|
|
|
Value: value,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type RichTextSectionUnknownElement struct {
|
|
|
|
Type RichTextSectionElementType `json:"type"`
|
|
|
|
Raw string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r RichTextSectionUnknownElement) RichTextSectionElementType() RichTextSectionElementType {
|
|
|
|
return r.Type
|
|
|
|
}
|
2024-05-23 14:44:31 -07:00
|
|
|
|
|
|
|
// RichTextQuote represents rich_text_quote element type.
|
|
|
|
type RichTextQuote RichTextSection
|
|
|
|
|
|
|
|
// RichTextElementType returns the type of the Element
|
|
|
|
func (s *RichTextQuote) RichTextElementType() RichTextElementType {
|
|
|
|
return s.Type
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *RichTextQuote) UnmarshalJSON(b []byte) error {
|
|
|
|
// reusing the RichTextSection struct, as it's the same as RichTextQuote.
|
|
|
|
var rts RichTextSection
|
|
|
|
if err := json.Unmarshal(b, &rts); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
*s = RichTextQuote(rts)
|
|
|
|
s.Type = RTEQuote
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// RichTextPreformatted represents rich_text_quote element type.
|
|
|
|
type RichTextPreformatted struct {
|
|
|
|
RichTextSection
|
|
|
|
Border int `json:"border"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// RichTextElementType returns the type of the Element
|
|
|
|
func (s *RichTextPreformatted) RichTextElementType() RichTextElementType {
|
|
|
|
return s.Type
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *RichTextPreformatted) UnmarshalJSON(b []byte) error {
|
|
|
|
var rts RichTextSection
|
|
|
|
if err := json.Unmarshal(b, &rts); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// we define standalone fields because we need to unmarshal the border
|
|
|
|
// field. We can not directly unmarshal the data into
|
|
|
|
// RichTextPreformatted because it will cause an infinite loop. We also
|
|
|
|
// can not define a struct with embedded RichTextSection and Border fields
|
|
|
|
// because the json package will not unmarshal the data into the
|
|
|
|
// standalone fields, once it sees UnmarshalJSON method on the embedded
|
|
|
|
// struct. The drawback is that we have to process the data twice, and
|
|
|
|
// have to define a standalone struct with the same set of fields as the
|
|
|
|
// original struct, which may become a maintenance burden (i.e. update the
|
|
|
|
// fields in two places, should it ever change).
|
|
|
|
var standalone struct {
|
|
|
|
Border int `json:"border"`
|
|
|
|
}
|
|
|
|
if err := json.Unmarshal(b, &standalone); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
*s = RichTextPreformatted{
|
|
|
|
RichTextSection: rts,
|
|
|
|
Border: standalone.Border,
|
|
|
|
}
|
|
|
|
s.Type = RTEPreformatted
|
|
|
|
return nil
|
|
|
|
}
|