storaget/lib.rs
1// The MIT License
2// Copyright 2020 Peter Mezei <mezeipetister@gmail.com>
3//
4// Permission is hereby granted, free of charge, to any person obtaining a copy
5// of this software and associated documentation files (the "Software"), to deal
6// in the Software without restriction, including without limitation the rights
7// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8// copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in all
12// copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20// SOFTWARE.
21//
22// Made with (L) from Hungary
23// If you need any help please contact me
24// at <mezeipetister@gmail.com>
25
26#![feature(test)]
27
28use serde::{Deserialize, Serialize};
29use std::convert::From;
30use std::default::Default;
31use std::fmt;
32use std::fs::File;
33use std::io;
34use std::io::{BufWriter, Read, Write};
35use std::iter::IntoIterator;
36use std::ops::{Deref, DerefMut};
37use std::path::{Path, PathBuf};
38
39/// PackResult<T>
40///
41/// Generic Pack result type
42/// contains Ok(T) or PackError
43///
44/// ```rust
45/// use storaget::*;
46/// let res_ok: PackResult<i32> = Ok(32);
47/// let res_err: PackResult<i32> = Err(PackError::ObjectNotFound);
48/// ```
49pub type PackResult<T> = Result<T, PackError>;
50
51/// Pack Error type
52/// For internal use
53pub enum PackError {
54 /// Any error that has a custom message.
55 /// Any kind of error that has no other
56 /// more specific variant in Error::*
57 InternalError(String),
58 /// Serialize Error
59 /// error occured during serialiuation
60 SerializeError(String),
61 /// Deserialize Error
62 /// error occured during deserialization
63 DeserializeError(String),
64 /// IO Error
65 /// error during file operations
66 IOError(String),
67 /// Object not found in a storage.
68 /// Usually using with get_by_id()
69 ObjectNotFound,
70 /// Path not found
71 /// Using at reading data from path.
72 PathNotFound,
73 /// ID Taken
74 /// When VecPack ID not available
75 IDTaken,
76}
77
78// serde_yaml::Error to PackError
79// implementation
80impl From<serde_yaml::Error> for PackError {
81 fn from(from: serde_yaml::Error) -> Self {
82 PackError::SerializeError(from.to_string())
83 }
84}
85
86// Well formatted display text for users
87// TODO: Use error code and language translation
88// for end-user error messages.
89impl fmt::Display for PackError {
90 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
91 match self {
92 PackError::InternalError(msg) => {
93 write!(f, "Internal error: {}", msg)
94 }
95 PackError::SerializeError(msg) => {
96 write!(f, "Pack serialization error: {}", msg)
97 }
98 PackError::DeserializeError(msg) => {
99 write!(f, "Pack deserialization error: {}", msg)
100 }
101 PackError::IOError(msg) => write!(f, "Pack IO error: {}", msg),
102 PackError::PathNotFound => write!(f, "Path not found"),
103 PackError::ObjectNotFound => {
104 write!(f, "Storage object not found in storage.")
105 }
106 PackError::IDTaken => write!(f, "VecPack ID already taken"),
107 }
108 }
109}
110
111// Well formatted debug text
112// TODO: how to support localitation?
113impl fmt::Debug for PackError {
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 match self {
116 PackError::InternalError(msg) => {
117 write!(f, "Internal error: {}", msg)
118 }
119 PackError::SerializeError(msg) => {
120 write!(f, "Pack serialization error: {}", msg)
121 }
122 PackError::DeserializeError(msg) => {
123 write!(f, "Pack deserialization error: {}", msg)
124 }
125 PackError::IOError(msg) => write!(f, "Pack IO error: {}", msg),
126 PackError::PathNotFound => write!(f, "Path not found"),
127 PackError::ObjectNotFound => {
128 write!(f, "Storage object not found in storage.")
129 }
130 PackError::IDTaken => write!(f, "VecPack ID already taken"),
131 }
132 }
133}
134
135impl From<io::Error> for PackError {
136 fn from(err: io::Error) -> Self {
137 PackError::IOError(format!("{}", err))
138 }
139}
140
141/// Pack<T>
142/// Small FS layer around type T
143/// Pack is responsible to sync T to the filesystem.
144pub struct Pack<T>
145where
146 T: Serialize + Sized + Clone,
147{
148 data: T,
149 path: PathBuf,
150}
151
152/// PackGuard<'a, T>
153/// Small mutable guard around type T
154/// that implements Drop trait, and save T
155/// to the filesystem when PackGuard is dropped.
156///
157/// Implements deref, deref_mut and drop
158pub struct PackGuard<'a, T>
159where
160 T: Serialize + Sized + Clone,
161{
162 data: &'a mut T,
163 path: &'a PathBuf,
164}
165
166/// VecPack<T>
167/// Small FS layer around a Vec<Pack<T>>
168/// The naming could be confusing a bit, as VecPack<T>
169/// is rather FSLayer<Vec<Pack<T>>>, but maybe this could
170/// be too long and unnecessary. So VecPack<T> behaves as
171/// a special Vec<Pack<T>>.
172pub struct VecPack<T>
173where
174 T: VecPackMember,
175{
176 data: Vec<Pack<T>>,
177 path: PathBuf,
178}
179
180/// This trait defines the requirements
181/// to be a member of a VecPack<T>
182pub trait VecPackMember: Serialize + Sized + Clone {
183 // type Target: fmt::Display + std::cmp::PartialEq;
184 fn get_id(&self) -> &str;
185}
186
187pub trait TryFrom {
188 type TryFrom: for<'de> Deserialize<'de>
189 + Serialize
190 + Default
191 + Sized
192 + Clone;
193}
194
195/// Save DATA OBJECT to its path
196/// Moved this logic into this separated private function
197/// as we use it from the Drop implementation and from save method.
198fn save_data_object<T>(path: &PathBuf, data: T) -> PackResult<()>
199where
200 T: Serialize,
201{
202 let mut buffer = BufWriter::new(File::create(path)?);
203 buffer.write_all(serde_yaml::to_string(&data)?.as_bytes())?;
204 buffer.flush()?;
205 Ok(())
206}
207
208#[derive(Serialize, Deserialize, Clone, Default)]
209pub struct NOTHING;
210
211impl<'a, T> Pack<T>
212where
213 for<'de> T: Serialize
214 + Deserialize<'de>
215 + Default
216 + Sized
217 + Clone
218 + 'a
219 + TryFrom
220 + std::convert::From<<T as TryFrom>::TryFrom>,
221{
222 // TODO: Why this is not an infinite loop when T == TryFrom?
223 pub fn try_load_from_path(path: PathBuf) -> PackResult<Pack<T>> {
224 match Pack::<T>::load_from_path(path.clone()) {
225 Ok(pack_t) => Ok(pack_t),
226 Err(_) => {
227 let data = Pack::<T::TryFrom>::load_from_path(path.clone())?
228 .into_inner()
229 .into();
230 let pack: Pack<T> = Pack {
231 data: data,
232 path: path,
233 };
234 pack.save()?;
235 Ok(pack)
236 }
237 }
238 }
239 pub fn try_load_or_init(
240 mut path: PathBuf,
241 file_id: &str,
242 ) -> PackResult<Pack<T>> {
243 if !path.exists() {
244 std::fs::create_dir_all(&path)?;
245 }
246 path.push(&format!("{}.yml", file_id));
247 if !path.exists() {
248 Pack::<T>::new(path.clone())?.save()?;
249 }
250 Pack::try_load_from_path(path)
251 }
252}
253
254impl<'a, T> Pack<T>
255where
256 for<'de> T: Serialize + Deserialize<'de> + Default + Sized + Clone + 'a,
257{
258 // New Pack<T>
259 // Private function
260 fn new(path: PathBuf) -> PackResult<Self> {
261 Ok(Pack {
262 data: T::default(),
263 path,
264 })
265 }
266 pub fn from_str(buffer: &str, path: PathBuf) -> PackResult<Pack<T>> {
267 match serde_yaml::from_str::<T>(&buffer) {
268 Ok(t) => Ok(Pack { data: t, path }),
269 Err(err) => Err(PackError::DeserializeError(err.to_string())),
270 }
271 }
272 /// Load Pack<T> from Path
273 /// If Path is file and exists, then it tries to load
274 /// then deserialize. Otherwise returns PackError.
275 pub fn load_from_path(path: PathBuf) -> PackResult<Pack<T>> {
276 let mut file = File::open(&path)?;
277 let mut buffer = String::new();
278 file.read_to_string(&mut buffer)?;
279 Self::from_str(&buffer, path)
280 }
281 /// Load or init Pack<T> from Path
282 /// If Path does not exist, then it tries to create;
283 /// Otherwise call Pack::load_from_path(Path).
284 pub fn load_or_init(
285 mut path: PathBuf,
286 file_id: &str,
287 ) -> PackResult<Pack<T>> {
288 if !path.exists() {
289 std::fs::create_dir_all(&path)?;
290 }
291 path.push(&format!("{}.yml", file_id));
292 if !path.exists() {
293 Pack::<T>::new(path.clone())?.save()?;
294 }
295 Pack::load_from_path(path)
296 }
297 /// Save Pack<T> manually
298 /// to FS. Returns PackError if something
299 /// wrong occures.
300 pub fn save(&self) -> PackResult<()> {
301 save_data_object(&self.path, &self.data)
302 }
303 /// Update Pack<T>
304 /// Tries to update T, if SUCCESS
305 /// then tries to save to FS, if SUCCESS
306 /// returns R. If Fail, then doing data T
307 /// rollback to backup, then return PackError.
308 pub fn update<F, R>(&mut self, mut f: F) -> PackResult<R>
309 where
310 F: FnMut(&mut T) -> R,
311 {
312 // First clone data as a backup.
313 let backup = self.data.clone();
314 // Let's do the update process.
315 let res = f(&mut self.data);
316 // Try to save data to the FS
317 match self.save() {
318 // If success, then return the update result(s)
319 Ok(_) => Ok(res),
320 // If there is error occured during
321 // saveing updated data
322 Err(err) => {
323 // Then rollback data to the backup.
324 self.data = backup;
325 // Return error
326 Err(err)
327 }
328 }
329 }
330 /// Get(Fn) -> R
331 /// Access data through closure
332 /// Unmutable data access
333 pub fn get<F, R>(&self, f: F) -> R
334 where
335 F: Fn(&T) -> R,
336 {
337 f(&self.data)
338 }
339 /// Map(Fn) -> R
340 /// Syntactic sugar for Get(Fn) -> R
341 pub fn map<F, R>(&self, f: F) -> R
342 where
343 F: Fn(&T) -> R,
344 {
345 f(&self.data)
346 }
347 /// as_mut() -> PackGuard<'a, T>
348 /// returns
349 pub fn as_mut(&mut self) -> PackGuard<'_, T> {
350 PackGuard {
351 data: &mut self.data,
352 path: &self.path,
353 }
354 }
355 pub fn into_inner(self) -> T {
356 self.data
357 }
358 pub fn unpack(&self) -> &T {
359 &self.data
360 }
361}
362
363impl<T> Deref for Pack<T>
364where
365 T: Serialize + Sized + Clone,
366{
367 type Target = T;
368
369 fn deref(&self) -> &Self::Target {
370 &self.data
371 }
372}
373
374impl<'a, T> Deref for PackGuard<'a, T>
375where
376 T: Serialize + Sized + Clone,
377{
378 type Target = T;
379
380 fn deref(&self) -> &Self::Target {
381 &self.data
382 }
383}
384
385impl<'a, T> DerefMut for PackGuard<'a, T>
386where
387 T: Serialize + Sized + Clone,
388{
389 fn deref_mut(&mut self) -> &mut Self::Target {
390 &mut self.data
391 }
392}
393
394impl<'a, T> Drop for PackGuard<'a, T>
395where
396 T: Serialize + Sized + Clone,
397{
398 fn drop(&mut self) {
399 // TODO: VERY IMPORTANT
400 // Implement error LOGGING!
401 // This auto save during drop cannot return PackError,
402 // we have two options:
403 // - Panic(),
404 // - & | error log
405 let _ = save_data_object(&self.path, &self.data);
406 }
407}
408
409// impl<T> VecPack<T>
410// where
411// for<'de> T: VecPackMember + Deserialize<'de> + Default + PackTryFrom,
412// {
413// pub fn try_load_or_init(path: PathBuf) -> PackResult<VecPack<T>> {
414// match Self::load_or_init(path) {
415// Ok(res) => Ok(res),
416// Err(err) => Self::try_from().into(),
417// }
418// }
419// }
420
421impl<T> VecPack<T>
422where
423 for<'de> T: VecPackMember
424 + Serialize
425 + Deserialize<'de>
426 + Default
427 + Sized
428 + Clone
429 + TryFrom
430 + std::convert::From<<T as TryFrom>::TryFrom>,
431{
432 pub fn try_load_or_init(path: PathBuf) -> PackResult<VecPack<T>> {
433 // If path is a file
434 // then panic!
435 if path.is_file() {
436 panic!(
437 "Given VecPack path is not a dir. Path: {}",
438 &path.to_str().unwrap()
439 );
440 }
441 // If path does not exist,
442 // then we create it.
443 if !path.exists() {
444 std::fs::create_dir_all(&path)?;
445 }
446 // Result empty VecPack<T>
447 let mut result: VecPack<T> = VecPack::new(path.clone())?;
448 // First collect all
449 // the file names from path
450 std::fs::read_dir(path.clone())?
451 .filter_map(|file| {
452 file.ok().and_then(|e| {
453 e.path().file_name().and_then(|n| {
454 n.to_str().map(|s| {
455 let mut p = path.clone();
456 p.push(s);
457 p
458 })
459 })
460 })
461 })
462 .collect::<Vec<PathBuf>>()
463 // Then iter over path vector
464 // and try to read and deserialize
465 // them.
466 .iter()
467 .for_each(|path| {
468 // Add deserialized T to VecPack<T>
469 result
470 .insert_pack(
471 // Create Pack<T> from T
472 Pack::<T>::try_load_from_path(path.clone()).expect(
473 &format!(
474 "Cannot deserialize file with ID: {}",
475 (&path).to_str().unwrap()
476 ),
477 ),
478 )
479 .expect(&format!(
480 "Error while adding file to VecPack with ID: {}",
481 (&path).to_str().unwrap()
482 ));
483 });
484 Ok(result)
485 }
486}
487
488impl<T> VecPack<T>
489where
490 for<'de> T: VecPackMember + Deserialize<'de> + Default,
491{
492 // TODO: Check FS operations. What if path is a file?
493 /// New VecPack<T>
494 /// Requires a PathBuf and returns an empty VecPack<T>
495 pub fn new(path: PathBuf) -> PackResult<VecPack<T>> {
496 // Check whether path is a dir, or a file
497 // If file, then panic!
498 if path.is_file() {
499 panic!(
500 "Given VecPack path is not a dir. Path: {}",
501 &path.to_str().unwrap()
502 );
503 }
504 // If path does not exist,
505 // then create it!
506 if !path.exists() {
507 std::fs::create_dir_all(&path)?;
508 }
509 // Create an empty VecPack<T>
510 Ok(VecPack {
511 data: Vec::new(),
512 path,
513 })
514 }
515 /// Load or init VecPack by a given Path
516 /// If path does not exist,
517 /// then we create it, then loads all the files,
518 /// and tries to deserialize them.
519 /// If a file cannot be read, or cannot be deserialized
520 /// then we panic!
521 pub fn load_or_init(path: PathBuf) -> PackResult<VecPack<T>> {
522 // If path is a file
523 // then panic!
524 if path.is_file() {
525 panic!(
526 "Given VecPack path is not a dir. Path: {}",
527 &path.to_str().unwrap()
528 );
529 }
530 // If path does not exist,
531 // then we create it.
532 if !path.exists() {
533 std::fs::create_dir_all(&path)?;
534 }
535 // Result empty VecPack<T>
536 let mut result: VecPack<T> = VecPack::new(path.clone())?;
537 // First collect all
538 // the file names from path
539 std::fs::read_dir(path.clone())?
540 .filter_map(|file| {
541 file.ok().and_then(|e| {
542 e.path().file_name().and_then(|n| {
543 n.to_str().map(|s| {
544 let mut p = path.clone();
545 p.push(s);
546 p
547 })
548 })
549 })
550 })
551 .collect::<Vec<PathBuf>>()
552 // Then iter over path vector
553 // and try to read and deserialize
554 // them.
555 .iter()
556 .for_each(|path| {
557 // Add deserialized T to VecPack<T>
558 result
559 .insert_pack(
560 // Create Pack<T> from T
561 Pack::<T>::load_from_path(path.clone()).expect(
562 &format!(
563 "Cannot deserialize file with ID: {}",
564 (&path).to_str().unwrap()
565 ),
566 ),
567 )
568 .expect(&format!(
569 "Error while adding file to VecPack with ID: {}",
570 (&path).to_str().unwrap()
571 ));
572 });
573 Ok(result)
574 }
575 /// Insert a new T to VecPack<T>
576 /// Only if ID is not taken
577 pub fn insert(&mut self, item: T) -> PackResult<()> {
578 // Check if ID whether available
579 if !&self.check_id_available(item.get_id()) {
580 return Err(PackError::IDTaken);
581 }
582 // TODO: Move file name creation to a central place!
583 let mut p = (&self.path).clone();
584 p.push(&format!("{}.yml", item.get_id()));
585 let p = Pack {
586 data: item,
587 path: p,
588 };
589 p.save()?;
590 self.data.push(p);
591 Ok(())
592 }
593 // pub fn remove_by_id(&mut self, id: &str) -> PackResult<()> {
594 // match self.iter().position(|i| i.get_id() == id) {
595 // Some(p) => {
596 // std::fs::remove_file(&self.find_id)?
597 // }
598 // None => Err(PackError::ObjectNotFound),
599 // }
600 // }
601 /// Insert Pack<T> to VecPack<T>
602 /// Only if ID is not taken
603 pub fn insert_pack(&mut self, item: Pack<T>) -> PackResult<()> {
604 if !&self.check_id_available(item.get_id()) {
605 return Err(PackError::IDTaken);
606 }
607 self.data.push(item);
608 Ok(())
609 }
610 /// Find ID and returns &Pack<T>
611 /// as an unmutable reference
612 pub fn find_id(&self, id: &str) -> PackResult<&Pack<T>> {
613 match self.iter().position(|i| i.get_id() == id) {
614 Some(p) => Ok(&self.get(p).unwrap()),
615 None => Err(PackError::ObjectNotFound),
616 }
617 }
618 /// Find ID and returns &mut Pack<T>
619 /// as a mutable reference
620 pub fn find_id_mut(&mut self, id: &str) -> PackResult<&mut Pack<T>> {
621 match &mut self.into_iter().position(|i| i.get_id() == id) {
622 Some(p) => Ok(self.as_vec_mut().get_mut(*p).unwrap()),
623 None => Err(PackError::ObjectNotFound),
624 }
625 }
626 /// Check ID is available
627 /// If ID is taken, returns false,
628 /// otherwise returns true
629 pub fn check_id_available(&self, id: &str) -> bool {
630 match self.iter().position(|i| i.get_id() == id) {
631 Some(_) => false,
632 None => true,
633 }
634 }
635 /// Returns data as a mutable
636 /// reference to Vec<Pack<T>>
637 pub fn as_vec_mut(&mut self) -> &mut Vec<Pack<T>> {
638 &mut self.data
639 }
640 /// Returns data as unmutable
641 /// reference to Vec<Pack<T>>
642 pub fn as_vec(&self) -> &Vec<Pack<T>> {
643 &self.data
644 }
645 /// Returns VecPack<T>
646 /// &Path
647 pub fn get_path(&self) -> &Path {
648 &self.path.as_path()
649 }
650}
651
652// Deref implementation for VecPack<T>
653// It returns an unmutable reference to &Vec<Pack<T>>
654impl<T> Deref for VecPack<T>
655where
656 T: VecPackMember,
657{
658 type Target = Vec<Pack<T>>;
659 fn deref(&self) -> &Self::Target {
660 &self.data
661 }
662}
663
664// VecPack mutable iterator
665// It implements Iterator and we use it to
666// get a mutable iterator for VecPack<T>
667// It only holds &'a mut Vec<Pack<T>>.
668pub struct VecPackIterMut<'a, T>
669where
670 T: Serialize + Sized + Clone + 'a,
671{
672 data: &'a mut [Pack<T>],
673}
674
675// Iterator implementation for VecPackIterMut<'a, T>
676// Many thank to Alice from Rust Forum
677//
678// See the thread here:
679// https://users.rust-lang.org/t/magic-lifetime-using-iterator-next/34729/5
680impl<'a, T> Iterator for VecPackIterMut<'a, T>
681where
682 T: Serialize + Sized + Clone + 'a,
683{
684 type Item = &'a mut Pack<T>;
685 fn next(&mut self) -> Option<Self::Item> {
686 let slice = std::mem::replace(&mut self.data, &mut []);
687 match slice.split_first_mut() {
688 Some((head, tail)) => {
689 self.data = tail;
690 Some(head)
691 }
692 None => None,
693 }
694 }
695}
696
697// Implement IntoIter for VecPack<T>
698// TODO: Maybe too dangerous!
699// TODO: Remove this implementation?
700// impl<T> IntoIterator for VecPack<T>
701// where
702// T: Serialize + Sized + Clone,
703// {
704// type Item = Pack<T>;
705// type IntoIter = std::vec::IntoIter<Self::Item>;
706//
707// fn into_iter(self) -> Self::IntoIter {
708// self.data.into_iter()
709// }
710// }
711
712// Implement IntoIter for &'a mut VecPack<T>
713impl<'a, T> IntoIterator for &'a mut VecPack<T>
714where
715 T: VecPackMember,
716{
717 type Item = &'a mut Pack<T>;
718 type IntoIter = VecPackIterMut<'a, T>;
719
720 fn into_iter(self) -> Self::IntoIter {
721 VecPackIterMut {
722 data: &mut self.data,
723 }
724 }
725}
726
727impl<'a, T> PackGuard<'a, T>
728where
729 for<'de> T: Serialize + Deserialize<'de> + Default + Sized + Clone + 'a,
730{
731 pub fn unpack(&mut self) -> &mut T {
732 &mut self.data
733 }
734}
735
736// fn demo(a: &mut VecPack<u32>) {
737// let b = &mut a
738// .into_iter()
739// .map(|i| {
740// (*i.as_mut()) += 1;
741// i.clone()
742// })
743// .collect::<Vec<u32>>();
744// println!("{:?}", b);
745// }