Inital push

This commit is contained in:
leedongmyung[desktop] 2023-10-28 22:06:44 +09:00
commit 5e74ae06f3
16 changed files with 794 additions and 0 deletions

56
.gitignore vendored Normal file
View file

@ -0,0 +1,56 @@
# Created by https://www.toptal.com/developers/gitignore/api/dart,visualstudiocode
# Edit at https://www.toptal.com/developers/gitignore?templates=dart,visualstudiocode
### Dart ###
# See https://www.dartlang.org/guides/libraries/private-files
# Files and directories created by pub
.dart_tool/
.packages
# If you're building an application, you may want to check-in your pubspec.lock
pubspec.lock
# Directory created by dartdoc
# If you don't generate documentation locally you can remove this line.
doc/api/
release/
# dotenv environment variables file
.env*
# Avoid committing generated Javascript files:
*.dart.js
*.info.json # Produced by the --dump-info flag.
*.js # When generated by dart2js. Don't specify *.js if your
# project includes source files written in JavaScript.
*.js_
*.js.deps
*.js.map
.flutter-plugins
.flutter-plugins-dependencies
### Dart Patch ###
# dotenv environment variables file
.env
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
# End of https://www.toptal.com/developers/gitignore/api/dart,visualstudiocode

29
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,29 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "oto_cli_linux",
"request": "launch",
"type": "dart",
"program": "./bin/oto_cli.dart",
"args": ["stop"]
},
{
"name": "oto_cli_mac",
"request": "launch",
"type": "dart",
"program": "./bin/oto_cli.dart",
"args": ["install"]
},
{
"name": "oto_cli_win",
"request": "launch",
"type": "dart",
"program": "./bin/oto_cli.dart",
"args": ["uninstall"]
}
]
}

3
CHANGELOG.md Normal file
View file

@ -0,0 +1,3 @@
## 1.0.0
- Initial version.

2
README.md Normal file
View file

@ -0,0 +1,2 @@
A sample command-line application with an entrypoint in `bin/`, library code
in `lib/`, and example unit test in `test/`.

30
analysis_options.yaml Normal file
View file

@ -0,0 +1,30 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.
include: package:lints/recommended.yaml
# Uncomment the following section to specify additional rules.
# linter:
# rules:
# - camel_case_types
# analyzer:
# exclude:
# - path/to/excluded/files/**
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints
# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options

15
bin/oto_cli.dart Normal file
View file

@ -0,0 +1,15 @@
import 'package:oto_cli/cli/commands/command_install.dart';
import 'package:oto_cli/cli/commands/command_start.dart';
import 'package:oto_cli/cli/cli.dart';
import 'package:oto_cli/cli/commands/command_template.dart';
void main(List<String> arguments) async {
CLI.initialize('desktop_service', arguments, [
CommandTemplate(),
CommandInstall(),
CommandUninstall(),
CommandStart(),
CommandStop()
]);
}

261
lib/cli/cli.dart Normal file
View file

@ -0,0 +1,261 @@
import 'dart:async';
import 'dart:io';
import 'package:build_manager/application.dart';
import 'package:dart_framework/utils/system_util.dart';
import 'package:oto_cli/cli/commands/command_base.dart';
import 'package:oto_cli/cli/commands/command_manager.dart';
import 'package:path/path.dart' as path;
enum Color {
black,
red,
green,
yellow,
blue,
magenta,
cyan,
lightGray,
darkGray,
lightRed,
lightGreen,
lightYellow,
lightBlue,
lightMagenta,
lightCyan,
white
}
enum Style { bold, underline, dim, reverse }
class CLIConfig {
String serviceName;
List<String> arguments;
List<CommandBase> commands;
late String executableFileName;
CLIConfig(this.serviceName, this.arguments, this.commands) {
var fileName = path.basename(Platform.resolvedExecutable);
fileName = fileName.replaceAll(path.extension(fileName), '');
executableFileName = fileName;
}
}
class CLI {
static final _colorCodeMap = {
Color.black: "30",
Color.red: "31",
Color.green: "32",
Color.yellow: "33",
Color.blue: "34",
Color.magenta: "35",
Color.cyan: "36",
Color.lightGray: "37",
Color.darkGray: "90",
Color.lightRed: "91",
Color.lightGreen: "92",
Color.lightYellow: "93",
Color.lightBlue: "94",
Color.lightMagenta: "95",
Color.lightCyan: "96",
Color.white: "97"
};
static final _backgroundCodeMap = {
Color.black: "40",
Color.red: "41",
Color.green: "42",
Color.yellow: "43",
Color.blue: "44",
Color.magenta: "45",
Color.cyan: "46",
Color.lightGray: "47",
Color.darkGray: "100",
Color.lightRed: "101",
Color.lightGreen: "102",
Color.lightYellow: "103",
Color.lightBlue: "104",
Color.lightMagenta: "105",
Color.lightCyan: "106",
Color.white: "107"
};
static final _styleCodeMap = {
Style.bold: "1",
Style.dim: "2",
Style.underline: "4",
Style.reverse: "7"
};
static Printer get printer {
if (Platform.isWindows) {
return Printer.windows();
} else {
return Printer.unix();
}
}
CLI._privateConstructor();
static final CLI _instance = CLI._privateConstructor();
static CLI get current => _instance;
static void initialize(
String serviceName, List<String> arguments, List<CommandBase> commands) {
current.start(serviceName, arguments, commands);
}
static String style(String message,
{Color? color, Color? background, Style? style}) {
return printer.getStyle(message,
color: color, background: background, style: style);
}
static final commandNameSpace = 18;
static get serviceName => CLI.current.config.serviceName;
static get executableFileName => CLI.current.config.executableFileName;
static Future print(List<String> printList) async {
await printer.printCLI(printList);
return simpleFuture;
}
late CLIConfig config;
late CommandManager _commandManager;
void start(String serviceName, List<String> arguments,
List<CommandBase> commands) async {
config = CLIConfig(serviceName, arguments, commands);
_commandManager = CommandManager(config.commands);
//find command
String? command;
var args = config.arguments.toList();
if (arguments.isNotEmpty) {
for (var value in args) {
command = value;
break;
}
args.removeAt(0);
}
await _commandManager.execute(command, args);
}
}
//Printer Base
abstract class Printer {
Printer();
factory Printer.windows() => _PrinterWindows();
factory Printer.unix() => _PrinterUnix();
String getStyle(String message,
{Color? color, Color? background, Style? style}) {
var combine = '';
if (color != null) {
combine = CLI._colorCodeMap[color]!;
}
if (background != null) {
combine = combineText(combine, CLI._backgroundCodeMap[background]!);
}
if (style != null) {
combine = combineText(combine, CLI._styleCodeMap[style]!);
}
return combine;
}
String combineText(String combine, String item) {
if (combine == '') {
combine = item;
} else {
combine += ';$item';
}
return combine;
}
Future<File> getTempFile(String content, String extension) async {
var c = Completer<File>();
var now = DateTime.now();
var time =
'${now.hour}${now.minute}${now.second}${now.millisecond}${now.microsecond}';
var tempFile =
File(path.join(Directory.current.path, 'temp_$time.$extension'));
await tempFile.writeAsString(content);
c.complete(tempFile);
return c.future;
}
Future printCLI(List<String> printList) async {}
}
//Printer windows
class _PrinterWindows extends Printer {
@override
String getStyle(String message,
{Color? color, Color? background, Style? style}) {
var combine = super
.getStyle(message, color: color, background: background, style: style);
return '%ESC%[${combine}m$message%ESC%[0m';
}
@override
Future printCLI(List<String> printList) async {
var c = Completer();
StringBuffer message = StringBuffer();
message.writeln('@echo off');
message.writeln('');
message.writeln('setlocal');
message.writeln('call :setESC');
for (var item in printList) {
if (item.isEmpty) {
message.writeln('echo.');
} else {
item = item
.replaceAll('<', '^<')
.replaceAll('>', '^>')
// .replaceAll('[', '^[')
.replaceAll(']', '^]')
.replaceAll('|', '^|');
message.writeln('echo $item');
}
}
message.writeln(':setESC');
var temp = await getTempFile(message.toString(), 'bat');
await Process.run('cmd', ['/C', temp.path]).then((ProcessResult result) {
print(result.stdout);
});
await temp.delete();
c.complete();
return c.future;
}
}
//Printer mac/linux
class _PrinterUnix extends Printer {
@override
String getStyle(String message,
{Color? color, Color? background, Style? style}) {
var combine = super
.getStyle(message, color: color, background: background, style: style);
return '\\e[${combine}m$message\\e[0m';
}
@override
Future printCLI(List<String> printList) async {
var c = Completer();
StringBuffer message = StringBuffer();
message.writeln("echo -e '");
for (var item in printList) {
message.writeln(item);
}
message.write("'");
var temp = await getTempFile(message.toString(), 'sh');
await Process.run('zsh', [temp.path]).then((ProcessResult result) {
print(result.stdout);
});
await temp.delete();
c.complete();
return c.future;
}
}

View file

@ -0,0 +1,87 @@
import 'dart:async';
import 'package:oto_cli/cli/cli.dart';
enum PrintType { error, warning, information }
abstract class CommandBase {
Map<String, List<String>> get arguments => {};
String get name;
String get usage => '';
Future execute(List<String> parameters);
String getCommandDescription() {
return ' $name'.padRight(CLI.commandNameSpace) + getDescription();
}
String getDescription();
//==================== print ======================
final messageTypeMap = {
PrintType.error: {"color": Color.red, "message": "Error:"},
PrintType.warning: {"color": Color.yellow, "message": "Warning:"},
PrintType.information: {"color": Color.green, "message": "Information:"}
};
void printOut(String message, PrintType type) {
var color = messageTypeMap[type]!["color"]! as Color;
var typeStr = messageTypeMap[type]!["message"]! as String;
message = '${CLI.style(typeStr, background: color)} $message';
if (type == PrintType.error) {
printHelp(append: [message]);
} else {
CLI.print([message]);
}
}
void printWarning(String message) {
printHelp(append: [
'${CLI.style('Warning:', background: Color.yellow)} $message'
]);
}
void printInfo(String message) {
printHelp(
append: ['${CLI.style('Error:', background: Color.blue)} $message']);
}
void printHelp({List<String>? append}) {
var exeName = CLI.executableFileName;
final argumentTag = '{ARGUMENTS}';
var list = [
'',
getDescription(),
'',
'Usage: ${CLI.style('$exeName $name $usage', color: Color.lightGreen)}',
argumentTag,
];
var index = list.indexOf(argumentTag);
list.removeAt(index);
if (arguments.isNotEmpty) {
List<String> argsDescs = ['', 'Available arguments:'];
for (var item in arguments.entries) {
var addedArg = false;
for (var argDesc in item.value) {
if (!addedArg) {
argDesc = ' ${item.key}'.padRight(CLI.commandNameSpace) + argDesc;
addedArg = true;
} else {
argDesc = ' '.padRight(CLI.commandNameSpace) + argDesc;
}
argsDescs.add(argDesc);
}
}
argsDescs.add('');
list.insertAll(index, argsDescs);
}
if (append != null) {
list.insertAll(0, append);
list.insert(0, '');
}
CLI.print(list);
}
}

View file

@ -0,0 +1,55 @@
import 'dart:async';
import 'dart:io';
import 'package:dart_framework/utils/os_startup.dart';
import 'package:oto_cli/cli/cli.dart';
import 'package:oto_cli/cli/commands/command_base.dart';
class CommandInstall extends CommandBase {
@override
String get name => 'install';
@override
Future execute(List<String> parameters) async {
var c = Completer();
var label = CLI.serviceName;
if (!await OSStartup.isRegistedStartup(label)) {
await OSStartup.registStartup(Platform.resolvedExecutable, label,
arguments: ['start']);
printOut('Installed successfully.', PrintType.information);
} else {
printOut('Installation failed because it is already installed.',
PrintType.error);
}
return c.future;
}
@override
String getDescription() {
var serviceName = CLI.current.config.serviceName;
return 'Install "$serviceName" to run in the background as a startup program.';
}
}
class CommandUninstall extends CommandBase {
@override
String get name => 'uninstall';
@override
Future execute(List<String> parameters) async {
var c = Completer<bool>();
var label = CLI.serviceName;
if (await OSStartup.isRegistedStartup(label)) {
await OSStartup.unregistStartup(label);
printOut('Uninstalled successfully.', PrintType.information);
} else {
printOut('It is already uninstalled.', PrintType.warning);
}
return c.future;
}
@override
String getDescription() {
return 'Delete it from Startup.';
}
}

View file

@ -0,0 +1,90 @@
import 'dart:async';
import 'package:dart_framework/utils/system_util.dart';
import 'command_base.dart';
import 'package:oto_cli/cli/cli.dart';
import 'package:path/path.dart' as path;
class CommandManager {
Map<String, CommandBase> commandMap = {};
CommandManager(List<CommandBase> commands) {
for (var command in commands) {
commandMap[command.name] = command;
}
}
Future execute(String? command, List<String> parameters) async {
if (command == null) {
printHelp();
} else {
if (commandMap.containsKey(command)) {
var argStr = '';
for (var arg in parameters) {
argStr += '$arg, ';
}
var printHelp = false;
var commandItem = commandMap[command]!;
if (argStr.isNotEmpty) {
if (parameters[0].contains('-h')) {
commandItem.printHelp();
printHelp = true;
} else {
argStr = argStr.replaceFirst(', ', '', argStr.length - 3);
argStr = ', Arguments: [$argStr]';
}
}
if (!printHelp) {
await CLI.print([
CLI.style('Execute command: $command$argStr', color: Color.green)
]);
await commandItem.execute(parameters);
}
} else {
printHelp(append: [
'${CLI.style('Error:', background: Color.red)} The ${CLI.style('"$command"', color: Color.red, style: Style.bold)} command does not exist.'
]);
}
}
return simpleFuture;
}
void printHelp({List<String>? append}) {
var serviceName = CLI.current.config.serviceName;
var exeName = CLI.executableFileName;
final commandTag = '{COMMAND}';
var list = [
'',
'A command-line $serviceName.',
'',
'Usage: ${CLI.style('$exeName <command> <-h|[arguments]>', color: Color.lightGreen)}',
'',
'Available Command:',
commandTag,
'',
'Command options:',
' -h Print help for the current command.',
'',
'',
"Run `$exeName <command> -h` for more information about a command.",
];
var index = list.indexOf(commandTag);
list.removeAt(index);
if (commandMap.isNotEmpty) {
List<String> commandDescs = [];
for (var item in commandMap.entries) {
commandDescs.add(item.value.getCommandDescription());
}
list.insertAll(index, commandDescs);
}
if (append != null) {
list.insertAll(0, append);
list.insert(0, '');
}
CLI.print(list);
}
}

View file

@ -0,0 +1,35 @@
import 'dart:async';
import 'package:oto_cli/cli/cli.dart';
import 'package:oto_cli/cli/commands/command_base.dart';
import 'package:dart_framework/utils/system_util.dart';
class CommandStart extends CommandBase {
@override
String get name => 'start';
@override
Future execute(List<String> parameters) {
return simpleFuture;
}
@override
String getDescription() {
return 'Run the program as a background service.';
}
}
class CommandStop extends CommandBase {
@override
String get name => 'stop';
@override
Future execute(List<String> parameters) {
return simpleFuture;
}
@override
String getDescription() {
return 'Exit the "${CLI.serviceName}" currently running in the background.';
}
}

View file

@ -0,0 +1,31 @@
import 'dart:async';
import 'package:dart_framework/utils/system_util.dart';
import 'package:oto_cli/cli/cli.dart';
import 'package:oto_cli/cli/commands/command_base.dart';
class CommandTemplate extends CommandBase {
@override
Map<String, List<String>> get arguments => {
"--s": [
"This is argument description template",
"This is second line."
],
"--g": ["Second argument"]
};
@override
String get name => 'template';
@override
String get usage => '--arg0 --arg1';
@override
Future execute(List<String> parameters) async {
return simpleFuture;
}
@override
String getDescription() {
var serviceName = CLI.current.config.serviceName;
return 'This is template command description from $serviceName.';
}
}

1
lib/test.dart Normal file
View file

@ -0,0 +1 @@

29
pubspec.yaml Normal file
View file

@ -0,0 +1,29 @@
name: oto_cli
description: A sample command-line application.
version: 1.0.0
# repository: https://github.com/my_org/my_repo
environment:
sdk: '>=2.18.6 <4.0.0'
# Add regular dependencies here.
dependencies:
# path: ^1.8.0
json_annotation: ^4.8.0
resource_importer: ^0.2.0
dart_framework:
# path: '../dart_framework'
git:
url: toki@toki-labs.com:/Volumes/Data2/git/dart_framework.git
ref: master
build_manager:
# path: ../build_manager
git:
url: toki@toki-labs.com:/Volumes/Data2/git/build_manager.git
ref: master
dev_dependencies:
lints: ^2.0.0
test: ^1.21.0
build_runner: ^2.1.4
json_serializable: ^6.0.1

View file

@ -0,0 +1,5 @@
import 'package:test/test.dart';
void main() {
test('calculate', () {});
}

65
test/test.bat Normal file
View file

@ -0,0 +1,65 @@
@echo off
setlocal
call :setESC
cls
echo %ESC%[101;93m STYLES %ESC%[0m
echo ^<ESC^>[0m %ESC%[0mReset%ESC%[0m
echo ^<ESC^>[1m %ESC%[1mBold%ESC%[0m
echo ^<ESC^>[4m %ESC%[4mUnderline%ESC%[0m
echo ^<ESC^>[7m %ESC%[7mInverse%ESC%[0m
echo.
echo %ESC%[101;93m NORMAL FOREGROUND COLORS %ESC%[0m
echo ^<ESC^>[30m %ESC%[30mBlack%ESC%[0m (black)
echo ^<ESC^>[31m %ESC%[31mRed%ESC%[0m
echo ^<ESC^>[32m %ESC%[32mGreen%ESC%[0m
echo ^<ESC^>[33m %ESC%[33mYellow%ESC%[0m
echo ^<ESC^>[34m %ESC%[34mBlue%ESC%[0m
echo ^<ESC^>[35m %ESC%[35mMagenta%ESC%[0m
echo ^<ESC^>[36m %ESC%[36mCyan%ESC%[0m
echo ^<ESC^>[37m %ESC%[37mWhite%ESC%[0m
echo.
echo %ESC%[101;93m NORMAL BACKGROUND COLORS %ESC%[0m
echo ^<ESC^>[40m %ESC%[40mBlack%ESC%[0m
echo ^<ESC^>[41m %ESC%[41mRed%ESC%[0m
echo ^<ESC^>[42m %ESC%[42mGreen%ESC%[0m
echo ^<ESC^>[43m %ESC%[43mYellow%ESC%[0m
echo ^<ESC^>[44m %ESC%[44mBlue%ESC%[0m
echo ^<ESC^>[45m %ESC%[45mMagenta%ESC%[0m
echo ^<ESC^>[46m %ESC%[46mCyan%ESC%[0m
echo ^<ESC^>[47m %ESC%[47mWhite%ESC%[0m (white)
echo.
echo %ESC%[101;93m STRONG FOREGROUND COLORS %ESC%[0m
echo ^<ESC^>[90m %ESC%[90mWhite%ESC%[0m
echo ^<ESC^>[91m %ESC%[91mRed%ESC%[0m
echo ^<ESC^>[92m %ESC%[92mGreen%ESC%[0m
echo ^<ESC^>[93m %ESC%[93mYellow%ESC%[0m
echo ^<ESC^>[94m %ESC%[94mBlue%ESC%[0m
echo ^<ESC^>[95m %ESC%[95mMagenta%ESC%[0m
echo ^<ESC^>[96m %ESC%[96mCyan%ESC%[0m
echo ^<ESC^>[97m %ESC%[97mWhite%ESC%[0m
echo.
echo %ESC%[101;93m STRONG BACKGROUND COLORS %ESC%[0m
echo ^<ESC^>[100m %ESC%[100mBlack%ESC%[0m
echo ^<ESC^>[101m %ESC%[101mRed%ESC%[0m
echo ^<ESC^>[102m %ESC%[102mGreen%ESC%[0m
echo ^<ESC^>[103m %ESC%[103mYellow%ESC%[0m
echo ^<ESC^>[104m %ESC%[104mBlue%ESC%[0m
echo ^<ESC^>[105m %ESC%[105mMagenta%ESC%[0m
echo ^<ESC^>[106m %ESC%[106mCyan%ESC%[0m
echo ^<ESC^>[107m %ESC%[107mWhite%ESC%[0m
echo.
echo %ESC%[101;93m COMBINATIONS %ESC%[0m
echo ^<ESC^>[31m %ESC%[31mred foreground color%ESC%[0m
echo ^<ESC^>[7m %ESC%[7minverse foreground ^<-^> background%ESC%[0m
echo ^<ESC^>[7;31m %ESC%[7;31minverse red foreground color%ESC%[0m
echo ^<ESC^>[7m and nested ^<ESC^>[31m %ESC%[7mbefore %ESC%[31mnested%ESC%[0m
echo ^<ESC^>[31m and nested ^<ESC^>[7m %ESC%[31mbefore %ESC%[7mnested%ESC%[0m
:setESC
for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do (
set ESC=%%b
exit /B 0
)
exit /B 0