class Net::IMAP::DataLite

def self.define(*args, &block)

etc.
be keywords (e.g: +next+ or +class+) or start with capital letters, "@",
member names which are valid local variable names. Member names can't
_NOTE:_ Unlike ruby 3.2's +Data.define+, DataLite.define only supports

Defines a new Data class.
def self.define(*args, &block)
  members = args.each_with_object({}) do |arg, members|
    arg = arg.to_str unless arg in Symbol | String if arg.respond_to?(:to_str)
    arg = arg.to_sym if     arg in String
    arg in Symbol     or  raise TypeError,     TYPE_ERROR    % [arg]
    arg in %r{=}      and raise ArgumentError, ATTRSET_ERROR % [arg]
    members.key?(arg) and raise ArgumentError, DUP_ERROR     % [arg]
    members[arg] = true
  end
  members = members.keys.freeze
  klass = ::Class.new(self)
  klass.singleton_class.undef_method :define
  klass.define_singleton_method(:members) { members }
  def klass.new(*args, **kwargs, &block)
    if kwargs.size.positive?
      if args.size.positive?
        raise ArgumentError, ARITY_ERROR % [args.size, 0]
      end
    elsif members.size < args.size
      expected = members.size.zero? ? 0 : 0..members.size
      raise ArgumentError, ARITY_ERROR % [args.size, expected]
    else
      kwargs = Hash[members.take(args.size).zip(args)]
    end
    allocate.tap do |instance|
      instance.__send__(:initialize, **kwargs, &block)
    end.freeze
  end
  klass.singleton_class.alias_method :[], :new
  klass.attr_reader(*members)
  # Dynamically defined initializer methods are in an included module,
  # rather than directly on DataLite (like in ruby 3.2+):
  # * simpler to handle required kwarg ArgumentErrors
  # * easier to ensure consistent ivar assignment order (object shape)
  # * faster than instance_variable_set
  klass.include(Module.new do
    if members.any?
      kwargs = members.map{"#{_1.name}:"}.join(", ")
      params = members.map(&:name).join(", ")
      ivars  = members.map{"@#{_1.name}"}.join(", ")
      attrs  = members.map{"attrs[:#{_1.name}]"}.join(", ")
      module_eval <<~RUBY, __FILE__, __LINE__ + 1
        protected
        def initialize(#{kwargs}) #{ivars} = #{params}; freeze end
        def marshal_load(attrs)   #{ivars} = #{attrs};  freeze end
      RUBY
    end
  end)
  klass.module_eval do _1.module_eval(&block) end if block_given?
  klass
end

def ==(other) self.class == other.class && to_h == other.to_h end

def ==(other)    self.class == other.class && to_h == other.to_h end

def __inspect_guard__(obj)

uncommon, but we'll support them for the sake of completeness.
Making circular references inside a Data object _should_ be very

recursively trigger a SystemStackError (stack level too deep).
Marks +obj+ as seen inside the block, so circuler references don't
Yields +true+ if +obj+ has been seen already, +false+ if it hasn't.
def __inspect_guard__(obj)
  preexisting = Thread.current[:__net_imap_data__inspect__]
  Thread.current[:__net_imap_data__inspect__] ||= {}.compare_by_identity
  inspect_guard = Thread.current[:__net_imap_data__inspect__]
  if inspect_guard.include?(obj)
    yield true
  else
    begin
      inspect_guard[obj] = true
      yield false
    ensure
      inspect_guard.delete(obj)
    end
  end
ensure
  unless preexisting.equal?(inspect_guard)
    Thread.current[:__net_imap_data__inspect__] = preexisting
  end
end

def __to_h__; Hash[members.map {|m| [m, send(m)] }] end

def __to_h__; Hash[members.map {|m| [m, send(m)] }] end

def deconstruct; __to_h__.values end

def deconstruct; __to_h__.values                                 end

def deconstruct_keys(keys)

def deconstruct_keys(keys)
  raise TypeError unless keys.is_a?(Array) || keys.nil?
  return __to_h__ if keys&.first.nil?
  __to_h__.slice(*keys)
end

def encode_with(coder) coder.map = to_h.transform_keys(&:to_s) end

def encode_with(coder) coder.map = to_h.transform_keys(&:to_s)        end

def eql?(other) self.class == other.class && hash == other.hash end

def eql?(other)  self.class == other.class && hash == other.hash end

def hash; [self.class, __to_h__].hash end

def hash;        [self.class, __to_h__].hash                     end

def init_with(coder) initialize(**coder.map.transform_keys(&:to_sym)) end

def init_with(coder) initialize(**coder.map.transform_keys(&:to_sym)) end

def initialize_copy(source) super.freeze end

def initialize_copy(source) super.freeze end

def inspect

def inspect
  __inspect_guard__(self) do |seen|
    return "#<data #{self.class}:...>" if seen
    attrs = __to_h__.map {|kv| "%s=%p" % kv }.join(", ")
    display = ["data", self.class.name, attrs].compact.join(" ")
    "#<#{display}>"
  end
end

def marshal_dump; __to_h__ end

def marshal_dump; __to_h__ end

def members; self.class.members end

#
def members;     self.class.members                              end

def to_h(&block) block ? __to_h__.to_h(&block) : __to_h__ end

def to_h(&block) block ? __to_h__.to_h(&block) : __to_h__        end

def with(**kwargs)

def with(**kwargs)
  return self if kwargs.empty?
  self.class.new(**__to_h__.merge(kwargs))
end