debian-mirror-gitlab/lib/gitlab/ci/ansi2json/line.rb

114 lines
3.3 KiB
Ruby
Raw Normal View History

2019-12-21 20:55:43 +05:30
# frozen_string_literal: true
module Gitlab
module Ci
module Ansi2json
# Line class is responsible for keeping the internal state of
# a log line and to finally serialize it as Hash.
class Line
# Line::Segment is a portion of a line that has its own style
# and text. Multiple segments make the line content.
class Segment
2021-10-27 15:23:28 +05:30
include EncodingHelper
2019-12-21 20:55:43 +05:30
attr_accessor :text, :style
def initialize(style:)
@text = +''
@style = style
end
def empty?
text.empty?
end
def to_h
2021-10-27 15:23:28 +05:30
# Without forcing the encoding to UTF-8 and then replacing
# invalid UTF-8 sequences we can get an error when serializing
# the Hash to JSON.
2023-04-23 21:23:45 +05:30
# Encoding::UndefinedConversionError (or possibly JSON::GeneratorError in json 2.6.1+):
2019-12-21 20:55:43 +05:30
# "\xE2" from ASCII-8BIT to UTF-8
2021-10-27 15:23:28 +05:30
{ text: encode_utf8_no_detect(text) }.tap do |result|
2019-12-21 20:55:43 +05:30
result[:style] = style.to_s if style.set?
end
end
end
attr_reader :offset, :sections, :segments, :current_segment,
2021-01-03 14:25:43 +05:30
:section_header, :section_duration, :section_options
2019-12-21 20:55:43 +05:30
def initialize(offset:, style:, sections: [])
@offset = offset
@segments = []
@sections = sections
@section_header = false
@duration = nil
@current_segment = Segment.new(style: style)
end
def <<(data)
@current_segment.text << data
end
2019-12-26 22:10:19 +05:30
def clear!
@segments.clear
@current_segment = Segment.new(style: style)
end
2019-12-21 20:55:43 +05:30
def style
@current_segment.style
end
def empty?
2019-12-26 22:10:19 +05:30
@segments.empty? && @current_segment.empty? && @section_duration.nil?
2019-12-21 20:55:43 +05:30
end
def update_style(ansi_commands)
@current_segment.style.update(ansi_commands)
end
def add_section(section)
@sections << section
end
2021-01-03 14:25:43 +05:30
def set_section_options(options)
@section_options = options
end
2019-12-21 20:55:43 +05:30
def set_as_section_header
@section_header = true
end
2021-09-30 23:02:18 +05:30
def set_section_duration(duration_in_seconds)
2022-10-02 17:18:49 +05:30
normalized_duration_in_seconds = duration_in_seconds.to_i.clamp(0, 1.year)
duration = ActiveSupport::Duration.build(normalized_duration_in_seconds)
2021-09-30 23:02:18 +05:30
hours = duration.in_hours.floor
hours = hours > 0 ? "%02d" % hours : nil
minutes = "%02d" % duration.parts[:minutes].to_i
seconds = "%02d" % duration.parts[:seconds].to_i
@section_duration = [hours, minutes, seconds].compact.join(':')
2019-12-21 20:55:43 +05:30
end
def flush_current_segment!
return if @current_segment.empty?
@segments << @current_segment.to_h
@current_segment = Segment.new(style: @current_segment.style)
end
def to_h
flush_current_segment!
{ offset: offset, content: @segments }.tap do |result|
result[:section] = sections.last if sections.any?
result[:section_header] = true if @section_header
result[:section_duration] = @section_duration if @section_duration
2021-01-03 14:25:43 +05:30
result[:section_options] = @section_options if @section_options
2019-12-21 20:55:43 +05:30
end
end
end
end
end
end