Skip to content

Instantly share code, notes, and snippets.

@masterashu
Last active March 28, 2020 06:23
Show Gist options
  • Save masterashu/56841a48fa132e59ba003a68e3a1e7f2 to your computer and use it in GitHub Desktop.
Save masterashu/56841a48fa132e59ba003a68e3a1e7f2 to your computer and use it in GitHub Desktop.
Mini YAML Writer utility
getObjects() sync* {
var obj = Map<String, dynamic>();
obj['Hello'] = 'World';
obj['Users'] = [
'192.168.13.25',
'192.168.1.3',
<String>['192.168.13.25', '192.168.1.3', '192.168.1.3']
];
obj['Home'] = <String>['localhost', '127.0.0.1'];
yield obj;
var obj2 = Map<String, dynamic>();
obj2['Hello'] = 'World';
obj2['Users'] = [
'192.168.13.25',
'192.168.1.3',
Map<String, dynamic>.fromIterables(["ABCD", "EFGH"], [132, 3353.3]),
];
obj2['Home'] = <String>['localhost', '127.0.0.1'];
yield obj2;
var obj3 = Map<dynamic, dynamic>();
obj3[<String>['localhost', '127.0.0.1']] = 'World';
obj3[<String>['192.168.13.25', '192.168.1.3', '192.168.1.3']] = [
<String>['192.168.13.25', '192.168.1.3', '192.168.1.3'],
];
obj3[Map<dynamic, dynamic>.fromIterables([
["ABCD", "EFGH"]
], [
[132, 3353.3]
])] = ['localhost', '127.0.0.1', 1324, 4636343];
yield obj3;
}
class YamlWriter {
static bool isScalar(node) => (node is String || node is num || node is bool);
/// Maximum no. of columns before line break occurs
final int maxColumns;
/// Maximum length of colums when scalar is printed in folding
final int foldingColumnLength;
/// Output File stream where to write
StringBuffer outputStream;
/// List of indentations
var _indents = <int>[0];
/// current starting indentation
int get indent => _indents.last;
/// Increases the indentation of the writer
void addIndent() => _indents.add(indent + 2);
/// Decreses the indentation of the writer
void popIndent() => _indents.removeLast();
/// Resets the indentation to Zero
void resetIndent() => _indents.removeWhere((n) => n > 0);
/// Position of current writing column
var column = 0;
/// write an Object to the output stream
void writeDocument(Object document) {
resetIndent();
writeDocumentStart();
if (document is String || document is num || document is bool) {
writeScalar(document);
} else {
writeNode(document);
}
resetIndent();
writeDocumentEnd();
}
void writeMap(Map map) {
for (var key in map.keys) {
bool complexKey = false;
writeIndent();
if (isScalar(key)) {
writeScalar(key);
} else if (key is Map) {
writeKeyEntry();
writeMap(key);
complexKey = true;
} else if (key is List) {
writeKeyEntry();
writeList(key);
complexKey = true;
} else {
print('Unknown type of object');
}
if (complexKey) popIndent();
writeValueEntry(complexKey);
var value = map[key];
if (isScalar(value)) {
writeScalar(value);
} else if (value is Map) {
writeNewLine();
writeMap(value);
} else if (value is List) {
writeNewLine();
writeList(value);
} else {
print('Unknown type of object');
}
popIndent();
if (column != 0) writeNewLine();
}
}
void writeList(List list) {
for (var node in list) {
writeIndent();
writeListEntry();
if (isScalar(node)) {
writeScalar(node);
} else if (node is Map) {
addIndent();
writeNewLine();
writeMap(node);
popIndent();
} else if (node is List) {
addIndent();
writeNewLine();
writeList(node);
popIndent();
} else {
print('Unknown type of object');
}
if (column != 0) writeNewLine();
}
}
void writeNode(Object node) {
if (node is Map) {
writeMap(node);
} else if (node is List) {
writeList(node);
} else {
writeScalar(node);
}
}
void writeScalar(Object scalar) {
var value = scalar.toString();
if (value.length > foldingColumnLength) {
return writeBlockScalar(scalar);
}
if (value.contains(RegExp(r'(\r)|(\n)|(\r\n)'))) {
if (value.length <= foldingColumnLength) {
return writeQuotedScalar(scalar);
} else {
return writeBlockScalar(scalar);
}
}
outputStream.write('$value');
column += value.length;
}
void writeBlockScalar(Object scalar, [bool folded = true]) {
outputStream.write('${folded ? '>' : '|'}');
writeNewLine();
writeIndent();
bool lastCR = false;
var buffer = StringBuffer();
for (var ch in scalar.toString().codeUnits) {
if (lastCR) {
lastCR = false;
continue;
}
switch (ch) {
case CharCode.CR:
lastCR = true;
continue;
case CharCode.LF:
if (lastCR) break;
var word = buffer.toString();
buffer.clear();
if (folded) {
if (column - indent + word.length > foldingColumnLength) {
writeNewLine();
writeIndent();
}
outputStream.write(word);
writeNewLine();
writeIndent();
outputStream.write(' ');
} else {
outputStream.write(word);
writeNewLine();
writeIndent();
}
break;
case CharCode.SP:
var word = buffer.toString();
buffer.clear();
if (folded && (column - indent + word.length > foldingColumnLength)) {
writeNewLine();
writeIndent();
}
outputStream.write(word + ' ');
break;
default:
buffer.write(String.fromCharCode(ch));
}
column++;
}
if (buffer.isNotEmpty) outputStream.write(buffer.toString());
}
writeQuotedScalar(Object scalar, [bool single = false]) {
bool lastCR = false;
outputStream.write('${single ? "'" : '"'}');
for (var ch in scalar.toString().codeUnits) {
if (lastCR) {
lastCR = false;
continue;
}
switch (ch) {
case CharCode.CR:
lastCR = true;
continue;
case CharCode.LF:
outputStream.write('\\n');
break;
case CharCode.TAB:
outputStream.write('\\t');
break;
case CharCode.SINGLE_QUOTE:
outputStream.write('${single ? "''" : "'"}');
break;
case CharCode.DOUBLE_QUOTE:
outputStream.write('${single ? '"' : '\"'}');
break;
default:
outputStream.write(String.fromCharCode(ch));
}
column++;
}
outputStream.write('${single ? "'" : '"'}');
}
void writeIndent() {
outputStream.write(' ' * indent);
column += indent;
}
void writeListEntry() {
outputStream.write('- ');
column += 2;
}
void writeKeyEntry() {
outputStream.write('? ');
addIndent();
writeNewLine();
}
void writeValueEntry([bool complexKey = false]) {
if (complexKey) writeIndent();
outputStream.write(': ');
column += 2;
addIndent();
}
void writeNewLine() {
outputStream.write('\n');
column = 0;
}
void writeDocumentStart() {
outputStream.write('---');
writeNewLine();
}
void writeDocumentEnd() {
outputStream.write('---');
writeNewLine();
}
YamlWriter({
this.outputStream,
this.maxColumns = 80,
this.foldingColumnLength = 40,
}) {
assert(outputStream != null);
}
}
class CharCode {
static const TAB = 0x9;
static const LF = 0xA;
static const CR = 0xD;
static const SP = 0x20;
static const DOLLAR = 0x24;
static const LEFT_PAREN = 0x28;
static const RIGHT_PAREN = 0x29;
static const PLUS = 0x2B;
static const COMMA = 0x2C;
static const HYPHEN = 0x2D;
static const PERIOD = 0x2E;
static const QUESTION = 0x3F;
static const COLON = 0x3A;
static const SEMICOLON = 0x3B;
static const EQUALS = 0x3D;
static const LEFT_SQUARE = 0x5B;
static const RIGHT_SQUARE = 0x5D;
static const LEFT_CURLY = 0x7B;
static const RIGHT_CURLY = 0x7D;
static const HASH = 0x23;
static const AMPERSAND = 0x26;
static const ASTERISK = 0x2A;
static const EXCLAMATION = 0x21;
static const VERTICAL_BAR = 0x7C;
static const LEFT_ANGLE = 0x3C;
static const RIGHT_ANGLE = 0x3E;
static const SINGLE_QUOTE = 0x27;
static const DOUBLE_QUOTE = 0x22;
static const PERCENT = 0x25;
static const AT = 0x40;
static const GRAVE_ACCENT = 0x60;
static const TILDE = 0x7E;
static const NULL = 0x0;
static const BELL = 0x7;
static const BACKSPACE = 0x8;
static const VERTICAL_TAB = 0xB;
static const FORM_FEED = 0xC;
static const ESCAPE = 0x1B;
static const SLASH = 0x2F;
static const BACKSLASH = 0x5C;
static const UNDERSCORE = 0x5F;
static const NEL = 0x85;
static const NBSP = 0xA0;
static const LINE_SEPARATOR = 0x2028;
static const PARAGRAPH_SEPARATOR = 0x2029;
static const BOM = 0xFEFF;
static const NUMBER_0 = 0x30;
static const NUMBER_9 = 0x39;
static const LETTER_A = 0x61;
static const LETTER_B = 0x62;
static const LETTER_E = 0x65;
static const LETTER_F = 0x66;
static const LETTER_N = 0x6E;
static const LETTER_R = 0x72;
static const LETTER_T = 0x74;
static const LETTER_U = 0x75;
static const LETTER_V = 0x76;
static const LETTER_X = 0x78;
static const LETTER_Z = 0x7A;
static const LETTER_CAP_A = 0x41;
static const LETTER_CAP_F = 0x46;
static const LETTER_CAP_L = 0x4C;
static const LETTER_CAP_N = 0x4E;
static const LETTER_CAP_P = 0x50;
static const LETTER_CAP_U = 0x55;
static const LETTER_CAP_X = 0x58;
static const LETTER_CAP_Z = 0x5A;
}
main(List<String> args) {
var buffer = StringBuffer();
var yamlWriter = YamlWriter(outputStream: buffer);
for (var object in getObjects()) {
yamlWriter.writeDocument(object);
}
print(buffer.toString());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment