rew_data_manager/
lib.rs

1use rew_core::utils;
2use anyhow::{Context, Result};
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use std::fs;
6use std::io::Read;
7use std::path::{Path, PathBuf};
8pub struct DataManager {
9  data_dir: PathBuf,
10}
11
12#[derive(Serialize, Deserialize, Debug)]
13pub enum DataFormat {
14  Text,
15  Json,
16  Yaml,
17  Binary,
18}
19
20impl DataManager {
21  pub fn new(user_id: &str, app_package: &str) -> Result<Self> {
22    let rew_root = utils::get_rew_root();
23    let data_dir = rew_root.join("data").join(user_id).join(app_package);
24
25    // Ensure the directory exists
26    fs::create_dir_all(&data_dir)
27      .with_context(|| format!("Failed to create data directory: {:?}", data_dir))?;
28
29    Ok(Self { data_dir })
30  }
31
32  pub fn get_path(&self, key: &str) -> PathBuf {
33    self.data_dir.join(key)
34  }
35
36  pub fn read(&self, key: &str) -> Result<String> {
37    let path = self.get_path(key);
38    fs::read_to_string(&path).with_context(|| format!("Failed to read data file: {:?}", path))
39  }
40
41  pub fn read_json(&self, key: &str) -> Result<Value> {
42    let content = self.read(key)?;
43    serde_json::from_str(&content)
44      .with_context(|| format!("Failed to parse JSON from data file: {}", key))
45  }
46
47  pub fn write(&self, key: &str, content: &str) -> Result<()> {
48    let path = self.get_path(key);
49
50    // Ensure parent directory exists
51    if let Some(parent) = path.parent() {
52      fs::create_dir_all(parent)
53        .with_context(|| format!("Failed to create parent directory: {:?}", parent))?;
54    }
55
56    fs::write(&path, content).with_context(|| format!("Failed to write data file: {:?}", path))
57  }
58
59  pub fn write_json(&self, key: &str, value: &Value) -> Result<()> {
60    let content = serde_json::to_string_pretty(value)
61      .with_context(|| format!("Failed to serialize JSON for data file: {}", key))?;
62    self.write(key, &content)
63  }
64
65  pub fn delete(&self, key: &str) -> Result<()> {
66    let path = self.get_path(key);
67    if path.exists() {
68      fs::remove_file(&path).with_context(|| format!("Failed to delete data file: {:?}", path))?;
69    }
70    Ok(())
71  }
72
73  pub fn exists(&self, key: &str) -> bool {
74    self.get_path(key).exists()
75  }
76
77  pub fn list(&self, prefix: &str) -> Result<Vec<String>> {
78    let dir = self.data_dir.join(prefix);
79    if !dir.exists() {
80      return Ok(Vec::new());
81    }
82
83    let mut result = Vec::new();
84    self.list_recursive(&dir, &mut result, prefix)?;
85    Ok(result)
86  }
87
88  fn list_recursive(&self, dir: &Path, result: &mut Vec<String>, _prefix: &str) -> Result<()> {
89    if !dir.exists() {
90      return Ok(());
91    }
92
93    for entry in
94      fs::read_dir(dir).with_context(|| format!("Failed to read directory: {:?}", dir))?
95    {
96      let entry = entry?;
97      let path = entry.path();
98
99      if path.is_file() {
100        let rel_path = path
101          .strip_prefix(&self.data_dir)
102          .unwrap_or(&path)
103          .to_string_lossy()
104          .to_string();
105        result.push(rel_path);
106      } else if path.is_dir() {
107        self.list_recursive(&path, result, _prefix)?;
108      }
109    }
110
111    Ok(())
112  }
113
114  // Read binary data
115  pub fn read_binary(&self, key: &str) -> Result<Vec<u8>> {
116    let path = self.get_path(key);
117    fs::read(&path).with_context(|| format!("Failed to read binary data file: {:?}", path))
118  }
119
120  // Write binary data
121  pub fn write_binary(&self, key: &str, data: &[u8]) -> Result<()> {
122    let path = self.get_path(key);
123
124    // Ensure parent directory exists
125    if let Some(parent) = path.parent() {
126      fs::create_dir_all(parent)
127        .with_context(|| format!("Failed to create parent directory: {:?}", parent))?;
128    }
129
130    fs::write(&path, data).with_context(|| format!("Failed to write binary data file: {:?}", path))
131  }
132
133  // Read YAML data
134  pub fn read_yaml(&self, key: &str) -> Result<Value> {
135    let content = self.read(key)?;
136    serde_yaml::from_str(&content)
137      .with_context(|| format!("Failed to parse YAML from data file: {}", key))
138  }
139
140  // Write YAML data
141  pub fn write_yaml(&self, key: &str, value: &Value) -> Result<()> {
142    let content = serde_yaml::to_string(value)
143      .with_context(|| format!("Failed to serialize YAML for data file: {}", key))?;
144    self.write(key, &content)
145  }
146
147  // Get file info including format detection
148  pub fn get_file_info(&self, key: &str) -> Result<(bool, DataFormat)> {
149    let path = self.get_path(key);
150
151    if !path.exists() {
152      return Ok((false, DataFormat::Text)); // Default to text for non-existent files
153    }
154
155    // Try to detect format based on extension
156    let format = if let Some(ext) = path.extension() {
157      match ext.to_str().unwrap_or("").to_lowercase().as_str() {
158        "json" => DataFormat::Json,
159        "yaml" | "yml" => DataFormat::Yaml,
160        "bin" | "dat" => DataFormat::Binary,
161        _ => {
162          // Try to detect based on content
163          self.detect_format(&path)?
164        }
165      }
166    } else {
167      // No extension, try to detect based on content
168      self.detect_format(&path)?
169    };
170
171    Ok((true, format))
172  }
173
174  // Detect format based on file content
175  fn detect_format(&self, path: &Path) -> Result<DataFormat> {
176    // Read a small sample of the file
177    let mut file = fs::File::open(path)?;
178    let mut buffer = [0u8; 512]; // Read first 512 bytes
179    let bytes_read = file.read(&mut buffer)?;
180
181    if bytes_read == 0 {
182      return Ok(DataFormat::Text); // Empty file, default to text
183    }
184
185    // Check for binary content
186    let sample = &buffer[..bytes_read];
187    if sample
188      .iter()
189      .any(|&b| b < 9 || (b > 13 && b < 32 && b != 27))
190    {
191      return Ok(DataFormat::Binary);
192    }
193
194    // Try to parse as JSON
195    let content = String::from_utf8_lossy(sample);
196    if (content.trim_start().starts_with('{') || content.trim_start().starts_with('['))
197      && serde_json::from_str::<Value>(&content).is_ok()
198    {
199      return Ok(DataFormat::Json);
200    }
201
202    // Try to parse as YAML
203    if content.contains(':')
204      && !content.contains('{')
205      && serde_yaml::from_str::<Value>(&content).is_ok()
206    {
207      return Ok(DataFormat::Yaml);
208    }
209
210    // Default to text
211    Ok(DataFormat::Text)
212  }
213}