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
use parking_lot::RwLock;

use crate::{
    AreaCode, AreaCodeData, AreaCodeItem, AreaCodeProvider, AreaCodeRelatedItem, AreaGeo,
    AreaGeoData, AreaGeoProvider, AreaResult, AreaSearchItem,
};

pub trait AreaDataProvider: Sized + 'static {
    fn code_data(&self) -> AreaResult<Vec<AreaCodeData>>;
    fn geo_data(&self) -> AreaResult<Vec<AreaGeoData>>;
    fn code_data_version(&self) -> String;
    fn geo_data_version(&self) -> String;
}

pub trait AreaStoreProvider: Sized + 'static {
    type C: AreaCodeProvider;
    type G: AreaGeoProvider;
    fn create_code(&self) -> AreaResult<AreaCode<Self::C>>;
    fn create_geo(&self) -> AreaResult<AreaGeo<Self::G>>;
}

pub struct Area<DO: AreaStoreProvider, DD: AreaDataProvider> {
    data_provider: DD,
    code: RwLock<AreaCode<DO::C>>,
    geo: RwLock<AreaGeo<DO::G>>,
}

impl<DO: AreaStoreProvider, DD: AreaDataProvider> Area<DO, DD> {
    pub fn new(dao_provider: DO, data_provider: DD) -> AreaResult<Self> {
        let code = dao_provider.create_code()?;
        let geo = dao_provider.create_geo()?;
        let out = Self {
            data_provider,
            code: RwLock::new(code),
            geo: RwLock::new(geo),
        };
        out.code_reload()?;
        out.geo_reload()?;
        Ok(out)
    }
    pub fn code_reload(&self) -> AreaResult<()> {
        let data_ver = self.data_provider.code_data_version();
        if data_ver.is_empty() || !self.code.read().version_match(&data_ver) {
            self.code
                .write()
                .load_data(self.data_provider.code_data()?, &data_ver)?;
        }
        Ok(())
    }
    pub fn geo_reload(&self) -> AreaResult<()> {
        let data_ver = self.data_provider.geo_data_version();
        if data_ver.is_empty() || !self.geo.read().version_match(&data_ver) {
            self.geo
                .write()
                .load_data(self.data_provider.geo_data()?, &data_ver)?;
        }
        Ok(())
    }
    pub fn code_childs(&self, code: &str) -> AreaResult<Vec<AreaCodeItem>> {
        self.code.read().childs(code).map(|mut e| {
            e.sort_by(|a, b| a.code.cmp(&b.code));
            e
        })
    }
    pub fn code_find(&self, code: &str) -> AreaResult<Vec<AreaCodeItem>> {
        self.code.read().find(code)
    }
    pub fn code_related(&self, code: &str) -> AreaResult<Vec<Vec<AreaCodeRelatedItem>>> {
        self.code.read().related(code).map(|e| {
            e.into_iter()
                .map(|mut ie| {
                    ie.sort_by(|a, b| a.item.code.cmp(&b.item.code));
                    ie
                })
                .collect::<Vec<_>>()
        })
    }
    pub fn code_search(&self, name: &str, limit: usize) -> AreaResult<Vec<AreaSearchItem>> {
        if name.trim().is_empty() {
            let mut out = Vec::with_capacity(limit);
            for tmp in self.code.read().childs("")? {
                out.push(AreaSearchItem {
                    item: vec![tmp],
                    source: 1.0,
                })
            }
            out.truncate(limit);
            return Ok(out);
        }
        self.code.read().search(name, limit)
    }
    pub fn geo_search(&self, lat: f64, lng: f64) -> AreaResult<Vec<AreaCodeItem>> {
        if !(0.0..=90.0).contains(&lat) || !(0.0..=180.0).contains(&lng) {
            return Ok(vec![]);
        }
        let tmp = self.geo.read();
        let code = tmp.search(&geo::coord! { x:lng, y:lat})?;
        self.code.read().find(&code)
    }
}