Dan Cojocaru
4 years ago
commit
fc03ea8820
9 changed files with 497 additions and 0 deletions
@ -0,0 +1,9 @@
|
||||
# Files and directories created by pub. |
||||
.dart_tool/ |
||||
.packages |
||||
|
||||
# Conventional directory for build output. |
||||
build/ |
||||
|
||||
# VS Code |
||||
.vscode |
@ -0,0 +1,16 @@
|
||||
# Defines a default set of lint rules enforced for projects at Google. For |
||||
# details and rationale, see |
||||
# https://github.com/dart-lang/pedantic#enabled-lints. |
||||
|
||||
include: package:pedantic/analysis_options.yaml |
||||
|
||||
# For lint rules and documentation, see http://dart-lang.github.io/linter/lints. |
||||
|
||||
# Uncomment to specify additional rules. |
||||
# linter: |
||||
# rules: |
||||
# - camel_case_types |
||||
|
||||
# analyzer: |
||||
# exclude: |
||||
# - path/to/excluded/files/** |
@ -0,0 +1,138 @@
|
||||
const String ESC = '\x1b'; |
||||
const String CSI = '$ESC['; |
||||
|
||||
class TextFormat { |
||||
final List<TextFormat> children; |
||||
|
||||
const TextFormat({required this.children}); |
||||
|
||||
@override |
||||
String toString() { |
||||
return children.join(''); |
||||
} |
||||
} |
||||
|
||||
class ANSIColorText extends TextFormat { |
||||
final ANSIColor? foreground; |
||||
final ANSIColor? background; |
||||
|
||||
ANSIColorText({this.foreground, this.background, required List<TextFormat> children}) : super(children: children); |
||||
|
||||
@override |
||||
String toString() { |
||||
var result = ''; |
||||
for (final child in children) { |
||||
result += foreground?.enabler ?? ''; |
||||
result += background?.backgroundEnabler ?? ''; |
||||
result += child.toString(); |
||||
} |
||||
result += foreground?.disabler ?? ''; |
||||
result += background?.backgroundDisabler ?? ''; |
||||
return result; |
||||
} |
||||
} |
||||
|
||||
class ANSIFormat extends TextFormat { |
||||
final bool? bold; |
||||
final bool? italic; |
||||
final bool? underline; |
||||
final bool? reverseColor; |
||||
|
||||
ANSIFormat({this.bold, this.italic, this.underline, this.reverseColor, required List<TextFormat> children}) : super(children: children); |
||||
|
||||
@override |
||||
String toString() { |
||||
var result = ''; |
||||
for (final child in children) { |
||||
if (bold != null) { |
||||
result += bold! ? ANSIBold().enabler : ANSIBold().disabler; |
||||
} |
||||
if (italic != null) { |
||||
result += italic! ? ANSIItalic().enabler : ANSIItalic().disabler; |
||||
} |
||||
if (underline != null) { |
||||
result += underline! ? ANSIUnderline().enabler : ANSIUnderline().disabler; |
||||
} |
||||
if (reverseColor != null) { |
||||
result += reverseColor! ? ANSIReverseVideo().enabler : ANSIReverseVideo().disabler; |
||||
} |
||||
result += child.toString(); |
||||
} |
||||
if (bold != null) { |
||||
result += bold! ? ANSIBold().disabler : ANSIBold().enabler; |
||||
} |
||||
if (italic != null) { |
||||
result += italic! ? ANSIItalic().disabler : ANSIItalic().enabler; |
||||
} |
||||
if (underline != null) { |
||||
result += underline! ? ANSIUnderline().disabler : ANSIUnderline().enabler; |
||||
} |
||||
if (reverseColor != null) { |
||||
result += reverseColor! ? ANSIReverseVideo().disabler : ANSIReverseVideo().enabler; |
||||
} |
||||
return result; |
||||
} |
||||
} |
||||
|
||||
class Text extends TextFormat { |
||||
final String value; |
||||
const Text(this.value) : super(children: const []); |
||||
@override |
||||
String toString() { |
||||
return value; |
||||
} |
||||
} |
||||
|
||||
class ANSIEscape { |
||||
final String enabler; |
||||
final String disabler; |
||||
|
||||
const ANSIEscape({required this.enabler, required this.disabler}); |
||||
} |
||||
|
||||
class ANSIBold extends ANSIEscape { |
||||
const ANSIBold() : super(enabler: '${CSI}1m', disabler: '${CSI}22m'); |
||||
} |
||||
|
||||
class ANSIItalic extends ANSIEscape { |
||||
const ANSIItalic() : super(enabler: '${CSI}3m', disabler: '${CSI}23m'); |
||||
} |
||||
|
||||
class ANSIUnderline extends ANSIEscape { |
||||
const ANSIUnderline() : super(enabler: '${CSI}4m', disabler: '${CSI}24m'); |
||||
} |
||||
|
||||
class ANSIReverseVideo extends ANSIEscape { |
||||
const ANSIReverseVideo() : super(enabler: '${CSI}7m', disabler: '${CSI}27m'); |
||||
} |
||||
|
||||
class ANSIColor extends ANSIEscape { |
||||
const ANSIColor.index(int index, [bool bright = false]) : super(enabler: '$CSI${bright ? 9 : 3}${index}m', disabler: '${CSI}39m'); |
||||
const ANSIColor.value(String value) : super(enabler: '$CSI${value}m', disabler: '${CSI}39m'); |
||||
String get backgroundEnabler { |
||||
final noCSI = enabler.substring(CSI.length); |
||||
final nom = noCSI.substring(0, noCSI.length - 1); |
||||
final number = int.parse(nom); |
||||
return '$CSI${number + 10}m'; |
||||
} |
||||
final String backgroundDisabler = '${CSI}49m'; |
||||
} |
||||
|
||||
class ANSIColors { |
||||
static const ANSIColor black = ANSIColor.index(0); |
||||
static const ANSIColor red = ANSIColor.index(1); |
||||
static const ANSIColor green = ANSIColor.index(2); |
||||
static const ANSIColor yellow = ANSIColor.index(3); |
||||
static const ANSIColor blue = ANSIColor.index(4); |
||||
static const ANSIColor magenta = ANSIColor.index(5); |
||||
static const ANSIColor cyan = ANSIColor.index(6); |
||||
static const ANSIColor white = ANSIColor.index(7); |
||||
static const ANSIColor brightBlack = ANSIColor.index(0, true); |
||||
static const ANSIColor brightRed = ANSIColor.index(1, true); |
||||
static const ANSIColor brightGreen = ANSIColor.index(2, true); |
||||
static const ANSIColor brightYellow = ANSIColor.index(3, true); |
||||
static const ANSIColor brightBlue = ANSIColor.index(4, true); |
||||
static const ANSIColor brightMagenta = ANSIColor.index(5, true); |
||||
static const ANSIColor brightCyan = ANSIColor.index(6, true); |
||||
static const ANSIColor brightWhite = ANSIColor.index(7, true); |
||||
} |
@ -0,0 +1,105 @@
|
||||
import 'dart:convert'; |
||||
import 'dart:io'; |
||||
|
||||
import 'package:args/args.dart'; |
||||
import 'package:json_path/json_path.dart'; |
||||
import 'package:rfc_6901/rfc_6901.dart'; |
||||
|
||||
import 'ppjson.dart'; |
||||
|
||||
void main(List<String> arguments) { |
||||
final parser = ArgParser() |
||||
..addOption('file', abbr: 'f', help: 'Read JSON from file instead of stdin', valueHelp: 'filename') |
||||
..addOption('input', help: 'Read input as parameter instead of stdin', valueHelp: 'json input') |
||||
..addOption('jsonpath', aliases: ['path'], help: 'Display only the matches of the JSON document', valueHelp: 'JSONPath query') |
||||
..addOption('jsonpointer', aliases: ['pointer'], abbr: 'p', help: 'Display only the matches of the JSON pointer') |
||||
..addOption('indent', abbr: 'i', help: 'Set space indentation level (prefix with t for tab indentation)', defaultsTo: '2') |
||||
..addOption('max-depth', abbr: 'm', help: 'Specify maximum nesting before stopping printing'); |
||||
// ..addFlag('force-color', help: "Output using colors even when the environment doesn't allow them", hide: true); |
||||
final ArgResults parsedArgs; |
||||
try { |
||||
parsedArgs = parser.parse(arguments); |
||||
} |
||||
on ArgParserException catch(e) { |
||||
print(e.message); |
||||
print(''); |
||||
print(parser.usage); |
||||
exit(1); |
||||
} |
||||
|
||||
// Get indent |
||||
final indentArg = parsedArgs['indent'] as String; |
||||
final indent = indentArg[0] == 't' ? '\t' * int.parse(indentArg.substring(1)) : ' ' * int.parse(indentArg); |
||||
|
||||
// Get max depth |
||||
final maxDepthArg = parsedArgs['max-depth'] as String?; |
||||
final maxDepth = int.tryParse(maxDepthArg ?? ''); |
||||
|
||||
// Get JSONPath |
||||
final jsonPath = parsedArgs['jsonpath'] as String?; |
||||
|
||||
// Get JSON Pointer |
||||
final jsonPointer = parsedArgs['jsonpointer'] as String?; |
||||
|
||||
// Read |
||||
String jsonInput; |
||||
if (parsedArgs['file'] != null) { |
||||
jsonInput = File(parsedArgs['file']).readAsStringSync(); |
||||
} |
||||
else if (parsedArgs['input'] != null) { |
||||
jsonInput = parsedArgs['input']; |
||||
} |
||||
else { |
||||
jsonInput = ''; |
||||
while (true) { |
||||
final line = stdin.readLineSync(retainNewlines: true); |
||||
if (line != null) { |
||||
jsonInput += line; |
||||
} |
||||
else { |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Parse |
||||
final decodedData; |
||||
try { |
||||
decodedData = jsonDecode(jsonInput); |
||||
} |
||||
catch(_) { |
||||
stderr.writeln('Unable to parse JSON input.'); |
||||
exit(1); |
||||
} |
||||
final List<dynamic> data; |
||||
try { |
||||
data = jsonPath != null ? followJsonPath(decodedData, jsonPath) : jsonPointer != null ? followJsonPointer(decodedData, jsonPointer) : [decodedData]; |
||||
} |
||||
catch (e) { |
||||
stderr.writeln('JSONPath Error: $e'); |
||||
exit(1); |
||||
} |
||||
|
||||
// Print |
||||
if (data.isEmpty) { |
||||
stderr.writeln('No match'); |
||||
} |
||||
else if (data.length == 1) { |
||||
stdout.write(prettyPrintJson(data[0], indent, maxDepth)); |
||||
} |
||||
else { |
||||
for (final match in data) { |
||||
print(prettyPrintJson(match, indent, maxDepth)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
List<dynamic> followJsonPath(dynamic object, String jsonPath) { |
||||
final path = JsonPath(jsonPath); |
||||
return path.read(object).map((e) => e.value).toList(growable: false); |
||||
} |
||||
|
||||
List<dynamic> followJsonPointer(dynamic object, String jsonPointer) { |
||||
final pointer = JsonPointer(jsonPointer); |
||||
return [pointer.read(object)]; |
||||
} |
@ -0,0 +1,158 @@
|
||||
import 'ansi_format.dart'; |
||||
|
||||
String prettyPrintJson(dynamic object, String indent, [int? maxDepth]) { |
||||
var result = ''; |
||||
final stack = <_PrettyPrintStackObject>[]; |
||||
stack.add(_PrettyPrintStackObject(object, 0)); |
||||
while (stack.isNotEmpty) { |
||||
final elem = stack.removeLast(); |
||||
final obj = elem.object is _PrettyPrintTrailingComma ? elem.object.object : elem.object; |
||||
final trailingComma = elem.object is _PrettyPrintTrailingComma; |
||||
var suppressTrailingComma = false; |
||||
|
||||
result += indent * elem.level; |
||||
String formatObject(dynamic obj) { |
||||
if (obj is Map) { |
||||
String startStr; |
||||
if (obj.isEmpty) { |
||||
startStr = '{}'; |
||||
} |
||||
else if (elem.level == maxDepth) { |
||||
startStr = '{...}'; |
||||
} |
||||
else { |
||||
startStr = '{'; |
||||
suppressTrailingComma = true; |
||||
} |
||||
final thisResult = ANSIColorText( |
||||
foreground: ANSIColors.blue, |
||||
children: [Text(startStr),], |
||||
).toString(); |
||||
if (elem.level != maxDepth && obj.isNotEmpty) { |
||||
stack.add(_PrettyPrintStackObject(trailingComma ? _PrettyPrintTrailingComma(_PrettyPrintMapEnd()) : _PrettyPrintMapEnd(), elem.level)); |
||||
final entries = obj.entries; |
||||
stack.add(_PrettyPrintStackObject(entries.last, elem.level + 1)); |
||||
stack.addAll(entries.toList(growable: false).reversed.skip(1).map((e) => _PrettyPrintStackObject(_PrettyPrintTrailingComma(e), elem.level + 1))); |
||||
} |
||||
return thisResult; |
||||
} |
||||
else if (obj is List) { |
||||
String startStr; |
||||
if (obj.isEmpty) { |
||||
startStr = '[]'; |
||||
} |
||||
else if (elem.level == maxDepth) { |
||||
startStr = '[...]'; |
||||
} |
||||
else { |
||||
startStr = '['; |
||||
suppressTrailingComma = true; |
||||
} |
||||
final thisResult = ANSIColorText( |
||||
foreground: ANSIColors.cyan, |
||||
children: [Text(startStr),], |
||||
).toString(); |
||||
if (elem.level != maxDepth && obj.isNotEmpty) { |
||||
stack.add(_PrettyPrintStackObject(trailingComma ? _PrettyPrintTrailingComma(_PrettyPrintArrayEnd()) : _PrettyPrintArrayEnd(), elem.level)); |
||||
stack.add(_PrettyPrintStackObject(obj.last, elem.level + 1)); |
||||
stack.addAll(obj.reversed.skip(1).map((o) => _PrettyPrintStackObject(_PrettyPrintTrailingComma(o), elem.level + 1))); |
||||
} |
||||
return thisResult; |
||||
} |
||||
else if (obj is MapEntry) { |
||||
return TextFormat( |
||||
children: [ |
||||
ANSIColorText( |
||||
foreground: ANSIColors.brightYellow, |
||||
children: [Text('"${obj.key}"'),] |
||||
), |
||||
Text(': '), |
||||
Text(formatObject(obj.value)), |
||||
], |
||||
).toString(); |
||||
} |
||||
else if (obj is String) { |
||||
return ANSIColorText( |
||||
foreground: ANSIColors.magenta, |
||||
children: [Text('"$obj"'),], |
||||
).toString(); |
||||
} |
||||
else if (obj is double || obj is int) { |
||||
return ANSIColorText( |
||||
foreground: ANSIColors.brightCyan, |
||||
children: [Text(obj.toString()),], |
||||
).toString(); |
||||
} |
||||
else if (obj == true) { |
||||
return ANSIFormat( |
||||
underline: true, |
||||
children: [ |
||||
ANSIColorText( |
||||
foreground: ANSIColors.green, |
||||
children: [Text(obj.toString()),], |
||||
), |
||||
], |
||||
).toString(); |
||||
} |
||||
else if (obj == false) { |
||||
return ANSIFormat( |
||||
underline: true, |
||||
children: [ |
||||
ANSIColorText( |
||||
foreground: ANSIColors.red, |
||||
children: [Text(obj.toString()),], |
||||
), |
||||
], |
||||
).toString(); |
||||
} |
||||
else if (obj == null) { |
||||
return ANSIFormat( |
||||
underline: true, |
||||
children: [ |
||||
ANSIColorText( |
||||
foreground: ANSIColors.brightBlue, |
||||
children: [Text('null'),], |
||||
), |
||||
], |
||||
).toString(); |
||||
} |
||||
else if (obj is _PrettyPrintMapEnd) { |
||||
return ANSIColorText( |
||||
foreground: ANSIColors.blue, |
||||
children: [Text('}'),], |
||||
).toString(); |
||||
} |
||||
else if (obj is _PrettyPrintArrayEnd) { |
||||
return ANSIColorText( |
||||
foreground: ANSIColors.cyan, |
||||
children: [Text(']'),], |
||||
).toString(); |
||||
} |
||||
else { |
||||
throw Exception('Unexpected object of unknown type: $obj ${obj.runtimeType}'); |
||||
} |
||||
} |
||||
result += formatObject(obj); |
||||
|
||||
if (trailingComma && !suppressTrailingComma) { |
||||
result += ','; |
||||
} |
||||
|
||||
result += '\n'; |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
class _PrettyPrintStackObject { |
||||
final dynamic object; |
||||
final int level; |
||||
_PrettyPrintStackObject(this.object, this.level); |
||||
} |
||||
|
||||
class _PrettyPrintTrailingComma { |
||||
final dynamic object; |
||||
_PrettyPrintTrailingComma(this.object); |
||||
} |
||||
|
||||
class _PrettyPrintMapEnd {} |
||||
class _PrettyPrintArrayEnd {} |
@ -0,0 +1,47 @@
|
||||
# Generated by pub |
||||
# See https://dart.dev/tools/pub/glossary#lockfile |
||||
packages: |
||||
args: |
||||
dependency: "direct main" |
||||
description: |
||||
name: args |
||||
url: "https://pub.dartlang.org" |
||||
source: hosted |
||||
version: "2.2.0" |
||||
json_path: |
||||
dependency: "direct main" |
||||
description: |
||||
name: json_path |
||||
url: "https://pub.dartlang.org" |
||||
source: hosted |
||||
version: "0.3.0" |
||||
meta: |
||||
dependency: transitive |
||||
description: |
||||
name: meta |
||||
url: "https://pub.dartlang.org" |
||||
source: hosted |
||||
version: "1.7.0" |
||||
pedantic: |
||||
dependency: "direct dev" |
||||
description: |
||||
name: pedantic |
||||
url: "https://pub.dartlang.org" |
||||
source: hosted |
||||
version: "1.11.1" |
||||
petitparser: |
||||
dependency: transitive |
||||
description: |
||||
name: petitparser |
||||
url: "https://pub.dartlang.org" |
||||
source: hosted |
||||
version: "4.2.0" |
||||
rfc_6901: |
||||
dependency: "direct main" |
||||
description: |
||||
name: rfc_6901 |
||||
url: "https://pub.dartlang.org" |
||||
source: hosted |
||||
version: "0.1.0" |
||||
sdks: |
||||
dart: ">=2.13.0 <3.0.0" |
@ -0,0 +1,16 @@
|
||||
name: kennson |
||||
description: A simple command-line application. |
||||
version: 1.0.0 |
||||
# homepage: https://www.example.com |
||||
|
||||
environment: |
||||
sdk: '>=2.12.0 <3.0.0' |
||||
|
||||
dependencies: |
||||
args: ^2.2.0 |
||||
json_path: ^0.3.0 |
||||
rfc_6901: ^0.1.0 |
||||
# path: ^1.8.0 |
||||
|
||||
dev_dependencies: |
||||
pedantic: ^1.10.0 |
Loading…
Reference in new issue