csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
231 lines
11 KiB
231 lines
11 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
|
|
using Microsoft.CodeAnalysis.CSharp;
|
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
|
|
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
|
|
|
|
|
namespace Tmds.DBus.SourceGenerator
|
|
{
|
|
public partial class DBusSourceGenerator
|
|
{
|
|
private static readonly DBusValue _byteValue = new() { Type = "y" };
|
|
private static readonly DBusValue _boolValue = new() { Type = "b" };
|
|
private static readonly DBusValue _int16Value = new() { Type = "n" };
|
|
private static readonly DBusValue _uInt16Value = new() { Type = "q" };
|
|
private static readonly DBusValue _int32Value = new() { Type = "i" };
|
|
private static readonly DBusValue _uInt32Value = new() { Type = "u" };
|
|
private static readonly DBusValue _int64Value = new() { Type = "x" };
|
|
private static readonly DBusValue _uInt64Value = new() { Type = "t" };
|
|
private static readonly DBusValue _doubleValue = new() { Type = "d" };
|
|
private static readonly DBusValue _stringValue = new() { Type = "s" };
|
|
private static readonly DBusValue _objectPathValue = new() { Type = "o" };
|
|
private static readonly DBusValue _signatureValue = new() { Type = "g" };
|
|
private static readonly DBusValue _variantValue = new() { Type = "v" };
|
|
private static readonly DBusValue _unixFdValue = new() { Type = "h" };
|
|
|
|
private static string Pascalize(string name, bool camel = false)
|
|
{
|
|
bool upperizeNext = !camel;
|
|
StringBuilder sb = new(name.Length);
|
|
foreach (char och in name)
|
|
{
|
|
char ch = och;
|
|
if (ch is '_' or '.')
|
|
{
|
|
upperizeNext = true;
|
|
}
|
|
else
|
|
{
|
|
if (upperizeNext)
|
|
{
|
|
ch = char.ToUpperInvariant(ch);
|
|
upperizeNext = false;
|
|
}
|
|
|
|
sb.Append(ch);
|
|
}
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string Camelize(string name)
|
|
{
|
|
StringBuilder sb = new(Pascalize(name));
|
|
sb[0] = char.ToLowerInvariant(sb[0]);
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string? ParseSignature(IReadOnlyList<DBusValue>? dBusValues)
|
|
{
|
|
if (dBusValues is null || dBusValues.Count == 0)
|
|
return null;
|
|
|
|
StringBuilder sb = new();
|
|
foreach (DBusValue dBusValue in dBusValues.Where(static argument => argument.Type is not null))
|
|
sb.Append(dBusValue.Type);
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static TypeSyntax? ParseReturnType(IReadOnlyList<DBusValue>? dBusValues, AccessMode accessMode) => dBusValues?.Count switch
|
|
{
|
|
0 or null => null,
|
|
1 => GetDotnetType(dBusValues[0], accessMode),
|
|
_ => TupleType()
|
|
.AddElements(
|
|
dBusValues.Select((dBusValue, i) => TupleElement(GetDotnetType(dBusValue, accessMode))
|
|
.WithIdentifier(
|
|
Identifier(dBusValue.Name is not null ? SanitizeIdentifier(Pascalize(dBusValue.Name)) : $"Item{i + 1}")))
|
|
.ToArray())
|
|
};
|
|
|
|
private static TypeSyntax ParseTaskReturnType(IReadOnlyList<DBusValue>? dBusValues, AccessMode accessMode) => dBusValues?.Count switch
|
|
{
|
|
0 or null => IdentifierName("Task"),
|
|
_ => GenericName("Task")
|
|
.AddTypeArgumentListArguments(
|
|
ParseReturnType(dBusValues, accessMode)!)
|
|
};
|
|
|
|
private static TypeSyntax ParseValueTaskReturnType(IReadOnlyList<DBusValue>? dBusValues, AccessMode accessMode) => dBusValues?.Count switch
|
|
{
|
|
0 or null => IdentifierName("ValueTask"),
|
|
_ => GenericName("ValueTask")
|
|
.AddTypeArgumentListArguments(
|
|
ParseReturnType(dBusValues, accessMode)!)
|
|
};
|
|
|
|
private static TypeSyntax ParseTaskCompletionSourceType(IReadOnlyList<DBusValue>? dBusValues, AccessMode accessMode) => dBusValues?.Count switch
|
|
{
|
|
0 or null => GenericName("TaskCompletionSource")
|
|
.AddTypeArgumentListArguments(
|
|
PredefinedType(
|
|
Token(SyntaxKind.BoolKeyword))),
|
|
_ => GenericName("TaskCompletionSource")
|
|
.AddTypeArgumentListArguments(
|
|
ParseReturnType(dBusValues, accessMode)!)
|
|
};
|
|
|
|
private static ParameterListSyntax ParseParameterList(IEnumerable<DBusValue> inArgs, AccessMode accessMode) => ParameterList(
|
|
SeparatedList(
|
|
inArgs.Select((x, i) =>
|
|
Parameter(Identifier(x.Name is not null ? SanitizeIdentifier(Camelize(x.Name)) : $"arg{i}")).WithType(GetDotnetType(x, accessMode)))));
|
|
|
|
private static string SanitizeSignature(string signature) =>
|
|
signature.Replace('{', 'e')
|
|
.Replace("}", null)
|
|
.Replace('(', 'r')
|
|
.Replace(')', 'z');
|
|
|
|
private static string SanitizeIdentifier(string identifier)
|
|
{
|
|
bool isAnyKeyword = SyntaxFacts.GetKeywordKind(identifier) != SyntaxKind.None || SyntaxFacts.GetContextualKeywordKind(identifier) != SyntaxKind.None;
|
|
return isAnyKeyword ? $"@{identifier}" : identifier;
|
|
}
|
|
|
|
internal static (DBusValue DBusValue, DBusValue[] InnerDBusTypes, DBusType DBusType) ParseDBusValue(string signature) =>
|
|
SignatureReader.Transform<(DBusValue, DBusValue[], DBusType)>(Encoding.ASCII.GetBytes(signature), MapDBusToDotNet);
|
|
|
|
private static (DBusValue, DBusValue[], DBusType) MapDBusToDotNet(DBusType dBusType, (DBusValue, DBusValue[], DBusType)[] inner)
|
|
{
|
|
DBusValue[] innerDBusTypes = inner.Select(static x => x.Item1).ToArray();
|
|
return dBusType switch
|
|
{
|
|
DBusType.Byte => (_byteValue, innerDBusTypes, dBusType),
|
|
DBusType.Bool => (_boolValue, innerDBusTypes, dBusType),
|
|
DBusType.Int16 => (_int16Value, innerDBusTypes, dBusType),
|
|
DBusType.UInt16 => (_uInt16Value, innerDBusTypes, dBusType),
|
|
DBusType.Int32 => (_int32Value, innerDBusTypes, dBusType),
|
|
DBusType.UInt32 => (_uInt32Value, innerDBusTypes, dBusType),
|
|
DBusType.Int64 => (_int64Value, innerDBusTypes, dBusType),
|
|
DBusType.UInt64 => (_uInt64Value, innerDBusTypes, dBusType),
|
|
DBusType.Double => (_doubleValue, innerDBusTypes, dBusType),
|
|
DBusType.String => (_stringValue, innerDBusTypes, dBusType),
|
|
DBusType.ObjectPath => (_objectPathValue, innerDBusTypes, dBusType),
|
|
DBusType.Signature => (_signatureValue, innerDBusTypes, dBusType),
|
|
DBusType.Variant => (_variantValue, innerDBusTypes, dBusType),
|
|
DBusType.UnixFd => (_unixFdValue, innerDBusTypes, dBusType),
|
|
DBusType.Array => (new DBusValue { Type = $"a{ParseSignature(innerDBusTypes)}"}, innerDBusTypes, dBusType),
|
|
DBusType.DictEntry => (new DBusValue { Type = $"a{{{ParseSignature(innerDBusTypes)}}}"}, innerDBusTypes, dBusType),
|
|
DBusType.Struct => (new DBusValue { Type = $"({ParseSignature(innerDBusTypes)})"}, innerDBusTypes, dBusType),
|
|
_ => throw new ArgumentOutOfRangeException(nameof(dBusType), dBusType, $"Cannot parse DBusType with value {dBusType}")
|
|
};
|
|
}
|
|
|
|
private static TypeSyntax GetDotnetType(DBusValue dBusValue, AccessMode accessMode, bool nullable = false)
|
|
{
|
|
switch (dBusValue.DBusType)
|
|
{
|
|
case DBusType.Byte:
|
|
return PredefinedType(Token(SyntaxKind.ByteKeyword));
|
|
case DBusType.Bool:
|
|
return PredefinedType(Token(SyntaxKind.BoolKeyword));
|
|
case DBusType.Int16:
|
|
return PredefinedType(Token(SyntaxKind.ShortKeyword));
|
|
case DBusType.UInt16:
|
|
return PredefinedType(Token(SyntaxKind.UShortKeyword));
|
|
case DBusType.Int32:
|
|
return PredefinedType(Token(SyntaxKind.IntKeyword));
|
|
case DBusType.UInt32:
|
|
return PredefinedType(Token(SyntaxKind.UIntKeyword));
|
|
case DBusType.Int64:
|
|
return PredefinedType(Token(SyntaxKind.LongKeyword));
|
|
case DBusType.UInt64:
|
|
return PredefinedType(Token(SyntaxKind.ULongKeyword));
|
|
case DBusType.Double:
|
|
return PredefinedType(Token(SyntaxKind.DoubleKeyword));
|
|
case DBusType.String:
|
|
TypeSyntax str = PredefinedType(Token(SyntaxKind.StringKeyword));
|
|
if (nullable)
|
|
str = NullableType(str);
|
|
return str;
|
|
case DBusType.ObjectPath:
|
|
return IdentifierName("ObjectPath");
|
|
case DBusType.Signature:
|
|
return IdentifierName("Signature");
|
|
case DBusType.Variant when accessMode == AccessMode.Read:
|
|
return IdentifierName("VariantValue");
|
|
case DBusType.Variant when accessMode == AccessMode.Write:
|
|
return IdentifierName("Variant");
|
|
case DBusType.UnixFd:
|
|
return IdentifierName("SafeFileHandle");
|
|
case DBusType.Array:
|
|
TypeSyntax arr = ArrayType(
|
|
GetDotnetType(dBusValue.InnerDBusTypes![0], accessMode, nullable))
|
|
.AddRankSpecifiers(ArrayRankSpecifier()
|
|
.AddSizes(OmittedArraySizeExpression()));
|
|
if (nullable)
|
|
arr = NullableType(arr);
|
|
return arr;
|
|
case DBusType.DictEntry:
|
|
TypeSyntax dict = GenericName("Dictionary")
|
|
.AddTypeArgumentListArguments(
|
|
GetDotnetType(dBusValue.InnerDBusTypes![0], accessMode),
|
|
GetDotnetType(dBusValue.InnerDBusTypes![1], accessMode, nullable));
|
|
if (nullable)
|
|
dict = NullableType(dict);
|
|
return dict;
|
|
case DBusType.Struct when dBusValue.InnerDBusTypes!.Length == 1:
|
|
return GenericName("ValueTuple")
|
|
.AddTypeArgumentListArguments(
|
|
GetDotnetType(dBusValue.InnerDBusTypes![0], accessMode, nullable));
|
|
case DBusType.Struct:
|
|
return TupleType()
|
|
.AddElements(
|
|
dBusValue.InnerDBusTypes!.Select(
|
|
dbusType => TupleElement(
|
|
GetDotnetType(dbusType, accessMode, nullable)))
|
|
.ToArray());
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(dBusValue.DBusType), dBusValue.DBusType,
|
|
$"Cannot parse DBusType with value {dBusValue.DBusType}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|