Class: Mdlint::CLI::OutputFormatter

Inherits:
Object
  • Object
show all
Defined in:
lib/mdlint/cli/output_formatter.rb,
sig/internal.rbs

Constant Summary collapse

LEVELS =
{ error: "error", warning: "warning", info: "note" }.freeze

Instance Method Summary collapse

Constructor Details

#initialize(format) ⇒ OutputFormatter

Returns a new instance of OutputFormatter.

Parameters:

  • format (Object)


10
11
12
# File 'lib/mdlint/cli/output_formatter.rb', line 10

def initialize(format)
  @format = format.to_s.downcase
end

Instance Method Details

#checkstyle(entries) ⇒ String

Parameters:

  • entries (Object)

Returns:

  • (String)


96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/mdlint/cli/output_formatter.rb', line 96

def checkstyle(entries)
  files = entries.group_by { |entry| entry[:filename] }.map do |filename, file_entries|
    errors = file_entries.map do |entry|
      violation = entry[:violation]
      attributes = {
        line: violation.line,
        column: violation.column,
        severity: violation.severity,
        message: "[#{violation.rule_id}] #{violation.message}",
        source: violation.rule_id
      }.compact.map { |key, value| "#{key}=\"#{xml_escape(value)}\"" }.join(" ")
      "    <error #{attributes}/>"
    end
    "  <file name=\"#{xml_escape(filename)}\">\n#{errors.join("\n")}\n  </file>"
  end
  "<checkstyle version=\"1.0\">\n#{files.join("\n")}\n</checkstyle>\n"
end

#github(entries) ⇒ String

Parameters:

  • entries (Object)

Returns:

  • (String)


47
48
49
50
51
52
53
54
55
# File 'lib/mdlint/cli/output_formatter.rb', line 47

def github(entries)
  entries.map do |entry|
    violation = entry[:violation]
    command = violation.error? ? "error" : violation.info? ? "notice" : "warning"
    location = "file=#{entry[:filename]},line=#{violation.line}"
    location += ",col=#{violation.column}" if violation.column
    "::#{command} #{location}::[#{violation.rule_id}] #{violation.message}"
  end.join("\n") + "\n"
end

#json(entries) ⇒ String

Parameters:

  • entries (Object)

Returns:

  • (String)


41
42
43
44
45
# File 'lib/mdlint/cli/output_formatter.rb', line 41

def json(entries)
  JSON.generate(entries.map do |entry|
    entry[:violation].to_h.merge(file: entry[:filename])
  end) + "\n"
end

#junit(entries) ⇒ String

Parameters:

  • entries (Object)

Returns:

  • (String)


114
115
116
117
118
119
120
121
# File 'lib/mdlint/cli/output_formatter.rb', line 114

def junit(entries)
  cases = entries.map do |entry|
    violation = entry[:violation]
    name = "#{entry[:filename]}:#{violation.line}:#{violation.rule_id}"
    "    <testcase name=\"#{xml_escape(name)}\"><failure message=\"#{xml_escape(violation.message)}\"/></testcase>"
  end
  "<testsuite name=\"mdlint\" tests=\"#{entries.length}\" failures=\"#{entries.length}\">\n#{cases.join("\n")}\n</testsuite>\n"
end

#render(entries) ⇒ String

Parameters:

  • entries (Object)

Returns:

  • (String)


14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/mdlint/cli/output_formatter.rb', line 14

def render(entries)
  return "" if entries.empty? && @format == "text"

  case @format
  when "json"
    json(entries)
  when "sarif"
    sarif(entries)
  when "github"
    github(entries)
  when "checkstyle"
    checkstyle(entries)
  when "junit"
    junit(entries)
  when "reviewdog", "rdjson"
    reviewdog(entries)
  else
    text(entries)
  end
end

#reviewdog(entries) ⇒ String

Parameters:

  • entries (Object)

Returns:

  • (String)


123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/mdlint/cli/output_formatter.rb', line 123

def reviewdog(entries)
  document = {
    source: { name: "mdlint" },
    diagnostics: entries.map do |entry|
      violation = entry[:violation]
      column = violation.column || 1
      {
        message: "[#{violation.rule_id}] #{violation.message}",
        location: {
          path: entry[:filename],
          range: {
            start: { line: violation.line, column: column },
            end: { line: violation.line, column: column + 1 }
          }
        },
        severity: violation.severity.to_s.upcase
      }
    end
  }
  JSON.generate(document) + "\n"
end

#sarif(entries) ⇒ String

Parameters:

  • entries (Object)

Returns:

  • (String)


57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/mdlint/cli/output_formatter.rb', line 57

def sarif(entries)
  rules = entries.map { |entry| entry[:violation].rule_id }.uniq.filter_map do |rule_id|
    rule = Mdlint::Linter::RuleRegistry.find(rule_id)
    next unless rule

    {
      id: rule.rule_id,
      shortDescription: { text: rule.description.to_s }
    }
  end
  rule_indexes = rules.each_with_index.to_h { |rule, index| [rule[:id], index] }

  document = {
    version: "2.1.0",
    "$schema": "https://json.schemastore.org/sarif-2.1.0.json",
    runs: [{
      tool: { driver: { name: "mdlint", rules: rules } },
      results: entries.map do |entry|
        violation = entry[:violation]
        location = {
          physicalLocation: {
            artifactLocation: { uri: entry[:filename] },
            region: { startLine: violation.line }
          }
        }
        location[:physicalLocation][:region][:startColumn] = violation.column if violation.column
        {
          ruleId: violation.rule_id,
          ruleIndex: rule_indexes[violation.rule_id],
          level: LEVELS.fetch(violation.severity, "warning"),
          message: { text: violation.message },
          locations: [location]
        }
      end
    }]
  }
  JSON.generate(document) + "\n"
end

#text(entries) ⇒ String

Parameters:

  • entries (Object)

Returns:

  • (String)


37
38
39
# File 'lib/mdlint/cli/output_formatter.rb', line 37

def text(entries)
  entries.map { |entry| "#{entry[:filename]}:#{entry[:violation]}" }.join("\n") + "\n"
end

#xml_escape(value) ⇒ String

Parameters:

  • value (Object)

Returns:

  • (String)


145
146
147
# File 'lib/mdlint/cli/output_formatter.rb', line 145

def xml_escape(value)
  value.to_s.gsub("&", "&amp;").gsub("<", "&lt;").gsub(">", "&gt;").gsub('"', "&quot;").gsub("'", "&apos;")
end