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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use std::env::current_dir;
use std::path::PathBuf;
use anyhow::Result;
use serde::{Deserialize, Serialize};
use tokio::fs::{self, File};
use tokio::io::AsyncWriteExt;
pub static VALID_HOP_FILENAMES: &[&str] = &[
"hop.yml",
"hop.yaml",
"hop.json",
".hoprc",
".hoprc.yml",
".hoprc.yaml",
".hoprc.json",
];
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct HopFile {
pub version: u8,
pub config: HopFileConfig,
#[serde(skip)]
pub path: PathBuf,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct HopFileConfig {
pub project_id: String,
pub deployment_id: String,
}
impl HopFile {
pub fn new(path: PathBuf, project: String, deployment: String) -> HopFile {
HopFile {
version: 1,
config: HopFileConfig {
project_id: project,
deployment_id: deployment,
},
path,
}
}
fn serialize(path: PathBuf, content: Self) -> Option<String> {
match path.extension() {
Some(ext) => match ext.to_str() {
Some("yml") | Some("yaml") => serde_yaml::to_string(&content).ok(),
Some("json") => serde_json::to_string(&content).ok(),
_ => None,
},
None => {
if let Ok(s) = serde_yaml::to_string(&content) {
Some(s)
} else if let Ok(s) = serde_json::to_string(&content) {
Some(s)
} else {
None
}
}
}
}
fn deserialize(path: PathBuf, content: &str) -> Option<Self> {
let hopfile: Option<Self> = match path.extension() {
Some(ext) => match ext.to_str() {
Some("yml") | Some("yaml") => serde_yaml::from_str(content).ok(),
Some("json") => serde_json::from_str(content).ok(),
_ => None,
},
None => {
if let Ok(s) = serde_yaml::from_str(content) {
Some(s)
} else if let Ok(s) = serde_json::from_str(content) {
Some(s)
} else {
None
}
}
};
hopfile.map(|mut hopfile| {
hopfile.path = path;
hopfile
})
}
pub async fn find(mut path: PathBuf) -> Option<Self> {
loop {
for filename in VALID_HOP_FILENAMES {
let file_path = path.join(filename);
if file_path.exists() {
let content = fs::read_to_string(file_path.clone()).await.ok()?;
return Self::deserialize(file_path, &content);
}
}
if !path.pop() {
break;
}
}
None
}
#[inline]
pub async fn find_current() -> Option<Self> {
Self::find(current_dir().ok()?).await
}
pub async fn save(self) -> Result<Self> {
let path = self.path.clone();
let content =
Self::serialize(path.clone(), self.clone()).expect("Failed to serialize hop file");
let mut file = File::create(&path).await?;
file.write_all(content.as_bytes()).await?;
log::info!("Saved hop file to {}", path.display());
Ok(self)
}
}