2020-04-08 15:19:00 +08:00
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System ;
2020-10-22 03:32:53 +08:00
using System.Diagnostics.CodeAnalysis ;
using System.IO ;
2020-11-03 01:33:43 +08:00
using System.IO.Abstractions ;
2020-03-25 10:55:02 +08:00
using System.Text.Json ;
2020-10-23 00:45:48 +08:00
using Microsoft.PowerToys.Settings.UI.Library.Interfaces ;
using Microsoft.PowerToys.Settings.UI.Library.Utilities ;
2020-03-25 10:55:02 +08:00
2020-10-23 00:45:48 +08:00
namespace Microsoft.PowerToys.Settings.UI.Library
2020-03-25 10:55:02 +08:00
{
2020-09-22 01:14:44 +08:00
public class SettingsUtils : ISettingsUtils
2020-03-25 10:55:02 +08:00
{
2020-04-18 06:25:08 +08:00
private const string DefaultFileName = "settings.json" ;
2020-04-20 21:03:26 +08:00
private const string DefaultModuleName = "" ;
2020-11-03 01:33:43 +08:00
private readonly IFile _file ;
private readonly ISettingsPath _settingsPath ;
2020-04-18 06:25:08 +08:00
2020-11-03 01:33:43 +08:00
public SettingsUtils ( )
: this ( new FileSystem ( ) )
2020-09-09 01:04:17 +08:00
{
}
2020-11-03 01:33:43 +08:00
public SettingsUtils ( IFileSystem fileSystem )
: this ( fileSystem ? . File , new SettingPath ( fileSystem ? . Directory , fileSystem ? . Path ) )
2020-04-04 10:02:38 +08:00
{
}
2020-11-03 01:33:43 +08:00
public SettingsUtils ( IFile file , ISettingsPath settingPath )
2020-04-04 10:02:38 +08:00
{
2020-11-03 01:33:43 +08:00
_file = file ? ? throw new ArgumentNullException ( nameof ( file ) ) ;
_settingsPath = settingPath ;
2020-09-22 01:14:44 +08:00
}
2020-11-03 01:33:43 +08:00
public bool SettingsExists ( string powertoy = DefaultModuleName , string fileName = DefaultFileName )
2020-09-22 01:14:44 +08:00
{
2020-11-03 01:33:43 +08:00
var settingsPath = _settingsPath . GetSettingsPath ( powertoy , fileName ) ;
return _file . Exists ( settingsPath ) ;
2020-04-04 10:02:38 +08:00
}
2020-11-03 01:33:43 +08:00
public void DeleteSettings ( string powertoy = "" )
2020-04-04 10:02:38 +08:00
{
2020-11-03 01:33:43 +08:00
_settingsPath . DeleteSettings ( powertoy ) ;
2020-04-04 10:02:38 +08:00
}
2021-01-14 20:14:29 +08:00
public T GetSettings < T > ( string powertoy = DefaultModuleName , string fileName = DefaultFileName )
where T : ISettingsConfig , new ( )
{
if ( ! SettingsExists ( powertoy , fileName ) )
{
throw new FileNotFoundException ( ) ;
}
2021-03-10 20:16:46 +08:00
// Given the file already exists, to deserialize the file and read its content.
2021-01-14 20:14:29 +08:00
T deserializedSettings = GetFile < T > ( powertoy , fileName ) ;
// If the file needs to be modified, to save the new configurations accordingly.
if ( deserializedSettings . UpgradeSettingsConfiguration ( ) )
{
SaveSettings ( deserializedSettings . ToJsonString ( ) , powertoy , fileName ) ;
}
return deserializedSettings ;
}
2020-04-04 10:02:38 +08:00
/// <summary>
/// Get a Deserialized object of the json settings string.
2020-09-24 04:20:32 +08:00
/// This function creates a file in the powertoy folder if it does not exist and returns an object with default properties.
2020-04-04 10:02:38 +08:00
/// </summary>
/// <returns>Deserialized json settings object.</returns>
2021-01-14 20:14:29 +08:00
public T GetSettingsOrDefault < T > ( string powertoy = DefaultModuleName , string fileName = DefaultFileName )
2020-09-24 04:20:32 +08:00
where T : ISettingsConfig , new ( )
{
2021-01-14 20:14:29 +08:00
try
2020-09-24 04:20:32 +08:00
{
2021-01-14 20:14:29 +08:00
return GetSettings < T > ( powertoy , fileName ) ;
}
2020-09-24 04:20:32 +08:00
2021-01-14 20:14:29 +08:00
// Catch json deserialization exceptions when the file is corrupt and has an invalid json.
// If there are any deserialization issues like in https://github.com/microsoft/PowerToys/issues/7500, log the error and create a new settings.json file.
// This is different from the case where we have trailing zeros following a valid json file, which we have handled by trimming the trailing zeros.
catch ( JsonException ex )
{
Logger . LogError ( $"Exception encountered while loading {powertoy} settings." , ex ) ;
}
catch ( FileNotFoundException )
{
Logger . LogInfo ( $"Settings file {fileName} for {powertoy} was not found." ) ;
2020-09-24 04:20:32 +08:00
}
2020-10-27 02:13:53 +08:00
// If the settings file does not exist or if the file is corrupt, to create a new object with default parameters and save it to a newly created settings file.
T newSettingsItem = new T ( ) ;
SaveSettings ( newSettingsItem . ToJsonString ( ) , powertoy , fileName ) ;
return newSettingsItem ;
2020-09-24 04:20:32 +08:00
}
// Given the powerToy folder name and filename to be accessed, this function deserializes and returns the file.
private T GetFile < T > ( string powertoyFolderName = DefaultModuleName , string fileName = DefaultFileName )
2020-03-25 10:55:02 +08:00
{
2020-09-12 07:15:18 +08:00
// Adding Trim('\0') to overcome possible NTFS file corruption.
// Look at issue https://github.com/microsoft/PowerToys/issues/6413 you'll see the file has a large sum of \0 to fill up a 4096 byte buffer for writing to disk
// This, while not totally ideal, does work around the problem by trimming the end.
// The file itself did write the content correctly but something is off with the actual end of the file, hence the 0x00 bug
2020-11-03 01:33:43 +08:00
var jsonSettingsString = _file . ReadAllText ( _settingsPath . GetSettingsPath ( powertoyFolderName , fileName ) ) . Trim ( '\0' ) ;
2020-03-25 10:55:02 +08:00
return JsonSerializer . Deserialize < T > ( jsonSettingsString ) ;
}
2020-04-08 01:19:14 +08:00
// Save settings to a json file.
2020-10-22 03:32:53 +08:00
[SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "General exceptions will be logged until we can better understand runtime exception scenarios")]
2020-09-22 01:14:44 +08:00
public void SaveSettings ( string jsonSettings , string powertoy = DefaultModuleName , string fileName = DefaultFileName )
2020-03-25 10:55:02 +08:00
{
2020-04-18 06:25:08 +08:00
try
2020-04-04 10:02:38 +08:00
{
2020-04-18 06:25:08 +08:00
if ( jsonSettings ! = null )
2020-04-04 10:02:38 +08:00
{
2020-11-03 01:33:43 +08:00
if ( ! _settingsPath . SettingsFolderExists ( powertoy ) )
2020-04-18 06:25:08 +08:00
{
2020-11-03 01:33:43 +08:00
_settingsPath . CreateSettingsFolder ( powertoy ) ;
2020-04-18 06:25:08 +08:00
}
2020-04-08 15:19:00 +08:00
2020-11-03 01:33:43 +08:00
_file . WriteAllText ( _settingsPath . GetSettingsPath ( powertoy , fileName ) , jsonSettings ) ;
2020-04-18 06:25:08 +08:00
}
}
2020-10-22 03:32:53 +08:00
catch ( Exception e )
2020-04-18 06:25:08 +08:00
{
2020-10-22 03:32:53 +08:00
Logger . LogError ( $"Exception encountered while saving {powertoy} settings." , e ) ;
#if DEBUG
if ( e is ArgumentException | | e is ArgumentNullException | | e is PathTooLongException )
{
2020-10-23 00:45:48 +08:00
throw ;
2020-10-22 03:32:53 +08:00
}
#endif
2020-04-04 10:02:38 +08:00
}
2020-03-25 10:55:02 +08:00
}
}
2020-05-06 05:28:44 +08:00
}