en Best Practices - ZeroHawkeye/wordZero GitHub Wiki

Best Practices

This chapter summarizes best practices for developing with WordZero, including performance optimization, error handling, code organization, and solutions to common problems.

🚀 Performance Optimization

1. Batch Operation Optimization

// ✅ Recommended: Batch content addition
func createLargeDocument() {
    doc := document.New()
    
    // Pre-allocate slice capacity
    data := make([]string, 0, 1000)
    for i := 0; i < 1000; i++ {
        data = append(data, fmt.Sprintf("Content of paragraph %d", i))
    }
    
    // Batch add paragraphs
    for _, text := range data {
        para := doc.AddParagraph(text)
        para.SetStyle(style.StyleNormal)
    }
}

// ❌ Avoid: Frequent small operations
func inefficientCreation() {
    doc := document.New()
    
    for i := 0; i < 1000; i++ {
        // Reformatting string each time
        text := fmt.Sprintf("Content of paragraph %d", i)
        para := doc.AddParagraph(text)
        para.SetStyle(style.StyleNormal)
        // Saving individually each time (wrong approach)
        // doc.Save(fmt.Sprintf("temp_%d.docx", i))
    }
}

2. Memory Management

// ✅ Recommended: Proper document lifecycle management
func processMultipleDocuments(files []string) error {
    for _, file := range files {
        func() {
            doc := document.New()
            defer func() {
                // Ensure document resources are released
                doc = nil
            }()
            
            // Process document
            processDocument(doc, file)
            
            // Save and release immediately
            if err := doc.Save(file); err != nil {
                log.Printf("Failed to save document: %v", err)
            }
        }()
    }
    return nil
}

3. Style Reuse

// ✅ Recommended: Create style constants
var (
    HeaderFormat = &document.TextFormat{
        FontName:  "Calibri",
        FontSize:  14,
        Bold:      true,
        FontColor: "2F5496",
    }
    
    BodyFormat = &document.TextFormat{
        FontName: "Calibri",
        FontSize: 11,
    }
    
    EmphasisFormat = &document.TextFormat{
        Bold:      true,
        FontColor: "FF0000",
    }
)

func addFormattedContent(doc *document.Document) {
    // Reuse predefined formats
    title := doc.AddParagraph("")
    title.AddFormattedText("Title", HeaderFormat)
    
    content := doc.AddParagraph("")
    content.AddFormattedText("Body text", BodyFormat)
    content.AddFormattedText("Emphasis", EmphasisFormat)
}

🔧 Code Organization

1. Modular Design

// Document builder pattern
type DocumentBuilder struct {
    doc    *document.Document
    config *BuilderConfig
}

type BuilderConfig struct {
    Title       string
    Author      string
    PageSize    document.PageSize
    PageMargins [4]int // Top, Right, Bottom, Left
}

func NewDocumentBuilder(config *BuilderConfig) *DocumentBuilder {
    return &DocumentBuilder{
        doc:    document.New(),
        config: config,
    }
}

func (b *DocumentBuilder) SetupPage() *DocumentBuilder {
    b.doc.SetPageSize(b.config.PageSize)
    b.doc.SetPageMargins(
        b.config.PageMargins[0],
        b.config.PageMargins[1], 
        b.config.PageMargins[2],
        b.config.PageMargins[3],
    )
    return b
}

func (b *DocumentBuilder) AddTitle() *DocumentBuilder {
    title := b.doc.AddParagraph(b.config.Title)
    title.SetStyle(style.StyleTitle)
    return b
}

func (b *DocumentBuilder) AddContent(content string) *DocumentBuilder {
    para := b.doc.AddParagraph(content)
    para.SetStyle(style.StyleNormal)
    return b
}

func (b *DocumentBuilder) Build() *document.Document {
    return b.doc
}

// Usage example
func createStandardDocument() {
    config := &BuilderConfig{
        Title:    "Standard Document",
        Author:   "WordZero",
        PageSize: document.PageSizeA4,
        PageMargins: [4]int{72, 72, 72, 72},
    }
    
    doc := NewDocumentBuilder(config).
        SetupPage().
        AddTitle().
        AddContent("Document content...").
        Build()
        
    doc.Save("standard_document.docx")
}

🛡️ Error Handling

1. Comprehensive Error Checking

// ✅ Recommended: Check all errors
func createDocumentSafely(filename string) error {
    doc := document.New()
    
    // Add content with error checking
    title := doc.AddParagraph("Document Title")
    if title == nil {
        return fmt.Errorf("failed to add title paragraph")
    }
    
    // Set style with validation
    if err := title.SetStyle(style.StyleTitle); err != nil {
        return fmt.Errorf("failed to set title style: %w", err)
    }
    
    // Save with error handling
    if err := doc.Save(filename); err != nil {
        return fmt.Errorf("failed to save document to %s: %w", filename, err)
    }
    
    return nil
}

2. Graceful Degradation

// Handle missing features gracefully
func addOptionalFeatures(doc *document.Document) {
    // Try to add advanced features, fall back to basic ones
    if err := doc.AddTableOfContents(); err != nil {
        log.Printf("Warning: Could not add TOC, skipping: %v", err)
        // Add simple heading instead
        doc.AddParagraph("Table of Contents").SetStyle(style.StyleHeading1)
    }
}

📋 Common Issues and Solutions

1. File Path Issues

// ✅ Recommended: Proper path handling
func saveToSafeLocation(doc *document.Document, filename string) error {
    // Ensure directory exists
    dir := filepath.Dir(filename)
    if err := os.MkdirAll(dir, 0755); err != nil {
        return fmt.Errorf("failed to create directory %s: %w", dir, err)
    }
    
    // Clean filename
    cleanName := filepath.Clean(filename)
    
    // Check if file already exists
    if _, err := os.Stat(cleanName); err == nil {
        log.Printf("Warning: File %s already exists, will be overwritten", cleanName)
    }
    
    return doc.Save(cleanName)
}

2. Resource Management

// ✅ Recommended: Proper cleanup
func processLargeDataset(data [][]string) error {
    doc := document.New()
    
    // Process in chunks to avoid memory issues
    chunkSize := 100
    for i := 0; i < len(data); i += chunkSize {
        end := i + chunkSize
        if end > len(data) {
            end = len(data)
        }
        
        chunk := data[i:end]
        if err := processChunk(doc, chunk); err != nil {
            return fmt.Errorf("failed to process chunk %d-%d: %w", i, end, err)
        }
        
        // Force garbage collection periodically
        if i%500 == 0 {
            runtime.GC()
        }
    }
    
    return doc.Save("large_dataset.docx")
}

🎯 Testing Best Practices

1. Unit Testing

func TestDocumentCreation(t *testing.T) {
    doc := document.New()
    
    // Test basic functionality
    para := doc.AddParagraph("Test paragraph")
    assert.NotNil(t, para, "Paragraph should not be nil")
    
    // Test style application
    err := para.SetStyle(style.StyleNormal)
    assert.NoError(t, err, "Should be able to set normal style")
    
    // Test document saving
    tempFile := filepath.Join(t.TempDir(), "test.docx")
    err = doc.Save(tempFile)
    assert.NoError(t, err, "Should be able to save document")
    
    // Verify file exists
    _, err = os.Stat(tempFile)
    assert.NoError(t, err, "Saved file should exist")
}

2. Integration Testing

func TestCompleteWorkflow(t *testing.T) {
    // Test complete document creation workflow
    tempDir := t.TempDir()
    filename := filepath.Join(tempDir, "integration_test.docx")
    
    // Create complex document
    doc := document.New()
    
    // Add various content types
    doc.AddParagraph("Title").SetStyle(style.StyleTitle)
    doc.AddParagraph("Subtitle").SetStyle(style.StyleSubtitle)
    
    // Add table
    table := doc.AddTable(3, 3)
    table.SetCellText(0, 0, "Header 1")
    table.SetCellText(0, 1, "Header 2")
    table.SetCellText(0, 2, "Header 3")
    
    // Save and verify
    err := doc.Save(filename)
    assert.NoError(t, err)
    
    // Verify file is valid DOCX
    verifyDocxFile(t, filename)
}

Following these best practices will help you create robust, efficient, and maintainable applications with WordZero.