mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-27 18:22:41 -08:00
refactor: simplify config and anki integration composition
This commit is contained in:
@@ -98,7 +98,22 @@ export class AnkiIntegration {
|
||||
}) => Promise<KikuFieldGroupingChoice>,
|
||||
knownWordCacheStatePath?: string,
|
||||
) {
|
||||
this.config = {
|
||||
this.config = this.normalizeConfig(config);
|
||||
this.client = new AnkiConnectClient(this.config.url!);
|
||||
this.mediaGenerator = new MediaGenerator();
|
||||
this.timingTracker = timingTracker;
|
||||
this.mpvClient = mpvClient;
|
||||
this.osdCallback = osdCallback || null;
|
||||
this.notificationCallback = notificationCallback || null;
|
||||
this.fieldGroupingCallback = fieldGroupingCallback || null;
|
||||
this.knownWordCache = this.createKnownWordCache(knownWordCacheStatePath);
|
||||
this.pollingRunner = this.createPollingRunner();
|
||||
this.cardCreationService = this.createCardCreationService();
|
||||
this.fieldGroupingService = this.createFieldGroupingService();
|
||||
}
|
||||
|
||||
private normalizeConfig(config: AnkiConnectConfig): AnkiConnectConfig {
|
||||
return {
|
||||
...DEFAULT_ANKI_CONNECT_CONFIG,
|
||||
...config,
|
||||
fields: {
|
||||
@@ -131,15 +146,10 @@ export class AnkiIntegration {
|
||||
...(config.isKiku ?? {}),
|
||||
},
|
||||
} as AnkiConnectConfig;
|
||||
}
|
||||
|
||||
this.client = new AnkiConnectClient(this.config.url!);
|
||||
this.mediaGenerator = new MediaGenerator();
|
||||
this.timingTracker = timingTracker;
|
||||
this.mpvClient = mpvClient;
|
||||
this.osdCallback = osdCallback || null;
|
||||
this.notificationCallback = notificationCallback || null;
|
||||
this.fieldGroupingCallback = fieldGroupingCallback || null;
|
||||
this.knownWordCache = new KnownWordCacheManager({
|
||||
private createKnownWordCache(knownWordCacheStatePath?: string): KnownWordCacheManager {
|
||||
return new KnownWordCacheManager({
|
||||
client: {
|
||||
findNotes: async (query, options) =>
|
||||
(await this.client.findNotes(query, options)) as unknown,
|
||||
@@ -149,7 +159,10 @@ export class AnkiIntegration {
|
||||
knownWordCacheStatePath,
|
||||
showStatusNotification: (message: string) => this.showStatusNotification(message),
|
||||
});
|
||||
this.pollingRunner = new PollingRunner({
|
||||
}
|
||||
|
||||
private createPollingRunner(): PollingRunner {
|
||||
return new PollingRunner({
|
||||
getDeck: () => this.config.deck,
|
||||
getPollingRate: () => this.config.pollingRate || DEFAULT_ANKI_CONNECT_CONFIG.pollingRate,
|
||||
findNotes: async (query, options) =>
|
||||
@@ -169,7 +182,10 @@ export class AnkiIntegration {
|
||||
logInfo: (...args) => log.info(args[0] as string, ...args.slice(1)),
|
||||
logWarn: (...args) => log.warn(args[0] as string, ...args.slice(1)),
|
||||
});
|
||||
this.cardCreationService = new CardCreationService({
|
||||
}
|
||||
|
||||
private createCardCreationService(): CardCreationService {
|
||||
return new CardCreationService({
|
||||
getConfig: () => this.config,
|
||||
getTimingTracker: () => this.timingTracker,
|
||||
getMpvClient: () => this.mpvClient,
|
||||
@@ -236,7 +252,10 @@ export class AnkiIntegration {
|
||||
this.previousNoteIds.add(noteId);
|
||||
},
|
||||
});
|
||||
this.fieldGroupingService = new FieldGroupingService({
|
||||
}
|
||||
|
||||
private createFieldGroupingService(): FieldGroupingService {
|
||||
return new FieldGroupingService({
|
||||
getEffectiveSentenceCardConfig: () => this.getEffectiveSentenceCardConfig(),
|
||||
isUpdateInProgress: () => this.updateInProgress,
|
||||
getDeck: () => this.config.deck,
|
||||
|
||||
@@ -96,12 +96,7 @@ export class ConfigService {
|
||||
|
||||
reloadConfig(): ResolvedConfig {
|
||||
const { config, path: configPath } = this.loadRawConfig();
|
||||
this.rawConfig = config;
|
||||
this.configPathInUse = configPath;
|
||||
const { resolved, warnings } = this.resolveConfig(config);
|
||||
this.resolvedConfig = resolved;
|
||||
this.warnings = warnings;
|
||||
return this.getConfig();
|
||||
return this.applyResolvedConfig(config, configPath);
|
||||
}
|
||||
|
||||
reloadConfigStrict(): ReloadConfigStrictResult {
|
||||
@@ -111,15 +106,11 @@ export class ConfigService {
|
||||
}
|
||||
|
||||
const { config, path: configPath } = loadResult;
|
||||
this.rawConfig = config;
|
||||
this.configPathInUse = configPath;
|
||||
const { resolved, warnings } = this.resolveConfig(config);
|
||||
this.resolvedConfig = resolved;
|
||||
this.warnings = warnings;
|
||||
const resolvedConfig = this.applyResolvedConfig(config, configPath);
|
||||
return {
|
||||
ok: true,
|
||||
config: this.getConfig(),
|
||||
warnings: [...warnings],
|
||||
config: resolvedConfig,
|
||||
warnings: this.getWarnings(),
|
||||
path: configPath,
|
||||
};
|
||||
}
|
||||
@@ -132,11 +123,7 @@ export class ConfigService {
|
||||
? this.configPathInUse
|
||||
: this.configFileJsonc;
|
||||
fs.writeFileSync(targetPath, JSON.stringify(config, null, 2));
|
||||
this.rawConfig = config;
|
||||
this.configPathInUse = targetPath;
|
||||
const { resolved, warnings } = this.resolveConfig(config);
|
||||
this.resolvedConfig = resolved;
|
||||
this.warnings = warnings;
|
||||
this.applyResolvedConfig(config, targetPath);
|
||||
}
|
||||
|
||||
patchRawConfig(patch: RawConfig): void {
|
||||
@@ -159,11 +146,7 @@ export class ConfigService {
|
||||
error: string;
|
||||
path: string;
|
||||
} {
|
||||
const configPath = fs.existsSync(this.configFileJsonc)
|
||||
? this.configFileJsonc
|
||||
: fs.existsSync(this.configFileJson)
|
||||
? this.configFileJson
|
||||
: this.configFileJsonc;
|
||||
const configPath = this.resolveExistingConfigPath();
|
||||
|
||||
if (!fs.existsSync(configPath)) {
|
||||
return { ok: true, config: {}, path: configPath };
|
||||
@@ -171,19 +154,7 @@ export class ConfigService {
|
||||
|
||||
try {
|
||||
const data = fs.readFileSync(configPath, 'utf-8');
|
||||
const parsed = configPath.endsWith('.jsonc')
|
||||
? (() => {
|
||||
const errors: ParseError[] = [];
|
||||
const result = parseJsonc(data, errors, {
|
||||
allowTrailingComma: true,
|
||||
disallowComments: false,
|
||||
});
|
||||
if (errors.length > 0) {
|
||||
throw new Error(`Invalid JSONC (${errors[0]?.error ?? 'unknown'})`);
|
||||
}
|
||||
return result;
|
||||
})()
|
||||
: JSON.parse(data);
|
||||
const parsed = this.parseConfigContent(configPath, data);
|
||||
return {
|
||||
ok: true,
|
||||
config: isObject(parsed) ? (parsed as Config) : {},
|
||||
@@ -195,6 +166,41 @@ export class ConfigService {
|
||||
}
|
||||
}
|
||||
|
||||
private applyResolvedConfig(config: RawConfig, configPath: string): ResolvedConfig {
|
||||
this.rawConfig = config;
|
||||
this.configPathInUse = configPath;
|
||||
const { resolved, warnings } = this.resolveConfig(config);
|
||||
this.resolvedConfig = resolved;
|
||||
this.warnings = warnings;
|
||||
return this.getConfig();
|
||||
}
|
||||
|
||||
private resolveExistingConfigPath(): string {
|
||||
if (fs.existsSync(this.configFileJsonc)) {
|
||||
return this.configFileJsonc;
|
||||
}
|
||||
if (fs.existsSync(this.configFileJson)) {
|
||||
return this.configFileJson;
|
||||
}
|
||||
return this.configFileJsonc;
|
||||
}
|
||||
|
||||
private parseConfigContent(configPath: string, data: string): unknown {
|
||||
if (!configPath.endsWith('.jsonc')) {
|
||||
return JSON.parse(data);
|
||||
}
|
||||
|
||||
const errors: ParseError[] = [];
|
||||
const result = parseJsonc(data, errors, {
|
||||
allowTrailingComma: true,
|
||||
disallowComments: false,
|
||||
});
|
||||
if (errors.length > 0) {
|
||||
throw new Error(`Invalid JSONC (${errors[0]?.error ?? 'unknown'})`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private resolveConfig(raw: RawConfig): {
|
||||
resolved: ResolvedConfig;
|
||||
warnings: ConfigValidationWarning[];
|
||||
|
||||
Reference in New Issue
Block a user