Skip to content

Commit 8b9cff4

Browse files
committed
partially fixes #17 - Hard to parse redis config file due to non section and space separated kv
1 parent 0875582 commit 8b9cff4

17 files changed

+360
-174
lines changed

src/ConfigParser.NullSection.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using System;
2+
using System.Linq;
3+
4+
namespace Salaros.Configuration
5+
{
6+
public partial class ConfigParser
7+
{
8+
public class NullConfigSection
9+
{
10+
private ConfigParser parent;
11+
12+
public NullConfigSection(ConfigParser parent)
13+
{
14+
this.parent = parent;
15+
}
16+
17+
public T GetValue<T>(string keyName, T defaultValue = default(T))
18+
{
19+
if (string.IsNullOrWhiteSpace(keyName))
20+
throw new ArgumentException("Key name must be a non-empty string.", nameof(keyName));
21+
22+
var iniKey = new ConfigKeyValue<T>(keyName, parent.Settings.KeyValueSeparator, defaultValue, -1);
23+
var key = parent.fileHeader.Section.Keys.FirstOrDefault(k => Equals(keyName, k.Name));
24+
if (key != null)
25+
return (T)key.ValueRaw;
26+
27+
parent.fileHeader.Section.AddLine(iniKey);
28+
return defaultValue;
29+
}
30+
31+
public string GetValue(string keyName, string defaultValue = null) => GetValue<string>(keyName, defaultValue);
32+
}
33+
}
34+
}

src/ConfigParser.cs

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
namespace Salaros.Configuration
1212
{
13-
public class ConfigParser
13+
public partial class ConfigParser
1414
{
1515
private static readonly ILog Logger = LogProvider.GetCurrentClassLogger();
1616
protected readonly ConfigSection fileHeader;
@@ -48,18 +48,19 @@ static ConfigParser()
4848
public ConfigParser(ConfigParserSettings settings = null)
4949
{
5050
Settings = settings ?? new ConfigParserSettings();
51+
NullSection = new NullConfigSection(this);
5152

5253
fileHeader = new ConfigSection();
5354
sections = new Dictionary<string, ConfigSection>();
5455
}
5556

5657
/// <inheritdoc />
5758
/// <summary>
58-
/// Initializes a new instance of the <see cref="T:Salaros.Configuration.ConfigParser" /> class.
59+
/// Initializes a new instance of the <see cref="Salaros.Configuration.ConfigParser" /> class.
5960
/// </summary>
6061
/// <param name="configFile">The configuration file (may be path or file content).</param>
6162
/// <param name="settings">The settings.</param>
62-
/// <exception cref="T:System.ArgumentException">configFilePath</exception>
63+
/// <exception cref="System.ArgumentException">configFilePath</exception>
6364
public ConfigParser(string configFile, ConfigParserSettings settings = null)
6465
: this(settings)
6566
{
@@ -111,16 +112,25 @@ public ConfigParser(string configFile, ConfigParserSettings settings = null)
111112
/// <value>
112113
/// The sections.
113114
/// </value>
114-
public ReadOnlyCollection<ConfigSection> Sections =>
115-
new ReadOnlyCollection<ConfigSection>(sections.Values.ToArray());
115+
#if NET40
116+
public ReadOnlyCollection<ConfigSection> Sections => new ReadOnlyCollection<ConfigSection>(
117+
#else
118+
public IReadOnlyCollection<ConfigSection> Sections => new Collection<ConfigSection>(
119+
#endif
120+
sections.Values.ToList());
116121

117122
/// <summary>
118123
/// Gets configuration file's lines.
119124
/// </summary>
120125
/// <value>The lines.</value>
121-
public ReadOnlyCollection<IConfigLine> Lines
122-
=> new ReadOnlyCollection<IConfigLine>(fileHeader.Lines.Concat(sections.Values.SelectMany(s => s.Lines))
123-
.ToArray());
126+
#if NET40
127+
public ReadOnlyCollection<IConfigLine> Lines => new ReadOnlyCollection<IConfigLine>(
128+
#else
129+
public IReadOnlyCollection<IConfigLine> Lines => new Collection<IConfigLine>(
130+
#endif
131+
fileHeader.Lines.Concat(sections.Values.SelectMany(s => s.Lines)).ToList());
132+
133+
public NullConfigSection NullSection { get; }
124134

125135
#endregion Properties
126136

@@ -143,19 +153,20 @@ public ReadOnlyCollection<IConfigLine> Lines
143153
/// </exception>
144154
internal virtual bool TryGetValue<T>(string sectionName, string keyName, out T value)
145155
{
146-
if (string.IsNullOrWhiteSpace(sectionName))
156+
if (sectionName is null)
147157
throw new ArgumentNullException(nameof(sectionName));
158+
148159
if (string.IsNullOrWhiteSpace(keyName))
149-
throw new ArgumentNullException(nameof(keyName));
160+
throw new ArgumentException("Key name must be a non-empty string.", nameof(keyName));
150161

151162
#pragma warning disable IDE0034 // Simplify 'default' expression
152163
value = default(T);
153164
#pragma warning restore IDE0034 // Simplify 'default' expression
154165

155166
if (!sections.TryGetValue(sectionName, out var section))
156-
return false;
167+
section = null;
157168

158-
var key = section.Keys.FirstOrDefault(k => Equals(keyName, k.Name));
169+
var key = (section ?? fileHeader?.Section).Keys.FirstOrDefault(k => Equals(keyName, k.Name));
159170
if (key == null)
160171
return false;
161172

@@ -173,13 +184,13 @@ internal virtual bool TryGetValue<T>(string sectionName, string keyName, out T v
173184
/// <typeparam name="T">The 1st type parameter.</typeparam>
174185
internal virtual T GetRawValue<T>(string sectionName, string keyName, T defaultValue)
175186
{
176-
if (string.IsNullOrWhiteSpace(sectionName))
187+
if (sectionName is null)
177188
throw new ArgumentNullException(nameof(sectionName));
189+
178190
if (string.IsNullOrWhiteSpace(keyName))
179-
throw new ArgumentNullException(nameof(keyName));
191+
throw new ArgumentException("Key name must be a non-empty string.", nameof(keyName));
180192

181193
var iniKey = new ConfigKeyValue<T>(keyName, Settings.KeyValueSeparator, defaultValue, -1);
182-
183194
if (!sections.TryGetValue(sectionName, out var section))
184195
{
185196
section = new ConfigSection(sectionName, Lines.Any() ? Lines.Max(l => l.LineNumber) : 0);
@@ -188,11 +199,14 @@ internal virtual T GetRawValue<T>(string sectionName, string keyName, T defaultV
188199
sections.Add(sectionName, section);
189200
}
190201

191-
var key = section.Keys.FirstOrDefault(k => Equals(keyName, k.Name));
202+
var key = (section ?? fileHeader?.Section).Keys.FirstOrDefault(k => Equals(keyName, k.Name));
192203
if (key != null)
193204
return (T)key.ValueRaw;
194205

195-
section.AddLine(iniKey);
206+
if (section is null && Settings.MultiLineValues.HasFlag(MultiLineValues.AllowEmptyTopSection))
207+
section = fileHeader.Section;
208+
209+
section?.AddLine(iniKey);
196210
return defaultValue;
197211
}
198212

@@ -421,7 +435,7 @@ public virtual bool ValueIsArray(string sectionName, string keyName)
421435
return values.Any() && string.IsNullOrWhiteSpace(values.First());
422436
}
423437

424-
#endregion
438+
#endregion
425439

426440
#region SetValue
427441

@@ -540,7 +554,7 @@ public virtual bool SetValue(string sectionName, string keyName, byte[] value)
540554
return SetValue(sectionName, keyName, EncodeByteArray(value));
541555
}
542556

543-
#endregion
557+
#endregion SetValue
544558

545559
#region Indexing
546560

@@ -564,7 +578,7 @@ public ConfigSection this[string sectionName]
564578
}
565579
}
566580

567-
#endregion
581+
#endregion Indexing
568582

569583
/// <summary>
570584
/// Save configuration file's content.
@@ -641,7 +655,7 @@ public override string ToString()
641655
);
642656
}
643657

644-
#endregion
658+
#endregion Methods
645659

646660
#region Helpers
647661

@@ -819,10 +833,17 @@ private void ReadKeyAndValue(ref ConfigSection currentSection, ref ConfigLine cu
819833
throw new ConfigParserException("Unknown key=value situation detected!", lineNumber);
820834
}
821835

822-
if (append)
823-
currentLine.Content = $"{currentLine.Content}{Settings.NewLine}{value}";
824-
else
825-
currentLine = new ConfigKeyValue<object>(keyName, separator, value, lineNumber);
836+
try
837+
{
838+
if (append)
839+
currentLine.Content = $"{currentLine.Content}{Settings.NewLine}{value}";
840+
else
841+
currentLine = new ConfigKeyValue<object>(keyName, separator, value, lineNumber);
842+
}
843+
catch (Exception ex)
844+
{
845+
throw new ConfigParserException($"Failed to parse the following line: '{lineRaw}'", lineNumber, ex);
846+
}
826847
}
827848

828849
/// <summary>

src/ConfigParserException.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ public class ConfigParserException : Exception
66
{
77
/// <inheritdoc />
88
/// <summary>
9-
/// Initializes a new instance of the <see cref="T:Salaros.Configuration.ConfigParserException" /> class.
9+
/// Initializes a new instance of the <see cref="Salaros.Configuration.ConfigParserException" /> class.
1010
/// </summary>
1111
/// <param name="message">Message.</param>
1212
/// <param name="lineNumber">Line number.</param>

src/Entries/ConfigComment.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ public class ConfigComment : ConfigLine
66

77
/// <inheritdoc />
88
/// <summary>
9-
/// Initializes a new instance of the <see cref="T:IniComment" /> class.
9+
/// Initializes a new instance of the <see cref="IniComment" /> class.
1010
/// </summary>
1111
/// <param name="delimiter">Delimiter.</param>
1212
/// <param name="comment"></param>
@@ -39,9 +39,9 @@ public string Comment
3939

4040
/// <inheritdoc />
4141
/// <summary>
42-
/// Returns a <see cref="T:System.String" /> that represents the current <see cref="T:Salaros.Config.IniComment" />.
42+
/// Returns a <see cref="System.String" /> that represents the current <see cref="Salaros.Config.IniComment" />.
4343
/// </summary>
44-
/// <returns>A <see cref="T:System.String" /> that represents the current <see cref="T:Salaros.Config.IniComment" />.</returns>
44+
/// <returns>A <see cref="System.String" /> that represents the current <see cref="Salaros.Config.IniComment" />.</returns>
4545
public override string ToString()
4646
{
4747
return (string.IsNullOrWhiteSpace(Comment))

src/Entries/ConfigKeyValue.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public class ConfigKeyValue<T> : ConfigLine, IConfigKeyValue
99
#region Constructors
1010

1111
/// <summary>
12-
/// Initializes a new instance of the <see cref="T:Salaros.Config.ConfigKeyValue`T" /> class.
12+
/// Initializes a new instance of the <see cref="Salaros.Config.ConfigKeyValue`T" /> class.
1313
/// </summary>
1414
/// <param name="keyName">Name of the key.</param>
1515
/// <param name="separator">The separator.</param>
@@ -23,7 +23,7 @@ public ConfigKeyValue(string keyName, string separator, T value, int lineNumber)
2323
if (string.IsNullOrWhiteSpace(keyName))
2424
throw new ArgumentNullException(nameof(keyName));
2525

26-
if (string.IsNullOrWhiteSpace(separator))
26+
if (separator is null)
2727
throw new ArgumentNullException(nameof(separator));
2828

2929
this.keyName = keyName;
@@ -102,9 +102,9 @@ public override string Content
102102

103103
/// <inheritdoc />
104104
/// <summary>
105-
/// Returns a <see cref="T:string" /> that represents the current <see cref="T:Salaros.Config.ConfigKeyValue" />.
105+
/// Returns a <see cref="string" /> that represents the current <see cref="Salaros.Config.ConfigKeyValue" />.
106106
/// </summary>
107-
/// <returns>A <see cref="T:string" /> that represents the current <see cref="T:Salaros.Config.ConfigKeyValue" />.</returns>
107+
/// <returns>A <see cref="string" /> that represents the current <see cref="Salaros.Config.ConfigKeyValue" />.</returns>
108108
public override string ToString()
109109
{
110110
return ToString(MultiLineValues.NotAllowed);
@@ -113,11 +113,11 @@ public override string ToString()
113113
/// ReSharper disable once InheritdocInvalidUsage
114114
/// <inheritdoc cref="ConfigLine" />
115115
/// <summary>
116-
/// Returns a <see cref="T:System.String" /> that represents this instance.
116+
/// Returns a <see cref="System.String" /> that represents this instance.
117117
/// </summary>
118118
/// <param name="multiLineSettings">The multi line settings.</param>
119119
/// <returns>
120-
/// A <see cref="T:System.String" /> that represents this instance.
120+
/// A <see cref="System.String" /> that represents this instance.
121121
/// </returns>
122122
public override string ToString(MultiLineValues multiLineSettings)
123123
{

src/Entries/ConfigLine.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,11 @@ public override string ToString()
9797

9898
/// <inheritdoc />
9999
/// <summary>
100-
/// Returns a <see cref="T:System.String" /> that represents this instance.
100+
/// Returns a <see cref="System.String" /> that represents this instance.
101101
/// </summary>
102102
/// <param name="multiLineSettings">The multi line settings.</param>
103103
/// <returns>
104-
/// A <see cref="T:System.String" /> that represents this instance.
104+
/// A <see cref="System.String" /> that represents this instance.
105105
/// </returns>
106106
public virtual string ToString(MultiLineValues multiLineSettings)
107107
{

0 commit comments

Comments
 (0)