summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--content.go42
-rw-r--r--document.go7
-rw-r--r--parse_test.go348
-rw-r--r--write_test.go150
4 files changed, 297 insertions, 250 deletions
diff --git a/content.go b/content.go
index 6a728f5..dbe0fa4 100644
--- a/content.go
+++ b/content.go
@@ -28,6 +28,9 @@ type ContainerBlock interface {
// Children returns the child blocks.
Children() []Block
+
+ // AppendChild adds a new child block to the end of the list of children.
+ AppendChild(Block)
}
// ContentBlock represents a block that holds other content blocks.
@@ -467,45 +470,12 @@ func parseContentList(p *Parser, block *TokenBlock) (*ListBlock, error) {
// TableBlock represents a "table" content block.
type TableBlock struct {
- rows []Block
+ ContentBlock
}
// NewTableBlock creates a new TableBlock.
func NewTableBlock() *TableBlock {
- return &TableBlock{}
-}
-
-// Name returns the block name "table".
-func (t *TableBlock) Name() string {
- return "table"
-}
-
-// Args returns the block's nil arguments.
-func (t *TableBlock) Args() []string {
- return nil
-}
-
-// WriteIndent writes the table header and contents indented by n tabs.
-func (t *TableBlock) WriteIndent(w io.Writer, n int) error {
- if err := WriteIndent(w, t.Name(), n); err != nil {
- return err
- }
- for _, row := range t.rows {
- if err := row.WriteIndent(w, n+1); err != nil {
- return err
- }
- }
- return nil
-}
-
-// Children returns the table's rows.
-func (t *TableBlock) Children() []Block {
- return t.rows
-}
-
-// AppendRow adds a new row to the end of the table.
-func (t *TableBlock) AppendRow(row Block) {
- t.rows = append(t.rows, row)
+ return &TableBlock{*NewContentBlock("table", "")}
}
func (t *TableBlock) parse(p *Parser, block *TokenBlock) (err error) {
@@ -526,7 +496,7 @@ func (t *TableBlock) parse(p *Parser, block *TokenBlock) (err error) {
err = parseUnknown(p, blk)
}
if b != nil {
- t.AppendRow(b)
+ t.AppendChild(b)
}
} else if err = p.Next(); err != nil {
break
diff --git a/document.go b/document.go
index c01837c..65eac3e 100644
--- a/document.go
+++ b/document.go
@@ -23,10 +23,15 @@ type Document struct {
Content *ContentBlock
}
+// NewDocument creates a new Document.
+func NewDocument() *Document {
+ return &Document{}
+}
+
// ParseDocument parses a CNM document from r.
func ParseDocument(r io.Reader) (doc *Document, err error) {
p := NewParser(r)
- doc = &Document{}
+ doc = NewDocument()
err = p.Next()
for err == nil {
token := p.Block()
diff --git a/parse_test.go b/parse_test.go
index fc0ef87..e70b833 100644
--- a/parse_test.go
+++ b/parse_test.go
@@ -196,19 +196,23 @@ var parseTests = map[string]*Document{
name: "content",
args: nil,
children: []Block{
- &TableBlock{rows: []Block{
- &RowBlock{ContentBlock{
- name: "row",
- args: nil,
- children: []Block{
- &TextBlock{
- Format: "",
- Contents: TextPlainContents{Paragraphs: []string{
- "test",
- }},
+ &TableBlock{ContentBlock{
+ name: "table",
+ args: nil,
+ children: []Block{
+ &RowBlock{ContentBlock{
+ name: "row",
+ args: nil,
+ children: []Block{
+ &TextBlock{
+ Format: "",
+ Contents: TextPlainContents{Paragraphs: []string{
+ "test",
+ }},
+ },
},
- },
- }},
+ }},
+ },
}},
},
},
@@ -379,79 +383,83 @@ content
name: "section",
args: []string{"of the"},
children: []Block{
- &TableBlock{[]Block{
- &HeaderBlock{ContentBlock{
- name: "header",
- args: nil,
- children: []Block{
- &TextBlock{
- Contents: TextPlainContents{[]string{
- "Column 1",
- }},
- },
- &TextBlock{
- Contents: TextPlainContents{[]string{
- "Column 2",
- }},
+ &TableBlock{ContentBlock{
+ name: "table",
+ args: nil,
+ children: []Block{
+ &HeaderBlock{ContentBlock{
+ name: "header",
+ args: nil,
+ children: []Block{
+ &TextBlock{
+ Contents: TextPlainContents{[]string{
+ "Column 1",
+ }},
+ },
+ &TextBlock{
+ Contents: TextPlainContents{[]string{
+ "Column 2",
+ }},
+ },
},
- },
- }},
- &RowBlock{ContentBlock{
- name: "row",
- args: nil,
- children: []Block{
- &TextBlock{
- Contents: TextPlainContents{[]string{
- "CNM",
- }},
+ }},
+ &RowBlock{ContentBlock{
+ name: "row",
+ args: nil,
+ children: []Block{
+ &TextBlock{
+ Contents: TextPlainContents{[]string{
+ "CNM",
+ }},
+ },
+ &TextBlock{
+ Contents: TextPlainContents{[]string{
+ "document",
+ "format",
+ }},
+ },
},
- &TextBlock{
- Contents: TextPlainContents{[]string{
- "document",
- "format",
+ }},
+ &RowBlock{ContentBlock{
+ name: "row",
+ args: nil,
+ children: []Block{
+ &SectionBlock{ContentBlock{
+ name: "section",
+ args: nil,
}},
- },
- },
- }},
- &RowBlock{ContentBlock{
- name: "row",
- args: nil,
- children: []Block{
- &SectionBlock{ContentBlock{
- name: "section",
- args: nil,
- }},
- &ListBlock{ContentBlock{
- name: "list",
- args: nil,
- children: []Block{
- &TextBlock{
- Contents: TextPlainContents{[]string{
- "ipsum",
- }},
- },
- &ListBlock{ContentBlock{
- name: "list",
- args: []string{"ordered"},
- children: []Block{
- &ListBlock{ContentBlock{
- name: "list",
- args: []string{"unordered"},
- children: []Block{
- &TextBlock{
- Contents: TextPlainContents{[]string{
- "dolor",
- "sit amet",
- }},
- },
- },
+ &ListBlock{ContentBlock{
+ name: "list",
+ args: nil,
+ children: []Block{
+ &TextBlock{
+ Contents: TextPlainContents{[]string{
+ "ipsum",
}},
},
- }},
- },
- }},
- },
- }},
+ &ListBlock{ContentBlock{
+ name: "list",
+ args: []string{"ordered"},
+ children: []Block{
+ &ListBlock{ContentBlock{
+ name: "list",
+ args: []string{"unordered"},
+ children: []Block{
+ &TextBlock{
+ Contents: TextPlainContents{[]string{
+ "dolor",
+ "sit amet",
+ }},
+ },
+ },
+ }},
+ },
+ }},
+ },
+ }},
+ },
+ }},
+ },
}},
},
}},
@@ -474,251 +482,311 @@ func TestParse(t *testing.T) {
if err != nil {
t.Fatalf("ParseDocument(%q): error: %v", k, err)
}
- if !documentEqual(d, v) {
- t.Fatalf("ParseDocument(%q):\nexpected:\n%#v\n got:\n%#v\n\n%#v\n%#v", k, v, d, v.Content.Children()[0], d.Content.Children()[0])
+ if !documentEqual(t, d, v) {
+ t.Fatalf("ParseDocument(%q):\nexpected:\n%#v\n got:\n%#v", k, v, d)
}
})
}
}
-func documentEqual(a, b *Document) bool {
+func documentEqual(t *testing.T, a, b *Document) bool {
if a.Title != b.Title {
+ t.Log("titles differ")
return false
}
if len(a.Links) != len(b.Links) {
+ t.Log("links count differ")
return false
}
for i := range a.Links {
- if !linkEqual(a.Links[i], b.Links[i]) {
+ if !linkEqual(t, a.Links[i], b.Links[i]) {
return false
}
}
- if !siteEqual(a.Site, b.Site) {
+ if !siteEqual(t, a.Site, b.Site) {
return false
}
- if !contentBlockEqual(a.Content, b.Content) {
+ if !contentBlockEqual(t, a.Content, b.Content) {
return false
}
return true
}
-func linkEqual(a, b Link) bool {
- return a == b
+func linkEqual(t *testing.T, a, b Link) bool {
+ if a != b {
+ t.Log("link differs")
+ return false
+ }
+ return true
}
-func siteEqual(a, b Site) bool {
+func siteEqual(t *testing.T, a, b Site) bool {
if a.Path != b.Path {
+ t.Log("site path differs")
return false
}
if a.Name != b.Name {
+ t.Log("site name differs")
return false
}
if len(a.Children) != len(b.Children) {
+ t.Log("site children count differs")
return false
}
for i := range a.Children {
- if !siteEqual(a.Children[i], b.Children[i]) {
+ if !siteEqual(t, a.Children[i], b.Children[i]) {
return false
}
}
return true
}
-func blockEqual(a, b Block) bool {
+func blockEqual(t *testing.T, a, b Block) bool {
switch va := a.(type) {
+ case *Document:
+ vb, ok := b.(*Document)
+ if !ok {
+ t.Log("type mismatch")
+ return false
+ }
+ return documentEqual(t, va, vb)
+
case *SectionBlock:
vb, ok := b.(*SectionBlock)
if !ok {
+ t.Log("type mismatch")
return false
}
- return sectionBlockEqual(va, vb)
+ return sectionBlockEqual(t, va, vb)
case *TextBlock:
vb, ok := b.(*TextBlock)
if !ok {
+ t.Log("type mismatch")
return false
}
- return textBlockEqual(va, vb)
+ return textBlockEqual(t, va, vb)
case *RawBlock:
vb, ok := b.(*RawBlock)
if !ok {
+ t.Log("type mismatch")
return false
}
- return rawBlockEqual(va, vb)
+ return rawBlockEqual(t, va, vb)
case *ListBlock:
vb, ok := b.(*ListBlock)
if !ok {
+ t.Log("type mismatch")
return false
}
- return listBlockEqual(va, vb)
+ return listBlockEqual(t, va, vb)
case *TableBlock:
vb, ok := b.(*TableBlock)
if !ok {
+ t.Log("type mismatch")
return false
}
- return tableBlockEqual(va, vb)
+ return tableBlockEqual(t, va, vb)
case *HeaderBlock:
vb, ok := b.(*HeaderBlock)
if !ok {
+ t.Log("type mismatch")
return false
}
- return headerBlockEqual(va, vb)
+ return headerBlockEqual(t, va, vb)
case *RowBlock:
vb, ok := b.(*RowBlock)
if !ok {
+ t.Log("type mismatch")
return false
}
- return rowBlockEqual(va, vb)
+ return rowBlockEqual(t, va, vb)
case *EmbedBlock:
vb, ok := b.(*EmbedBlock)
if !ok {
+ t.Log("type mismatch")
return false
}
- return embedBlockEqual(va, vb)
+ return embedBlockEqual(t, va, vb)
case *ContentBlock:
vb, ok := b.(*ContentBlock)
if !ok {
+ t.Logf("type mismatch: %T, %T", a, b)
return false
}
- return contentBlockEqual(va, vb)
+ return contentBlockEqual(t, va, vb)
+
+ case nil:
+ return b == nil
default: // shouldn't happen
+ t.Logf("unknown type: %T", a)
return false
}
}
-func contentBlockEqual(a, b *ContentBlock) bool {
+func contentBlockEqual(t *testing.T, a, b *ContentBlock) bool {
if (a == nil) != (b == nil) {
+ t.Log("content blocks are not both nil or non-nil")
return false
}
if a == nil {
return true
}
if a.Name() != b.Name() {
+ t.Log("content block names differ")
return false
}
- if !argsEqual(a.Args(), b.Args()) {
+ if !argsEqual(t, a.Args(), b.Args()) {
return false
}
ca, cb := a.Children(), b.Children()
if len(ca) != len(cb) {
+ t.Log("content block children count differs")
return false
}
for i := range ca {
- if !blockEqual(ca[i], cb[i]) {
+ if !blockEqual(t, ca[i], cb[i]) {
return false
}
}
return true
}
-func argsEqual(a, b []string) bool {
+func argsEqual(t *testing.T, a, b []string) bool {
if len(a) != len(b) {
+ t.Log("args count differs")
return false
}
for i := range a {
if a[i] != b[i] {
+ t.Log("arg differs")
return false
}
}
return true
}
-func sectionBlockEqual(a, b *SectionBlock) bool {
- return a.Title() == b.Title() && contentBlockEqual(&a.ContentBlock, &b.ContentBlock)
+func sectionBlockEqual(t *testing.T, a, b *SectionBlock) bool {
+ if a.Title() != b.Title() {
+ t.Log("section titles differ")
+ return false
+ }
+ if !contentBlockEqual(t, &a.ContentBlock, &b.ContentBlock) {
+ return false
+ }
+ return true
}
-func textBlockEqual(a, b *TextBlock) bool {
- if !argsEqual(a.Args(), b.Args()) {
+func textBlockEqual(t *testing.T, a, b *TextBlock) bool {
+ if !argsEqual(t, a.Args(), b.Args()) {
+ return false
+ }
+ if !textContentsEqual(t, a.Contents, b.Contents) {
return false
}
- return textContentsEqual(a.Contents, b.Contents)
+ return true
}
-func textContentsEqual(a, b TextContents) bool {
+func textContentsEqual(t *testing.T, a, b TextContents) bool {
switch va := a.(type) {
case TextPlainContents:
vb, ok := b.(TextPlainContents)
if !ok {
+ t.Log("text content type differs")
return false
}
- return textPlainContentsEqual(va, vb)
+ return textPlainContentsEqual(t, va, vb)
case TextPreContents:
vb, ok := b.(TextPreContents)
if !ok {
+ t.Log("text content type differs")
return false
}
- return textPreContentsEqual(va, vb)
+ return textPreContentsEqual(t, va, vb)
case TextRawContents:
vb, ok := b.(TextRawContents)
if !ok {
+ t.Log("text content type differs")
return false
}
- return textRawContentsEqual(va, vb)
+ return textRawContentsEqual(t, va, vb)
default: // handle unknown contents, e.g. text fmt
- return reflect.TypeOf(a) == reflect.TypeOf(b) && reflect.DeepEqual(a, b)
+ if reflect.TypeOf(a) != reflect.TypeOf(b) {
+ t.Log("unknown text content format type differs")
+ return false
+ }
+ if !reflect.DeepEqual(a, b) {
+ t.Log("unknown text content format contents differ")
+ return false
+ }
+ return true
}
}
-func textPlainContentsEqual(a, b TextPlainContents) bool {
+func textPlainContentsEqual(t *testing.T, a, b TextPlainContents) bool {
if len(a.Paragraphs) != len(b.Paragraphs) {
+ t.Log("text plain paragraph count differs")
return false
}
for i := range a.Paragraphs {
if a.Paragraphs[i] != b.Paragraphs[i] {
+ t.Log("text plain paragraph differs")
return false
}
}
return true
}
-func textPreContentsEqual(a, b TextPreContents) bool {
- return a == b
+func textPreContentsEqual(t *testing.T, a, b TextPreContents) bool {
+ if a.Text != b.Text {
+ t.Log("text pre contents differ")
+ return false
+ }
+ return true
}
-func textRawContentsEqual(a, b TextRawContents) bool {
- return a.Text == b.Text
+func textRawContentsEqual(t *testing.T, a, b TextRawContents) bool {
+ if a.Text != b.Text {
+ t.Log("text raw contents differ")
+ return false
+ }
+ return true
}
-func rawBlockEqual(a, b *RawBlock) bool {
- return argsEqual(a.Args(), b.Args()) && textRawContentsEqual(a.Contents, b.Contents)
+func rawBlockEqual(t *testing.T, a, b *RawBlock) bool {
+ return argsEqual(t, a.Args(), b.Args()) && textRawContentsEqual(t, a.Contents, b.Contents)
}
-func listBlockEqual(a, b *ListBlock) bool {
- return a.Ordered() == b.Ordered() && contentBlockEqual(&a.ContentBlock, &b.ContentBlock)
+func listBlockEqual(t *testing.T, a, b *ListBlock) bool {
+ if a.Ordered() != b.Ordered() {
+ t.Log("list block mode differs")
+ }
+ return contentBlockEqual(t, &a.ContentBlock, &b.ContentBlock)
}
-func tableBlockEqual(a, b *TableBlock) bool {
- ra, rb := a.Children(), b.Children()
- if len(ra) != len(rb) {
- return false
- }
- for i := range ra {
- if !blockEqual(ra[i], rb[i]) {
- return false
- }
- }
- return argsEqual(a.Args(), b.Args())
+func tableBlockEqual(t *testing.T, a, b *TableBlock) bool {
+ return contentBlockEqual(t, &a.ContentBlock, &b.ContentBlock)
}
-func rowBlockEqual(a, b *RowBlock) bool {
- return contentBlockEqual(&a.ContentBlock, &b.ContentBlock)
+func rowBlockEqual(t *testing.T, a, b *RowBlock) bool {
+ return contentBlockEqual(t, &a.ContentBlock, &b.ContentBlock)
}
-func headerBlockEqual(a, b *HeaderBlock) bool {
- return contentBlockEqual(&a.ContentBlock, &b.ContentBlock)
+func headerBlockEqual(t *testing.T, a, b *HeaderBlock) bool {
+ return contentBlockEqual(t, &a.ContentBlock, &b.ContentBlock)
}
-func embedBlockEqual(a, b *EmbedBlock) bool {
- return argsEqual(a.Args(), b.Args())
+func embedBlockEqual(t *testing.T, a, b *EmbedBlock) bool {
+ return argsEqual(t, a.Args(), b.Args())
}
diff --git a/write_test.go b/write_test.go
index bf829a0..18809b1 100644
--- a/write_test.go
+++ b/write_test.go
@@ -108,79 +108,83 @@ content
name: "section",
args: []string{"of the"},
children: []Block{
- &TableBlock{[]Block{
- &HeaderBlock{ContentBlock{
- name: "header",
- args: []string{},
- children: []Block{
- &TextBlock{
- Contents: TextPlainContents{[]string{
- "Column 1",
- }},
- },
- &TextBlock{
- Contents: TextPlainContents{[]string{
- "Column 2",
- }},
+ &TableBlock{ContentBlock{
+ name: "table",
+ args: nil,
+ children: []Block{
+ &HeaderBlock{ContentBlock{
+ name: "header",
+ args: []string{},
+ children: []Block{
+ &TextBlock{
+ Contents: TextPlainContents{[]string{
+ "Column 1",
+ }},
+ },
+ &TextBlock{
+ Contents: TextPlainContents{[]string{
+ "Column 2",
+ }},
+ },
},
- },
- }},
- &RowBlock{ContentBlock{
- name: "row",
- args: []string{},
- children: []Block{
- &TextBlock{
- Contents: TextPlainContents{[]string{
- "CNM",
- }},
+ }},
+ &RowBlock{ContentBlock{
+ name: "row",
+ args: []string{},
+ children: []Block{
+ &TextBlock{
+ Contents: TextPlainContents{[]string{
+ "CNM",
+ }},
+ },
+ &TextBlock{
+ Contents: TextPlainContents{[]string{
+ "document",
+ "format",
+ }},
+ },
},
- &TextBlock{
- Contents: TextPlainContents{[]string{
- "document",
- "format",
+ }},
+ &RowBlock{ContentBlock{
+ name: "row",
+ args: []string{""},
+ children: []Block{
+ &SectionBlock{ContentBlock{
+ name: "section",
+ args: []string{"", "", ""},
}},
- },
- },
- }},
- &RowBlock{ContentBlock{
- name: "row",
- args: []string{""},
- children: []Block{
- &SectionBlock{ContentBlock{
- name: "section",
- args: []string{"", "", ""},
- }},
- &ListBlock{ContentBlock{
- name: "list",
- args: nil,
- children: []Block{
- &TextBlock{
- Contents: TextPlainContents{[]string{
- "ipsum",
- }},
- },
- &ListBlock{ContentBlock{
- name: "list",
- args: []string{"ordered"},
- children: []Block{
- &ListBlock{ContentBlock{
- name: "list",
- args: []string{"unordered"},
- children: []Block{
- &TextBlock{
- Contents: TextPlainContents{[]string{
- "dolor",
- "sit amet",
- }},
- },
- },
+ &ListBlock{ContentBlock{
+ name: "list",
+ args: nil,
+ children: []Block{
+ &TextBlock{
+ Contents: TextPlainContents{[]string{
+ "ipsum",
}},
},
- }},
- },
- }},
- },
- }},
+ &ListBlock{ContentBlock{
+ name: "list",
+ args: []string{"ordered"},
+ children: []Block{
+ &ListBlock{ContentBlock{
+ name: "list",
+ args: []string{"unordered"},
+ children: []Block{
+ &TextBlock{
+ Contents: TextPlainContents{[]string{
+ "dolor",
+ "sit amet",
+ }},
+ },
+ },
+ }},
+ },
+ }},
+ },
+ }},
+ },
+ }},
+ },
}},
},
}},
@@ -205,12 +209,12 @@ func TestWrite(t *testing.T) {
t.Fatalf("Write error: %v", err)
}
w := buf.String()
- t.Log("====================")
- t.Log("expected:\n" + k)
- t.Log("--------------------")
- t.Log(" got:\n" + w)
- t.Log("====================")
if k != w {
+ t.Log("====================")
+ t.Log("expected:\n" + k)
+ t.Log("--------------------")
+ t.Log(" got:\n" + w)
+ t.Log("====================")
t.Fatal("Write: output did not match expected document")
}
})