function classFileCreationFunctionWithBaseClassAndPlugins()

in src/pluggable-objc-file-creation.ts [371:626]


function classFileCreationFunctionWithBaseClassAndPlugins<T>(
  baseClassName: string,
  baseClassLibraryName: string | null,
  diagnosticIgnores: List.List<string>,
  pathToValueFile: File.AbsoluteFilePath,
  plugins: List.List<ObjCGenerationPlugIn<T>>,
  typeProvider: ObjCGenerationTypeInfoProvider<T>,
): (
  typeInformation: T,
  typeName: string,
  comments: string[],
  file?: Code.File,
) => Either.Either<Error.Error, Code.File> {
  return function (
    typeInformation: T,
    typeName: string,
    comments: string[],
    file?: Code.File,
  ): Either.Either<Error.Error, Code.File> {
    const fileType = List.foldl<
      ObjCGenerationPlugIn<T>,
      Either.Either<Error.Error, Code.FileType | null>
    >(
      (soFar, plugin) => buildFileType(typeInformation, soFar, plugin),
      Either.Right<Error.Error, Code.FileType | null>(null),
      plugins,
    );
    const nullabilityEither = List.foldl<
      ObjCGenerationPlugIn<T>,
      Either.Either<Error.Error, ObjC.ClassNullability | null>
    >(
      (soFar, plugin) => buildNullability(typeInformation, soFar, plugin),
      Either.Right<Error.Error, ObjC.ClassNullability | null>(null),
      plugins,
    );

    const nullability = Either.match(
      function () {
        return ObjC.ClassNullability.default;
      },
      function (maybeNullability: ObjC.ClassNullability | null) {
        return Maybe.match(
          function (nullability: ObjC.ClassNullability) {
            return nullability;
          },
          function () {
            return ObjC.ClassNullability.default;
          },
          maybeNullability,
        );
      },
      nullabilityEither,
    );

    const visibility = typeProvider.visibilityForType(typeInformation);

    const customPluginBaseClass: Either.Either<
      Error.Error,
      ObjC.BaseClass | null
    > = List.foldl(
      (currentEither, nextPlugin) => {
        return Either.mbind((maybeCurrentBaseClass) => {
          const maybeNextBaseClass = nextPlugin.baseClass(typeInformation);

          return Maybe.match(
            // If we have two conflicting base classes from different plugins,
            // we need to fail as there's no way to choose a class to generate.
            ([currentBaseClass, nextBaseClass]) =>
              Either.Left(
                Error.Error(
                  `Conflicting base classes ${currentBaseClass.className} and ${nextBaseClass.className}`,
                ),
              ),
            // If we only have one of the two, then we can continue with
            // whichever one happens to be present.
            () =>
              Either.Right(Maybe.or(maybeNextBaseClass, maybeCurrentBaseClass)),
            Maybe.and(maybeCurrentBaseClass, maybeNextBaseClass),
          );
        }, currentEither);
      },
      // We start with Nothing rather than with the configuration-specified base
      // class so that we don't always produce an error.
      Either.Right(null as ObjC.BaseClass | null),
      plugins,
    );

    // Allow the plugin-specified base class to override the one that was
    // specified in the configuration file.
    const baseClass: Either.Either<Error.Error, ObjC.BaseClass> = Either.map(
      (maybeCustom) =>
        Maybe.match(
          (custom) => custom,
          () => ({className: baseClassName, libraryName: baseClassLibraryName}),
          maybeCustom,
        ),
      customPluginBaseClass,
    );

    return Either.map(([maybeFileType, baseClass]) => {
      const fileType = Maybe.match(
        function (fileType: Code.FileType) {
          return fileType;
        },
        function () {
          return Code.FileType.ObjectiveC;
        },
        maybeFileType,
      );

      const functions = List.foldl<ObjCGenerationPlugIn<T>, ObjC.Function[]>(
        (soFar, plugin) => buildFunctions(typeInformation, soFar, plugin),
        [],
        plugins,
      );

      const classes = createClassesForObjectSpecType<T>(
        typeInformation,
        typeName,
        comments,
        baseClass.className,
        functions,
        plugins,
        nullability,
        visibility,
      );

      const imports = importListWithBaseImportAppended(
        baseClass.className,
        baseClass.libraryName,
        List.foldl<ObjCGenerationPlugIn<T>, ObjC.Import[]>(
          (soFar, plugin) => buildImports(typeInformation, soFar, plugin),
          [],
          plugins,
        ),
      );

      const enumerations = List.foldl<
        ObjCGenerationPlugIn<T>,
        ObjC.Enumeration[]
      >(
        (soFar, plugin) => buildEnumerations(typeInformation, soFar, plugin),
        [],
        plugins,
      );

      const forwardDeclarations = List.foldl<
        ObjCGenerationPlugIn<T>,
        ObjC.ForwardDeclaration[]
      >(
        (soFar, plugin) =>
          buildForwardDeclarations(typeInformation, soFar, plugin),
        [],
        plugins,
      );

      const blockTypes = List.foldl<ObjCGenerationPlugIn<T>, ObjC.BlockType[]>(
        (soFar, plugin) => buildBlockTypes(typeInformation, soFar, plugin),
        [],
        plugins,
      );

      const staticConstants = List.foldl<
        ObjCGenerationPlugIn<T>,
        ObjC.Constant[]
      >(
        (soFar, plugin) => buildStaticConstants(typeInformation, soFar, plugin),
        [],
        plugins,
      );

      const globalVariables = List.foldl<
        ObjCGenerationPlugIn<T>,
        ObjC.GlobalVariable[]
      >(
        (soFar, plugin) => buildglobalVariables(typeInformation, soFar, plugin),
        [],
        plugins,
      );

      const macros = List.foldl<ObjCGenerationPlugIn<T>, ObjC.Macro[]>(
        (soFar, plugin) => buildMacros(typeInformation, soFar, plugin),
        [],
        plugins,
      );

      const protocols = List.foldl<ObjCGenerationPlugIn<T>, ObjC.Protocol[]>(
        (soFar, plugin) => buildProtocols(typeInformation, soFar, plugin),
        [],
        plugins,
      );

      const structs = List.foldl<ObjCGenerationPlugIn<T>, Code.Struct[]>(
        (soFar, plugin) => buildStructs(typeInformation, soFar, plugin),
        [],
        plugins,
      );

      const finalFunctions = classes.length > 0 ? [] : functions;

      if (file != null) {
        var newFile = file;
        newFile.imports = file.imports.concat(
          List.foldl<ObjCGenerationPlugIn<T>, ObjC.Import[]>(
            (soFar, plugin) => buildImports(typeInformation, soFar, plugin),
            [],
            plugins,
          ),
        );
        newFile.enumerations = file.enumerations.concat(enumerations);
        newFile.forwardDeclarations =
          file.forwardDeclarations.concat(forwardDeclarations);
        newFile.blockTypes = file.blockTypes.concat(blockTypes);
        newFile.diagnosticIgnores = file.diagnosticIgnores.concat(
          List.toArray(diagnosticIgnores),
        );
        newFile.staticConstants = file.staticConstants.concat(staticConstants);
        newFile.globalVariables = file.globalVariables.concat(globalVariables);
        newFile.functions = file.functions.concat(finalFunctions);
        newFile.macros = file.macros.concat(macros);
        newFile.classes = file.classes.concat(classes);
        newFile.structs = file.structs.concat(structs);
        newFile.cppClasses = file.cppClasses;
        return newFile;
      } else {
        return {
          name: typeName,
          type: fileType,
          imports: imports,
          comments: commentListWithPathToValueFile(
            pathToValueFile,
            List.foldl<ObjCGenerationPlugIn<T>, ObjC.Comment[]>(
              (soFar, plugin) => buildComments(typeInformation, soFar, plugin),
              [],
              plugins,
            ),
          ),
          enumerations: enumerations,
          forwardDeclarations: forwardDeclarations,
          blockTypes: blockTypes,
          diagnosticIgnores: List.toArray(diagnosticIgnores),
          staticConstants: staticConstants,
          globalVariables: globalVariables,
          functions: finalFunctions,
          nullability: classes.length > 0 ? undefined : nullability,
          macros: macros,
          protocols: protocols,
          classes: classes,
          structs: structs,
          cppClasses: [],
          namespaces: [],
        };
      }
    }, Either.and(fileType, baseClass));
  };
}