diff --git a/model.go b/model.go index f9c0f87..8ced3c3 100644 --- a/model.go +++ b/model.go @@ -16,7 +16,17 @@ type RenderContext struct { } type Book struct { + // deprecated: since v0.5.0 of mdbook, Sections field is deprecated in favor of Items Sections []*BookItem `json:"sections"` + Items []*BookItem `json:"items"` +} + +// GetItems returns the list of book items +func (b *Book) GetItems() []*BookItem { + if len(b.Items) > 0 { + return b.Items + } + return b.Sections } type BookItem struct { @@ -41,27 +51,29 @@ type Separator struct { type PartTitle string type Config struct { - Book *BookConfig `json:"book,omitempty"` - Build *BuildConfig `json:"build,omitempty"` - Rust *RustConfig `json:"rust,omitempty"` - Output map[string]interface{} `json:"output,omitempty"` + Book *BookConfig `json:"book,omitempty"` + Build *BuildConfig `json:"build,omitempty"` + Rust *RustConfig `json:"rust,omitempty"` + Output map[string]any `json:"output,omitempty"` + Preprocessor map[string]any `json:"preprocessor,omitempty"` } type BookConfig struct { - Title string `json:"title,omitempty"` - Authors []string `json:"authors,omitempty"` - Description string `json:"description,omitempty"` - Src string `json:"src"` - Multilingual bool `json:"multilingual"` - Language string `json:"language,omitempty"` - TextDirection string `json:"text_direction,omitempty"` + Title string `json:"title,omitempty"` + Authors []string `json:"authors,omitempty"` + Description string `json:"description,omitempty"` + Src string `json:"src"` + // deprecated: since v0.5.0 of mdbook, Multilingual field has been removed + Multilingual bool `json:"multilingual,omitempty"` + Language string `json:"language,omitempty"` + TextDirection string `json:"text-direction,omitempty"` } type BuildConfig struct { - BuildDir string `json:"build_dir,omitempty"` - CreateMissing bool `json:"create_missing"` - UseDefaultPreprocessors bool `json:"use_default_preprocessors"` - ExtraWatchDirs []string `json:"extra_watch_dirs,omitempty"` + BuildDir string `json:"build-dir,omitempty"` + CreateMissing bool `json:"create-missing"` + UseDefaultPreprocessors bool `json:"use-default-preprocessors"` + ExtraWatchDirs []string `json:"extra-watch-dirs,omitempty"` } type RustConfig struct { diff --git a/model_test.go b/model_test.go new file mode 100644 index 0000000..d70b0cf --- /dev/null +++ b/model_test.go @@ -0,0 +1,116 @@ +package mdbook + +import ( + "os" + "path/filepath" + "testing" +) + +func TestParseRenderContext(t *testing.T) { + tests := []struct { + name string + file string + version string + }{ + {name: "v0.4", file: "render_context_v04.json", version: "0.4.52"}, + {name: "v0.5", file: "render_context_v05.json", version: "0.5.2"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f, err := os.Open(filepath.Join("testdata", tt.file)) + if err != nil { + t.Fatalf("failed to open testdata file: %v", err) + } + defer func() { + if err := f.Close(); err != nil { + t.Fatalf("failed to close testdata file: %v", err) + } + }() + + ctx, err := ParseRenderContextFromReader(f) + if err != nil { + t.Fatalf("failed to parse: %v", err) + } + + if ctx.Version != tt.version { + t.Errorf("version = %q, want %q", ctx.Version, tt.version) + } + if ctx.Root != "/tmp/test-book" { + t.Errorf("root = %q, want %q", ctx.Root, "/tmp/test-book") + } + if ctx.Destination != "/tmp/test-book/book" { + t.Errorf("destination = %q, want %q", ctx.Destination, "/tmp/test-book/book") + } + + // Book items (via GetItems for backward compat) + if ctx.Book == nil { + t.Fatal("book is nil") + } + items := ctx.Book.GetItems() + if len(items) != 3 { + t.Fatalf("GetItems() length = %d, want 3", len(items)) + } + + if items[0].Chapter == nil { + t.Fatal("item 0 should be a Chapter") + } + if items[0].Chapter.Name != "Chapter 1" { + t.Errorf("chapter name = %q, want %q", items[0].Chapter.Name, "Chapter 1") + } + if items[0].Chapter.Path != "chapter_1.md" { + t.Errorf("chapter path = %q, want %q", items[0].Chapter.Path, "chapter_1.md") + } + if items[1].Separator == nil { + t.Error("item 1 should be a Separator") + } + if items[2].PartTitle == nil { + t.Fatal("item 2 should be a PartTitle") + } + if *items[2].PartTitle != "Part 2" { + t.Errorf("part title = %q, want %q", *items[2].PartTitle, "Part 2") + } + + // Config + if ctx.Config == nil { + t.Fatal("config is nil") + } + if ctx.Config.Book == nil { + t.Fatal("config.book is nil") + } + if ctx.Config.Book.Title != "Test Book" { + t.Errorf("config.book.title = %q, want %q", ctx.Config.Book.Title, "Test Book") + } + if ctx.Config.Book.Language != "en" { + t.Errorf("config.book.language = %q, want %q", ctx.Config.Book.Language, "en") + } + + if ctx.Config.Build == nil { + t.Fatal("config.build is nil") + } + if ctx.Config.Build.BuildDir != "output" { + t.Errorf("config.build.build-dir = %q, want %q", ctx.Config.Build.BuildDir, "output") + } + if !ctx.Config.Build.CreateMissing { + t.Error("config.build.create-missing = false, want true") + } + if !ctx.Config.Build.UseDefaultPreprocessors { + t.Error("config.build.use-default-preprocessors = false, want true") + } + + if ctx.Config.Rust == nil { + t.Fatal("config.rust is nil") + } + if ctx.Config.Rust.Edition != "2021" { + t.Errorf("config.rust.edition = %q, want %q", ctx.Config.Rust.Edition, "2021") + } + + if ctx.Config.Preprocessor == nil { + t.Fatal("config.preprocessor is nil") + } + if _, ok := ctx.Config.Preprocessor["test"]; !ok { + t.Error("config.preprocessor[\"test\"] missing") + } + }) + } +} diff --git a/processor.go b/processor.go index bd290ab..049d302 100644 --- a/processor.go +++ b/processor.go @@ -39,9 +39,8 @@ func Process(renderContext *RenderContext, handler Handler) error { } func (p *Processor) Process() error { - for _, section := range p.renderContext.Book.Sections { - err := p.handleBookItem(section) - if err != nil { + for _, item := range p.renderContext.Book.GetItems() { + if err := p.handleBookItem(item); err != nil { return err } } diff --git a/testdata/render_context_v04.json b/testdata/render_context_v04.json new file mode 100644 index 0000000..847fc04 --- /dev/null +++ b/testdata/render_context_v04.json @@ -0,0 +1,48 @@ +{ + "version": "0.4.52", + "root": "/tmp/test-book", + "book": { + "sections": [ + { + "Chapter": { + "name": "Chapter 1", + "content": "# Chapter 1\n\nHello world\n", + "number": [1], + "sub_items": [], + "path": "chapter_1.md", + "source_path": "chapter_1.md", + "parent_names": [] + } + }, + "Separator", + { + "PartTitle": "Part 2" + } + ], + "__non_exhaustive": null + }, + "config": { + "book": { + "authors": ["test"], + "language": "en", + "multilingual": false, + "src": "src", + "title": "Test Book" + }, + "build": { + "build-dir": "output", + "create-missing": true, + "use-default-preprocessors": true, + "extra-watch-dirs": [] + }, + "rust": { + "edition": "2021" + }, + "preprocessor": { + "test": { + "command": "./test-cmd" + } + } + }, + "destination": "/tmp/test-book/book" +} diff --git a/testdata/render_context_v05.json b/testdata/render_context_v05.json new file mode 100644 index 0000000..cb81f94 --- /dev/null +++ b/testdata/render_context_v05.json @@ -0,0 +1,50 @@ +{ + "version": "0.5.2", + "root": "/tmp/test-book", + "book": { + "items": [ + { + "Chapter": { + "name": "Chapter 1", + "content": "# Chapter 1\n\nHello world\n", + "number": [1], + "sub_items": [], + "path": "chapter_1.md", + "source_path": "chapter_1.md", + "parent_names": [] + } + }, + "Separator", + { + "PartTitle": "Part 2" + } + ] + }, + "config": { + "book": { + "title": "Test Book", + "authors": ["test"], + "description": null, + "language": "en", + "text-direction": null + }, + "build": { + "build-dir": "output", + "create-missing": true, + "use-default-preprocessors": true, + "extra-watch-dirs": [] + }, + "rust": { + "edition": "2021" + }, + "preprocessor": { + "test": { + "command": "./test-cmd" + } + }, + "output": { + "html": {} + } + }, + "destination": "/tmp/test-book/book" +}