Skip to content

Commit

Permalink
Merge branch 'feature/main' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
souhait0614 committed Jul 8, 2024
2 parents f2c6ed5 + e6a7d39 commit 9b12c8e
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 34 deletions.
2 changes: 1 addition & 1 deletion deno.jsonc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@submarin-converter/core",
"version": "0.1.2",
"version": "0.2.0",
"exports": "./src/index.ts",
"imports": {
"@std/assert": "jsr:@std/assert@^0.226.0",
Expand Down
101 changes: 89 additions & 12 deletions src/Converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ import type {
ConverterConvertResult,
ConverterConvertResultDetail,
ConverterConvertUsingPlugin,
ConverterEndConvertFunctionHandler,
ConverterEndPluginConvertHandler,
ConverterOption,
Plugin,
PluginConvertFunction,
} from "./types.ts";
import { defaultConverterOption } from "./constants.ts";
import { Logger } from "./logger.ts";

const makeFailedToConvertFunctionError = (
name: string,
Expand Down Expand Up @@ -58,15 +61,20 @@ export class Converter<
> {
plugins: TPlugins;
converterOption: Required<ConverterOption>;
private logger: Logger;
private onEndConvertFunction?: ConverterEndConvertFunctionHandler<TPlugins>;
private onEndPluginConvert?: ConverterEndPluginConvertHandler<TPlugins>;

/**
* Converterインスタンスを構築します
*
* @param {TPlugins} plugins - コンバータで使用するプラグイン
* @param {Object} [options] - コンバータのオプション設定
* @param {TPlugins} plugins - 使用するプラグイン
* @param {Object} [options]
* @param {Object} [options.pluginOptions] - 各プラグインのオプション
* @param {Object} [options.extendConvertFunctions] - 各プラグインの変換関数を拡張するための関数
* @param {ConverterOption} [options.converterOption] - 一般的なコンバータオプション
* @param {ConverterOption} [options.converterOption] - Converter本体のオプション
* @param {ConverterEndConvertFunctionHandler<TPlugins>} [options.onEndConvertFunction] - Converter.convertでプラグインのConvertFunctionが実行されたあとに呼び出されるコールバック関数
* @param {ConverterEndPluginConvertHandler<TPlugins>} [options.onEndPluginConvert] - Converter.convertでプラグインでの変換が成功または失敗したあとに呼び出されるコールバック関数
*/
constructor(
plugins: TPlugins,
Expand All @@ -82,8 +90,18 @@ export class Converter<
) => TPlugins[P]["convertFunctions"];
};
converterOption?: ConverterOption;
onEndConvertFunction?: ConverterEndConvertFunctionHandler<TPlugins>;
onEndPluginConvert?: ConverterEndPluginConvertHandler<TPlugins>;
} = {},
) {
this.logger = new Logger(
options.converterOption?.logLevel ?? defaultConverterOption.logLevel,
);
this.logger.debug("Logger is initialized:", this.logger);
this.logger.debug("Converter constructor is called:", {
plugins,
options,
});
const tempPlugins: Partial<
Record<TPluginIDs, Plugin<object | undefined>>
> = {};
Expand All @@ -94,26 +112,42 @@ export class Converter<
name as TPluginIDs
];
if (defaultOption) {
tempPlugins[name as TPluginIDs] = {
const plugin = {
defaultOption: pluginOption
? deepMerge(defaultOption, pluginOption)
: defaultOption,
convertFunctions: extendConvertFunction?.(convertFunctions) ??
convertFunctions,
};
tempPlugins[name as TPluginIDs] = plugin;
this.logger.debug(`Plugin "${name}" is loaded.`, plugin);
} else {
tempPlugins[name as TPluginIDs] = {
const plugin = {
convertFunctions: (extendConvertFunction?.(convertFunctions) ??
convertFunctions) as PluginConvertFunction<undefined>[],
};
tempPlugins[name as TPluginIDs] = plugin;
this.logger.debug(`Plugin "${name}" is loaded.`, plugin);
}
},
);
this.plugins = tempPlugins as TPlugins;
this.logger.debug("Plugins are loaded:", this.plugins);
this.converterOption = {
...defaultConverterOption,
...options.converterOption,
};
this.logger.debug("ConverterOption is initialized:", this.converterOption);
this.onEndConvertFunction = options.onEndConvertFunction;
this.logger.debug(
"EndConvertFunction is initialized:",
this.onEndConvertFunction,
);
this.onEndPluginConvert = options.onEndPluginConvert;
this.logger.debug(
"EndPluginConvert is initialized:",
this.onEndPluginConvert,
);
}

/**
Expand All @@ -133,22 +167,36 @@ export class Converter<
): Promise<
ConverterConvertResult<TPlugins, TUsingPlugins>
> {
this.logger.debug("convert is called:", {
text,
usingPlugins,
}, this);
let convertedText = text;
const details: Array<
ConverterConvertResultDetail<
TPlugins,
TPluginIDs
>
> = [];
for await (const usingPlugin of usingPlugins) {
for await (
const [usingPluginsIndexString, usingPlugin] of Object.entries(
usingPlugins,
)
) {
const usingPluginsIndex = Number(usingPluginsIndexString);
const { name, option } = typeof usingPlugin === "string"
? { name: usingPlugin, option: undefined }
: usingPlugin;
const plugin: TPlugins[typeof name] | undefined = this.plugins[name];
this.logger.debug(`using plugin: "${name}"`, {
option,
plugin,
});
if (!plugin) {
if (this.converterOption.interruptWithPluginError) {
throw new Error(`Plugin "${name}" is not found.`);
}
this.logger.warn(`Plugin "${name}" is not found.`);
break;
}
const mergedOption = plugin.defaultOption
Expand All @@ -157,6 +205,10 @@ export class Converter<
option ?? {},
)
: {};
this.logger.debug("merged option:", {
option,
mergedOption,
});
const detail = {
ok: false,
order: {
Expand All @@ -169,37 +221,62 @@ export class Converter<
TPluginIDs
>;
for await (
const [indexString, convertFunction] of Object.entries(
const [convertFunctionIndexString, convertFunction] of Object.entries(
plugin.convertFunctions,
)
) {
const index = Number(indexString);
const convertFunctionIndex = Number(convertFunctionIndexString);
try {
this.logger.debug(
`try convert function index: ${convertFunctionIndex}`,
{
convertFunction,
mergedOption,
convertedText,
},
);
convertedText = await convertFunction(
convertedText,
mergedOption,
);
detail.ok = true;
detail.convertedText = convertedText;
details.push(detail);
this.logger.debug("convert function succeeded:", {
convertFunction,
mergedOption,
convertedText,
});
break;
} catch (error) {
console.error(makeFailedToConvertFunctionError(name, index, error));
this.logger.error(
makeFailedToConvertFunctionError(name, convertFunctionIndex, error)
.message,
error,
);
detail.errors ??= [];
detail.errors.push(error);
} finally {
this.onEndConvertFunction?.(
structuredClone(detail),
usingPluginsIndex,
convertFunctionIndex,
);
}
details.push(detail);
}
details.push(detail);
this.onEndPluginConvert?.(detail, usingPluginsIndex);
if (this.converterOption.interruptWithPluginError) {
throw makeFailedToAllConvertFunctionError(name, detail.errors!);
}
}
return {
const result = {
text: convertedText,
details: details as ConverterConvertResult<
TPlugins,
TUsingPlugins
>["details"],
};
this.logger.debug("convert result:", result);
return result;
}
}
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ import type { ConverterOption } from "./types.ts";

export const defaultConverterOption: Required<ConverterOption> = {
interruptWithPluginError: false,
logLevel: "info",
};
38 changes: 38 additions & 0 deletions src/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const logLevels = {
error: 0,
warn: 1,
info: 2,
debug: 3,
} as const satisfies Partial<Record<keyof typeof console, number>>;

export type LogLevels = keyof typeof logLevels;

export class Logger {
private readonly level: number;

constructor(level: LogLevels) {
this.level = logLevels[level] ?? logLevels["info"];
}

private log(level: LogLevels, message: string, ...args: unknown[]) {
if (logLevels[level] <= this.level) {
console[level](`[SubmarinConverter] ${message}`, ...args);
}
}

error(message: string, ...args: unknown[]) {
this.log("error", message, ...args);
}

warn(message: string, ...args: unknown[]) {
this.log("warn", message, ...args);
}

info(message: string, ...args: unknown[]) {
this.log("info", message, ...args);
}

debug(message: string, ...args: unknown[]) {
this.log("debug", message, ...args);
}
}
37 changes: 34 additions & 3 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { LogLevels } from "./logger.ts";

export type PromiseOrValue<T> = T | PromiseLike<T>;

/** Pluginの文字列変換関数 */
Expand All @@ -11,8 +13,8 @@ export type PluginConvertFunction<
export interface PluginMetaData {
displayName?: string;
description?: string;
homepage?: string;
author?: string;
homepage?: string | string[];
author?: string | string[];
version?: string;
repository?: string;
}
Expand Down Expand Up @@ -50,7 +52,7 @@ export type Plugin<
/** Converter本体のオプション */
export interface ConverterOption {
interruptWithPluginError?: boolean;
// TODO: エラー出力の制御オプションの追加
logLevel?: LogLevels;
}

export type ConverterConvertUsingPlugin<
Expand Down Expand Up @@ -133,3 +135,32 @@ export interface ConverterConvertResult<
>;
};
}

export type ConverterEndPluginConvertHandler<
TPlugins extends Record<
string,
Plugin<object | undefined>
>,
TPluginIDs extends Extract<keyof TPlugins, string> = Extract<
keyof TPlugins,
string
>,
> = (
detail: ConverterConvertResultDetail<TPlugins, TPluginIDs>,
usingPluginsIndex: number,
) => void;

export type ConverterEndConvertFunctionHandler<
TPlugins extends Record<
string,
Plugin<object | undefined>
>,
TPluginIDs extends Extract<keyof TPlugins, string> = Extract<
keyof TPlugins,
string
>,
> = (
detail: ConverterConvertResultDetail<TPlugins, TPluginIDs>,
usingPluginsIndex: number,
convertFunctionIndex: number,
) => void;
Loading

0 comments on commit 9b12c8e

Please sign in to comment.