#!/usr/bin/env ruby
#
# Generate src/metadata/value_conversions.rs
#

require "open3"

Configuration = Struct.new(:as, :variant, :name, :copy, :into) do
  def initialize(as:, variant:, name: variant.downcase, copy: false, into: true)
    super(as, variant, name, copy, into)
  end

  alias copy? copy
  alias into? into

  def render_as_method
    bound = (copy? ? "val" : "ref val")
    <<~RUST
      /// Returns the value as a `Some(#{as_option_type})` if it is a `MetadataValue::#{variant}`, or `None` otherwise.
      pub fn as_#{name}(&self) -> Option<#{as_option_type}> {
          match *self {
              Value::#{variant}(#{bound}) => Some(val),
              _ => None,
          }
      }

    RUST
  end

  def render_into_method
    return "" unless into?
    <<~RUST
      /// Consumes `self` and returns the inner value as a `Some(#{as})` if it is a `MetadataValue::#{variant}`, or `None` otherwise.
      pub fn into_#{name}(self) -> Option<#{as}> {
          match self {
              Value::#{variant}(val) => Some(val),
              _ => None,
          }
      }

    RUST
  end

  private
  def as_option_type
    if copy?
      as
    else
      "&#{as}"
    end
  end
end

number_types = %w[u8 u16 u32 u64 i16 i32 i64 f64]

configurations = number_types.map { |name|
  Configuration.new(as: name, variant: name.upcase, name: name, copy: true)
}
configurations += [
  Configuration.new(as: "bool", variant: "Bool", copy: true),
  Configuration.new(as: "String", variant: "String", name: "string"),
  Configuration.new(as: "HashMap<String, Value>", variant: "Map"),
  Configuration.new(as: "Vec<Value>", variant: "Array"),
  Configuration.new(as: "str", variant: "String", name: "str", into: false),
]

code = <<RUST
// This file was generated by #{$0}.
impl Value {
RUST

configurations.each do |config|
  code << config.render_as_method
end

configurations.each do |config|
  code << config.render_into_method
end

code << "\n}"

stdout, stderr, status = Open3.capture3("rustfmt", stdin_data: code)
$stderr.puts stderr
if status.success?
  File.write("src/metadata/value_conversions.rs", stdout)
else
  $stderr.puts "\n\nFailed to rustfmt code."
  exit 1
end
