1use std::fs::{self, File};
3use std::io::{Write, BufRead, BufReader};
4use indoc::indoc;
5use crate::context::Context;
6use std::path::PathBuf;
7use std::error::Error;
8
9pub fn create_source_files(context: &Context) -> Result<(), Box<dyn std::error::Error>> {
10 let src_path = context.project_path.join("src");
11 fs::create_dir(&src_path)?;
12
13 match context.template_name.as_deref() {
14 Some("GuiApplication") => {
15 create_file_from_template(&src_path, "Main.cpp", MAIN_CPP_TEMPLATE)?;
17 create_file_from_template(
18 &src_path,
19 "MainComponent.cpp",
20 MAIN_COMPONENT_CPP_TEMPLATE,
21 )?;
22 create_file_from_template(&src_path, "MainComponent.h", MAIN_COMPONENT_H_TEMPLATE)?;
23 create_file_from_template(&src_path, "CMakeLists.txt", GUI_APP_CMAKE_TEMPLATE)?;
24 }
25 Some("AudioPlugin") => {
26 create_file_from_template(
28 &src_path,
29 "PluginProcessor.cpp",
30 PLUGIN_PROCESSOR_CPP_TEMPLATE,
31 )?;
32 create_file_from_template(
33 &src_path,
34 "PluginProcessor.h",
35 PLUGIN_PROCESSOR_H_TEMPLATE,
36 )?;
37 create_file_from_template(&src_path, "PluginEditor.cpp", PLUGIN_EDITOR_CPP_TEMPLATE)?;
38 create_file_from_template(&src_path, "PluginEditor.h", PLUGIN_EDITOR_H_TEMPLATE)?;
39 create_file_from_template(&src_path, "CMakeLists.txt", AUDIO_PLUGIN_CMAKE_TEMPLATE)?;
40 }
41 Some("ConsoleApp") => {
42 create_file_from_template(&src_path, "Main.cpp", CONSOLE_APP_MAIN_CPP_TEMPLATE)?;
44 create_file_from_template(&src_path, "CMakeLists.txt", CONSOLE_APP_CMAKE_TEMPLATE)?;
45 }
46 _ => {
47 return Err(format!("Unknown template: {:?}", context.template_name).into());
48 }
49 }
50 Ok(())
51}
52
53pub fn add_class(context: &Context, element_type: &str, element_name: &str) -> Result<(), Box<dyn Error>> {
55 let src_path = context.project_path.join("src");
57
58 let mut adjusted_element_name = element_name.to_string();
60 let (header_template, cpp_template) = match element_type {
61 "class" => (CLASS_H_TEMPLATE, CLASS_CPP_TEMPLATE),
62 "component" => {
63 adjusted_element_name.push_str("Component");
65 (COMPONENT_H_TEMPLATE, COMPONENT_CPP_TEMPLATE)
66 },
67 _ => return Err(format!("Invalid element type: {}", element_type).into()),
68 };
69
70 let header_file_name = format!("{}.h", adjusted_element_name);
72 let cpp_file_name = format!("{}.cpp", adjusted_element_name);
73
74 let header_path = src_path.join(&header_file_name);
76 let cpp_path = src_path.join(&cpp_file_name);
77
78 if header_path.exists() || cpp_path.exists() {
79 return Err(format!("{} '{}' already exists in the project.", element_type, adjusted_element_name).into());
80 }
81
82 let header_file_name = format!("{}.h", adjusted_element_name);
84 let cpp_file_name = format!("{}.cpp", adjusted_element_name);
85
86 create_classfile_from_template(&src_path, &header_file_name, header_template, &adjusted_element_name)?;
88 create_classfile_from_template(&src_path, &cpp_file_name, cpp_template, &adjusted_element_name)?;
89
90 let cmakelists_path = src_path.join("CMakeLists.txt");
92 let file = File::open(&cmakelists_path)?;
93 let reader = BufReader::new(file);
94 let lines: Vec<String> = reader.lines().collect::<Result<_, _>>()?;
95 let mut new_lines = Vec::new();
96 let mut found_target_sources = false;
97 let mut added = false;
98
99 for line in lines.iter() {
101 new_lines.push(line.to_string());
102 if line.trim_start().starts_with("target_sources(${PROJECT_NAME}") {
103 found_target_sources = true;
104 }
105 if found_target_sources && line.trim_start().starts_with("PRIVATE") && !added {
106 let indentation = line.chars().take_while(|c| c.is_whitespace()).count() + 4;
108 let new_cpp_line = format!("{:indent$}{}", "", cpp_file_name, indent = indentation);
109 new_lines.push(new_cpp_line);
110 added = true;
111 }
112 }
113
114 if !added {
116 return Err("Could not find 'PRIVATE' after 'target_sources' in CMakeLists.txt".into());
117 }
118
119 let new_content = new_lines.join("\n");
121 let mut cmakelists_file = File::create(&cmakelists_path)?;
122 cmakelists_file.write_all(new_content.as_bytes())?;
123
124 println!("{} '{}' added successfully!", element_type, adjusted_element_name);
126 Ok(())
127}
128
129fn create_classfile_from_template(
130 src_path: &PathBuf,
131 file_name: &str,
132 template: &[u8],
133 element_name: &str,
134) -> Result<(), Box<dyn std::error::Error>> {
135 let mut file = File::create(src_path.join(file_name))?;
136 let content = String::from_utf8_lossy(template).replace("Template", element_name);
137 file.write_all(content.as_bytes())?;
138 println!("Created file: {}", src_path.join(file_name).display());
139 Ok(())
140}
141
142fn create_file_from_template(
143 src_path: &PathBuf,
144 file_name: &str,
145 template: &[u8],
146) -> Result<(), Box<dyn std::error::Error>> {
147 let mut file = File::create(src_path.join(file_name))?;
148 file.write_all(template)?;
149 println!("Created file: {}", src_path.join(file_name).display());
150 Ok(())
151}
152const MAIN_CPP_TEMPLATE: &[u8] = include_bytes!("../templates/GuiApplicationTemplate/Main.cpp.template");
154const MAIN_COMPONENT_CPP_TEMPLATE: &[u8] =
155 include_bytes!("../templates/GuiApplicationTemplate/MainComponent.cpp.template");
156const MAIN_COMPONENT_H_TEMPLATE: &[u8] =
157 include_bytes!("../templates/GuiApplicationTemplate/MainComponent.h.template");
158const GUI_APP_CMAKE_TEMPLATE: &[u8] =
159 include_bytes!("../templates/GuiApplicationTemplate/CMakeLists.txt.template");
160
161const PLUGIN_PROCESSOR_CPP_TEMPLATE: &[u8] =
162 include_bytes!("../templates/AudioPluginTemplate/PluginProcessor.cpp.template");
163const PLUGIN_PROCESSOR_H_TEMPLATE: &[u8] =
164 include_bytes!("../templates/AudioPluginTemplate/PluginProcessor.h.template");
165const PLUGIN_EDITOR_CPP_TEMPLATE: &[u8] =
166 include_bytes!("../templates/AudioPluginTemplate/PluginEditor.cpp.template");
167const PLUGIN_EDITOR_H_TEMPLATE: &[u8] =
168 include_bytes!("../templates/AudioPluginTemplate/PluginEditor.h.template");
169const AUDIO_PLUGIN_CMAKE_TEMPLATE: &[u8] =
170 include_bytes!("../templates/AudioPluginTemplate/CMakeLists.txt.template");
171
172const CONSOLE_APP_CMAKE_TEMPLATE: &[u8] =
173 include_bytes!("../templates/ConsoleAppTemplate/CMakeLists.txt.template");
174
175const CONSOLE_APP_MAIN_CPP_TEMPLATE: &[u8] =
176 include_bytes!("../templates/ConsoleAppTemplate/Main.cpp.template");
177
178const CLASS_H_TEMPLATE: &[u8] = include_bytes!("../templates/ClassTemplates/Class.h.template");
179const CLASS_CPP_TEMPLATE: &[u8] = include_bytes!("../templates/ClassTemplates/Class.cpp.template");
180const COMPONENT_H_TEMPLATE: &[u8] = include_bytes!("../templates/ClassTemplates/Component.h.template");
181const COMPONENT_CPP_TEMPLATE: &[u8] = include_bytes!("../templates/ClassTemplates/Component.cpp.template");
182
183pub fn create_cmakelists(context: &Context) -> Result<(), Box<dyn std::error::Error>> {
184 let cmakelists_path = context.project_path.join("CMakeLists.txt");
185 let mut cmakelists_file = File::create(cmakelists_path)?;
186
187 let cmake_content = format!(
188 indoc! {
189 "cmake_minimum_required(VERSION 3.24)
190 project({} VERSION 0.0.1)
191 add_subdirectory(modules/JUCE)
192 add_subdirectory(src)"
193 },
194 context.project_name
195 );
196
197 cmakelists_file
198 .write_all(cmake_content.as_bytes())?;
199 Ok(())
200}
201