Nuke.Unreal
Build Unreal apps in Style.
Loading...
Searching...
No Matches
ConfigSection.cs
1using System;
2using System.Collections.Generic;
3using System.Linq;
4
5namespace Nuke.Unreal.Ini;
6
7/// <summary>
8/// Represents a section block in an Unreal INI configuration under a `[SectionName]` header
9/// </summary>
10public class ConfigSection
11{
12 /// <summary>
13 /// Maintain the original position for this section as they were in the source file
14 /// so serialization doesn't introduce that much unnecessary changes.
15 /// </summary>
16 public int Order { get; set; }
17
18 /// <summary>
19 /// Section name without square brackets
20 /// </summary>
21 public string? Name { get; set; }
22
23 /// <summary>
24 /// Individual config items underneath this section. Here it's referred to as commands because
25 /// Unreal INI config items can procedurally add or remove items from containers.
26 /// </summary>
27 public List<ConfigCommand> Commands { get; private set; } = new();
28
29 /// <summary>
30 /// Get all commands matching given name.
31 /// </summary>
32 /// <returns>
33 /// Matched commands or an empty enumerable if there wasn't any matching the given name.
34 /// </returns>
35 public IEnumerable<ConfigCommand> this[string key] => Commands.Where(c => c.Name == key);
36
37 /// <summary>
38 /// Parse an entire config item (or a line) as a command and add it to this section.
39 /// </summary>
40 /// <param name="line"></param>
41 /// <param name="order">
42 /// Maintain the original position for this section as they were in the source file
43 /// so serialization doesn't introduce that much unnecessary changes.
44 /// </param>
45 public void SetLine(string line, int order)
46 {
47 if (string.IsNullOrWhiteSpace(line) || line[0] == '[')
48 {
49 return;
50 }
51
52 var command = new ConfigCommand(line, order);
53 if (command.Type == CommandType.Set)
54 {
55 Commands = Commands.Where(c => c.Name != command.Name).ToList();
56 }
57
58 Commands.Add(command);
59 }
60
61 /// <summary>
62 /// Remove all given keys from this section.
63 /// </summary>
64 public void Remove(IEnumerable<string> keys)
65 {
66 Commands = Commands.Where(c => !keys.Any(k => c.Name == k)).ToList();
67 }
68
69 /// <summary>
70 /// Remove all given keys from this section.
71 /// </summary>
72 public void Remove(params string[] keys) => Remove(keys.AsEnumerable());
73
74 /// <summary>
75 /// Gets a config item in this section with specified key.
76 /// </summary>
77 /// <param name="key"></param>
78 /// <param name="fallback"></param>
79 /// <returns>
80 /// Config item or a given fallback if it doesn't exist.
81 /// </returns>
82 public ConfigCommand GetFirst(string key, ConfigCommand fallback = default) =>
83 this[key].Append(fallback).FirstOrDefault();
84
85 /// <summary>
86 /// Set or add an individual config item
87 /// </summary>
88 /// <param name="key">Name of the config item</param>
89 /// <param name="value">Associated value after `=` sign.</param>
90 /// <param name="type">Command type of config item.</param>
91 public void Set(string key, string value, CommandType type = CommandType.Set)
92 {
93 char typeChar = (char)type;
94 string typeString = typeChar == 0 ? "" : typeChar.ToString();
95 var order = Commands.Count > 0 ? Commands.Max(s => s.Order) + 1 : 0;
96 SetLine(typeString + key + "=" + value, order);
97 }
98
99 /// <summary>
100 /// Merge another section into this one, overriding existing values and adding new ones.
101 /// </summary>
102 public void Merge(ConfigSection from)
103 {
104 foreach(var command in from.Commands)
105 {
106 switch (command.Type)
107 {
108 case CommandType.Set:
109 var index = Commands.FindIndex(c => c.Name == command.Name);
110 if (index >= 0)
111 {
112 var existingCommand = Commands[index];
113 existingCommand.Value = command.Value;
114 Commands[index] = existingCommand;
115 }
116 else Commands.Add(command);
117 break;
118
119 case CommandType.Add:
120 case CommandType.Remove:
121
122 if (Commands.Any(c => c == command)) continue;
123 else
124 {
125 var inverseCommandType = CommandType.Remove;
126 if (command.Type == CommandType.Remove)
127 inverseCommandType = CommandType.Add;
128
129 var inverseCommand = command;
130 inverseCommand.Type = inverseCommandType;
131 if (Commands.Any(c => c == inverseCommand))
132 {
133 Commands = Commands.Where(c => c != inverseCommand).ToList();
134 }
135 else Commands.Add(command);
136 }
137 break;
138 case CommandType.Clear:
139 Commands = Commands.Where(c => c.Name != command.Name).ToList();
140 Commands.Add(command);
141 break;
142 }
143 }
144 }
145
146 /// <summary>
147 /// Convert this section to INI format.
148 /// </summary>
149 /// <returns></returns>
150 public string Serialize()
151 {
152 var body = string.Join(Environment.NewLine, Commands.Select(c => c.Serialize()));
153 return Environment.NewLine + "[" + Name + "]" + Environment.NewLine + body;
154 }
155
156 /// <summary>
157 /// Deep copy this section.
158 /// </summary>
159 public ConfigSection Copy() => new() {
160 Name = Name,
161 Order = Order,
162 Commands = Commands.ToList()
163 };
164}
Represents a section block in an Unreal INI configuration under a [SectionName] header.
void Set(string key, string value, CommandType type=CommandType.Set)
Set or add an individual config item.
void Remove(params string[] keys)
Remove all given keys from this section.
void Remove(IEnumerable< string > keys)
Remove all given keys from this section.
void SetLine(string line, int order)
Parse an entire config item (or a line) as a command and add it to this section.
string? Name
Section name without square brackets.
int Order
Maintain the original position for this section as they were in the source file so serialization does...
List< ConfigCommand > Commands
Individual config items underneath this section. Here it's referred to as commands because Unreal INI...
string Serialize()
Convert this section to INI format.
ConfigCommand GetFirst(string key, ConfigCommand fallback=default)
Gets a config item in this section with specified key.
ConfigSection Copy()
Deep copy this section.
void Merge(ConfigSection from)
Merge another section into this one, overriding existing values and adding new ones.
CommandType
Unreal INI config items are read top to bottom, reading like individual commands, especially when man...
Structural representation of a config line in Unreal.