mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2025-05-03 09:39:47 +00:00
Implemented the plugin base system
This commit is contained in:
parent
f3c9ff52bf
commit
28009e185a
20
app/MindWork AI Studio/Tools/PluginSystem/NoModuleLoader.cs
Normal file
20
app/MindWork AI Studio/Tools/PluginSystem/NoModuleLoader.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using Lua;
|
||||
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
/// <summary>
|
||||
/// This Lua module loader does not load any modules.
|
||||
/// </summary>
|
||||
public sealed class NoModuleLoader : ILuaModuleLoader
|
||||
{
|
||||
#region Implementation of ILuaModuleLoader
|
||||
|
||||
public bool Exists(string moduleName) => false;
|
||||
|
||||
public ValueTask<LuaModule> LoadAsync(string moduleName, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return ValueTask.FromResult(new LuaModule());
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
10
app/MindWork AI Studio/Tools/PluginSystem/NoPlugin.cs
Normal file
10
app/MindWork AI Studio/Tools/PluginSystem/NoPlugin.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using Lua;
|
||||
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a plugin that could not be loaded.
|
||||
/// </summary>
|
||||
/// <param name="state">The Lua state that the plugin was loaded into.</param>
|
||||
/// <param name="parsingError">The error message that occurred while parsing the plugin.</param>
|
||||
public sealed class NoPlugin(LuaState state, string parsingError) : PluginBase(state, PluginType.NONE, parsingError);
|
327
app/MindWork AI Studio/Tools/PluginSystem/PluginBase.cs
Normal file
327
app/MindWork AI Studio/Tools/PluginSystem/PluginBase.cs
Normal file
@ -0,0 +1,327 @@
|
||||
using Lua;
|
||||
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the base of any AI Studio plugin.
|
||||
/// </summary>
|
||||
public abstract class PluginBase
|
||||
{
|
||||
private readonly string parseError;
|
||||
|
||||
protected readonly LuaState state;
|
||||
protected readonly Guid pluginId;
|
||||
protected readonly string pluginName;
|
||||
protected readonly PluginType pluginType;
|
||||
protected readonly string pluginDescription;
|
||||
protected readonly PluginVersion pluginVersion;
|
||||
protected readonly string[] pluginAuthors;
|
||||
protected readonly string supportContact;
|
||||
protected readonly string sourceURL;
|
||||
protected readonly PluginCategory[] pluginCategories;
|
||||
protected readonly PluginTargetGroup[] pluginTargetGroups;
|
||||
|
||||
private readonly bool isInitialized;
|
||||
private bool isValid;
|
||||
|
||||
protected PluginBase(LuaState state, PluginType type, string parseError = "")
|
||||
{
|
||||
this.state = state;
|
||||
this.pluginType = type;
|
||||
this.pluginId = this.Id();
|
||||
this.pluginName = this.Name();
|
||||
this.pluginDescription = this.Description();
|
||||
this.pluginVersion = this.Version();
|
||||
this.pluginAuthors = this.Authors();
|
||||
this.supportContact = this.SupportContact();
|
||||
this.sourceURL = this.SourceURL();
|
||||
this.pluginCategories = this.Categories();
|
||||
this.pluginTargetGroups = this.TargetGroups();
|
||||
this.parseError = parseError;
|
||||
|
||||
// For security reasons, we don't want to allow the plugin to load modules:
|
||||
this.state.ModuleLoader = new NoModuleLoader();
|
||||
|
||||
//
|
||||
// Check if the plugin is valid:
|
||||
//
|
||||
if(!string.IsNullOrWhiteSpace(this.parseError))
|
||||
this.isValid = false;
|
||||
|
||||
if(this is NoPlugin)
|
||||
this.isValid = false;
|
||||
|
||||
this.isInitialized = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the plugin is valid.
|
||||
/// </summary>
|
||||
/// <returns>The state of the plugin, which may contain an error message.</returns>
|
||||
public PluginState IsValid()
|
||||
{
|
||||
if(!string.IsNullOrWhiteSpace(this.parseError))
|
||||
{
|
||||
this.isValid = false;
|
||||
return new(false, this.parseError);
|
||||
}
|
||||
|
||||
if(this is NoPlugin)
|
||||
{
|
||||
this.isValid = false;
|
||||
return new(false, "Plugin is not valid.");
|
||||
}
|
||||
|
||||
if(this.Id() == Guid.Empty)
|
||||
{
|
||||
this.isValid = false;
|
||||
return new(false, "The field ID does not exist, is empty, or is not a valid GUID / UUID. The ID must be formatted in the 8-4-4-4-12 format (XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX).");
|
||||
}
|
||||
|
||||
if(string.IsNullOrWhiteSpace(this.Name()))
|
||||
{
|
||||
this.isValid = false;
|
||||
return new(false, "The field NAME does not exist, is empty, or is not a valid string.");
|
||||
}
|
||||
|
||||
if(string.IsNullOrWhiteSpace(this.Description()))
|
||||
{
|
||||
this.isValid = false;
|
||||
return new(false, "The field DESCRIPTION does not exist, is empty, or is not a valid string.");
|
||||
}
|
||||
|
||||
if(this.Version() == PluginVersion.NONE)
|
||||
{
|
||||
this.isValid = false;
|
||||
return new(false, "The field VERSION does not exist, is empty, or is not a valid version number. The version number must be formatted as string in the major.minor.patch format (X.X.X).");
|
||||
}
|
||||
|
||||
if(this.pluginType == PluginType.NONE)
|
||||
{
|
||||
this.isValid = false;
|
||||
return new(false, $"The field TYPE does not exist, is empty, or is not a valid plugin type. Valid types are: {CommonTools.GetAllEnumValues(PluginType.NONE)}.");
|
||||
}
|
||||
|
||||
if(this.Authors().Length == 0)
|
||||
{
|
||||
this.isValid = false;
|
||||
return new(false, "The table AUTHORS does not exist, is empty, or is not a valid table of strings.");
|
||||
}
|
||||
|
||||
if(string.IsNullOrWhiteSpace(this.SupportContact()))
|
||||
{
|
||||
this.isValid = false;
|
||||
return new(false, "The field SUPPORT_CONTACT does not exist, is empty, or is not a valid string.");
|
||||
}
|
||||
|
||||
if(string.IsNullOrWhiteSpace(this.SourceURL()))
|
||||
{
|
||||
this.isValid = false;
|
||||
return new(false, "The field SOURCE_URL does not exist, is empty, or is not a valid string. Additional, it must start with 'http://' or 'https://'.");
|
||||
}
|
||||
|
||||
if(this.Categories().Length == 0)
|
||||
{
|
||||
this.isValid = false;
|
||||
return new(false, $"The table CATEGORIES does not exist, is empty, or is not a valid table of strings. Valid categories are: {CommonTools.GetAllEnumValues(PluginCategory.NONE)}.");
|
||||
}
|
||||
|
||||
if(this.TargetGroups().Length == 0)
|
||||
{
|
||||
this.isValid = false;
|
||||
return new(false, $"The table TARGET_GROUPS does not exist, is empty, or is not a valid table of strings. Valid target groups are: {CommonTools.GetAllEnumValues(PluginTargetGroup.NONE)}.");
|
||||
}
|
||||
|
||||
this.isValid = true;
|
||||
return new(true, string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the intended target groups for the plugin.
|
||||
/// </summary>
|
||||
/// <returns>The target groups.</returns>
|
||||
public PluginTargetGroup[] TargetGroups()
|
||||
{
|
||||
if(this.isInitialized)
|
||||
return this.pluginTargetGroups;
|
||||
|
||||
if(!this.isValid)
|
||||
return [];
|
||||
|
||||
if (!this.state.Environment["TARGET_GROUPS"].TryRead<LuaTable>(out var targetGroups))
|
||||
return [];
|
||||
|
||||
var targetGroupList = new List<PluginTargetGroup>();
|
||||
foreach(var luaTargetGroup in targetGroups.GetArraySpan())
|
||||
if(luaTargetGroup.TryRead<string>(out var targetGroupName))
|
||||
if(Enum.TryParse<PluginTargetGroup>(targetGroupName, out var targetGroup) && targetGroup != PluginTargetGroup.NONE)
|
||||
targetGroupList.Add(targetGroup);
|
||||
|
||||
return targetGroupList.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the plugin categories.
|
||||
/// </summary>
|
||||
/// <returns>The plugin categories.</returns>
|
||||
public PluginCategory[] Categories()
|
||||
{
|
||||
if(this.isInitialized)
|
||||
return this.pluginCategories;
|
||||
|
||||
if(!this.isValid)
|
||||
return [];
|
||||
|
||||
if (!this.state.Environment["CATEGORIES"].TryRead<LuaTable>(out var categories))
|
||||
return [];
|
||||
|
||||
var categoryList = new List<PluginCategory>();
|
||||
foreach(var luaCategory in categories.GetArraySpan())
|
||||
if(luaCategory.TryRead<string>(out var categoryName))
|
||||
if(Enum.TryParse<PluginCategory>(categoryName, out var category) && category != PluginCategory.NONE)
|
||||
categoryList.Add(category);
|
||||
|
||||
return categoryList.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the source URL of the plugin.
|
||||
/// </summary>
|
||||
/// <returns>The source URL.</returns>
|
||||
public string SourceURL()
|
||||
{
|
||||
if(this.isInitialized)
|
||||
return this.sourceURL;
|
||||
|
||||
if(!this.isValid)
|
||||
return string.Empty;
|
||||
|
||||
if (!this.state.Environment["SOURCE_URL"].TryRead<string>(out var url))
|
||||
return string.Empty;
|
||||
|
||||
if(!url.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase) && !url.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase))
|
||||
return string.Empty;
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the support contact of the plugin.
|
||||
/// </summary>
|
||||
/// <returns>The support contact.</returns>
|
||||
public string SupportContact()
|
||||
{
|
||||
if(this.isInitialized)
|
||||
return this.supportContact;
|
||||
|
||||
if(!this.isValid)
|
||||
return string.Empty;
|
||||
|
||||
if (!this.state.Environment["SUPPORT_CONTACT"].TryRead<string>(out var contact))
|
||||
return string.Empty;
|
||||
|
||||
return contact;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the ID of the plugin.
|
||||
/// </summary>
|
||||
/// <returns>The plugin ID.</returns>
|
||||
public Guid Id()
|
||||
{
|
||||
if(this.isInitialized)
|
||||
return this.pluginId;
|
||||
|
||||
if(!this.isValid)
|
||||
return Guid.Empty;
|
||||
|
||||
if (!this.state.Environment["ID"].TryRead<string>(out var idText))
|
||||
return Guid.Empty;
|
||||
|
||||
if (!Guid.TryParse(idText, out var id))
|
||||
return Guid.Empty;
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the name of the plugin.
|
||||
/// </summary>
|
||||
/// <returns>The plugin name.</returns>
|
||||
public string Name()
|
||||
{
|
||||
if(this.isInitialized)
|
||||
return this.pluginName;
|
||||
|
||||
if(!this.isValid)
|
||||
return string.Empty;
|
||||
|
||||
if (!this.state.Environment["NAME"].TryRead<string>(out var name))
|
||||
return string.Empty;
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the description of the plugin.
|
||||
/// </summary>
|
||||
/// <returns>The plugin description.</returns>
|
||||
public string Description()
|
||||
{
|
||||
if(this.isInitialized)
|
||||
return this.pluginDescription;
|
||||
|
||||
if(!this.isValid)
|
||||
return string.Empty;
|
||||
|
||||
if (!this.state.Environment["DESCRIPTION"].TryRead<string>(out var description))
|
||||
return string.Empty;
|
||||
|
||||
return description;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the version of the plugin.
|
||||
/// </summary>
|
||||
/// <returns>The plugin version.</returns>
|
||||
public PluginVersion Version()
|
||||
{
|
||||
if(this.isInitialized)
|
||||
return this.pluginVersion;
|
||||
|
||||
if(!this.isValid)
|
||||
return PluginVersion.NONE;
|
||||
|
||||
if (!this.state.Environment["VERSION"].TryRead<string>(out var versionText))
|
||||
return PluginVersion.NONE;
|
||||
|
||||
if (!PluginVersion.TryParse(versionText, out var version))
|
||||
return PluginVersion.NONE;
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the authors of the plugin.
|
||||
/// </summary>
|
||||
/// <returns>The plugin authors.</returns>
|
||||
public string[] Authors()
|
||||
{
|
||||
if(this.isInitialized)
|
||||
return this.pluginAuthors;
|
||||
|
||||
if (!this.isValid)
|
||||
return [];
|
||||
|
||||
if (!this.state.Environment["AUTHORS"].TryRead<LuaTable>(out var authors))
|
||||
return [];
|
||||
|
||||
var authorList = new List<string>();
|
||||
foreach(var author in authors.GetArraySpan())
|
||||
if(author.TryRead<string>(out var authorName))
|
||||
authorList.Add(authorName);
|
||||
|
||||
return authorList.ToArray();
|
||||
}
|
||||
}
|
33
app/MindWork AI Studio/Tools/PluginSystem/PluginCategory.cs
Normal file
33
app/MindWork AI Studio/Tools/PluginSystem/PluginCategory.cs
Normal file
@ -0,0 +1,33 @@
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
public enum PluginCategory
|
||||
{
|
||||
NONE,
|
||||
CORE,
|
||||
|
||||
BUSINESS,
|
||||
INDUSTRY,
|
||||
UTILITY,
|
||||
SOFTWARE_DEVELOPMENT,
|
||||
GAMING,
|
||||
EDUCATION,
|
||||
ENTERTAINMENT,
|
||||
SOCIAL,
|
||||
SHOPPING,
|
||||
TRAVEL,
|
||||
HEALTH,
|
||||
FITNESS,
|
||||
FOOD,
|
||||
PARTY,
|
||||
SPORTS,
|
||||
NEWS,
|
||||
WEATHER,
|
||||
MUSIC,
|
||||
POLITICAL,
|
||||
SCIENCE,
|
||||
TECHNOLOGY,
|
||||
ART,
|
||||
FICTION,
|
||||
WRITING,
|
||||
CONTENT_CREATION,
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
public static class PluginCategoryExtensions
|
||||
{
|
||||
public static string GetName(this PluginCategory type) => type switch
|
||||
{
|
||||
PluginCategory.NONE => "None",
|
||||
PluginCategory.CORE => "AI Studio Core",
|
||||
|
||||
PluginCategory.BUSINESS => "Business",
|
||||
PluginCategory.INDUSTRY => "Industry",
|
||||
PluginCategory.UTILITY => "Utility",
|
||||
PluginCategory.SOFTWARE_DEVELOPMENT => "Software Development",
|
||||
PluginCategory.GAMING => "Gaming",
|
||||
PluginCategory.EDUCATION => "Education",
|
||||
PluginCategory.ENTERTAINMENT => "Entertainment",
|
||||
PluginCategory.SOCIAL => "Social",
|
||||
PluginCategory.SHOPPING => "Shopping",
|
||||
PluginCategory.TRAVEL => "Travel",
|
||||
PluginCategory.HEALTH => "Health",
|
||||
PluginCategory.FITNESS => "Fitness",
|
||||
PluginCategory.FOOD => "Food",
|
||||
PluginCategory.PARTY => "Party",
|
||||
PluginCategory.SPORTS => "Sports",
|
||||
PluginCategory.NEWS => "News",
|
||||
PluginCategory.WEATHER => "Weather",
|
||||
PluginCategory.MUSIC => "Music",
|
||||
PluginCategory.POLITICAL => "Political",
|
||||
PluginCategory.SCIENCE => "Science",
|
||||
PluginCategory.TECHNOLOGY => "Technology",
|
||||
PluginCategory.ART => "Art",
|
||||
PluginCategory.FICTION => "Fiction",
|
||||
PluginCategory.WRITING => "Writing",
|
||||
PluginCategory.CONTENT_CREATION => "Content Creation",
|
||||
|
||||
_ => "Unknown plugin category",
|
||||
};
|
||||
}
|
40
app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.cs
Normal file
40
app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using System.Text;
|
||||
|
||||
using Lua;
|
||||
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
public static class PluginFactory
|
||||
{
|
||||
public static async Task LoadAll()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static async Task<PluginBase> Load(string code, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var state = LuaState.Create();
|
||||
|
||||
try
|
||||
{
|
||||
await state.DoStringAsync(code, cancellationToken: cancellationToken);
|
||||
}
|
||||
catch (LuaParseException e)
|
||||
{
|
||||
return new NoPlugin(state, $"Was not able to parse the plugin: {e.Message}");
|
||||
}
|
||||
|
||||
if (!state.Environment["TYPE"].TryRead<string>(out var typeText))
|
||||
return new NoPlugin(state, "TYPE does not exist or is not a valid string.");
|
||||
|
||||
if (!Enum.TryParse<PluginType>(typeText, out var type))
|
||||
return new NoPlugin(state, $"TYPE is not a valid plugin type. Valid types are: {CommonTools.GetAllEnumValues<PluginType>()}");
|
||||
|
||||
return type switch
|
||||
{
|
||||
PluginType.LANGUAGE => new PluginLanguage(state, type),
|
||||
|
||||
_ => new NoPlugin(state, "This plugin type is not supported yet. Please try again with a future version of AI Studio.")
|
||||
};
|
||||
}
|
||||
}
|
8
app/MindWork AI Studio/Tools/PluginSystem/PluginState.cs
Normal file
8
app/MindWork AI Studio/Tools/PluginSystem/PluginState.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the state of a plugin.
|
||||
/// </summary>
|
||||
/// <param name="Valid">True, when the plugin is valid.</param>
|
||||
/// <param name="Message">When the plugin is invalid, this contains the error message.</param>
|
||||
public readonly record struct PluginState(bool Valid, string Message);
|
@ -0,0 +1,20 @@
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
public enum PluginTargetGroup
|
||||
{
|
||||
NONE,
|
||||
|
||||
EVERYONE,
|
||||
CHILDREN,
|
||||
TEENAGERS,
|
||||
STUDENTS,
|
||||
ADULTS,
|
||||
|
||||
INDUSTRIAL_WORKERS,
|
||||
OFFICE_WORKERS,
|
||||
BUSINESS_PROFESSIONALS,
|
||||
SOFTWARE_DEVELOPERS,
|
||||
SCIENTISTS,
|
||||
TEACHERS,
|
||||
ARTISTS,
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
public static class PluginTargetGroupExtensions
|
||||
{
|
||||
public static string Name(this PluginTargetGroup group) => group switch
|
||||
{
|
||||
PluginTargetGroup.NONE => "No target group",
|
||||
|
||||
PluginTargetGroup.EVERYONE => "Everyone",
|
||||
PluginTargetGroup.CHILDREN => "Children",
|
||||
PluginTargetGroup.TEENAGERS => "Teenagers",
|
||||
PluginTargetGroup.STUDENTS => "Students",
|
||||
PluginTargetGroup.ADULTS => "Adults",
|
||||
|
||||
PluginTargetGroup.INDUSTRIAL_WORKERS => "Industrial workers",
|
||||
PluginTargetGroup.OFFICE_WORKERS => "Office workers",
|
||||
PluginTargetGroup.BUSINESS_PROFESSIONALS => "Business professionals",
|
||||
PluginTargetGroup.SOFTWARE_DEVELOPERS => "Software developers",
|
||||
PluginTargetGroup.SCIENTISTS => "Scientists",
|
||||
PluginTargetGroup.TEACHERS => "Teachers",
|
||||
PluginTargetGroup.ARTISTS => "Artists",
|
||||
|
||||
_ => "Unknown target group",
|
||||
};
|
||||
}
|
10
app/MindWork AI Studio/Tools/PluginSystem/PluginType.cs
Normal file
10
app/MindWork AI Studio/Tools/PluginSystem/PluginType.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
public enum PluginType
|
||||
{
|
||||
NONE,
|
||||
|
||||
LANGUAGE,
|
||||
ASSISTANT,
|
||||
CONFIGURATION,
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
public static class PluginTypeExtensions
|
||||
{
|
||||
public static string GetName(this PluginType type) => type switch
|
||||
{
|
||||
PluginType.LANGUAGE => "Language plugin",
|
||||
PluginType.ASSISTANT => "Assistant plugin",
|
||||
PluginType.CONFIGURATION => "Configuration plugin",
|
||||
|
||||
_ => "Unknown plugin type",
|
||||
};
|
||||
}
|
90
app/MindWork AI Studio/Tools/PluginSystem/PluginVersion.cs
Normal file
90
app/MindWork AI Studio/Tools/PluginSystem/PluginVersion.cs
Normal file
@ -0,0 +1,90 @@
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a version number for a plugin.
|
||||
/// </summary>
|
||||
/// <param name="Major">The major version number.</param>
|
||||
/// <param name="Minor">The minor version number.</param>
|
||||
/// <param name="Patch">The patch version number.</param>
|
||||
public readonly record struct PluginVersion(int Major, int Minor, int Patch) : IComparable<PluginVersion>
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents no version number.
|
||||
/// </summary>
|
||||
public static readonly PluginVersion NONE = new(0, 0, 0);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to parse the input string as a plugin version number.
|
||||
/// </summary>
|
||||
/// <param name="input">The input string to parse.</param>
|
||||
/// <param name="version">The parsed version number.</param>
|
||||
/// <returns>True when the input string was successfully parsed; otherwise, false.</returns>
|
||||
public static bool TryParse(string input, out PluginVersion version)
|
||||
{
|
||||
try
|
||||
{
|
||||
version = Parse(input);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
version = NONE;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the input string as a plugin version number.
|
||||
/// </summary>
|
||||
/// <param name="input">The input string to parse.</param>
|
||||
/// <returns>The parsed version number.</returns>
|
||||
/// <exception cref="FormatException">The input string is not in the correct format.</exception>
|
||||
public static PluginVersion Parse(string input)
|
||||
{
|
||||
var segments = input.Split('.');
|
||||
if (segments.Length != 3)
|
||||
throw new FormatException("The input string must be in the format 'major.minor.patch'.");
|
||||
|
||||
var major = int.Parse(segments[0]);
|
||||
var minor = int.Parse(segments[1]);
|
||||
var patch = int.Parse(segments[2]);
|
||||
|
||||
if(major < 0 || minor < 0 || patch < 0)
|
||||
throw new FormatException("The major, minor, and patch numbers must be greater than or equal to 0.");
|
||||
|
||||
return new PluginVersion(major, minor, patch);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the plugin version number to a string in the format 'major.minor.patch'.
|
||||
/// </summary>
|
||||
/// <returns>The plugin version number as a string.</returns>
|
||||
public override string ToString() => $"{this.Major}.{this.Minor}.{this.Patch}";
|
||||
|
||||
/// <summary>
|
||||
/// Compares the plugin version number to another plugin version number.
|
||||
/// </summary>
|
||||
/// <param name="other">The other plugin version number to compare to.</param>
|
||||
/// <returns>A value indicating the relative order of the plugin version numbers.</returns>
|
||||
public int CompareTo(PluginVersion other)
|
||||
{
|
||||
var majorCompare = this.Major.CompareTo(other.Major);
|
||||
if (majorCompare != 0)
|
||||
return majorCompare;
|
||||
|
||||
var minorCompare = this.Minor.CompareTo(other.Minor);
|
||||
if (minorCompare != 0)
|
||||
return minorCompare;
|
||||
|
||||
return this.Patch.CompareTo(other.Patch);
|
||||
}
|
||||
|
||||
public static bool operator >(PluginVersion left, PluginVersion right) => left.CompareTo(right) > 0;
|
||||
|
||||
public static bool operator <(PluginVersion left, PluginVersion right) => left.CompareTo(right) < 0;
|
||||
|
||||
public static bool operator >=(PluginVersion left, PluginVersion right) => left.CompareTo(right) >= 0;
|
||||
|
||||
public static bool operator <=(PluginVersion left, PluginVersion right) => left.CompareTo(right) <= 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user