static string GetType(PyClass pyClass, string methodName, string rustType, string sphinxType) { // The type in the docs (sphinx type) is more accurate than the type in the source code // since `u32` is used in the source code if it's an enum value. if (sphinxType != string.Empty) { var sphinxTypes = ParseUtils.SplitSphinxTypes(sphinxType).ToList(); var convertedTypes = new List <string>(); foreach (var stype in sphinxTypes) { if (!ParseUtils.TryConvertSphinxTypeToTypeName(stype, out var typeName)) { typeName = stype; } convertedTypes.Add(typeName); } int index = convertedTypes.Count == 1 ? -1 : convertedTypes.IndexOf("None"); if (index >= 0) { convertedTypes.RemoveAt(index); } string typeStr; if (convertedTypes.Count > 1) { typeStr = "Union[" + string.Join(", ", convertedTypes.ToArray()) + "]"; } else { typeStr = convertedTypes[0]; } if (index >= 0) { return("Optional[" + typeStr + "]"); } return(typeStr); } if (ParseUtils.TryRemovePrefixSuffix(rustType, "PyResult<", ">", out var extractedType)) { rustType = extractedType; } switch (rustType) { case "i8" or "i16" or "i32" or "i64" or "isize" or "u8" or "u16" or "u32" or "u64" or "usize": return("int"); case "bool": return("bool"); case "&str" or "String": return("str"); case "PyRef<Self>" or "PyRefMut<Self>" or "Self": return(pyClass.Name); case "&PyAny": return("Any"); default: if (ParseUtils.TryRemovePrefixSuffix(rustType, "IterNextOutput<", ", ()>", out extractedType)) { return(extractedType); } break; } throw new InvalidOperationException($"Method {pyClass.Name}.{methodName}(): Couldn't convert Rust/sphinx type to Python type: Rust=`{rustType}`, sphinx=`{sphinxType}`"); }
// Gets all required enum fields that must be part of the pyi file because they're // default values in some methods. Dictionary <EnumType, HashSet <EnumValue> > GetRequiredEnumFields(List <PyClass> classes) { var reqEnumFields = new Dictionary <EnumType, HashSet <EnumValue> >(); var argToEnumType = new Dictionary <string, EnumType>(StringComparer.Ordinal); foreach (var pyClass in classes) { foreach (var method in pyClass.Methods) { DocComments docComments; if (method.Attributes.Any(AttributeKind.New)) { docComments = pyClass.DocComments; } else { docComments = method.DocComments; } var docs = docComments.Sections.OfType <ArgsDocCommentSection>().FirstOrDefault(); if (docs is null) { continue; } int hasThis = method.Arguments.Count != 0 && method.Arguments[0].IsSelf ? 1 : 0; if (docs.Args.Length != (method.Arguments.Count - hasThis)) { throw new InvalidOperationException(); } argToEnumType.Clear(); for (int i = 0; i < docs.Args.Length; i++) { var docArg = docs.Args[i]; if (docArg.Name != method.Arguments[hasThis + i].Name) { throw new InvalidOperationException(); } if (!ParseUtils.TryConvertSphinxTypeToTypeName(docArg.SphinxType, out var typeName)) { continue; } if (!exportedPythonTypes.TryFindByName(typeName, out var enumType)) { continue; } argToEnumType.Add(docArg.Name, enumType); } var argsAttr = method.Attributes.Attributes.FirstOrDefault(a => a.Kind == AttributeKind.Args); if (argsAttr is null) { continue; } foreach (var(name, value) in ParseUtils.GetArgsNameValues(argsAttr.Text)) { if (!argToEnumType.TryGetValue(name, out var enumType)) { continue; } if (!uint.TryParse(value, out var rawValue)) { throw new InvalidOperationException($"Couldn't parse {value} as an integer"); } var enumValue = enumType.Values.FirstOrDefault(a => a.Value == rawValue); if (enumValue is null) { throw new InvalidOperationException($"Couldn't find an enum value in {enumType.RawName} with a value equal to {value}"); } if (!reqEnumFields.TryGetValue(enumType, out var hash)) { reqEnumFields.Add(enumType, hash = new HashSet <EnumValue>()); } hash.Add(enumValue); } } } return(reqEnumFields); }