cyfs_util/cache/
tracker_cache.rs

1use cyfs_base::*;
2
3use async_trait::async_trait;
4use int_enum::IntEnum;
5use std::convert::TryFrom;
6use std::str::FromStr;
7use std::sync::Arc;
8
9#[repr(u8)]
10#[derive(Debug, Clone, Copy, Eq, PartialEq, IntEnum)]
11pub enum TrackerDirection {
12    Unknown = 0,
13    From = 1,
14    To = 2,
15    Store = 3,
16}
17
18impl Into<u8> for TrackerDirection {
19    fn into(self) -> u8 {
20        unsafe { std::mem::transmute(self as u8) }
21    }
22}
23
24impl From<u8> for TrackerDirection {
25    fn from(code: u8) -> Self {
26        match TrackerDirection::from_int(code) {
27            Ok(code) => code,
28            Err(e) => {
29                error!("unknown TrackerDirection code: {} {}", code, e);
30                TrackerDirection::Unknown
31            }
32        }
33    }
34}
35
36// path: [range_begin, range_end)
37#[derive(Debug, Eq, PartialEq, Clone)]
38pub struct PostionFileRange {
39    pub path: String,
40    pub range_begin: u64,
41    pub range_end: u64,
42}
43
44impl PostionFileRange {
45    pub fn encode(&self) -> String {
46        format!("{}:{}:{}", self.range_begin, self.range_end, self.path)
47    }
48
49    pub fn decode(value: &str) -> BuckyResult<Self> {
50        let parts: Vec<&str> = value.split(':').collect();
51        let range_begin = u64::from_str(parts[0]).map_err(|e| {
52            let msg = format!("invalid range_begin string: {}, {}", parts[0], e);
53            error!("{}", msg);
54            BuckyError::new(BuckyErrorCode::InvalidFormat, msg)
55        })?;
56
57        let range_end = u64::from_str(parts[1]).map_err(|e| {
58            let msg = format!("invalid range_end string: {}, {}", parts[0], e);
59            error!("{}", msg);
60            BuckyError::new(BuckyErrorCode::InvalidFormat, msg)
61        })?;
62
63        let path = parts[2..].join(":");
64        Ok(Self {
65            path,
66            range_begin,
67            range_end,
68        })
69    }
70}
71
72impl ToString for PostionFileRange {
73    fn to_string(&self) -> String {
74        self.encode()
75    }
76}
77
78impl FromStr for PostionFileRange {
79    type Err = BuckyError;
80
81    fn from_str(value: &str) -> Result<Self, Self::Err> {
82        PostionFileRange::decode(value)
83    }
84}
85
86#[derive(Debug, Eq, PartialEq, Clone)]
87pub enum TrackerPostion {
88    Unknown(String),
89    Device(DeviceId),
90    File(String),
91    FileRange(PostionFileRange),
92    ChunkManager,
93}
94
95impl Into<(u8, String)> for TrackerPostion {
96    fn into(self) -> (u8, String) {
97        match self {
98            Self::Unknown(v) => (0, v),
99            Self::Device(device_id) => (1, device_id.to_string()),
100            Self::File(v) => (2, v),
101            Self::FileRange(v) => (3, v.to_string()),
102            TrackerPostion::ChunkManager => (4, "ChunkManager".to_string())
103        }
104    }
105}
106
107impl TryFrom<(u8, String)> for TrackerPostion {
108    type Error = BuckyError;
109
110    fn try_from((code, value): (u8, String)) -> Result<Self, Self::Error> {
111        let ret = match code {
112            0 => Self::Unknown(value),
113            1 => {
114                let device_id = DeviceId::from_str(&value).map_err(|e| {
115                    let msg = format!("invalid device_id string: {}, {}", value, e);
116                    error!("{}", msg);
117                    BuckyError::new(BuckyErrorCode::InvalidFormat, msg)
118                })?;
119
120                Self::Device(device_id)
121            }
122            2 => Self::File(value),
123            3 => {
124                let file_range = PostionFileRange::from_str(&value)?;
125                Self::FileRange(file_range)
126            }
127            4 => {
128                Self::ChunkManager
129            }
130            _ => {
131                error!("unknown TrackerPostion code: {}", code);
132                Self::Unknown(value)
133            }
134        };
135
136        Ok(ret)
137    }
138}
139
140pub struct AddTrackerPositonRequest {
141    pub id: String,
142    pub direction: TrackerDirection,
143    pub pos: TrackerPostion,
144    pub flags: u32,
145}
146
147pub struct RemoveTrackerPositionRequest {
148    pub id: String,
149    pub direction: Option<TrackerDirection>,
150    pub pos: Option<TrackerPostion>,
151}
152
153pub struct GetTrackerPositionRequest {
154    pub id: String,
155    pub direction: Option<TrackerDirection>,
156}
157
158#[derive(Debug)]
159pub struct TrackerPositionCacheData {
160    pub direction: TrackerDirection,
161    pub pos: TrackerPostion,
162    pub insert_time: u64,
163    pub flags: u32,
164}
165
166#[async_trait]
167pub trait TrackerCache: Sync + Send + 'static {
168    fn clone(&self) -> Box<dyn TrackerCache>;
169
170    async fn add_position(&self, req: &AddTrackerPositonRequest) -> BuckyResult<()>;
171    async fn remove_position(&self, req: &RemoveTrackerPositionRequest) -> BuckyResult<usize>;
172
173    async fn get_position(
174        &self,
175        req: &GetTrackerPositionRequest,
176    ) -> BuckyResult<Vec<TrackerPositionCacheData>>;
177}
178
179pub type TrackerCacheRef = Arc<Box<dyn TrackerCache>>;
180
181#[cfg(test)]
182mod tests {
183    use crate::*;
184    use std::convert::TryFrom;
185    use std::str::FromStr;
186
187    #[test]
188    fn test_file_range() {
189        let item = PostionFileRange {
190            path: "xxxxxx:xxxx".to_owned(),
191            range_begin: 1000,
192            range_end: 2000,
193        };
194
195        let value = item.to_string();
196        println!("{}", value);
197
198        let r_item = PostionFileRange::from_str(&value).unwrap();
199        assert!(r_item.path == item.path);
200        assert!(r_item.range_begin == item.range_begin);
201        assert!(r_item.range_end == item.range_end);
202
203        let r_item2 = r_item.clone();
204        let pos = TrackerPostion::FileRange(r_item);
205        let value: (u8, String) = pos.into();
206        let r_pos = TrackerPostion::try_from(value).unwrap();
207        if let TrackerPostion::FileRange(v) = r_pos {
208            assert!(v == r_item2);
209        } else {
210            assert!(false);
211        }
212    }
213}