class Inspec::Metadata

Use valid? to determine if the metadata is coherent.
This allows the check CLI command to analyse the issues.
A Metadata object may be created and finalized with invalid data.
Extract metadata.rb information

def self.finalize(metadata, profile_id, options, logger = nil)

def self.finalize(metadata, profile_id, options, logger = nil)
  return nil if metadata.nil?
  param = metadata.params || {}
  options ||= {}
  param['version'] = param['version'].to_s unless param['version'].nil?
  metadata.params = symbolize_keys(param)
  metadata.params[:supports] = finalize_supports(metadata.params[:supports], logger)
  finalize_name(metadata, profile_id, options[:target])
  metadata
end

def self.finalize_name(metadata, profile_id, original_target)

def self.finalize_name(metadata, profile_id, original_target)
  # profile_id always overwrites whatever already exists as the name
  unless profile_id.to_s.empty?
    metadata.params[:name] = profile_id.to_s
    return
  end
  # don't overwrite an existing name
  return unless metadata.params[:name].nil?
  # if there's a title, there is no need to set a name too
  return unless metadata.params[:title].nil?
  # create a new name based on the original target if it exists
  # Crudely slug the target to not contain slashes, to avoid breaking
  # unit tests that look for warning sequences
  return if original_target.to_s.empty?
  metadata.params[:title] = "tests from #{original_target}"
  metadata.params[:name] = metadata.params[:title].gsub(%r{[\/\\]}, '.')
end

def self.finalize_supports(supports, logger)

def self.finalize_supports(supports, logger)
  case x = supports
  when Hash   then [finalize_supports_elem(x, logger)]
  when Array  then x.map { |e| finalize_supports_elem(e, logger) }.compact
  when nil    then []
  else
    logger ||= Logger.new(nil)
    logger.warn(
      "Do not use deprecated `supports: #{x}` syntax. Instead use:\n"\
      "supports:\n  - os-family: #{x}\n\n",
    )
    [{ :'os-family' => x }] # rubocop:disable Style/HashSyntax
  end
end

def self.finalize_supports_elem(elem, logger)

def self.finalize_supports_elem(elem, logger)
  case x = elem
  when Hash
    x[:release] = x[:release].to_s if x[:release]
    x
  when Array
    logger.warn(
      'Failed to read supports entry that is an array. Please use '\
      'the `supports: {os-family: xyz}` syntax.',
    )
    nil
  when nil then nil
  else
    logger ||= Logger.new(nil)
    logger.warn(
      "Do not use deprecated `supports: #{x}` syntax. Instead use:\n"\
      "supports:\n  - os-family: #{x}\n\n",
    )
    { :'os-family' => x } # rubocop:disable Style/HashSyntax
  end
end

def self.from_file(path, profile_id, logger = nil)

def self.from_file(path, profile_id, logger = nil)
  unless File.file?(path)
    logger ||= Logger.new(nil)
    logger.error "Can't find metadata file #{path}"
    return nil
  end
  from_ref(File.basename(path), File.read(path), profile_id, logger)
end

def self.from_ref(ref, content, profile_id, logger = nil)

def self.from_ref(ref, content, profile_id, logger = nil)
  # NOTE there doesn't have to exist an actual file, it may come from an
  # archive (i.e., content)
  case File.basename(ref)
  when 'inspec.yml'
    from_yaml(ref, content, profile_id, logger)
  when 'metadata.rb'
    from_ruby(ref, content, profile_id, logger)
  else
    logger ||= Logger.new(nil)
    logger.error "Don't know how to handle metadata in #{ref}"
    nil
  end
end

def self.from_ruby(ref, content, profile_id, logger = nil)

def self.from_ruby(ref, content, profile_id, logger = nil)
  res = Metadata.new(ref, logger)
  res.instance_eval(content, ref, 1)
  res.content = content
  finalize(res, profile_id, {}, logger)
end

def self.from_yaml(ref, content, profile_id, logger = nil)

def self.from_yaml(ref, content, profile_id, logger = nil)
  res = Metadata.new(ref, logger)
  res.params = YAML.load(content)
  res.content = content
  finalize(res, profile_id, {}, logger)
end

def self.symbolize_keys(obj)

def self.symbolize_keys(obj)
  return obj.map { |i| symbolize_keys(i) } if obj.is_a?(Array)
  return obj unless obj.is_a?(Hash)
  obj.each_with_object({}) do |(k, v), h|
    v = symbolize_keys(v) if v.is_a?(Hash)
    v = symbolize_keys(v) if v.is_a?(Array)
    h[k.to_sym] = v
  end
end

def dependencies

def dependencies
  params[:depends] || []
end

def initialize(ref, logger = nil)

def initialize(ref, logger = nil)
  @ref = ref
  @logger = logger || Logger.new(nil)
  @content = ''
  @params = {}
  @missing_methods = []
end

def inspec_requirement

def inspec_requirement
  # using Gem::Requirement here to allow nil values which
  # translate to [">= 0"]
  Gem::Requirement.create(params[:inspec_version])
end

def method_missing(sth, *args)

def method_missing(sth, *args)
  @logger.warn "#{ref} doesn't support: #{sth} #{args}"
  @missing_methods.push(sth)
end

def supports(sth, version = nil)

def supports(sth, version = nil)
  # Ignore supports with metadata.rb. This file is legacy and the way it
  # it handles `supports` deprecated. A deprecation warning will be printed
  # already.
end

def supports_platform?(backend)

def supports_platform?(backend)
  backend.platform.supported?(params[:supports])
end

def supports_runtime?

def supports_runtime?
  running = Gem::Version.new(Inspec::VERSION)
  inspec_requirement.satisfied_by?(running)
end

def unsupported

def unsupported
  @missing_methods
end

def valid # rubocop:disable Metrics/AbcSize

rubocop:disable Metrics/AbcSize
return all warn and errors
def valid # rubocop:disable Metrics/AbcSize
  errors = []
  warnings = []
  %w{name version}.each do |field|
    next unless params[field.to_sym].nil?
    errors.push("Missing profile #{field} in #{ref}")
  end
  if %r{[\/\\]} =~ params[:name]
    errors.push("The profile name (#{params[:name]}) contains a slash" \
                  ' which is not permitted. Please remove all slashes from `inspec.yml`.')
  end
  # if version is set, ensure it is correct
  if !params[:version].nil? && !valid_version?(params[:version])
    errors.push('Version needs to be in SemVer format')
  end
  %w{title summary maintainer copyright license}.each do |field|
    next unless params[field.to_sym].nil?
    warnings.push("Missing profile #{field} in #{ref}")
  end
  # if version is set, ensure it is in SPDX format
  if !params[:license].nil? && !Spdx.valid_license?(params[:license])
    warnings.push("License '#{params[:license]}' needs to be in SPDX format. See https://spdx.org/licenses/.")
  end
  [errors, warnings]
end

def valid?

returns true or false
def valid?
  errors, _warnings = valid
  errors.empty? && unsupported.empty?
end

def valid_version?(value)

def valid_version?(value)
  Semverse::Version.new(value)
  true
rescue Semverse::InvalidVersionFormat
  false
end