refactor: extract base exporter, add sentinel errors, document unused fields

This commit is contained in:
2026-09-21 09:46:39 +02:00
parent 74906ee6a3
commit defce7d2d3
12 changed files with 136 additions and 42 deletions
+20
View File
@@ -0,0 +1,20 @@
package exporters
import "github.com/kjanat/articulate-parser/internal/interfaces"
// baseExporter contains common fields shared by all exporter implementations.
// Embed this struct in concrete exporters to inherit HTMLCleaner and Logger.
type baseExporter struct {
// htmlCleaner is used to convert HTML content to plain text.
htmlCleaner interfaces.HTMLCleaner
// logger is used for logging warnings and errors.
logger interfaces.Logger
}
// newBaseExporter creates a new baseExporter with the given dependencies.
func newBaseExporter(htmlCleaner interfaces.HTMLCleaner, logger interfaces.Logger) baseExporter {
return baseExporter{
htmlCleaner: htmlCleaner,
logger: logger,
}
}
+2 -6
View File
@@ -26,10 +26,7 @@ const (
// It converts Articulate Rise course data into a Microsoft Word document
// using the go-docx package.
type DocxExporter struct {
// htmlCleaner is used to convert HTML content to plain text
htmlCleaner interfaces.HTMLCleaner
// logger is used for logging warnings and errors
logger interfaces.Logger
baseExporter
}
// NewDocxExporter creates a new DocxExporter instance.
@@ -43,8 +40,7 @@ type DocxExporter struct {
// - An implementation of the Exporter interface for DOCX format
func NewDocxExporter(htmlCleaner interfaces.HTMLCleaner, logger interfaces.Logger) interfaces.Exporter {
return &DocxExporter{
htmlCleaner: htmlCleaner,
logger: logger,
baseExporter: newBaseExporter(htmlCleaner, logger),
}
}
+2 -1
View File
@@ -4,6 +4,7 @@ import (
"fmt"
"strings"
"github.com/kjanat/articulate-parser/internal/apperrors"
"github.com/kjanat/articulate-parser/internal/interfaces"
)
@@ -56,7 +57,7 @@ func (f *Factory) CreateExporter(format string) (interfaces.Exporter, error) {
case FormatHTML, formatAliasHTML:
return NewHTMLExporter(f.htmlCleaner, f.logger), nil
default:
return nil, fmt.Errorf("unsupported export format: %s", format)
return nil, fmt.Errorf("%w: %s", apperrors.ErrUnsupportedFormat, format)
}
}
+4 -8
View File
@@ -20,12 +20,9 @@ var htmlTemplate string
// HTMLExporter implements the Exporter interface for HTML format.
// It converts Articulate Rise course data into a structured HTML document using templates.
type HTMLExporter struct {
// htmlCleaner is used to convert HTML content to plain text when needed
htmlCleaner interfaces.HTMLCleaner
// tmpl holds the parsed HTML template
baseExporter
// tmpl holds the parsed HTML template.
tmpl *template.Template
// logger is used for logging warnings and errors
logger interfaces.Logger
}
// NewHTMLExporter creates a new HTMLExporter instance.
@@ -52,9 +49,8 @@ func NewHTMLExporter(htmlCleaner interfaces.HTMLCleaner, logger interfaces.Logge
tmpl := template.Must(template.New("html").Funcs(funcMap).Parse(htmlTemplate))
return &HTMLExporter{
htmlCleaner: htmlCleaner,
tmpl: tmpl,
logger: logger,
baseExporter: newBaseExporter(htmlCleaner, logger),
tmpl: tmpl,
}
}
+2 -6
View File
@@ -16,10 +16,7 @@ import (
// MarkdownExporter implements the Exporter interface for Markdown format.
// It converts Articulate Rise course data into a structured Markdown document.
type MarkdownExporter struct {
// htmlCleaner is used to convert HTML content to plain text
htmlCleaner interfaces.HTMLCleaner
// logger is used for logging warnings and errors
logger interfaces.Logger
baseExporter
}
// NewMarkdownExporter creates a new MarkdownExporter instance.
@@ -33,8 +30,7 @@ type MarkdownExporter struct {
// - An implementation of the Exporter interface for Markdown format
func NewMarkdownExporter(htmlCleaner interfaces.HTMLCleaner, logger interfaces.Logger) interfaces.Exporter {
return &MarkdownExporter{
htmlCleaner: htmlCleaner,
logger: logger,
baseExporter: newBaseExporter(htmlCleaner, logger),
}
}
+13 -13
View File
@@ -124,7 +124,7 @@ func TestMarkdownExporter_Export_InvalidPath(t *testing.T) {
// TestMarkdownExporter_ProcessTextItem tests the processTextItem method.
func TestMarkdownExporter_ProcessTextItem(t *testing.T) {
htmlCleaner := services.NewHTMLCleaner()
exporter := &MarkdownExporter{htmlCleaner: htmlCleaner}
exporter := &MarkdownExporter{baseExporter: baseExporter{htmlCleaner: htmlCleaner}}
var buf bytes.Buffer
item := models.Item{
@@ -153,7 +153,7 @@ func TestMarkdownExporter_ProcessTextItem(t *testing.T) {
// TestMarkdownExporter_ProcessListItem tests the processListItem method.
func TestMarkdownExporter_ProcessListItem(t *testing.T) {
htmlCleaner := services.NewHTMLCleaner()
exporter := &MarkdownExporter{htmlCleaner: htmlCleaner}
exporter := &MarkdownExporter{baseExporter: baseExporter{htmlCleaner: htmlCleaner}}
var buf bytes.Buffer
item := models.Item{
@@ -178,7 +178,7 @@ func TestMarkdownExporter_ProcessListItem(t *testing.T) {
// TestMarkdownExporter_ProcessMultimediaItem tests the processMultimediaItem method.
func TestMarkdownExporter_ProcessMultimediaItem(t *testing.T) {
htmlCleaner := services.NewHTMLCleaner()
exporter := &MarkdownExporter{htmlCleaner: htmlCleaner}
exporter := &MarkdownExporter{baseExporter: baseExporter{htmlCleaner: htmlCleaner}}
var buf bytes.Buffer
item := models.Item{
@@ -217,7 +217,7 @@ func TestMarkdownExporter_ProcessMultimediaItem(t *testing.T) {
// TestMarkdownExporter_ProcessImageItem tests the processImageItem method.
func TestMarkdownExporter_ProcessImageItem(t *testing.T) {
htmlCleaner := services.NewHTMLCleaner()
exporter := &MarkdownExporter{htmlCleaner: htmlCleaner}
exporter := &MarkdownExporter{baseExporter: baseExporter{htmlCleaner: htmlCleaner}}
var buf bytes.Buffer
item := models.Item{
@@ -252,7 +252,7 @@ func TestMarkdownExporter_ProcessImageItem(t *testing.T) {
// TestMarkdownExporter_ProcessKnowledgeCheckItem tests the processKnowledgeCheckItem method.
func TestMarkdownExporter_ProcessKnowledgeCheckItem(t *testing.T) {
htmlCleaner := services.NewHTMLCleaner()
exporter := &MarkdownExporter{htmlCleaner: htmlCleaner}
exporter := &MarkdownExporter{baseExporter: baseExporter{htmlCleaner: htmlCleaner}}
var buf bytes.Buffer
item := models.Item{
@@ -294,7 +294,7 @@ func TestMarkdownExporter_ProcessKnowledgeCheckItem(t *testing.T) {
// TestMarkdownExporter_ProcessInteractiveItem tests the processInteractiveItem method.
func TestMarkdownExporter_ProcessInteractiveItem(t *testing.T) {
htmlCleaner := services.NewHTMLCleaner()
exporter := &MarkdownExporter{htmlCleaner: htmlCleaner}
exporter := &MarkdownExporter{baseExporter: baseExporter{htmlCleaner: htmlCleaner}}
var buf bytes.Buffer
item := models.Item{
@@ -319,7 +319,7 @@ func TestMarkdownExporter_ProcessInteractiveItem(t *testing.T) {
// TestMarkdownExporter_ProcessDividerItem tests the processDividerItem method.
func TestMarkdownExporter_ProcessDividerItem(t *testing.T) {
htmlCleaner := services.NewHTMLCleaner()
exporter := &MarkdownExporter{htmlCleaner: htmlCleaner}
exporter := &MarkdownExporter{baseExporter: baseExporter{htmlCleaner: htmlCleaner}}
var buf bytes.Buffer
exporter.processDividerItem(&buf)
@@ -335,7 +335,7 @@ func TestMarkdownExporter_ProcessDividerItem(t *testing.T) {
// TestMarkdownExporter_ProcessUnknownItem tests the processUnknownItem method.
func TestMarkdownExporter_ProcessUnknownItem(t *testing.T) {
htmlCleaner := services.NewHTMLCleaner()
exporter := &MarkdownExporter{htmlCleaner: htmlCleaner}
exporter := &MarkdownExporter{baseExporter: baseExporter{htmlCleaner: htmlCleaner}}
var buf bytes.Buffer
item := models.Item{
@@ -366,7 +366,7 @@ func TestMarkdownExporter_ProcessUnknownItem(t *testing.T) {
// TestMarkdownExporter_ProcessVideoMedia tests the processVideoMedia method.
func TestMarkdownExporter_ProcessVideoMedia(t *testing.T) {
htmlCleaner := services.NewHTMLCleaner()
exporter := &MarkdownExporter{htmlCleaner: htmlCleaner}
exporter := &MarkdownExporter{baseExporter: baseExporter{htmlCleaner: htmlCleaner}}
var buf bytes.Buffer
media := &models.Media{
@@ -391,7 +391,7 @@ func TestMarkdownExporter_ProcessVideoMedia(t *testing.T) {
// TestMarkdownExporter_ProcessImageMedia tests the processImageMedia method.
func TestMarkdownExporter_ProcessImageMedia(t *testing.T) {
htmlCleaner := services.NewHTMLCleaner()
exporter := &MarkdownExporter{htmlCleaner: htmlCleaner}
exporter := &MarkdownExporter{baseExporter: baseExporter{htmlCleaner: htmlCleaner}}
var buf bytes.Buffer
media := &models.Media{
@@ -413,7 +413,7 @@ func TestMarkdownExporter_ProcessImageMedia(t *testing.T) {
// TestMarkdownExporter_ProcessAnswers tests the processAnswers method.
func TestMarkdownExporter_ProcessAnswers(t *testing.T) {
htmlCleaner := services.NewHTMLCleaner()
exporter := &MarkdownExporter{htmlCleaner: htmlCleaner}
exporter := &MarkdownExporter{baseExporter: baseExporter{htmlCleaner: htmlCleaner}}
var buf bytes.Buffer
answers := []models.Answer{
@@ -443,7 +443,7 @@ func TestMarkdownExporter_ProcessAnswers(t *testing.T) {
// TestMarkdownExporter_ProcessItemToMarkdown_AllTypes tests all item types.
func TestMarkdownExporter_ProcessItemToMarkdown_AllTypes(t *testing.T) {
htmlCleaner := services.NewHTMLCleaner()
exporter := &MarkdownExporter{htmlCleaner: htmlCleaner}
exporter := &MarkdownExporter{baseExporter: baseExporter{htmlCleaner: htmlCleaner}}
tests := []struct {
name string
@@ -673,7 +673,7 @@ func BenchmarkMarkdownExporter_Export(b *testing.B) {
// BenchmarkMarkdownExporter_ProcessTextItem benchmarks the processTextItem method.
func BenchmarkMarkdownExporter_ProcessTextItem(b *testing.B) {
htmlCleaner := services.NewHTMLCleaner()
exporter := &MarkdownExporter{htmlCleaner: htmlCleaner}
exporter := &MarkdownExporter{baseExporter: baseExporter{htmlCleaner: htmlCleaner}}
item := models.Item{
Type: "text",