1#[macro_use]
2extern crate quick_error;
3extern crate serde;
4#[macro_use]
5extern crate serde_derive;
6extern crate serde_json;
7
8use std::env;
9use std::fs::File;
10use std::path::{Path, PathBuf};
11
12#[cfg(not(target_os = "windows"))]
13fn get_config_path() -> Option<PathBuf> {
14 let home = match env::home_dir() {
15 Some(path) => path,
16 None => return None,
17 };
18 let path = Path::new(&home).join(".dropbox/info.json");
19 if path.is_file() {
20 Some(path)
21 } else {
22 None
23 }
24}
25
26#[cfg(target_os = "windows")]
27fn get_config_path() -> Option<PathBuf> {
28 const CFG_PATH_SUFFIX: &'static str = "Dropbox/info.json";
29 let mut appdata = String::new();
30 let mut localappdata = String::new();
31 for (key, value) in env::vars() {
32 if key == "APPDATA" {
33 appdata = value;
34 } else if key == "LOCALAPPDATA" {
35 localappdata = value;
36 }
37 if appdata != "" && localappdata != "" {
38 break;
39 }
40 }
41
42 let roamingpath = Path::new(&appdata).join(CFG_PATH_SUFFIX);
43 let localpath = Path::new(&localappdata).join(CFG_PATH_SUFFIX);
44 if roamingpath.is_file() {
45 Some(roamingpath)
46 } else if localpath.is_file() {
47 Some(localpath)
48 } else {
49 None
50 }
51}
52
53#[derive(Debug, Deserialize)]
54struct Account {
55 path: String,
56 host: i64,
57 is_team: bool,
58 subscription_type: String,
59}
60
61#[derive(Debug, Deserialize)]
62pub struct Info {
63 personal: Option<Account>,
64 business: Option<Account>,
65}
66
67quick_error! {
68 #[derive(Debug)]
69 pub enum Error {
70 NotConfiguredError {
71 description("Dropbox not configured")
72 }
73 CantReadConfigError{
74 from(std::io::Error)
75 description("can't read configuration")
76 }
77 InvalidConfigError{
78 from(serde_json::Error)
79 description("configuration invalid")
80 }
81 AccountNotConfiguredError {
82 description("account type not configured")
83 }
84 }
85}
86
87pub fn read_info() -> Result<Info, Error> {
89 let cfg_path = match get_config_path() {
90 Some(path) => path,
91 None => return Err(Error::NotConfiguredError),
92 };
93 let file = File::open(cfg_path)?;
94 let info: Info = serde_json::from_reader(file).unwrap();
95 Ok(info)
96}
97
98fn get_dir(account_type: &str) -> Result<String, Error> {
99 let info = read_info()?;
100 let data = match account_type {
101 "personal" => info.personal,
102 "business" => info.business,
103 _ => unreachable!(),
104 };
105 if let Some(account) = data {
106 Ok(account.path.clone())
107 } else {
108 Err(Error::AccountNotConfiguredError)
109 }
110}
111
112pub fn personal_dir() -> Result<String, Error> {
114 get_dir("personal")
115}
116
117pub fn business_dir() -> Result<String, Error> {
119 get_dir("business")
120}
121
122pub struct SmartPath {
123 local_root: PathBuf,
124 target: PathBuf,
125}
126
127impl SmartPath {
128 fn new<S>(root: S, path: S) -> SmartPath
129 where
130 S: Into<String>,
131 {
132 let raw_target = PathBuf::from(path.into());
133 let target: PathBuf = if raw_target.starts_with("/") {
134 raw_target.strip_prefix("/").unwrap().into()
135 } else {
136 raw_target.clone()
137 };
138 SmartPath {
139 local_root: PathBuf::from(root.into()),
140 target: target,
141 }
142 }
143
144 pub fn new_personal<S>(path: S) -> Result<SmartPath, Error>
147 where
148 S: Into<String>,
149 {
150 let root = personal_dir()?;
151 Ok(SmartPath::new(root, path.into()))
152 }
153
154 pub fn new_business<S>(path: S) -> Result<SmartPath, Error>
157 where
158 S: Into<String>,
159 {
160 let root = business_dir()?;
161 Ok(SmartPath::new(root, path.into()))
162 }
163
164 pub fn local(&self) -> PathBuf {
166 if self.target.components().count() > 0 {
167 self.local_root.join(&self.target)
168 } else {
169 self.local_root.clone()
170 }
171 }
172
173 pub fn remote(&self) -> PathBuf {
175 PathBuf::from("/").join(&self.target)
176 }
177}