// This source code is released into the public domain. module; #include export module nihil.ucl:object_cast; import nihil.std; import nihil.core; import :type; import :object; import :array; namespace nihil::ucl { // // Ensure a UCL object is convertible to another type. // // Implementation for basic types. template struct convert_check { [[nodiscard]] auto check(::ucl_object_t const *from) -> std::expected { auto from_type = static_cast(::ucl_object_type(from)); auto to_type = To::ucl_type; // Converting from anything to object is permitted. if (to_type == object_type::object) return {}; // Converting between two equal types is permitted. if (from_type == to_type) return {}; // Otherwise, this is an error. return std::unexpected(type_mismatch(to_type, from_type)); } }; // Implementation for array. template struct convert_check> { [[nodiscard]] auto check(::ucl_object_t const *from) -> std::expected { using To = array; auto from_type = static_cast(::ucl_object_type(from)); auto to_type = To::ucl_type; // If the source type is not an array, this is an error. if (from_type != object_type::array) co_return std::unexpected( type_mismatch(to_type, from_type)); for (std::size_t i = 0, size = ::ucl_array_size(from); i < size; ++i) { auto const *arr_obj = ::ucl_array_find_index(from, i); co_await convert_check{} .check(arr_obj); } co_return {}; } }; /* * Convert a UCL object to another type. */ export template auto object_cast(object const &from) -> std::expected { auto const *uobj = from.get_ucl_object(); co_await convert_check{}.check(uobj); co_return To(nihil::ucl::ref, uobj); } } // namespace nihil::ucl