public static ServiceSpecification generate(
            Iterable<ServiceInfo> services,
            Function<DescriptiveTypeSignature, ? extends DescriptiveTypeInfo> descriptiveTypeInfoFactory) {
        if (Iterables.isEmpty(services)) {
            return emptyServiceSpecification;
        }

        // Collect all descriptive types referred by the services.
        final Set<DescriptiveTypeSignature> descriptiveTypes =
                Streams.stream(services)
                       .flatMap(s -> s.findDescriptiveTypes().stream())
                       .collect(toImmutableSortedSet(comparing(TypeSignature::name)));

        final Map<String, EnumInfo> enums = new HashMap<>();
        final Map<String, StructInfo> structs = new HashMap<>();
        final Map<String, ExceptionInfo> exceptions = new HashMap<>();

        generateDescriptiveTypeInfos(descriptiveTypeInfoFactory, enums, structs, exceptions, descriptiveTypes);

        return new ServiceSpecification(services, enums.values(), structs.values(), exceptions.values());
    }
Generates a new {@link ServiceSpecification} from the specified {@link ServiceInfo}s and
the factory {@link Function} that creates {@link DescriptiveTypeInfo}s for the enum, struct or
exception types referred by the specified {@link ServiceInfo}s.
core/src/main/java/com/linecorp/armeria/server/docs/ServiceSpecification.java