Browse Source

First kennson version

master
Dan Cojocaru 4 years ago
commit
fc03ea8820
Signed by: kbruen
GPG Key ID: 818A889458EDC937
  1. 9
      .gitignore
  2. 5
      CHANGELOG.md
  3. 3
      README.md
  4. 16
      analysis_options.yaml
  5. 138
      bin/ansi_format.dart
  6. 105
      bin/kennson.dart
  7. 158
      bin/ppjson.dart
  8. 47
      pubspec.lock
  9. 16
      pubspec.yaml

9
.gitignore vendored

@ -0,0 +1,9 @@
# Files and directories created by pub.
.dart_tool/
.packages
# Conventional directory for build output.
build/
# VS Code
.vscode

5
CHANGELOG.md

@ -0,0 +1,5 @@
# Changelog
## 1.0.0
- Initial version.

3
README.md

@ -0,0 +1,3 @@
# kennson
A terminal utility to pretty print JSON.

16
analysis_options.yaml

@ -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/**

138
bin/ansi_format.dart

@ -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);
}

105
bin/kennson.dart

@ -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)];
}

158
bin/ppjson.dart

@ -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 {}

47
pubspec.lock

@ -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"

16
pubspec.yaml

@ -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…
Cancel
Save