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
use std::marker::PhantomData;

use lazy_db::*;
use soulog::*;

pub fn write<T>(list: &[T], f: impl Fn(FileWrapper, &T) -> Result<(), LDBError>, container: &LazyContainer, mut logger: impl Logger) {
    if_err!((logger) [ListIO, err => ("While clearing container: {err:?}")] retry container.wipe());

    for (i, x) in list.iter().enumerate() {
        let data_writer = 
            if_err!((logger) [ListIO, err => ("While writing element of list: {:?}", err)] retry container.data_writer(i.to_string()));
        if_err!((logger) [ListIO, err => ("While writing element of list: {:?}", err)] {f(data_writer, x)} crash {
            log!((logger.error) ListIO("{err:#?}") as Fatal);
            logger.crash()
        })
    }

    if_err!((logger) [ListIO, err => ("{:?}", err)] retry {
        let data_writer = if_err!((logger) [ListIO, err => ("While writing list length: {:?}", err)] retry container.data_writer("length"));
        LazyData::new_u16(data_writer, list.len() as u16)
    })
}

pub fn push(f: impl Fn(FileWrapper) -> Result<(), LDBError>, container: &LazyContainer, mut logger: impl Logger) {
    let length = load_length(container, logger.hollow());

    let data_writer = if_err!((logger) [ListIO, err => ("While pushing to list: {:?}", err)] retry container.data_writer(length.to_string()));
    if_err!((logger) [ListIO, err => ("While pushing to list: {:?}", err)] {f(data_writer)} crash {
        log!((logger.error) ListIO("{err:#?}") as Fatal);
        logger.crash()
    });

    if_err!((logger) [ListIO, err => ("{:?}", err)] retry {
        let data_writer = if_err!((logger) [ListIO, err => ("While writing list length: {:?}", err)] retry container.data_writer("length"));
        LazyData::new_u16(data_writer, length + 1)
    })
}

pub fn pop<T>(f: impl Fn(LazyData) -> Result<T, LDBError>, container: &LazyContainer, mut logger: impl Logger) -> Option<T> {
    let length = load_length(container, logger.hollow());

    if length == 0 {
        return None;
    }

    let idx = (length - 1).to_string();
    let item = if_err!((logger) [ListIO, err => ("While reading list element: {:?}", err)] retry container.read_data(&idx));
    let item = if_err!((logger) [ListIO, err => ("While reading list element: {:?}", err)] {f(item)} crash {
        log!((logger.error) ListIO("{err:#?}") as Fatal);
        logger.crash()
    });

    if_err!((logger) {container.remove(idx)} else(err) {
        log!((logger.vital) ListIO("While removing item: {err:?}; ignoring error...") as Inconvenience)
    });

    if_err!((logger) [ListIO, err => ("{:?}", err)] retry {
        let data_writer = if_err!((logger) [ListIO, err => ("While writing list length: {:?}", err)] retry container.data_writer("length"));
        LazyData::new_u16(data_writer, length - 1)
    });

    Some(item)
}

pub fn read<T>(f: impl Fn(LazyData) -> Result<T, LDBError>, container: &LazyContainer, mut logger: impl Logger) -> Box<[T]> {
    let length = load_length(container, logger.hollow());

    let mut list = Vec::<T>::with_capacity(length as usize);

    for i in 0..length {
        let item = if_err!((logger) [ListIO, err => ("While reading list element: {:?}", err)] retry container.read_data(i.to_string()));
        let item = if_err!((logger) [ListIO, err => ("While reading list element: {:?}", err)] {f(item)} crash {
            log!((logger.error) ListIO("{err:#?}") as Fatal);
            logger.crash()
        });
        list.push(item)
    }

    list.into_boxed_slice()
}

pub fn load_length(container: &LazyContainer, mut logger: impl Logger) -> u16 {
    let length = if_err!((logger) [ListIO, err => ("While reading list length: {:?}", err)] retry container.read_data("length"));
    if_err!((logger) [ListIO, err => ("While reading list length: {:?}", err)] {length.collect_u16()} crash {
        log!((logger.error) ListIO("{err:#?}") as Fatal);
        logger.crash()
    })
}

pub struct List<T, L: Logger, F: Fn(LazyData) -> Result<T, LDBError>> {
    _phantom_marker: PhantomData<T>,
    read_f: F,
    container: LazyContainer,
    logger: L,
    length: u16,
    idx: u16,
}

impl<T, L: Logger, F: Fn(LazyData) -> Result<T, LDBError>> List<T, L, F> {
    pub fn init(container: LazyContainer, read_f: F, mut logger: L) -> Self {
        if_err!((logger) [List, err => ("While inititalising list: {err:?}")] retry write_container!((container) length = new_u16(0)));
        Self {
            _phantom_marker: PhantomData,
            container,
            read_f,
            logger: logger.hollow(),
            length: 0,
            idx: 0,
        }
    }

    pub fn load(container: LazyContainer, read_f: F, mut logger: L) -> Self {
        let length = if_err!((logger) [List, err => ("While loading list: {err:?}")] retry 
            if_err!((logger) [List, err => ("While loading list: {err:?}")] retry
                search_container!((container) length)
            ).collect_u16()
        );

        Self {
            _phantom_marker: PhantomData,
            container,
            read_f,
            length,
            logger: logger.hollow(),
            idx: 0,
        }
    }
}

impl<T, L:Logger, F: Fn(LazyData) -> Result<T, LDBError>> Iterator for List<T, L, F> {
    type Item = T;

    fn next(&mut self) -> Option<T> {
        if self.idx >= self.length { return None };
        let mut logger = self.logger.hollow();
        let f: &F = &self.read_f;

        let item = if_err!((logger) [ListIO, err => ("While reading list element: {:?}", err)] retry self.container.read_data(self.idx.to_string()));
        Some(if_err!((logger) [ListIO, err => ("While reading list element: {:?}", err)] {f(item)} crash {
            log!((logger.error) ListIO("{err:#?}") as Fatal);
            logger.crash()
        }))
    }
}