-
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Internals are reworked: now package is relying on a tree-like structure to improve maintainability and scalability. - Fixed `indentSize` validation (only values >=1 are allowed). - Resolved #6: ```yaml # BEFORE: field: - - 5 - 2 - a: 3 b: 6 # AFTER: field: - - 5 - 2 - a: 3 b: 6 ``` - Update min Dart SDK version constraint from `2.12.0` to `3.0.0` - Update `lints: ^2.0.1` to `lints: ^3.0.0` BREAKING CHANGES: - `YAMLWriter` renamed to `YamlWriter` ([Effective Dart reference](https://dart.dev/effective-dart/style#do-capitalize-acronyms-and-abbreviations-longer-than-two-letters-like-words)). - Deprecated `identSize` property is removed. Use `indentSize` instead. - `toEncodable` property is removed. Use constructor parameter instead (`YamlWriter#toEncodable`).
- Loading branch information
Showing
9 changed files
with
328 additions
and
258 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
import 'dart:math'; | ||
|
||
import 'yaml_context.dart'; | ||
|
||
sealed class Node { | ||
bool get requiresNewLine => false; | ||
|
||
List<String> toYaml(YamlContext context); | ||
} | ||
|
||
class NullNode extends Node { | ||
@override | ||
List<String> toYaml(YamlContext context) => ['null']; | ||
} | ||
|
||
class NumNode extends Node { | ||
final num number; | ||
|
||
NumNode(this.number); | ||
|
||
@override | ||
List<String> toYaml(YamlContext context) => ['$number']; | ||
} | ||
|
||
class BoolNode extends Node { | ||
final bool boolean; | ||
|
||
BoolNode(this.boolean); | ||
|
||
@override | ||
List<String> toYaml(YamlContext context) => ['$boolean']; | ||
} | ||
|
||
class StringNode extends Node { | ||
final String text; | ||
|
||
StringNode(this.text); | ||
|
||
@override | ||
List<String> toYaml(YamlContext context) { | ||
List<String> yamlLines = []; | ||
|
||
if (text.contains('\n')) { | ||
bool endsWithLineBreak = text.endsWith('\n'); | ||
|
||
List<String> lines; | ||
if (endsWithLineBreak) { | ||
yamlLines.add('|'); | ||
lines = text.substring(0, text.length - 1).split('\n'); | ||
} else { | ||
yamlLines.add('|-'); | ||
lines = text.split('\n'); | ||
} | ||
|
||
for (int index = 0; index < lines.length; index++) { | ||
yamlLines.add(lines[index]); | ||
} | ||
} else { | ||
var containsSingleQuote = text.contains("'"); | ||
|
||
if (context.allowUnquotedStrings && | ||
!containsSingleQuote && | ||
_isValidUnquotedString(text)) { | ||
yamlLines.add(text); | ||
} else if (!containsSingleQuote) { | ||
yamlLines.add('\'$text\''); | ||
} else { | ||
var str = text.replaceAll('\\', '\\\\').replaceAll('"', '\\"'); | ||
yamlLines.add('"$str"'); | ||
} | ||
} | ||
|
||
return yamlLines; | ||
} | ||
|
||
static final _regexpInvalidUnquotedChars = RegExp( | ||
r'[^0-9a-zA-ZàèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝâêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿÄËÏÖÜŸçÇßØøÅåÆæœ@/. \t-]'); | ||
|
||
bool _isValidUnquotedString(String s) => | ||
!_regexpInvalidUnquotedChars.hasMatch(s) && | ||
!s.startsWith('@') && | ||
!s.startsWith('-'); | ||
} | ||
|
||
class ListNode extends Node { | ||
final List<Node> subnodes; | ||
|
||
ListNode(this.subnodes); | ||
|
||
@override | ||
bool get requiresNewLine => subnodes.isNotEmpty; | ||
|
||
@override | ||
List<String> toYaml(YamlContext context) { | ||
if (subnodes.isEmpty) { | ||
return ['[]']; | ||
} | ||
|
||
final List<String> lines = []; | ||
|
||
for (final node in subnodes) { | ||
final nodeYaml = node.toYaml(context); | ||
|
||
final firstIndent = "-${' ' * max(1, context.indentSize - 1)}"; | ||
final subsequentIndent = ' ' * firstIndent.length; | ||
|
||
lines.add("$firstIndent${nodeYaml.first}"); | ||
for (int i = 1; i < nodeYaml.length; i++) { | ||
lines.add("$subsequentIndent${nodeYaml[i]}"); | ||
} | ||
} | ||
|
||
return lines; | ||
} | ||
} | ||
|
||
class MapNode extends Node { | ||
final Map<String, Node> subnodesMap; | ||
|
||
MapNode(this.subnodesMap); | ||
|
||
@override | ||
bool get requiresNewLine => subnodesMap.isNotEmpty; | ||
|
||
@override | ||
List<String> toYaml(YamlContext context) { | ||
if (subnodesMap.isEmpty) { | ||
return ['{}']; | ||
} | ||
|
||
final List<String> lines = []; | ||
|
||
for (final entry in subnodesMap.entries) { | ||
final key = entry.key; | ||
final node = entry.value; | ||
|
||
final nodeYaml = node.toYaml(context); | ||
|
||
final indent = ' ' * context.indentSize; | ||
|
||
if (node.requiresNewLine) { | ||
lines.add("$key: "); | ||
for (final line in nodeYaml) { | ||
lines.add("$indent$line"); | ||
} | ||
} else { | ||
lines.add("$key: ${nodeYaml.first}"); | ||
for (int i = 1; i < nodeYaml.length; i++) { | ||
lines.add("$indent${nodeYaml[i]}"); | ||
} | ||
} | ||
} | ||
|
||
return lines; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
class YamlContext { | ||
final int indentSize; | ||
|
||
final bool allowUnquotedStrings; | ||
|
||
YamlContext({ | ||
required this.indentSize, | ||
required this.allowUnquotedStrings, | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import 'dart:convert'; | ||
import 'dart:math'; | ||
|
||
import 'package:yaml_writer/src/node.dart'; | ||
|
||
import 'yaml_context.dart'; | ||
|
||
@Deprecated('Use YamlWriter.') | ||
typedef YAMLWriter = YamlWriter; | ||
|
||
/// YAML Writer. | ||
class YamlWriter extends Converter<Object?, String> { | ||
static dynamic _defaultToEncodable(dynamic object) => object.toJson(); | ||
|
||
/// The indentation size. | ||
/// | ||
/// Must be greater or equal to `1`. | ||
/// | ||
/// Defaults to `2`. | ||
final int indentSize; | ||
|
||
/// If `true` it will allow unquoted strings. | ||
final bool allowUnquotedStrings; | ||
|
||
/// Used to convert objects to an encodable version. | ||
final Object? Function(dynamic object) toEncodable; | ||
|
||
YamlWriter({ | ||
int indentSize = 2, | ||
this.allowUnquotedStrings = false, | ||
Object? Function(dynamic object)? toEncodable, | ||
}) : indentSize = max(1, indentSize), | ||
toEncodable = toEncodable ?? _defaultToEncodable; | ||
|
||
/// Converts [input] to an YAML document as [String]. | ||
/// | ||
/// This implements `dart:convert` [Converter]. | ||
/// | ||
/// - Calls [write]. | ||
@override | ||
String convert(Object? input) => write(input); | ||
|
||
/// Writes [object] to an YAML document as [String]. | ||
String write(Object? object) { | ||
final node = _parseNode(object); | ||
final context = YamlContext( | ||
indentSize: indentSize, | ||
allowUnquotedStrings: allowUnquotedStrings, | ||
); | ||
final yaml = node.toYaml(context); | ||
return '${yaml.join('\n')}\n'; | ||
} | ||
|
||
Node _parseNode(Object? object) { | ||
if (object == null) { | ||
return NullNode(); | ||
} else if (object is num) { | ||
return NumNode(object); | ||
} else if (object is bool) { | ||
return BoolNode(object); | ||
} else if (object is String) { | ||
return StringNode(object); | ||
} else if (object is List) { | ||
return ListNode(object.map(_parseNode).toList()); | ||
} else if (object is Map) { | ||
return MapNode(object.map((k, v) => MapEntry(k, _parseNode(v)))); | ||
} else { | ||
return _parseNode(toEncodable(object)); | ||
} | ||
} | ||
} |
Oops, something went wrong.