diff --git a/back/src/Kyoo.Abstractions/Kyoo.Abstractions.csproj b/back/src/Kyoo.Abstractions/Kyoo.Abstractions.csproj
index 7d92d9c8..283f5509 100644
--- a/back/src/Kyoo.Abstractions/Kyoo.Abstractions.csproj
+++ b/back/src/Kyoo.Abstractions/Kyoo.Abstractions.csproj
@@ -14,6 +14,7 @@
+
diff --git a/back/src/Kyoo.Abstractions/Models/Utils/Filter.cs b/back/src/Kyoo.Abstractions/Models/Utils/Filter.cs
index dee6cbed..79ba0c41 100644
--- a/back/src/Kyoo.Abstractions/Models/Utils/Filter.cs
+++ b/back/src/Kyoo.Abstractions/Models/Utils/Filter.cs
@@ -17,8 +17,13 @@
// along with Kyoo. If not, see .
using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Linq.Expressions;
+using System.Reflection;
+using Sprache;
+
namespace Kyoo.Abstractions.Models.Utils;
public abstract record Filter
@@ -46,24 +51,154 @@ public abstract record Filter : Filter
public record Eq(string property, object value) : Filter;
- public record Ne(string property, T2 value) : Filter;
+ public record Ne(string property, object value) : Filter;
- public record Gt(string property, T2 value) : Filter;
+ public record Gt(string property, object value) : Filter;
- public record Ge(string property, T2 value) : Filter;
+ public record Ge(string property, object value) : Filter;
- public record Lt(string property, T2 value) : Filter;
+ public record Lt(string property, object value) : Filter;
- public record Le(string property, T2 value) : Filter;
+ public record Le(string property, object value) : Filter;
- public record Has(string property, T2 value) : Filter;
+ public record Has(string property, object value) : Filter;
public record In(string property, object[] value) : Filter;
public record Lambda(Expression> lambda) : Filter;
- public static Filter From(string filter)
+ public static class FilterParsers
{
+ public static readonly Parser> Filter =
+ Parse.Ref(() => Bracket)
+ .Or(Parse.Ref(() => Not))
+ .Or(Parse.Ref(() => Eq))
+ .Or(Parse.Ref(() => Ne))
+ .Or(Parse.Ref(() => Gt))
+ .Or(Parse.Ref(() => Ge))
+ .Or(Parse.Ref(() => Lt))
+ .Or(Parse.Ref(() => Le));
+ public static readonly Parser> CompleteFilter =
+ Parse.Ref(() => Or)
+ .Or(Parse.Ref(() => And))
+ .Or(Filter);
+
+ public static readonly Parser> Bracket =
+ from open in Parse.Char('(').Token()
+ from filter in CompleteFilter
+ from close in Parse.Char(')').Token()
+ select filter;
+
+ public static readonly Parser> AndOperator = Parse.IgnoreCase("and")
+ .Or(Parse.String("&&"))
+ .Token();
+
+ public static readonly Parser> OrOperator = Parse.IgnoreCase("or")
+ .Or(Parse.String("||"))
+ .Token();
+
+ public static readonly Parser> And = Parse.ChainOperator(AndOperator, Filter, (_, a, b) => new Filter.And(a, b));
+
+ public static readonly Parser> Or = Parse.ChainOperator(OrOperator, And.Or(Filter), (_, a, b) => new Filter.Or(a, b));
+
+ public static readonly Parser> Not =
+ from not in Parse.IgnoreCase("not")
+ .Or(Parse.String("!"))
+ .Token()
+ from filter in CompleteFilter
+ select new Filter.Not(filter);
+
+ private static Parser> _GetOperationParser(Parser