1 /++
2 Resource loader module
3 
4 Using the loader, you can load various resources that
5 will be kept in memory. They can be accessed by their
6 name or path, they can also be unloaded from memory.
7 
8 Please note that it is unnecessary to reload `download` since it does
9 not exist, it is implemented using the download-upload method. It will
10 load the resource from the .temp folder.
11 
12 Macros:
13     LREF = <a href="#$1">$1</a>
14     HREF = <a href="$1">$2</a>
15 
16 Authors: $(HREF https://github.com/TodNaz,TodNaz)
17 Copyright: Copyright (c) 2020 - 2021, TodNaz.
18 License: $(HREF https://github.com/TodNaz/Tida/blob/master/LICENSE,MIT)
19 +/
20 module tida.loader;
21 
22 import std.path : baseName, stripExtension;
23 import std.file : exists,mkdir;
24 
25 __gshared Loader _loader;
26 
27 /// Loader instance.
28 Loader loader() @trusted
29 {
30     return _loader;
31 }
32 
33 /// Resource descriptor
34 struct Resource
35 {
36 public:
37     Object object; /// Object
38     string type; /// Type name object
39     string path; /// Releative path object
40     string name; /// Local name object
41     bool isFont = false;
42 
43 @trusted:
44     /++
45     Initializes a resource. For this, he saves the name of the type and later,
46     according to this type, he can determine further calls to it.
47 
48     Params:
49         resource = Object resource.
50     +/
51     void init(T)(T resource)
52     {
53         object = resource;
54         type = typeid(T).toString;
55     }
56 
57     /++
58     The method to get the object.
59     If the object turns out to be the wrong one, the contract will work.
60     +/
61     T get(T)()
62     in(typeid(T).toString == type)
63     do
64     {
65         return cast(T) object;
66     }
67 
68     string getPath()
69     {
70         return path;
71     }
72 
73     string getName()
74     {
75         return name;
76     }
77 
78     void free()
79     {
80         destroy(object);
81     }
82 }
83 
84 /++
85 Resource loader. Loads resources, fonts and more
86 and keeps it in memory.
87 +/
88 class Loader
89 {
90     import std.path;
91     import std.exception : enforce;
92 
93 private:
94     Resource[] resources;
95 
96 public @safe:
97     /++
98     Will load the resource, having only its path as
99     input. The entire loading implementation lies with
100     the resource itself. The manager will simply keep
101     this resource in memory.
102 
103     Params:
104         T = Data type.
105         path = Path to the file.
106         name = Name.
107 
108     Retunrs:
109         T
110 
111     Throws: `LoadException` if the loader determines
112         that the file does not exist. There may be other
113         errors while loading, see their documentation,
114         for example `Image`.
115 
116     Example:
117     ---
118     Image img = loader.load!Image("a.png");
119     ---
120     +/
121     T load(T)(immutable string path,string name = "null")
122     {
123         if (this.get!T(path) !is null)
124             return this.get!T(path);
125 
126         T obj = new T();
127         Resource res;
128 
129         synchronized
130         {
131             enforce!Exception(path.exists, "Not find file `" ~ path ~ "`!");
132 
133             if(name == "null")
134                 name = path.baseName.stripExtension;
135 
136             obj.load(path);
137 
138             res.path = path;
139             res.name = name;
140             res.init!T(obj);
141 
142             this.resources ~= (res);
143         }
144 
145         return obj;
146     }
147 
148     /++
149     Loads multiple resources in one fell swoop using an associated array.
150 
151     Params:
152         T = Data type.
153         paths = Paths and names for loading resources.
154 
155     Throws: `LoadException` if the loader determines
156         that the file does not exist. There may be other
157         errors while loading, see their documentation,
158         for example `Image`.
159 
160     Example:
161     ---
162     loader.load!Image([
163         "op1" : "image.png",
164         "op2" : "image2.png"
165     ]);
166     ---
167     +/
168     void load(T)(immutable string[string] paths)
169     {
170         foreach (key; paths.keys)
171         {
172             this.load!T(paths[key],key);
173         }
174     }
175 
176     private size_t pos(T)(T res)
177     {
178         foreach (size_t i; 0 .. resources.length)
179         {
180             if (resources[i].object is res)
181             {
182                 return i;
183             }
184         }
185 
186         throw new Exception("Unknown resource");
187     }
188 
189     /++
190     Frees the resource from memory by calling the `free`
191     construct on the resource if it has unreleased pointers
192     and so on, and later removes the resource from the array,
193     letting the garbage collector destroy this object.
194 
195     Params:
196         T = Resource class
197         path = Name or Path to file resource
198 
199     Example:
200     ---
201     loader.free!Image("myImage");
202     ---
203     +/
204     void free(T)(immutable string path) @trusted
205     {
206         auto obj = get!T(path);
207 
208         if (obj is null)
209             return;
210 
211         resources.remove(pos(obj));
212         synchronized destroy(obj);
213     }
214 
215     /++
216     Frees the resource from memory by calling the `free`
217     construct on the resource if it has unreleased pointers
218     and so on, and later removes the resource from the array,
219     letting the garbage collector destroy this object.
220 
221     Params:
222         T = Resource class
223         obj = Resource object
224 
225     Example:
226     ---
227     auto myImage = loader.load!Image(...);
228     loader.free(myImage);
229         ---
230     +/
231     void free(T)(T obj) @trusted
232     {
233         if (obj is null)
234             return;
235 
236         resources.remove(pos!T(obj));
237         synchronized destroy(obj);
238     }
239 
240     /++
241     Returns a resource by name or path.
242 
243     Params:
244         name = name resource(or path)
245 
246     Returns:
247         `null` if the resource is not found.
248         If found, will return a `T` of the
249         appropriate size.
250     +/
251     T get(T)(immutable string name)
252     {
253         foreach (e; this.resources)
254         {
255             if (e.getPath() == name)
256                 return e.get!T;
257 
258             if (e.getName() == name)
259                 return e.get!T;
260         }
261 
262         return null;
263     }
264 
265     /++
266     Will add a resource that was not loaded through the manager.
267     Please note that it must have a path and a name.
268 
269     Params:
270         res = Resource.
271     +/
272     void add(Resource res)
273     {
274         this.resources ~= (res);
275     }
276 
277     ~this() @safe
278     {
279         foreach (res; resources)
280         {
281             res.free();
282         }
283     }
284 }