@@ -13,6 +13,7 @@ namespace NginxConfigParser;
1313public class NginxConfig
1414{
1515 private static readonly Regex KeyRegex = new ( @"^[\w]+(\[\d+\])?$" ) ;
16+ private static readonly Encoding Utf8NoBom = new UTF8Encoding ( encoderShouldEmitUTF8Identifier : false ) ;
1617
1718 private readonly Parser _parser ;
1819
@@ -54,8 +55,6 @@ public static NginxConfig LoadFrom(string fileName)
5455 throw new FileNotFoundException ( fileName ) ;
5556 }
5657
57- // var fs = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite);
58-
5958 var content = File . ReadAllText ( fileName ) ;
6059
6160 return Load ( content ) ;
@@ -179,7 +178,7 @@ public NginxConfig AddOrUpdate(string keyPath, string value, bool addAsGroup = f
179178
180179 for ( int i = 0 ; i < length ; i ++ )
181180 {
182- var ( key , index ) = ResolveKey ( paths [ i ] ) ;
181+ var ( key , index , _ ) = ResolveKey ( paths [ i ] ) ;
183182
184183 IToken find = null ;
185184 IEnumerable < IToken > findTokens ;
@@ -268,7 +267,7 @@ public NginxConfig Remove(string keyPath)
268267
269268 for ( int i = 0 ; i < length ; i ++ )
270269 {
271- var ( key , index ) = ResolveKey ( paths [ i ] ) ;
270+ var ( key , index , hasIndex ) = ResolveKey ( paths [ i ] ) ;
272271
273272 IToken find = null ;
274273 IEnumerable < IToken > findTokens ;
@@ -280,19 +279,21 @@ public NginxConfig Remove(string keyPath)
280279
281280 if ( i == length - 1 )
282281 {
283- // remove
284- if ( groupToken == null )
282+ IList < IToken > targetList = groupToken == null ? tokens : groupToken . Tokens ;
283+
284+ if ( hasIndex )
285285 {
286- foreach ( var item in findTokens )
287- {
288- tokens . Remove ( item ) ;
289- }
286+ var findTokensCount = findTokens . Count ( ) ;
287+ if ( index < 0 || index >= findTokensCount )
288+ throw new IndexOutOfRangeException ( $ "The key '{ key } ' index must be >= 0 and < { findTokensCount } ") ;
289+
290+ targetList . Remove ( findTokens . ElementAt ( index ) ) ;
290291 }
291292 else
292293 {
293294 foreach ( var item in findTokens )
294295 {
295- groupToken . Tokens . Remove ( item ) ;
296+ targetList . Remove ( item ) ;
296297 }
297298 }
298299 }
@@ -332,7 +333,7 @@ public void Save(string fileName)
332333 throw new ArgumentException ( $ "'{ nameof ( fileName ) } ' cannot be null or whitespace.", nameof ( fileName ) ) ;
333334 }
334335
335- Save ( fileName , Encoding . Default ) ;
336+ Save ( fileName , Utf8NoBom ) ;
336337 }
337338
338339 /// <summary>
@@ -378,33 +379,38 @@ public override string ToString()
378379
379380 private void WriteTokenString ( IEnumerable < IToken > tokens , TextWriter textWriter , int level = 0 )
380381 {
381- var normalTokens = tokens . Where ( x => x is CommentToken || x is ValueToken ) ;
382- var groupTokens = tokens . Where ( x => x is GroupToken ) ;
383-
384382 textWriter . NewLine = Environment . NewLine ;
385383
386- foreach ( var token in normalTokens )
384+ var tokenList = tokens as IList < IToken > ?? tokens . ToList ( ) ;
385+ var wroteAny = false ;
386+
387+ foreach ( var token in tokenList )
387388 {
388389 if ( token is CommentToken comment )
390+ {
389391 textWriter . WriteLine ( PadLeftSpace ( comment . ToString ( ) , level ) ) ;
390- else if ( token is ValueToken vaue )
391- textWriter . WriteLine ( PadLeftSpace ( vaue . ToString ( ) , level ) ) ;
392- }
393-
394- foreach ( GroupToken group in groupTokens )
395- {
396- //if (group.Parent != null)
397- textWriter . WriteLine ( ) ;
392+ wroteAny = true ;
393+ }
394+ else if ( token is ValueToken value )
395+ {
396+ textWriter . WriteLine ( PadLeftSpace ( value . ToString ( ) , level ) ) ;
397+ wroteAny = true ;
398+ }
399+ else if ( token is GroupToken group )
400+ {
401+ if ( wroteAny )
402+ textWriter . WriteLine ( ) ;
398403
399- if ( ! string . IsNullOrWhiteSpace ( group . Comment ) )
400- textWriter . WriteLine ( PadLeftSpace ( $ "{ group . Key } { group . Value } {{ # { group . Comment } ", level ) ) ;
401- else
402- textWriter . WriteLine ( PadLeftSpace ( $ "{ group . Key } { group . Value } {{ ", level ) ) ;
404+ if ( ! string . IsNullOrWhiteSpace ( group . Comment ) )
405+ textWriter . WriteLine ( PadLeftSpace ( $ "{ group . Key } { group . Value } {{ # { group . Comment } ", level ) ) ;
406+ else
407+ textWriter . WriteLine ( PadLeftSpace ( $ "{ group . Key } { group . Value } {{ ", level ) ) ;
403408
404- WriteTokenString ( group . Tokens , textWriter , level + 1 ) ;
409+ WriteTokenString ( group . Tokens , textWriter , level + 1 ) ;
405410
406- // end
407- textWriter . WriteLine ( PadLeftSpace ( "}" , level ) ) ;
411+ textWriter . WriteLine ( PadLeftSpace ( "}" , level ) ) ;
412+ wroteAny = true ;
413+ }
408414 }
409415 }
410416
@@ -423,7 +429,7 @@ private IValueToken GetTokenFromPath(string keyPath)
423429
424430 foreach ( var key in paths )
425431 {
426- var ( keyName , index ) = ResolveKey ( key ) ;
432+ var ( keyName , index , _ ) = ResolveKey ( key ) ;
427433
428434 result = FindToken ( tokens , keyName , index ) ;
429435 if ( result != null )
@@ -446,13 +452,13 @@ private IList<IValueToken> GetTokensFromPath(string keyPath)
446452
447453 var paths = keyPath . Split ( ':' ) ;
448454
449- IEnumerable < IValueToken > result = null ;
455+ IEnumerable < IValueToken > result = Array . Empty < IValueToken > ( ) ;
450456
451457 IValueToken current = null ;
452458
453459 for ( int i = 0 ; i < paths . Length ; i ++ )
454460 {
455- var ( keyName , index ) = ResolveKey ( paths [ i ] ) ;
461+ var ( keyName , index , _ ) = ResolveKey ( paths [ i ] ) ;
456462
457463 if ( i == paths . Length - 1 )
458464 {
@@ -466,6 +472,10 @@ private IList<IValueToken> GetTokensFromPath(string keyPath)
466472 {
467473 tokens = groupToken . Tokens . ToList ( ) ;
468474 }
475+ else
476+ {
477+ return new List < IValueToken > ( ) ;
478+ }
469479 }
470480 }
471481
@@ -482,7 +492,7 @@ private static IEnumerable<IValueToken> FindTokens(IEnumerable<IToken> tokens, s
482492 return tokens . Where ( x => x is IValueToken valueToken && valueToken . Key == key ) . Cast < IValueToken > ( ) . ToArray ( ) ;
483493 }
484494
485- private ( string key , int index ) ResolveKey ( string key )
495+ private ( string key , int index , bool hasIndex ) ResolveKey ( string key )
486496 {
487497 if ( ! KeyRegex . IsMatch ( key ) )
488498 {
@@ -493,19 +503,21 @@ private static IEnumerable<IValueToken> FindTokens(IEnumerable<IToken> tokens, s
493503
494504 var index = 0 ;
495505 string keyName = key ;
506+ var hasIndex = false ;
496507
497508 if ( numberStartSymbol > 0 )
498509 {
510+ hasIndex = true ;
499511 var numberStartIndex = numberStartSymbol + 1 ;
500512
501513 if ( ! int . TryParse ( key . Substring ( numberStartIndex , key . Length - 1 - numberStartIndex ) , out index ) )
502514 {
503- // TODO
515+ throw new Exception ( $ "The key ' { key } ' index format is incorrect" ) ;
504516 }
505517
506518 keyName = key . Substring ( 0 , numberStartSymbol ) ;
507519 }
508520
509- return ( keyName , index ) ;
521+ return ( keyName , index , hasIndex ) ;
510522 }
511523}
0 commit comments