-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathImmutableLens.cs
87 lines (73 loc) · 2.82 KB
/
ImmutableLens.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using ImmutableObjectGraph;
namespace ImmutableObjectGraphLens
{
public interface IImmutableLens<TRoot, TTarget>
{
TRoot Set(TRoot root, TTarget value);
TTarget Get(TRoot root);
}
public class ImmutableLens<TRoot, TTarget> : IImmutableLens<TRoot, TTarget>
{
private readonly Expression<Func<TRoot, TTarget>> _Selector;
private readonly Func<TRoot, TTarget> _CompiledSelector;
public ImmutableLens(Expression<Func<TRoot, TTarget>> selector )
{
_Selector = selector;
_CompiledSelector = selector.Compile();
}
public TRoot Set(TRoot root, TTarget value)
{
return root.WithProps(value, _Selector);
}
public TTarget Get(TRoot root)
{
return _CompiledSelector(root);
}
}
public static class ImmutableLens
{
public static ImmutableLens<TRoot, TTarget> CreateLens<TRoot,TTarget>
(this Expression<Func<TRoot, TTarget>> selector)
{
return new ImmutableLens<TRoot, TTarget>(selector);
}
public static TRoot With<TRoot,TTarget>(this TRoot root, string prop, TTarget target)
{
var method = root.GetType().GetMethod("With");
return (TRoot) method
.InvokeWithNamedParameters(root, new Dictionary<string, object>() {{prop, typeof(Optional).InvokeWithGeneric("For", target)}});
}
public static TRoot WithProps<TRoot,TTarget>(this TRoot root, TTarget target, params string [] props)
{
var nodes = props
.SkipLast(1)
.Scan((object) root, (o, prop) => o.Get<object>(FirstCharToUpper(prop)))
.StartWith(root)
.Reverse()
.ToList();
props = props.Reverse().ToArray();
return (TRoot) nodes
.Zip(props, (n, p) => new {n, p})
.Aggregate((object) target, (value, o) => o.n.With(o.p, value));
}
public static TRoot WithProps<TRoot, TTarget> (this TRoot root , TTarget target, Expression<Func<TRoot, TTarget>> selector)
{
var props = ReactiveUI.Reflection.ExpressionToPropertyNames(selector.Body)
.Split('.')
.Select(s=>s.ToLower(CultureInfo.InvariantCulture))
.ToArray();
return WithProps(root, target, props);
}
private static string FirstCharToUpper(string input)
{
if (String.IsNullOrEmpty(input))
throw new ArgumentException("ARGH!");
return input.First().ToString().ToUpper() + String.Join("", input.Skip(1));
}
}
}