• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

fogfish / iq / 20679518684

03 Jan 2026 03:54PM UTC coverage: 34.494% (+0.1%) from 34.393%
20679518684

push

github

web-flow
(fea) flatMap signature for conduit processor api (#72)

84 of 154 new or added lines in 6 files covered. (54.55%)

5 existing lines in 4 files now uncovered.

1209 of 3505 relevant lines covered (34.49%)

0.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.1
/internal/iosystem/processor/jsonify.go
1
//
2
// Copyright (C) 2025 Dmitry Kolesnikov
3
//
4
// This file may be modified and distributed under the terms
5
// of the MIT license.  See the LICENSE file for details.
6
// https://github.com/fogfish/iq
7
//
8

9
package processor
10

11
import (
12
        "bytes"
13
        "context"
14
        "encoding/json"
15
        "fmt"
16
        "io"
17

18
        "github.com/TylerBrock/colorjson"
19
        "github.com/fogfish/iq/internal/iosystem"
20
)
21

22
// Jsonify is a processor that formats JSON documents with color and indentation.
23
// It only processes documents with content-type "application/json" in metadata.
24
// Other documents are passed through unchanged.
25
//
26
// The processor:
27
//   - Checks document metadata for "content-type"
28
//   - Formats JSON with color syntax highlighting
29
//   - Pretty-prints with configurable indentation
30
//   - Passes through non-JSON documents unchanged
31
//
32
// Example:
33
//
34
//        proc := NewJSONFormatter(JSONFormatConfig{
35
//            Indent: 2,
36
//            Color:  true,
37
//        })
38
//        docs, err := proc.Process(ctx, inputDoc)
39
type Jsonify struct {
40
        config JsonifyConfig
41
}
42

43
// JsonifyConfig configures the JSON formatter processor.
44
type JsonifyConfig struct {
45
        Indent int  // Number of spaces for indentation (default: 2)
46
        Color  bool // Enable color syntax highlighting (default: true)
47
}
48

49
// NewJsonify creates a processor that formats JSON documents.
50
func NewJsonify(config JsonifyConfig) *Jsonify {
1✔
51
        // Set defaults
1✔
52
        if config.Indent == 0 {
1✔
53
                config.Indent = 2
×
54
        }
×
55

56
        return &Jsonify{
1✔
57
                config: config,
1✔
58
        }
1✔
59
}
60

61
// Process formats a JSON document with color and indentation.
62
// Only processes documents with content-type "application/json".
63
// Other documents are passed through unchanged.
64
func (p *Jsonify) Process(ctx context.Context, docs []*iosystem.Document) ([]*iosystem.Document, error) {
1✔
65
        // Passthrough EOF or empty
1✔
66
        if len(docs) == 0 || (len(docs) == 1 && docs[0].Type == iosystem.ContentEOF) {
1✔
NEW
67
                return docs, nil
×
UNCOV
68
        }
×
69

70
        results := make([]*iosystem.Document, 0, len(docs))
1✔
71

1✔
72
        for _, doc := range docs {
2✔
73
                if doc == nil {
1✔
NEW
74
                        return nil, fmt.Errorf("document is nil")
×
NEW
75
                }
×
76

77
                // Check if document is JSON
78
                contentType := doc.Type
1✔
79
                if contentType != iosystem.ContentJSON {
2✔
80
                        results = append(results, doc)
1✔
81
                        continue
1✔
82
                }
83

84
                // Read document content
85
                content, err := io.ReadAll(doc.Reader)
1✔
86
                if err != nil {
1✔
NEW
87
                        // On read error, pass through unchanged
×
NEW
88
                        doc.Reader = bytes.NewReader(content)
×
NEW
89
                        results = append(results, doc)
×
NEW
90
                        continue
×
91
                }
92

93
                // Parse JSON to validate and prepare for formatting
94
                var obj any
1✔
95
                if err := json.Unmarshal(content, &obj); err != nil {
2✔
96
                        // Invalid JSON, pass through unchanged
1✔
97
                        doc.Reader = bytes.NewReader(content)
1✔
98
                        results = append(results, doc)
1✔
99
                        continue
1✔
100
                }
101

102
                // Format JSON with color
103
                var formatted []byte
1✔
104
                if p.config.Color {
2✔
105
                        f := colorjson.NewFormatter()
1✔
106
                        f.Indent = p.config.Indent
1✔
107
                        formatted, err = f.Marshal(obj)
1✔
108
                        if err != nil {
1✔
NEW
109
                                return nil, fmt.Errorf("failed to format JSON for '%s': %w", doc.Path, err)
×
NEW
110
                        }
×
111
                } else {
1✔
112
                        // Format without color (standard pretty-print)
1✔
113
                        formatted, err = json.MarshalIndent(obj, "", bytesIndent(p.config.Indent))
1✔
114
                        if err != nil {
1✔
NEW
115
                                return nil, fmt.Errorf("failed to format JSON for '%s': %w", doc.Path, err)
×
NEW
116
                        }
×
117
                }
118

119
                // Create output document
120
                out := &iosystem.Document{
1✔
121
                        Type:     doc.Type,
1✔
122
                        Path:     doc.Path,
1✔
123
                        Reader:   bytes.NewReader(formatted),
1✔
124
                        Metadata: copyMetadata(doc.Metadata),
1✔
125
                }
1✔
126

1✔
127
                results = append(results, out)
1✔
128
        }
129

130
        return results, nil
1✔
131
}
132

133
// Close releases resources. For JSONFormatter, this is a no-op.
134
func (p *Jsonify) Close() error {
1✔
135
        return nil
1✔
136
}
1✔
137

138
// bytesIndent creates an indentation string with n spaces.
139
func bytesIndent(n int) string {
1✔
140
        indent := make([]byte, n)
1✔
141
        for i := range indent {
2✔
142
                indent[i] = ' '
1✔
143
        }
1✔
144
        return string(indent)
1✔
145
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc