-
Notifications
You must be signed in to change notification settings - Fork 5
/
Command.cs
151 lines (132 loc) · 5.57 KB
/
Command.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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// Copyright Bastian Eicher et al.
// Licensed under the GNU Lesser Public License
using ZeroInstall.Model.Design;
namespace ZeroInstall.Model;
/// <summary>
/// A command says how to run an <see cref="Implementation"/> as a program.
/// </summary>
/// <seealso cref="Element.Commands"/>
[Description("A command says how to run an implementation as a program.")]
[Serializable, XmlRoot("command", Namespace = Feed.XmlNamespace), XmlType("command", Namespace = Feed.XmlNamespace)]
[Equatable]
public partial class Command : FeedElement, IArgBaseContainer, IBindingContainer, IDependencyContainer, ICloneable<Command>
{
#region Constants
/// <summary>
/// Canonical <see cref="Name"/> corresponding to <see cref="Element.Main"/>.
/// </summary>
public const string NameRun = "run";
/// <summary>
/// Conventional <see cref="Name"/> for GUI-only versions of applications.
/// </summary>
public const string NameRunGui = "run-gui";
/// <summary>
/// Canonical <see cref="Name"/> corresponding to <see cref="Element.SelfTest"/>.
/// </summary>
public const string NameTest = "test";
/// <summary>
/// Canonical <see cref="Name"/> used by 0compile.
/// </summary>
public const string NameCompile = "compile";
#endregion
/// <summary>
/// The name of the command. Well-known names are <see cref="NameRun"/>, <see cref="NameTest"/> and <see cref="NameCompile"/>.
/// </summary>
[Description("The name of the command.")]
[TypeConverter(typeof(CommandNameConverter))]
[XmlAttribute("name")]
public required string Name { get; set; }
/// <summary>
/// The relative path of an executable inside the implementation that should be executed to run this command.
/// </summary>
[Description("The relative path of an executable inside the implementation that should be executed to run this command.")]
[XmlAttribute("path")]
public string? Path { get; set; }
/// <summary>
/// A list of command-line arguments to be passed to an implementation executable.
/// </summary>
[Browsable(false)]
[XmlElement(typeof(Arg)), XmlElement(typeof(ForEachArgs))]
[OrderedEquality]
public List<ArgBase> Arguments { get; } = [];
/// <summary>
/// A list of <see cref="Binding"/>s for <see cref="Implementation"/>s to locate <see cref="Dependency"/>s.
/// </summary>
[Browsable(false)]
[XmlElement(typeof(GenericBinding)), XmlElement(typeof(EnvironmentBinding)), XmlElement(typeof(OverlayBinding)), XmlElement(typeof(ExecutableInVar)), XmlElement(typeof(ExecutableInPath))]
[OrderedEquality]
public List<Binding> Bindings { get; } = [];
/// <summary>
/// Switches the working directory of a process on startup to a location within an implementation.
/// </summary>
[Browsable(false)]
[XmlElement("working-dir")]
public WorkingDir? WorkingDir { get; set; }
/// <summary>
/// A list of interfaces this command depends upon.
/// </summary>
[Browsable(false)]
[XmlElement("requires")]
[OrderedEquality]
public List<Dependency> Dependencies { get; } = [];
/// <summary>
/// A list of interfaces that are restricted to specific versions when used.
/// </summary>
[Browsable(false)]
[XmlElement("restricts")]
[OrderedEquality]
public List<Restriction> Restrictions { get; } = [];
/// <summary>
/// A special kind of dependency: the program that is used to run this one. For example, a Python program might specify Python as its runner.
/// </summary>
[Browsable(false)]
[XmlElement("runner")]
public Runner? Runner { get; set; }
#region Normalize
/// <summary>
/// Converts legacy elements, sets default values, etc..
/// </summary>
/// <exception cref="InvalidDataException">A required property is not set or invalid.</exception>
public virtual void Normalize()
{
EnsureAttribute(Name, "name");
// Apply if-0install-version filter
Arguments.RemoveAll(FilterMismatch);
Dependencies.RemoveAll(FilterMismatch);
Restrictions.RemoveAll(FilterMismatch);
Bindings.RemoveAll(FilterMismatch);
if (FilterMismatch(WorkingDir)) WorkingDir = null;
foreach (var argument in Arguments) argument.Normalize();
foreach (var binding in Bindings) binding.Normalize();
Runner?.Normalize();
foreach (var dependency in Dependencies) dependency.Normalize();
foreach (var restriction in Restrictions) restriction.Normalize();
}
#endregion
#region Conversion
/// <summary>
/// Returns the Command in the form "Name (Path)". Not safe for parsing!
/// </summary>
public override string ToString() => $"{Name} ({Path})";
#endregion
#region Clone
/// <summary>
/// Creates a deep copy of this <see cref="Command"/> instance.
/// </summary>
/// <returns>The new copy of the <see cref="Command"/>.</returns>
public Command Clone() => new()
{
UnknownAttributes = UnknownAttributes,
UnknownElements = UnknownElements,
IfZeroInstallVersion = IfZeroInstallVersion,
Name = Name,
Path = Path,
WorkingDir = WorkingDir?.Clone(),
Runner = Runner?.CloneRunner(),
Arguments = {Arguments.CloneElements()},
Bindings = {Bindings.CloneElements()},
Dependencies = {Dependencies.CloneElements()},
Restrictions = {Restrictions.CloneElements()}
};
#endregion
}