module DataModel
	# Symbol type
	class Builtin::Symbol < Type
		include Errors

		# Arguments for this type
		class Arguments < Struct
			prop :optional, :boolean, default: false
			prop :included, [:array, :symbol], default: []
			prop :excluded, [:array, :symbol], default: []
		end

		# read a value, and validate it
		# @param val [Object] the value to read
		# @param coerce [Boolean] whether to coerce the value
		# @return [Array(Object, Error)] the result of reading the value
		def read(val, coerce: false)
			args = Arguments.new(type_args)
			err = Error.new

			# optional & missing
			if args.optional && val.nil?
				return [val, err]
			end

			if !args.optional && val.nil?
				err.add(missing_error(type_name))
				return [val, err]
			end

			# type error
			if !val.is_a?(Symbol) && !coerce
				err.add(type_error(type_name, val))
				return [val, err]
			end

			# attempt coercion
			if !val.is_a?(Symbol) && coerce
				if val.is_a?(String)
					val = val.intern
				elsif val.respond_to?(:to_sym)
					val = val.to_sym
				else
					err.add(coerce_error(type_name, val))
					return [val, err]
				end
			end

			# inclusion
			if args.included.any? && !args.included.include?(val)
				err.add(inclusion_error(args.included))
				return [val, err]
			end

			# exclusion
			if args.excluded.any? && args.excluded.include?(val)
				err.add(exclusion_error(args.excluded))
				return [val, err]
			end

			# done
			return [val, err]
		end
	end
end