Skip to main content

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// }