lib/lutaml/model/schema/json_schema_parser.rb



require "json-schema"

module Lutaml
  module Model
    module Schema
      class JsonSchemaParser
        def self.parse(schema_json)
          schema = JSON::Schema.parse(schema_json)
          definitions = schema.schema.fetch("$defs", {})
          definitions.map do |class_name, class_schema|
            generate_class_definition(class_name, class_schema)
          end.join("\n")
        end

        def self.generate_class_definition(class_name, class_schema)
          attributes = class_schema["properties"] || {}
          required_attributes = class_schema["required"] || []

          <<~RUBY
            class #{class_name} < Lutaml::Model::Serializable
              #{generate_attributes(attributes, required_attributes)}

              json do
                #{generate_json_mappings(attributes)}
              end
            end
          RUBY
        end

        def self.generate_attributes(attributes, required_attributes)
          attributes.map do |name, schema|
            type = schema["type"]
            ruby_type = get_ruby_type(type, schema)
            attributes = [
              "attribute :#{name}",
              "Lutaml::Model::Type::#{ruby_type}",
              "required: #{required_attributes.include?(name).inspect}",
            ]

            attributes.join(", ")
          end.join("\n  ")
        end

        def self.generate_json_mappings(attributes)
          attributes.keys.map do |name|
            "map '#{name}', to: :#{name}"
          end.join("\n    ")
        end

        def self.get_ruby_type(type, schema)
          case type
          when "integer"
            "Integer"
          when "boolean"
            "Boolean"
          when "number"
            "Float"
          when "array"
            item_schema = schema["items"]
            item_type = get_ruby_type(item_schema["type"], item_schema)
            "Array.of(#{item_type})"
          when "object"
            object_class_name(schema)
          else
            "String" # Default to string for unknown types
          end
        end

        def self.object_class_name(schema)
          nested_class_name = schema["title"] || "NestedObject"
          nested_class_definition = generate_class_definition(
            nested_class_name, schema
          )
          @nested_classes ||= []
          @nested_classes << nested_class_definition
          nested_class_name
        end

        def self.nested_classes
          @nested_classes ||= []
        end

        def self.generate(schema_json)
          @nested_classes = []
          main_classes = parse(schema_json)
          (nested_classes + [main_classes]).join("\n")
        end
      end
    end
  end
end