public RustInterfaceMethodInfo(RustCodeGen gen, Marshaller.ComMethodInfo cmi) { RustCodeGen = gen; ComMethodInfo = cmi; ParameterInfos = cmi.ParameterInfos.Select((pi) => new RustParameterInfo(gen, pi)).ToArray(); string name = cmi.MethodInfo.Name; if (name.StartsWith("get_") || name.StartsWith("set_")) { NativeName = name.Substring(0, 4) + SnakeCaseConverter.Join(DotNetPascalCaseConverter.Split(name.Substring(4))).ToLowerInvariant(); } else { NativeName = SnakeCaseConverter.Join(DotNetPascalCaseConverter.Split(name)).ToLowerInvariant(); } // Retrieve the documentation entry if (gen.options.RustdocEntrySource != null) { if (name.StartsWith("get_") || name.StartsWith("set_")) { var prop = cmi.MethodInfo.DeclaringType.GetProperty(name.Substring(4)); if (prop != null) { RustdocEntry = gen.options.RustdocEntrySource.GetEntryForProperty(prop, name.StartsWith("set_")); } } else { RustdocEntry = gen.options.RustdocEntrySource.GetEntryForMethod(cmi.MethodInfo); } } if (cmi.ReturnsHresult) { NativeReturnTypeName = $"{gen.options.NgscomCratePath}::HResult"; } else { NativeReturnTypeName = RustCodeGen.TranslateNativeType(cmi.NativeReturnType, false, false); } }
void GenerateInterface(Type type) { var info = new Marshaller.InterfaceInfo(type); var typeInfo = type.GetTypeInfo(); var name = type.Name; if (!name.StartsWith("I")) { throw new NotSupportedException($"The name of interface {type.FullName} doesn't start with the character 'I'."); } var nameDecomposed = DotNetPascalCaseConverter.Split(name.Substring(1)); nameDecomposed[0] = "I" + nameDecomposed[0]; var upperSnakeCaseName = SnakeCaseConverter.Join(nameDecomposed).ToUpperInvariant(); var lowerSnakeCaseName = SnakeCaseConverter.Join(nameDecomposed).ToLowerInvariant(); var iidIdt = "IID_" + upperSnakeCaseName; var iid = info.ComGuid.ToByteArray(); stringBuilder.AppendLine($"com_iid!({iidIdt} = [{ByteArrayToIntegerConstant(iid, 0, 4, false)}, " + $"{ByteArrayToIntegerConstant(iid, 4, 2, false)}, {ByteArrayToIntegerConstant(iid, 6, 2, false)}, " + $"[{ByteArrayToIntegerConstant(iid, 8, 8, true)}]]);"); var baseTypes = new List <Type>(); var indirectlyBaseTypes = new HashSet <Type>(); Action <Type> scanIndirectBase = null; scanIndirectBase = (theType) => { indirectlyBaseTypes.Add(theType); foreach (var bt in theType.GetTypeInfo().ImplementedInterfaces) { scanIndirectBase(bt); } }; foreach (var bt in typeInfo.ImplementedInterfaces) { baseTypes.Add(bt); var ti2 = bt.GetTypeInfo(); if (!ti2.IsInterface) { throw new InvalidOperationException(); } foreach (var bt2 in ti2.ImplementedInterfaces) { scanIndirectBase(bt2); } } var realBaseTypes = baseTypes.Where((t) => !indirectlyBaseTypes.Contains(t)).ToArray(); if (realBaseTypes.Length > 1) { throw new NotSupportedException($"Cannot emit interop code for the interface {type.FullName} because " + "Interfaces with multiple base interface are not supported by NgsCOM."); } foreach (var t in realBaseTypes) { EnqueueTypeGeneration(t); } var baseDeclarations = new List <string>(); foreach (var t in realBaseTypes) { var nativeName = GetOutputIdentifier(t); baseDeclarations.Add($"({nativeName}, {nativeName}Trait)"); } foreach (var t in indirectlyBaseTypes) { var nativeName = GetOutputIdentifier(t); baseDeclarations.Add(nativeName); } stringBuilder.AppendLine("com_interface! {"); var methods = info.ComMethodInfos .Where((cmi) => cmi.MethodInfo.DeclaringType == info.Type) .Select((cmi) => new RustInterfaceMethodInfo(this, cmi)).ToArray(); // Emit the documentation comment { var doc = options.RustdocEntrySource?.GetEntryForType(type); if (doc.HasValue) { GenerateDocComment(doc, "\t"); } else { stringBuilder.AppendLine($"\t/// `{type.FullName}`"); } stringBuilder.AppendLine("\t///"); stringBuilder.AppendLine("\t/// # COM interop"); stringBuilder.AppendLine($"\t/// This type was generated automatically from `{type.FullName}`."); // Emit the template code here stringBuilder.AppendLine("\t///"); stringBuilder.AppendLine($"\t/// Use the following template code to create a COM class"); stringBuilder.AppendLine($"\t/// that implements `{name}`:"); stringBuilder.AppendLine("\t///"); stringBuilder.AppendLine("\t/// ```ignore"); stringBuilder.AppendLine("\t/// com_impl! {"); stringBuilder.AppendLine("\t/// class MyClassName {"); stringBuilder.AppendLine($"\t/// {lowerSnakeCaseName}: {name};"); stringBuilder.AppendLine("\t/// @data: MyClassNameData;"); stringBuilder.AppendLine("\t/// }"); stringBuilder.AppendLine("\t/// }"); stringBuilder.AppendLine("\t///"); stringBuilder.AppendLine($"\t/// impl {name}Trait for MyClassName {{"); stringBuilder.AppendLine("\t///"); foreach (var rimi in methods) { stringBuilder.AppendLine($"\t/// {rimi.GetSignature(false)} {{"); if (rimi.ComMethodInfo.ReturnsHresult) { stringBuilder.AppendLine("\t/// \thresults::E_NOTIMPL"); } else { stringBuilder.AppendLine("\t/// \tunimplemented!()"); } stringBuilder.AppendLine("\t/// }"); stringBuilder.AppendLine("\t///"); } stringBuilder.AppendLine("\t/// }"); stringBuilder.AppendLine("\t/// ```"); stringBuilder.AppendLine("\t///"); } stringBuilder.AppendLine($"\tinterface ({name}, {name}Trait): {string.Join(", ", baseDeclarations)} {{"); stringBuilder.AppendLine($"\t\tiid: {iidIdt},"); stringBuilder.AppendLine($"\t\tvtable: {name}Vtbl,"); stringBuilder.AppendLine(); foreach (var rimi in methods) { GenerateDocComment(rimi.RustdocEntry, "\t\t"); stringBuilder.AppendLine($"\t\t{rimi.GetSignature(true)};"); } stringBuilder.AppendLine("\t}"); stringBuilder.AppendLine("}"); stringBuilder.AppendLine(); }