• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

b1f6c1c4 / ProfessionalAccounting / 327

28 Apr 2025 05:37PM UTC coverage: 51.346% (-3.0%) from 54.325%
327

push

appveyor

b1f6c1c4
ci buildx

6620 of 12893 relevant lines covered (51.35%)

126.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

56.1
/AccountingServer.Shell/IShellComponent.cs
1
/* Copyright (C) 2020-2025 b1f6c1c4
2
 *
3
 * This file is part of ProfessionalAccounting.
4
 *
5
 * ProfessionalAccounting is free software: you can redistribute it and/or
6
 * modify it under the terms of the GNU Affero General Public License as
7
 * published by the Free Software Foundation, version 3.
8
 *
9
 * ProfessionalAccounting is distributed in the hope that it will be useful, but
10
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License
12
 * for more details.
13
 *
14
 * You should have received a copy of the GNU Affero General Public License
15
 * along with ProfessionalAccounting.  If not, see
16
 * <https://www.gnu.org/licenses/>.
17
 */
18

19
using System;
20
using System.Collections;
21
using System.Collections.Generic;
22
using System.Linq;
23

24
namespace AccountingServer.Shell;
25

26
/// <summary>
27
///     表达式解释组件
28
/// </summary>
29
internal interface IShellComponent
30
{
31
    /// <summary>
32
    ///     执行表达式
33
    /// </summary>
34
    /// <param name="expr">表达式</param>
35
    /// <param name="ctx">客户端上下文</param>
36
    /// <param name="term">表达式头</param>
37
    /// <returns>执行结果</returns>
38
    IAsyncEnumerable<string> Execute(string expr, Context ctx, string term);
39

40
    /// <summary>
41
    ///     粗略判断表达式是否可执行
42
    /// </summary>
43
    /// <param name="expr">表达式</param>
44
    /// <returns>是否可执行</returns>
45
    bool IsExecutable(string expr);
46
}
47

48
/// <summary>
49
///     从委托创建表达式解释组件
50
/// </summary>
51
internal class ShellComponent : IShellComponent
52
{
53
    /// <summary>
54
    ///     操作
55
    /// </summary>
56
    private readonly Func<string, Context, IAsyncEnumerable<string>> m_Action;
57

58
    /// <summary>
59
    ///     首段字符串
60
    /// </summary>
61
    private readonly string m_Initial;
62

63
    public ShellComponent(string initial, Func<string, Context, IAsyncEnumerable<string>> action)
240✔
64
    {
240✔
65
        m_Initial = initial;
240✔
66
        m_Action = action;
240✔
67
    }
240✔
68

69
    /// <inheritdoc />
70
    public IAsyncEnumerable<string> Execute(string expr, Context ctx, string term)
71
    {
×
72
        if (m_Initial == null)
×
73
            return m_Action(expr, ctx);
×
74

75
        ctx.Identity.WillInvoke(string.IsNullOrEmpty(term) ? m_Initial : $"{term}-{m_Initial}");
×
76
        return m_Action(expr.Rest(), ctx);
×
77
    }
×
78

79
    /// <inheritdoc />
80
    public bool IsExecutable(string expr) => m_Initial == null || expr.Initial() == m_Initial;
×
81
}
82

83
internal class ComponentAdapter : IShellComponent
84
{
85
    /// <summary>
86
    ///     首段字符串
87
    /// </summary>
88
    private readonly string m_Initial;
89

90
    private readonly IShellComponent m_Component;
91

92
    public ComponentAdapter(string initial, IShellComponent component)
20✔
93
    {
20✔
94
        m_Initial = initial;
20✔
95
        m_Component = component;
20✔
96
    }
20✔
97

98
    /// <inheritdoc />
99
    public IAsyncEnumerable<string> Execute(string expr, Context ctx, string term)
100
    {
×
101
        if (m_Initial == null)
×
102
            return m_Component.Execute(expr, ctx, term);
×
103

104
        var next = string.IsNullOrEmpty(term) ? m_Initial : $"{term}-{m_Initial}";
×
105
        ctx.Identity.WillInvoke(next);
×
106
        return m_Component.Execute(expr.Rest(), ctx, next);
×
107
    }
×
108

109
    /// <inheritdoc />
110
    public bool IsExecutable(string expr) => m_Initial == null || expr.Initial() == m_Initial;
×
111
}
112

113
/// <summary>
114
///     复合表达式解释组件
115
/// </summary>
116
internal sealed class ShellComposer : IShellComponent, IEnumerable
117
{
118
    private readonly List<IShellComponent> m_Components = new();
50✔
119

120
    /// <inheritdoc />
121
    public IEnumerator GetEnumerator() => m_Components.GetEnumerator();
×
122

123
    /// <inheritdoc />
124
    public IAsyncEnumerable<string> Execute(string expr, Context ctx, string term)
125
        => FirstExecutable(expr).Execute(expr, ctx, term);
7✔
126

127
    /// <inheritdoc />
128
    public bool IsExecutable(string expr) => m_Components.Any(s => s.IsExecutable(expr));
×
129

130
    public void Add(IShellComponent shell) => m_Components.Add(shell);
340✔
131

132
    /// <summary>
133
    ///     第一个可以执行的组件
134
    /// </summary>
135
    /// <param name="expr">表达式</param>
136
    /// <returns>组件</returns>
137
    private IShellComponent FirstExecutable(string expr) =>
138
        m_Components.FirstOrDefault(s => s.IsExecutable(expr)) ?? throw new InvalidOperationException("表达式无效");
49✔
139
}
140

141
internal static class ExprHelper
142
{
143
    /// <summary>
144
    ///     首段字符串
145
    /// </summary>
146
    /// <param name="str">原字符串</param>
147
    /// <returns>首段</returns>
148
    public static string Initial(this string str)
149
    {
37✔
150
        if (str == null)
37✔
151
            return null;
×
152

153
        var id = str.IndexOfAny(new[] { ' ', '-' });
37✔
154
        return id < 0 ? str : str[..id];
37✔
155
    }
37✔
156

157
    /// <summary>
158
    ///     首段字符串
159
    /// </summary>
160
    /// <param name="str">原字符串</param>
161
    /// <returns>首段</returns>
162
    public static string Rest(this string str)
163
    {
1✔
164
        var id = str.IndexOfAny(new[] { ' ', '-' });
1✔
165
        return id < 0 ? "" : str[(id + 1)..].TrimStart();
1✔
166
    }
1✔
167
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc