class TerraformLandscape::TerraformPlan

#######################################################################
explanation of the plan to the user.
This allows us to easily inspect the plan and present a more readable
Represents the parsed output of ‘terraform plan`.
#######################################################################

def attribute_indent_amount_for_resource(resource)

def attribute_indent_amount_for_resource(resource)
  longest_name_length = resource[:attributes].keys.reduce(0) do |longest, name|
    name.length > longest ? name.length : longest
  end
  longest_name_length + 8
end

def display(output)

def display(output)
  @out = output
  @ast.each do |resource|
    display_resource(resource)
    @out.newline
  end
end

def display_added_or_removed_attribute(

def display_added_or_removed_attribute(
  change_color,
  attribute_name,
  attribute_value,
  attribute_value_indent,
  attribute_value_indent_amount
)
  @out.print "    #{attribute_name}:".ljust(attribute_value_indent_amount, ' ')
    .colorize(change_color)
  evaluated_string = eval(attribute_value) # rubocop:disable Lint/Eval
  if json?(evaluated_string)
    @out.print to_pretty_json(evaluated_string).gsub("\n", "\n" + attribute_value_indent)
      .colorize(change_color)
  else
    @out.print "\"#{evaluated_string.colorize(change_color)}\""
  end
  @out.newline
end

def display_attribute(

def display_attribute(
  resource,
  change_color,
  attribute_name,
  attribute_value,
  attribute_value_indent_amount
)
  attribute_value_indent = ' ' * attribute_value_indent_amount
  if resource[:change] == :~
    display_modified_attribute(change_color,
                               attribute_name,
                               attribute_value,
                               attribute_value_indent,
                               attribute_value_indent_amount)
  else
    display_added_or_removed_attribute(change_color,
                                       attribute_name,
                                       attribute_value,
                                       attribute_value_indent,
                                       attribute_value_indent_amount)
  end
end

def display_diff(old, new, indent)

def display_diff(old, new, indent)
  @out.print Diffy::Diff.new(old, new, { context: @diff_context_lines })
    .to_s(String.disable_colorization ? :text : :color)
    .gsub("\n", "\n" + indent)
    .strip
end

def display_modified_attribute( # rubocop:disable Metrics/AbcSize, Metrics/MethodLength

rubocop:disable Metrics/AbcSize, Metrics/MethodLength
def display_modified_attribute( # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
  change_color,
  attribute_name,
  attribute_value,
  attribute_value_indent,
  attribute_value_indent_amount
)
  # Since the attribute line is always of the form
  # "old value" => "new value", we can add curly braces and parse with
  # `eval` to obtain a hash with a single key/value.
  old, new = eval("{#{attribute_value}}").to_a.first # rubocop:disable Lint/Eval
  return if old == new # Don't show unchanged attributes
  @out.print "    #{attribute_name}:".ljust(attribute_value_indent_amount, ' ')
    .colorize(change_color)
  if json?(new)
    # Value looks like JSON, so prettify it to make it more readable
    fancy_old = "#{to_pretty_json(old)}\n"
    fancy_new = "#{to_pretty_json(new)}\n"
    display_diff(fancy_old, fancy_new, attribute_value_indent)
  elsif old.include?("\n") || new.include?("\n")
    # Multiline content, so display nicer diff
    display_diff("#{old}\n", "#{new}\n", attribute_value_indent)
  else
    # Typical values, so just show before/after
    @out.print '"' + old.colorize(:red) + '"'
    @out.print ' => '.colorize(:light_black)
    @out.print '"' + new.colorize(:green) + '"'
  end
  @out.newline
end

def display_resource(resource)

def display_resource(resource)
  change_color = CHANGE_SYMBOL_TO_COLOR[resource[:change]]
  @out.puts "#{resource[:change]} #{resource[:resource_type]}." \
            "#{resource[:resource_name]}".colorize(change_color)
  # Determine longest attribute name so we align all values at same indentation
  attribute_value_indent_amount = attribute_indent_amount_for_resource(resource)
  resource[:attributes].each do |attribute_name, attribute_value|
    display_attribute(resource,
                      change_color,
                      attribute_name,
                      attribute_value,
                      attribute_value_indent_amount)
  end
end

def from_output(string)

def from_output(string)
  return new([]) if string.strip.empty?
  tree = parser.parse(string)
  raise ParseError, parser.failure_reason unless tree
  new(tree.to_ast)
end

def initialize(plan_ast, options = {})

Create a plan from an abstract syntax tree (AST).
def initialize(plan_ast, options = {})
  @ast = plan_ast
  @diff_context_lines = options.fetch(:diff_context_lines, DEFAULT_DIFF_CONTEXT_LINES)
end

def json?(value)

def json?(value)
  ['{', '['].include?(value.to_s[0]) &&
    (JSON.parse(value) rescue nil) # rubocop:disable Style/RescueModifier
end

def parser

def parser
  @parser ||=
    begin
      Treetop.load(GRAMMAR_FILE)
      TerraformPlanParser.new
    end
end

def to_pretty_json(value)

def to_pretty_json(value)
  JSON.pretty_generate(JSON.parse(value),
                       {
                         indent: '  ',
                         space: ' ',
                         object_nl: "\n",
                         array_nl: "\n"
                       })
end