extractMethodResponseTypeWithField function

Future<Map<String, dynamic>> extractMethodResponseTypeWithField(
  1. String filePath,
  2. String methodName,
  3. dynamic searchFields, [
  4. bool returnRawType = true,
  5. bool stripFutureType = true,
])

Extracts the type of the 'data' field from a method's response type.

Implementation

Future<Map<String, dynamic>> extractMethodResponseTypeWithField(
  String filePath,
  String methodName,
  dynamic searchFields, [
  bool returnRawType = true,
  bool stripFutureType = true,
]) async {
  searchFields =
      searchFields.replaceAll(RegExp(r'\s+'), "").split(",") as List<String>;
  if (searchFields == null ||
      searchFields is List<String> && searchFields.isEmpty) {
    searchFields = ["data", "body"];
  }
  // }
  // printInfo("search declaration for field '$searchFields'");
  final initialUnit = parseFileByPath(filePath);
  if (initialUnit == null) {
    throw Exception('File not found or failed to parse: $filePath');
  }

  final methodVisitor = _MethodVisitor(methodName);
  initialUnit.accept(methodVisitor);
  final returnType = methodVisitor.returnType;
  if (returnType == null) {
    throw Exception(
      'Method "$methodName" not found or has no return type in $filePath',
    );
  }
  if (returnRawType) {
    // print(" raw return type: $returnType");
    if (stripFutureType) {
      return {"responseDataType": unwrapFuture(returnType.toString())};
    }
    return {"responseDataType": returnType.toString()};
  }

  final innerType = getInnerTypeByPosition(returnType, at: 0);
  if (innerType == "void") {
    return {"responseDataType": innerType};
  }

  final filesToSearch = <String>{filePath};
  // final fileDir = p.dirname(filePath);

  final partOfDirective = initialUnit.directives
      .whereType<PartOfDirective>()
      .firstOrNull;
  if (partOfDirective != null && partOfDirective.libraryName != null) {
    final libraryName = partOfDirective.libraryName!.name;
    final projectRoot = getProjectRootPath(scriptPath: filePath);

    // Search for the library file within the project root.
    final libraryFilePath = findLibraryFile(projectRoot!, libraryName);

    if (libraryFilePath != null) {
      final libraryDir = p.dirname(libraryFilePath);
      filesToSearch.add(libraryFilePath);
      final libraryUnit = parseFileByPath(libraryFilePath);
      if (libraryUnit != null) {
        for (final directive
            in libraryUnit.directives.whereType<PartDirective>()) {
          final partUri = directive.uri.stringValue;
          if (partUri != null) {
            final partPath = p.normalize(p.join(libraryDir, partUri));
            filesToSearch.add(partPath);
          }
        }
      }
    }
  }

  // print({filesToSearch.toList().join("\n")});
  ClassDeclaration? classNode;
  String? classDeclarationPath;
  for (final path in filesToSearch) {
    classNode = findClassInFile(path, innerType);
    if (classNode != null) {
      classDeclarationPath = path;
      printInfo("found by findClassInFile ($classDeclarationPath}");
      break;
    }
  }

  // TODO: bellow maybe never reach
  if (classNode == null) {
    final projectRoot = getProjectRootPath(scriptPath: filePath);
    if (projectRoot != null) {
      final result = findClassInProjectWithFilePath(projectRoot, innerType);
      if (result != null) {
        classNode = result.classNode;
        classDeclarationPath = result.filePath;
        printInfo(
          "found in getProjectRootPath+findClassInProjectWithFilePath ($classDeclarationPath)",
        );
      }
    }
  }
  // END TODO

  bool foundBy_findClassDeclarationWithHttpResponse = false;
  if (classNode == null) {
    try {
      classNode = await findClassDeclarationWithHttpResponse(
        codePath: filePath,
      );
      if (classNode != null) {
        foundBy_findClassDeclarationWithHttpResponse = true;
        // printSuccess("found by findClassDeclarationWithHttpResponse");
      }
    } catch (e) {}
  }
  if (classNode == null) {
    throw Exception(
      'ClassType "$innerType" not found in the library, its parts, its direct imports, or the project.',
    );
  }

  // print(classNode);
  String? responseDataType;
  TypeAnnotation? dataTypeNode;

  for (final field in searchFields) {
    final fieldVisitor = FieldVisitor(fieldName: field);
    classNode.accept(fieldVisitor);
    responseDataType = fieldVisitor.dataType;
    dataTypeNode = fieldVisitor.dataTypeNode;
    if (responseDataType != null && dataTypeNode != null) {
      break;
    }
  }

  // print("$innerType");
  if (responseDataType == null || dataTypeNode == null) {
    throw Exception('Field "$searchFields" not found in class "$innerType"');
  }

  String? importUri;
  if (classDeclarationPath != null) {
    importUri = importClauseToPath(classDeclarationPath);
  }

  // printInfo("↓↓↓ result ↓↓↓");
  return {
    "searchFields": searchFields,
    "hitClass": innerType,
    "hitField": foundBy_findClassDeclarationWithHttpResponse ? "body" : "data",
    "responseDataType": responseDataType,
    "importUri": foundBy_findClassDeclarationWithHttpResponse
        ? "import 'package:http/src/response.dart';"
        : importUri,
    "classDeclarationPath": classDeclarationPath,
  };
}