helixlauncher_core/
instance.rs1use std::{
2 fs::{self, File},
3 io::BufReader,
4 path::{Path, PathBuf},
5};
6
7use serde::{de::DeserializeOwned, Deserialize, Serialize};
8
9#[derive(Serialize, Deserialize, Debug)]
10pub struct Instance {
11 pub name: String,
12 pub components: Vec<Component>,
13 pub launch: InstanceLaunch,
14}
15
16#[derive(Default, Serialize, Deserialize, Debug)]
17pub struct InstanceLaunch {
18 pub args: Vec<String>,
19 pub jvm_args: Vec<String>,
20 pub prelaunch_command: Option<String>,
21 pub postlaunch_command: Option<String>,
22 pub allocation: Option<RamAllocation>,
23 }
25
26type Mebibytes = u32;
27
28#[derive(Serialize, Deserialize, Debug)]
29pub struct RamAllocation {
30 min: Mebibytes,
31 max: Mebibytes,
32}
33
34#[derive(Serialize, Deserialize, Debug)]
35pub struct Component {
36 pub id: String,
37 pub version: String,
38}
39
40const INSTX_CONFIG_NAME: &str = "instance.helix.json";
41const SUBDIR_CONFIG_NAME: &str = "directory.helix.json";
42
43impl Instance {
44 pub fn new(name: String, mc_version: String, launch: InstanceLaunch, instances_dir: &Path) -> Self {
52 let instance = Self { name, components: vec![Component {id: String::from("net.minecraft"), version: mc_version}], launch };
53
54 let mut instance_dir = instances_dir.join(&instance.name);
56 if instance_dir.try_exists().unwrap() {
57 todo!("Resolve folder collision (1)");
58 }
59
60 fs::create_dir_all(instance_dir.join(".minecraft")).unwrap();
62
63 let instance_json = File::create(instance_dir.join(INSTX_CONFIG_NAME)).unwrap();
65 serde_json::to_writer_pretty(instance_json, &instance).unwrap();
66
67 instance
68 }
69
70 pub fn from_path<P: AsRef<Path>>(path: P) -> Self {
77 if !InstanceFolderSearchItems::is_instance(&path) {
78 panic!("put a real option/result here!");
79 }
80 let instance_json = path.as_ref().join(INSTX_CONFIG_NAME);
81 read_json_file(&instance_json)
82 }
83
84 pub fn list_instances<P: AsRef<Path>>(instances_dir: P) -> Vec<Self> {
85 fs::read_dir(instances_dir)
86 .unwrap()
87 .map(|i| Self::from_path(i.unwrap().path()))
88 .collect()
89 }
90}
91
92fn read_json_file<T: DeserializeOwned>(path: &Path) -> T {
93 let file = BufReader::new(File::open(path).unwrap());
94 serde_json::from_reader(file).unwrap()
95}
96
97#[derive(Serialize, Deserialize)]
98pub struct InstanceDirectory {
99 name: String,
100 children: Vec<String>,
101 relative_path: PathBuf,
102}
103impl InstanceDirectory {
104 pub fn base(instances_dir: PathBuf) -> Self {
110 let directory_json = instances_dir.join(SUBDIR_CONFIG_NAME);
111
112 if instances_dir.try_exists().unwrap() {
116 read_json_file(&directory_json)
117 } else {
118 let inst_dir = Self {
120 name: String::from("Base Directory"),
121 children: vec![],
122 relative_path: PathBuf::from("."),
123 };
124 let directory_json = File::create(directory_json).unwrap();
125 serde_json::to_writer_pretty(directory_json, &inst_dir).unwrap();
126
127 inst_dir
128 }
129 }
130 pub fn new(name: String, parent: &mut Self, instances_dir: PathBuf) -> Self {
131 let inst_dir = Self {
132 name: name.clone(),
133 children: vec![],
134 relative_path: parent.relative_path.join(name),
135 };
136
137 let instance_dir = instances_dir.join(inst_dir.relative_path.clone());
139
140 fs::create_dir_all(&instance_dir).unwrap();
141
142 let directory_json = instance_dir.join(SUBDIR_CONFIG_NAME);
144 let directory_json = File::create(directory_json).unwrap();
145 serde_json::to_writer_pretty(directory_json, &inst_dir).unwrap();
146
147 parent.children.push(inst_dir.name.clone());
149 let parent_dir = instances_dir.join(parent.relative_path.clone());
150 let directory_json = parent_dir.join(SUBDIR_CONFIG_NAME);
151 let directory_json = File::create(directory_json).unwrap();
152 serde_json::to_writer_pretty(directory_json, &parent).unwrap();
153
154 inst_dir
155 }
156}
157enum InstanceFolderSearchItems {
158 InstanceDir,
159 DirectoryDir,
160 UnknownDir,
161}
162impl InstanceFolderSearchItems {
163 fn identify_item<P: AsRef<Path>>(path: P) -> Self {
164 fs::read_dir(path)
166 .unwrap()
167 .find_map(|file| {
168 let file_name = file.unwrap().file_name();
169 if file_name == INSTX_CONFIG_NAME {
170 Some(InstanceFolderSearchItems::InstanceDir)
171 } else if file_name == SUBDIR_CONFIG_NAME {
172 Some(InstanceFolderSearchItems::DirectoryDir)
173 } else {
174 None
175 }
176 })
177 .unwrap_or(InstanceFolderSearchItems::UnknownDir)
178 }
179 fn is_instance<P: AsRef<Path>>(path: P) -> bool {
180 fs::read_dir(path)
182 .unwrap()
183 .any(|file| file.unwrap().file_name() == INSTX_CONFIG_NAME)
184 }
185}
186