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#[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}