diff --git a/src/vs/platform/storage/electron-main/storageMainService.ts b/src/vs/platform/storage/electron-main/storageMainService.ts new file mode 100644 index 0000000000000000000000000000000000000000..35f2b909ecb690282628e3962c90c4b4f6ade924 --- /dev/null +++ b/src/vs/platform/storage/electron-main/storageMainService.ts @@ -0,0 +1,159 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { Event, Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { ILogService, LogLevel } from 'vs/platform/log/common/log'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IStorage, Storage, IStorageLoggingOptions } from 'vs/base/node/storage'; +import { join } from 'path'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; + +export const IStorageMainService = createDecorator('storageMainService'); + +export interface IStorageMainService { + + _serviceBrand: any; + + /** + * Emitted whenever data is updated or deleted. + */ + readonly onDidChangeStorage: Event; + + /** + * Emitted when the storage is about to persist. This is the right time + * to persist data to ensure it is stored before the application shuts + * down. + */ + readonly onWillSaveState: Event; + + /** + * Retrieve an element stored with the given key from storage. Use + * the provided defaultValue if the element is null or undefined. + */ + get(key: string, fallbackValue: string): string; + + /** + * Retrieve an element stored with the given key from storage. Use + * the provided defaultValue if the element is null or undefined. The element + * will be converted to a boolean. + */ + getBoolean(key: string, fallbackValue: boolean): boolean; + + /** + * Retrieve an element stored with the given key from storage. Use + * the provided defaultValue if the element is null or undefined. The element + * will be converted to a number using parseInt with a base of 10. + */ + getInteger(key: string, fallbackValue: number): number; + + /** + * Store a string value under the given key to storage. The value will + * be converted to a string. + */ + store(key: string, value: any): void; + + /** + * Delete an element stored under the provided key from storage. + */ + remove(key: string): void; +} + +export interface IStorageChangeEvent { + key: string; +} + +export class StorageMainService extends Disposable implements IStorageMainService { + + _serviceBrand: any; + + private static STORAGE_NAME = 'storage.db'; + + private _onDidChangeStorage: Emitter = this._register(new Emitter()); + get onDidChangeStorage(): Event { return this._onDidChangeStorage.event; } + + private _onWillSaveState: Emitter = this._register(new Emitter()); + get onWillSaveState(): Event { return this._onWillSaveState.event; } + + private storage: IStorage; + + constructor( + @ILogService private logService: ILogService, + @IEnvironmentService environmentService: IEnvironmentService, + @ITelemetryService private telemetryService: ITelemetryService + ) { + super(); + + this.storage = new Storage({ + path: join(environmentService.globalStorageHome, StorageMainService.STORAGE_NAME), + logging: this.createLogginOptions() + }); + + this.registerListeners(); + } + + private createLogginOptions(): IStorageLoggingOptions { + const loggedStorageErrors = new Set(); + + return { + logTrace: (this.logService.getLevel() === LogLevel.Trace) ? msg => this.logService.trace(msg) : void 0, + logError: error => { + this.logService.error(error); + + const errorStr = `${error}`; + if (!loggedStorageErrors.has(errorStr)) { + loggedStorageErrors.add(errorStr); + + /* __GDPR__ + "sqliteMainStorageError" : { + "storageError": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + this.telemetryService.publicLog('sqliteMainStorageError', { + 'storageError': errorStr + }); + } + } + } as IStorageLoggingOptions; + } + + private registerListeners(): void { + this._register(this.storage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key }))); + } + + initialize(): Thenable { + return this.storage.init(); + } + + get(key: string, fallbackValue: string): string { + return this.storage.get(key, fallbackValue); + } + + getBoolean(key: string, fallbackValue: boolean): boolean { + return this.storage.getBoolean(key, fallbackValue); + } + + getInteger(key: string, fallbackValue: number): number { + return this.storage.getInteger(key, fallbackValue); + } + + store(key: string, value: any): Thenable { + return this.storage.set(key, value); + } + + remove(key: string): Thenable { + return this.storage.delete(key); + } + + close(): Thenable { + + // Signal as event so that clients can still store data + this._onWillSaveState.fire(); + + // Do it + return this.storage.close().then(() => void 0); + } +} \ No newline at end of file