func processAssociatedTypes()

in Sources/MockoloFramework/Templates/NominalTemplate.swift [270:355]


    func processAssociatedTypes(`in` models: [(String, Model)], acl: String) -> (
        aliasItems: String,
        typeparameters: String,
        whereClauses: String,
        renderedModelNames: Set<String>
    ) {
        let addAcl = declKindOfMockAnnotatedBaseType == .protocol ? acl : ""
        let allWhereConstraints = genericWhereConstraints + models.flatMap { ($1 as? AssociatedTypeModel)?.whereConstraints ?? [] }
        let hasWhereConstraints = !allWhereConstraints.isEmpty

        let aliasModels = [String: [TypealiasRenderableModel]](
            grouping: models.compactMap { $1 as? TypealiasRenderableModel },
            by: \.name
        ).sorted(path: \.key)

        // If there is a where, do not output typealias as it may not satisfy the conditions
        if hasWhereConstraints {
            let aliasItems = aliasModels.compactMap { (name, candidates) in
                if let defaultType = candidates.firstNonNil(\.defaultType) {
                    return """
                    \(1.tab)// Unavailable due to the presence of generic constraints
                    \(1.tab)// \(addAcl)\(String.typealias) \(name) = \(defaultType.displayName)
                    
                    """
                }
                return nil
            }.joined(separator: "\n")
            let typeparameters = aliasModels.map { (name, candidates) in
                mergeAssociatedTypes(
                    name: name,
                    models: candidates.compactMap { $0 as? AssociatedTypeModel }
                )
            }
            return (
                aliasItems: aliasItems,
                typeparameters: typeparameters.isEmpty ? "" : "<\(typeparameters.joined(separator: ", "))>",
                whereClauses: allWhereConstraints.isEmpty ? "" : "where \(allWhereConstraints.joined(separator: ", ")) ",
                renderedModelNames: Set(aliasModels.map(\.key))
            )
        }

        var aliasItems: String = ""
        var typeparameters: [String] = []
        var renderedModelNames: Set<String> = []
        for (name, candidates) in aliasModels {
            // If only one default type exists and the others have no constraints, use it directly.
            let havingDefaults = candidates.filter { $0.defaultType != nil }
            if havingDefaults.count == 1 && !candidates
                .filter({ $0 !== havingDefaults[0] })
                .contains(where: \.hasGenericConstraints) {
                continue
            }

            // If there are no constraints on all candidates, use Any automatically.
            if candidates.allSatisfy({ !$0.hasGenericConstraints }) {
                aliasItems.append("\(1.tab)\(addAcl)\(String.typealias) \(name) = Any\n")
            } else {
                // The other cases, gather all constraints
                let typeparameter = mergeAssociatedTypes(
                    name: name,
                    models: candidates.compactMap { $0 as? AssociatedTypeModel }
                )
                typeparameters.append(typeparameter)
            }

            renderedModelNames.insert(name)
        }

        return (
            aliasItems: aliasItems,
            typeparameters: typeparameters.isEmpty ? "" : "<\(typeparameters.joined(separator: ", "))>",
            whereClauses: allWhereConstraints.isEmpty ? "" : "where \(allWhereConstraints.joined(separator: ", ")) ",
            renderedModelNames: renderedModelNames
        )

        func mergeAssociatedTypes(name: String, models: [AssociatedTypeModel]) -> String {
            let inheritances = models.flatMap(\.inheritances)

            let typeparameter = if inheritances.isEmpty {
                name
            } else {
                "\(name): \(inheritances.joined(separator: " & "))"
            }
            return typeparameter
        }
    }