# frozen_string_literal: true
require_relative "match_p"
using CSV::MatchP if CSV.const_defined?(:MatchP)
# Note: Don't use this class directly. This is an internal class.
# A CSV::Writer receives an output, prepares the header, format and output.
# It allows us to write new rows in the object and rewind it.
def initialize(output, options)
if @options[:write_headers] and @headers
@fields_converter = @options[:fields_converter]
row = @headers.collect {|header| row[header]}
@headers ||= row if @use_headers
row = @fields_converter.convert(row, nil, lineno) if @fields_converter
converted_row = row.collect do |field|
line = converted_row.join(@column_separator) + @row_separator
line = line.encode(@output_encoding)
# Winds back to the beginning
@headers = nil if @options[:headers].nil?
@encoding = @options[:encoding]
headers = @options[:headers]
@headers = CSV.parse_line(headers,
col_sep: @options[:column_separator],
row_sep: @options[:row_separator],
quote_char: @options[:quote_character])
converter = @options[:header_fields_converter]
@headers = converter.convert(@headers, nil, 0)
@headers.each do |header|
header.freeze if header.is_a?(String)
@column_separator = @options[:column_separator].to_s.encode(@encoding)
row_separator = @options[:row_separator]
if row_separator == :auto
@row_separator = $INPUT_RECORD_SEPARATOR.encode(@encoding)
@row_separator = row_separator.to_s.encode(@encoding)
@quote_character = @options[:quote_character]
@force_quotes = @options[:force_quotes]
Regexp.new("[\r\n".encode(@encoding) +
Regexp.escape(@column_separator) +
Regexp.escape(@quote_character.encode(@encoding)) +
@quote_empty = @options.fetch(:quote_empty, true)
return unless @output.is_a?(StringIO)
output_encoding = @output.internal_encoding || @output.external_encoding
if @encoding != output_encoding
if @options[:force_encoding]
@output_encoding = output_encoding
compatible_encoding = Encoding.compatible?(@encoding, output_encoding)
@output.set_encoding(compatible_encoding)
@output.seek(0, IO::SEEK_END)
encoded_quote_character = @quote_character.encode(field.encoding)
encoded_quote_character +
field.gsub(encoded_quote_character,
encoded_quote_character * 2) +
if field.nil? # represent +nil+ fields as empty unquoted fields
field = String(field) # Stringify fields
# represent empty fields as empty quoted fields
if (@quote_empty and field.empty?) or @quotable_pattern.match?(field)