aboutsummaryrefslogtreecommitdiff
path: root/cmnext-cli/src/Storage/PersistentDataStore.cs
blob: 93f7284ac675bbd12fe368da071ba0b34ed4b59b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*
* Copyright (c) 2024 Vaughn Nugent
* 
* Package: CMNext.Cli
* File: Program.cs 
*
* CMNext.Cli is free software: you can redistribute it and/or modify 
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 2 of the License,
* or (at your option) any later version.
*
* CMNext.Cli is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License 
* along with CMNext.Cli. If not, see http://www.gnu.org/licenses/.
*/


using System.IO;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using System.IO.IsolatedStorage;

using VNLib.Utils.Extensions;

using VNLib.Utils.IO;
using VNLib.Utils.Memory;
using Typin.Console;

namespace CMNext.Cli.Storage
{
    public sealed class PersistentDataStore
    {
        private readonly IsolatedStorageDirectory _dir;
        private readonly CancellationToken _cancellation;

        /// <summary>
        /// Creates a new isolated storage store with the provided name
        /// that can be used to store and retreive <see cref="IStorable"/>
        /// data.
        /// </summary>
        /// <param name="storeName">The directory name to store data in</param>
        /// <returns>The new <see cref="PersistentDataStore"/></returns>
        public PersistentDataStore(IConsole console)
        {
            //Isolated storage for the current user scoped to the application
            IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication();
            _dir = new(isf, Program.StorePath);
            _cancellation = console.GetCancellationToken();
        }

        /// Restores the contents of the specified file to the provided entity
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fileName"></param>
        /// <param name="entity"></param>
        /// <param name="cancellation"></param>
        /// <returns></returns>
        public async Task<bool> RestoreAsync<T>(string fileName, T entity) where T : IStorable
        {
            if (!_dir.FileExists(fileName))
            {
                return false;
            }

            using VnMemoryStream _memStream = new();

            //read the file into memory
            await using (IsolatedStorageFileStream stream = _dir.OpenFile(fileName, FileMode.Open, FileAccess.Read))
            {
                await stream.CopyToAsync(_memStream, 4096, MemoryUtil.Shared, _cancellation);
            }

            //reset the stream
            _memStream.Seek(0, SeekOrigin.Begin);

            //load the entity from the stream
            return entity.Load(_memStream);
        }

        /// <summary>
        /// Stores the contents of the provided entity to the specified file
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fileName">The name of the file to write</param>
        /// <param name="entity">The storable entity to store</param>
        /// <returns>A task that resolves when the save has been completed</returns>
        public async Task SaveAsync<T>(string fileName, T entity) where T : IStorable
        {
            using VnMemoryStream _memStream = new();

            //save the entity to the memory stream
            entity.Save(_memStream);

            //reset the stream
            _memStream.Seek(0, SeekOrigin.Begin);

            //write the stream to the file
            await using IsolatedStorageFileStream stream = _dir.OpenFile(fileName, FileMode.Create, FileAccess.Write);
            await _memStream.CopyToAsync(stream, 4096, _cancellation);
        }

        /// <summary>
        /// Reads the contents of the specified file as a json object directly
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fileName">The name of the file to read</param>
        /// <param name="cancellation">A token to cancel the operation</param>
        /// <returns>A task that resolves the object if found, null otherwise</returns>
        public async Task<T?> ReadJsonAsync<T>(string fileName) where T : class
        {
            //Make sure file exists
            if (!_dir.FileExists(fileName))
            {
                return null;
            }

            //Read the file directly into the desserializer
            await using IsolatedStorageFileStream stream = _dir.OpenFile(fileName, FileMode.Open, FileAccess.Read);

            return await JsonSerializer.DeserializeAsync<T>(stream, cancellationToken: _cancellation);
        }

        /// <summary>
        /// Reads the contents of the specified file as a json object directly
        /// or returns a new instance of the object if the file does not exist
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fileName">The name of the file to read data from</param>
        /// <returns>A task that resolves the read object</returns>
        public async Task<T> ReadJsonOrDefaultAsync<T>(string fileName) where T : class, new()
        {
            T? result = await ReadJsonAsync<T>(fileName);
            return result ?? new();
        }

        /// <summary>
        /// Saves the provided entity as a json object to the specified file
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fileName">The name of the file to write data to</param>
        /// <param name="entity">The object to store</param>
        /// <returns>A task that resolves when the data has been stored</returns>
        public async Task SaveJsonAsync<T>(string fileName, T entity) where T : class
        {
            await using IsolatedStorageFileStream stream = _dir.OpenFile(fileName, FileMode.Create, FileAccess.Write);
            await JsonSerializer.SerializeAsync(stream, entity, cancellationToken: _cancellation);
        }
    }
}