Skip to content

Commit

Permalink
Merge pull request #1260 from rusq/rich-text-stuff
Browse files Browse the repository at this point in the history
Add rich_text_quote and rich_text_preformatted
  • Loading branch information
parsley42 committed Feb 16, 2024
2 parents 5dbee1c + e1cc0bf commit 2a9c35c
Show file tree
Hide file tree
Showing 2 changed files with 231 additions and 3 deletions.
68 changes: 67 additions & 1 deletion block_rich_text.go
Expand Up @@ -42,7 +42,10 @@ func (e *RichTextBlock) UnmarshalJSON(b []byte) error {
elem = &RichTextSection{}
case RTEList:
elem = &RichTextList{}

case RTEQuote:
elem = &RichTextQuote{}
case RTEPreformatted:
elem = &RichTextPreformatted{}
default:
elems = append(elems, &RichTextUnknown{
Type: s.Type,
Expand Down Expand Up @@ -150,6 +153,10 @@ func (e *RichTextList) UnmarshalJSON(b []byte) error {
elem = &RichTextSection{}
case RTEList:
elem = &RichTextList{}
case RTEQuote:
elem = &RichTextQuote{}
case RTEPreformatted:
elem = &RichTextPreformatted{}
default:
elems = append(elems, &RichTextUnknown{
Type: s.Type,
Expand Down Expand Up @@ -460,3 +467,62 @@ type RichTextSectionUnknownElement struct {
func (r RichTextSectionUnknownElement) RichTextSectionElementType() RichTextSectionElementType {
return r.Type
}

// 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
}
166 changes: 164 additions & 2 deletions block_rich_text_test.go
Expand Up @@ -27,6 +27,60 @@ const (
}
]
}`

richTextQuotePayload = `{
"type": "rich_text",
"block_id": "G7G",
"elements": [
{
"type": "rich_text_section",
"elements": [
{
"type": "text",
"text": "Holy moly\n\n"
}
]
},
{
"type": "rich_text_preformatted",
"elements": [
{
"type": "text",
"text": "Preformatted\n\n"
}
],
"border": 2
},
{
"type": "rich_text_quote",
"elements": [
{
"type": "text",
"text": "Quote\n\n"
}
]
},
{
"type": "rich_text_quote",
"elements": [
{
"type": "text",
"text": "Another quote"
}
]
},
{
"type": "rich_text_preformatted",
"elements": [
{
"type": "text",
"text": "Another preformatted\n\n"
}
],
"border": 42
}
]
}`
)

func TestRichTextBlock_UnmarshalJSON(t *testing.T) {
Expand Down Expand Up @@ -55,6 +109,38 @@ func TestRichTextBlock_UnmarshalJSON(t *testing.T) {
},
nil,
},
{
[]byte(dummyPayload),
RichTextBlock{
Type: MBTRichText,
BlockID: "FaYCD",
Elements: []RichTextElement{
&RichTextSection{
Type: RTESection,
Elements: []RichTextSectionElement{
&RichTextSectionChannelElement{Type: RTSEChannel, ChannelID: "C012345678"},
&RichTextSectionTextElement{Type: RTSEText, Text: "dummy_text"},
},
},
},
},
nil,
},
{
[]byte(richTextQuotePayload),
RichTextBlock{
Type: MBTRichText,
BlockID: "G7G",
Elements: []RichTextElement{
&RichTextSection{Type: RTESection, Elements: []RichTextSectionElement{&RichTextSectionTextElement{Type: RTSEText, Text: "Holy moly\n\n"}}},
&RichTextPreformatted{RichTextSection: RichTextSection{Type: RTEPreformatted, Elements: []RichTextSectionElement{&RichTextSectionTextElement{Type: RTSEText, Text: "Preformatted\n\n"}}}, Border: 2},
&RichTextQuote{Type: RTEQuote, Elements: []RichTextSectionElement{&RichTextSectionTextElement{Type: RTSEText, Text: "Quote\n\n"}}},
&RichTextQuote{Type: RTEQuote, Elements: []RichTextSectionElement{&RichTextSectionTextElement{Type: RTSEText, Text: "Another quote"}}},
&RichTextPreformatted{RichTextSection: RichTextSection{Type: RTEPreformatted, Elements: []RichTextSectionElement{&RichTextSectionTextElement{Type: RTSEText, Text: "Another preformatted\n\n"}}}, Border: 42},
},
},
nil,
},
}
for _, tc := range cases {
var actual RichTextBlock
Expand All @@ -63,10 +149,10 @@ func TestRichTextBlock_UnmarshalJSON(t *testing.T) {
if tc.err == nil {
t.Errorf("unexpected error: %s", err)
}
t.Errorf("expected error is %s, but got %s", tc.err, err)
t.Errorf("expected error is %v, but got %v", tc.err, err)
}
if tc.err != nil {
t.Errorf("expected to raise an error %s", tc.err)
t.Errorf("expected to raise an error %v", tc.err)
}
if diff := deep.Equal(actual, tc.expected); diff != nil {
t.Errorf("actual value does not match expected one\n%s", diff)
Expand Down Expand Up @@ -199,3 +285,79 @@ func TestRichTextList_UnmarshalJSON(t *testing.T) {
}
}
}

func TestRichTextQuote_Marshal(t *testing.T) {
t.Run("rich_text_section", func(t *testing.T) {
const rawRSE = "{\"type\":\"rich_text_section\",\"elements\":[{\"type\":\"text\",\"text\":\"Some Text\"}]}"

var got RichTextSection
if err := json.Unmarshal([]byte(rawRSE), &got); err != nil {
t.Fatal(err)
}
want := RichTextSection{
Type: RTESection,
Elements: []RichTextSectionElement{
&RichTextSectionTextElement{Type: RTSEText, Text: "Some Text"},
},
}

if diff := deep.Equal(got, want); diff != nil {
t.Errorf("actual value does not match expected one\n%s", diff)
}
b, err := json.Marshal(got)
if err != nil {
t.Fatal(err)
}
if diff := deep.Equal(string(b), rawRSE); diff != nil {
t.Errorf("actual value does not match expected one\n%s", diff)
}
})
t.Run("rich_text_quote", func(t *testing.T) {
const rawRTS = "{\"type\":\"rich_text_quote\",\"elements\":[{\"type\":\"text\",\"text\":\"Some text\"}]}"

var got RichTextQuote
if err := json.Unmarshal([]byte(rawRTS), &got); err != nil {
t.Fatal(err)
}
want := RichTextQuote{
Type: RTEQuote,
Elements: []RichTextSectionElement{
&RichTextSectionTextElement{Type: RTSEText, Text: "Some text"},
},
}
if diff := deep.Equal(got, want); diff != nil {
t.Errorf("actual value does not match expected one\n%s", diff)
}
b, err := json.Marshal(got)
if err != nil {
t.Fatal(err)
}
if diff := deep.Equal(string(b), rawRTS); diff != nil {
t.Errorf("actual value does not match expected one\n%s", diff)
}
})
t.Run("rich_text_preformatted", func(t *testing.T) {
const rawRTP = "{\"type\":\"rich_text_preformatted\",\"elements\":[{\"type\":\"text\",\"text\":\"Some other text\"}],\"border\":2}"
want := RichTextPreformatted{
RichTextSection: RichTextSection{
Type: RTEPreformatted,
Elements: []RichTextSectionElement{&RichTextSectionTextElement{Type: RTSEText, Text: "Some other text"}},
},
Border: 2,
}
var got RichTextPreformatted
if err := json.Unmarshal([]byte(rawRTP), &got); err != nil {
t.Fatal(err)
}
if diff := deep.Equal(got, want); diff != nil {
t.Errorf("actual value does not match expected one\n%s", diff)
}
b, err := json.Marshal(got)
if err != nil {
t.Fatal(err)
}
if diff := deep.Equal(string(b), rawRTP); diff != nil {
t.Errorf("actual value does not match expected one\n%s", diff)
}
})
}

0 comments on commit 2a9c35c

Please sign in to comment.