1use std::{
2 collections::HashMap,
3 fmt::Display,
4 io::Write,
5 ops::{Deref, DerefMut},
6 path::PathBuf,
7};
8
9use serde::{Deserialize, Serialize};
10
11use crate::{cookie::CookieJar, endpoint::Headers, Ctx, PairMap};
12
13#[derive(Default, Debug, Clone, Serialize, Deserialize)]
14pub struct Variables(pub HashMap<String, String>);
15
16impl Deref for Variables {
17 type Target = HashMap<String, String>;
18
19 fn deref(&self) -> &Self::Target {
20 &self.0
21 }
22}
23
24impl DerefMut for Variables {
25 fn deref_mut(&mut self) -> &mut Self::Target {
26 &mut self.0
27 }
28}
29
30impl Display for Variables {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 for (key, value) in self.iter() {
33 writeln!(f, "{key}={value}")?;
34 }
35
36 Ok(())
37 }
38}
39
40impl PairMap<'_> for Variables {
41 const NAME: &'static str = "variable";
42
43 fn map(&mut self) -> &mut HashMap<String, String> {
44 &mut self.0
45 }
46}
47
48impl Variables {
49 pub fn parse(file_content: &str) -> Self {
50 let mut variables = Variables::default();
51
52 for var in file_content.split('\n').filter(|line| !line.is_empty()) {
53 variables.set(var);
54 }
55
56 variables
57 }
58}
59
60#[derive(Clone, Serialize, Deserialize)]
61pub struct Env {
62 pub name: String,
63 pub variables: Variables,
64 pub headers: Headers,
65}
66
67impl Default for Env {
68 fn default() -> Self {
69 Self {
70 name: String::from("default"),
71 variables: Variables::default(),
72 headers: Headers::default(),
73 }
74 }
75}
76
77impl Env {
78 pub fn new(name: &str) -> Self {
79 Self {
80 name: name.to_string(),
81 ..Default::default()
82 }
83 }
84
85 pub fn dir(&self, ctx: &Ctx) -> PathBuf {
86 ctx.path().join("env").join(&self.name)
87 }
88
89 pub fn write(&self, ctx: &Ctx) -> Result<(), Box<dyn std::error::Error>> {
90 let dir = self.dir(ctx);
91
92 std::fs::create_dir(dir)?;
93
94 self.update(ctx)?;
95
96 Ok(())
97 }
98
99 pub fn update(&self, ctx: &Ctx) -> Result<(), Box<dyn std::error::Error>> {
100 let mut var_file = std::fs::OpenOptions::new()
101 .create(true)
102 .write(true)
103 .truncate(true)
104 .open(self.dir(ctx).join("variables"))?;
105 let mut headers_file = std::fs::OpenOptions::new()
106 .create(true)
107 .write(true)
108 .truncate(true)
109 .open(self.dir(ctx).join("headers"))?;
110
111 if !self.variables.is_empty() {
112 var_file.write_all(format!("{}", self.variables).as_bytes())?;
113 }
114 if !self.headers.0.is_empty() {
115 headers_file.write_all(format!("{}", self.headers).as_bytes())?;
116 }
117
118 Ok(())
119 }
120
121 pub fn exists(&self, ctx: &Ctx) -> bool {
123 self.dir(ctx).exists()
124 }
125
126 pub fn parse(ctx: &Ctx, name: &str) -> Result<Self, Box<dyn std::error::Error>> {
127 let mut env = Self::new(name);
128
129 if let Ok(var_contents) = std::fs::read_to_string(env.dir(ctx).join("variables")) {
130 env.variables = Variables::parse(&var_contents);
131 }
132 if let Ok(header_contents) = std::fs::read_to_string(env.dir(ctx).join("headers")) {
133 env.headers = Headers::parse(&header_contents);
134 }
135
136 Ok(env)
137 }
138
139 pub fn cookie_jar(&self, ctx: &Ctx) -> CookieJar {
140 let path = self.dir(ctx).join(CookieJar::FILENAME);
141 let mut jar = CookieJar::read(&path).unwrap_or_default();
142
143 jar.path = path;
144
145 jar
146 }
147}