-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from RubenPX/Tauri-Plugins-C#
Plugin Route Funcionality (Structuring and serializing comunication)
- Loading branch information
Showing
15 changed files
with
390 additions
and
70 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace TauriComunication { | ||
public class AssemblyDependency { | ||
|
||
public static Assembly? AssemblyResolve(object sender, ResolveEventArgs args, string pluginName) { | ||
if (args.Name.StartsWith("System")) return null; | ||
|
||
String DLLName = new AssemblyName(args.Name).Name + ".dll"; | ||
|
||
try { | ||
var compatible = AppDomain.CurrentDomain.GetAssemblies().Where(asm => asm.FullName == args.Name).First(); | ||
Console.WriteLine($"Requested loaded ASM, returning {compatible.GetName()}"); | ||
return compatible; | ||
} catch (Exception) { | ||
} | ||
|
||
string currentDirectory = Directory.GetCurrentDirectory(); | ||
string dependencyPath = Path.Combine(currentDirectory, "plugins", pluginName, "Dependencies", DLLName); | ||
|
||
return Assembly.LoadFile(dependencyPath); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using TauriComunication.Route; | ||
|
||
namespace TauriComunication { | ||
public class PluginInfo { | ||
public string dllPath { get; private set; } | ||
public Assembly assembly { get; private set; } | ||
public Type[] types { get; private set; } | ||
public List<MethodInfo> methods { get; private set; } = new List<MethodInfo>(); | ||
|
||
public string PluginName { get; private set; } | ||
|
||
static byte[] loadFile(string filename) { | ||
FileStream fs = new FileStream(filename, FileMode.Open); | ||
byte[] buffer = new byte[(int)fs.Length]; | ||
fs.Read(buffer, 0, buffer.Length); | ||
fs.Close(); | ||
|
||
return buffer; | ||
} | ||
|
||
public PluginInfo(string dllPath) { | ||
this.assembly = AppDomain.CurrentDomain.Load(loadFile(dllPath)); | ||
this.PluginName = assembly.GetName().Name; | ||
|
||
AppDomain.CurrentDomain.AssemblyResolve += (object? sender, ResolveEventArgs args) => AssemblyDependency.AssemblyResolve(sender, args, this.PluginName); | ||
Debugger.Launch(); | ||
|
||
Console.WriteLine($"Loaded dll {Path.GetFileNameWithoutExtension(dllPath)}. Name: {PluginName}"); | ||
|
||
this.types = this.assembly.GetTypes(); | ||
|
||
foreach (var type in this.types) { | ||
|
||
//Console.WriteLine($"Class: {type.Name}"); | ||
//foreach (var item in type.GetMethods(BindingFlags.Static | BindingFlags.Public)) { | ||
// string methds = string.Join(',', item.GetParameters().Select(t => $"{t.Name}, {t.ParameterType.FullName}")); | ||
// Console.WriteLine($"Method: {item.Name}, {item.Attributes}, {methds}"); | ||
//} | ||
|
||
var compatibleMethods = type.GetMethods(BindingFlags.Static | BindingFlags.Public).Where(m => { | ||
// Verify custom RouteMethodAttribute atribute | ||
if (m.GetCustomAttribute<RouteMethodAttribute>() == null) return false; | ||
|
||
// Verify parameters | ||
ParameterInfo[] ps = m.GetParameters(); | ||
if (ps.Length != 1 && ps.Length != 2) return false; | ||
if (ps[0].ParameterType.FullName != typeof(RouteRequest).FullName) return false; | ||
if (ps[1] != null && ps[1].ParameterType.FullName != typeof(RouteResponse).FullName) return false; | ||
|
||
// verify return | ||
if (m.ReturnType.FullName != typeof(RouteResponse).FullName) return false; | ||
|
||
return true; | ||
}).ToArray(); | ||
|
||
foreach (var mthd in compatibleMethods) { | ||
Console.WriteLine($"RouteMethod found: {mthd.GetType().Name}: {mthd.Name}"); | ||
} | ||
|
||
this.methods.AddRange(compatibleMethods); | ||
} | ||
} | ||
|
||
public MethodInfo? GetMethodName(string name) => methods.FirstOrDefault(m => m.Name == name, null); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
using Newtonsoft.Json; | ||
using Newtonsoft.Json.Linq; | ||
using System.Diagnostics; | ||
using System.Reflection; | ||
using System.Text.Json.Serialization; | ||
using TauriComunication.Route; | ||
|
||
namespace TauriComunication { | ||
public class PluginManager { | ||
private static PluginManager instance; | ||
|
||
private readonly PluginInfo[] plugins; | ||
|
||
private PluginManager() { | ||
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => AssemblyDependency.AssemblyResolve(sender, args, Directory.GetCurrentDirectory()); | ||
this.plugins = this.loadPluginsRoutes().ToArray(); | ||
} | ||
|
||
private List<PluginInfo> loadPluginsRoutes() { | ||
var pluginsDirectory = Path.Combine(Directory.GetCurrentDirectory(), "plugins"); | ||
|
||
if (!Directory.Exists(pluginsDirectory)) { | ||
Console.WriteLine("Plugins directory does not exist"); | ||
return []; | ||
} | ||
|
||
string[] dllList = Directory.GetFiles(pluginsDirectory, "*.plugin.dll"); | ||
|
||
// Instance with list of nulls | ||
List<PluginInfo> plugins = new List<PluginInfo>(); | ||
|
||
foreach (var dllPath in dllList) { | ||
try { | ||
plugins.Add(new PluginInfo(dllPath)); | ||
} catch (Exception ex) { | ||
Console.WriteLine($"Failed to load {Path.GetFileName(dllPath)}: {ex.Message}"); | ||
} | ||
} | ||
|
||
return plugins; | ||
} | ||
|
||
internal RouteResponse handleRoute(RouteRequest routeRequest) { | ||
RouteResponse preResponse = new RouteResponse() { id = routeRequest.id }; | ||
|
||
if (routeRequest == null) return preResponse.Error("Object RouteRequest is required"); | ||
if (routeRequest.id == null) return preResponse.Error("String parameter id is required"); | ||
if (routeRequest.plugin == null) return preResponse.Error("string parameter plugin is required"); | ||
if (routeRequest.method == null) return preResponse.Error("string parameter method is required"); | ||
|
||
// Convert to object | ||
if (routeRequest.data.GetType().FullName == typeof(JObject).FullName) routeRequest.data = ((JObject)routeRequest.data).ToObject(typeof(object)); | ||
|
||
PluginInfo? foundPlugin = null; | ||
try { | ||
foundPlugin = this.plugins.Where(x => x.PluginName == routeRequest.plugin || x.PluginName == $"{routeRequest.plugin}.plugin").First(); | ||
} catch (InvalidOperationException) { | ||
Console.WriteLine($"[PluginManager] Plugin {routeRequest.plugin} not found..."); | ||
return preResponse.Error($"Plugin {routeRequest.plugin} not found..."); | ||
} | ||
MethodInfo? foundMethod; | ||
try { | ||
foundMethod = foundPlugin.methods.Where(m => m.Name == routeRequest.method).First(); | ||
} catch (InvalidOperationException) { | ||
Console.WriteLine($"[{routeRequest.plugin}] Method {routeRequest.method} not found..."); | ||
return preResponse.Error($"Method {routeRequest.method} not found..."); | ||
} | ||
|
||
try { | ||
return (RouteResponse?)foundMethod.Invoke(null, [routeRequest, preResponse]); ; | ||
} catch (Exception ex) { | ||
Console.WriteLine($"[{routeRequest.plugin}][{routeRequest.method}] error: {ex.ToString()}"); | ||
return preResponse.Error($"{ex.Message}"); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// This method handle all requests and redirect to any RouteHandler | ||
/// </summary> | ||
/// <param name="inputData"></param> | ||
/// <returns></returns> | ||
public static string processRequest(string? inputData) { | ||
if (inputData is null or "") return JsonConvert.SerializeObject(new RouteResponse() { error = "Input is empty..." }); | ||
if (instance == null) instance = new PluginManager(); | ||
|
||
// If is init, avoid send process | ||
if (inputData is "INIT") return JsonConvert.SerializeObject(new RouteResponse() { data = "OK!" }); | ||
|
||
RouteRequest request; | ||
|
||
try { | ||
request = JsonConvert.DeserializeObject<RouteRequest>(inputData); | ||
} catch (Exception) { | ||
return JsonConvert.SerializeObject(new RouteResponse() { error = "Failed to parse request JSON" }); | ||
} | ||
|
||
try { | ||
RouteResponse response = instance.handleRoute(request); | ||
return JsonConvert.SerializeObject(response); | ||
} catch (Exception ex) { | ||
Console.WriteLine($"[PluginManager] Failed to process request. {ex.Message}"); | ||
return JsonConvert.SerializeObject(new RouteResponse() { error = $"Failed to process request. {ex.Message}" }); | ||
} | ||
} | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
src-netcore/TauriComunication/Route/RouteMethodAttribute.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
namespace TauriComunication.Route; | ||
|
||
/// <summary> | ||
/// This atribute specifies entrypoint of any Route handler. This is searched by reflexion | ||
/// <br /><br /> | ||
/// <code> | ||
/// [<see cref="RouteHandler"/>]<br /> | ||
/// public static <see cref="RouteResponse"/> methodName(<see cref="RouteRequest"/> route) | ||
/// </code> | ||
/// </summary> | ||
public class RouteMethodAttribute : Attribute { | ||
public string? methodName { get; } | ||
|
||
public RouteMethodAttribute(string methodName) { | ||
this.methodName = methodName; | ||
} | ||
|
||
public RouteMethodAttribute() {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
using System.Text.Json; | ||
|
||
namespace TauriComunication.Route; | ||
|
||
public class RouteRequest { | ||
public string id; | ||
public string plugin; | ||
public string method; | ||
public object? data; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
namespace TauriComunication.Route; | ||
|
||
public class RouteResponse { | ||
public string id; | ||
public string? error; | ||
public object? data; | ||
|
||
internal RouteResponse() { } | ||
|
||
public RouteResponse Ok(object? data) { | ||
this.data = data; | ||
return this; | ||
} | ||
|
||
public RouteResponse Error(string error) { | ||
this.data = error; | ||
return this; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
using Newtonsoft.Json.Linq; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace TauriComunication { | ||
public class Utils { | ||
public static T ParseObject<T>(object data) { | ||
if (data == null) throw new ArgumentNullException("data"); | ||
return ((JObject)data).ToObject<T>()!; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,23 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Runtime.InteropServices; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using System.Xml.Linq; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace TauriIPC; | ||
namespace TauriIPC; | ||
|
||
public static class CString { | ||
private static unsafe delegate*<char*, int, byte*> CopyToCString; | ||
private static unsafe delegate*<char*, int, byte*> CopyToCString; | ||
|
||
[UnmanagedCallersOnly] | ||
public static unsafe void SetCopyToCStringFunctionPtr(delegate*<char*, int, byte*> copyToCString) => CopyToCString = copyToCString; | ||
[UnmanagedCallersOnly] | ||
public static unsafe void SetCopyToCStringFunctionPtr(delegate*<char*, int, byte*> copyToCString) => CopyToCString = copyToCString; | ||
|
||
/* This function parse string from a region from memory an return a string region from a pointer */ | ||
[UnmanagedCallersOnly] | ||
public unsafe static byte* runUTF8(/* byte* */IntPtr textPtr, int textLength) { | ||
var text = Marshal.PtrToStringUTF8(textPtr, textLength); | ||
if (text == null || text.Length == 0) text = null; | ||
/* This function parse string from a region from memory an return a string region from a pointer */ | ||
[UnmanagedCallersOnly] | ||
public static unsafe byte* runUTF8(/* byte* */IntPtr textPtr, int textLength) { | ||
var text = Marshal.PtrToStringUTF8(textPtr, textLength); | ||
if (text == null || text.Length == 0) text = null; | ||
|
||
Console.WriteLine(text); | ||
var loginText = TestApp.Main.login(text); | ||
var loginText = TauriComunication.PluginManager.processRequest(text); | ||
|
||
fixed (char* ptr = loginText) { | ||
return CopyToCString(ptr, loginText.Length); | ||
} | ||
} | ||
fixed (char* ptr = loginText) { | ||
return CopyToCString(ptr, loginText.Length); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.