Logo

index : jinit

---

  • summary
  • about
  • tree
  • log
  • branches
<< path: root/public/jinit.git/html/src/main.jai blob: 834b3df9bb84ce5ff1590ad3228027b2dd4e446a [raw] [clear marker]

        
0/*
1 * MIT License
2 *
3 * Copyright (c) 2025 dev@ptrace.dev
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in all
13 * copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *
23 */
24
25/*
26 +Version: 2.3.1
27*/
28
29#import "Basic";
30#import "String";
31File :: #import "File";
32FileUtils :: #import "File_Utilities";
33Process :: #import "Process";
34Cli :: #import "Command_Line";
35
36
37// if enabled, it does not create any directories, files or SCM repositories
38DRY_RUN :: false;
39
40// if enabled, the dry run won't emit any logs
41DONT_BE_CHATTY :: false;
42
43
44Rules :: struct {
45 arg_name: string;
46 description: string;
47 template: string;
48 dirs_in_workplace: []string;
49 files: []File_Entity;
50 build_file_name: string = BUILD_FN;
51}
52
53File_Entity :: struct {
54 file_name: string;
55 content: string;
56}
57
58Arguments :: struct {
59 g: bool; @"?If provided, no SCM repository will be created"
60 l: bool; @"?List available template types"
61 t: string; @"?Pass a template type"
62 r: string; @"?Replace the replace marker with a given string"
63}
64
65
66log_dry :: (msg: string) {
67 #if DONT_BE_CHATTY return;
68 log("[Dry Run]: %", msg);
69}
70
71create_scm_project :: (root_fp: string, args: Arguments) {
72 if SCM_IS_TURNED_OFF || args.g return;
73
74 #if DRY_RUN {
75 log_dry("Creating SCM Project");
76 return;
77 }
78
79 if FileUtils.file_exists(SCM_DOT_DIR) return;
80
81 command := SCM_COMMANDS;
82
83 process_result, output_str, error_str := Process.run_command(
84 ..command,
85 working_directory = root_fp,
86 capture_and_return_output = true
87 );
88
89 if process_result.exit_code != 0 {
90 if error_str log("Error: %", error_str);
91 if output_str log("Error: %", output_str);
92 exit(1);
93 }
94}
95
96create_directories :: (root_fp: string, rules: Rules) {
97 if !rules.dirs_in_workplace return;
98
99 for rules.dirs_in_workplace {
100 #if DRY_RUN {
101 log_dry(tprint("mkdir: %/%", root_fp, it));
102 return;
103 }
104 File.make_directory_if_it_does_not_exist(tprint("%/%", root_fp, it));
105 }
106}
107
108create_file :: (fp: string, content: string = "") {
109 #if DRY_RUN {
110 log_dry(tprint("file_write: % -> content:\n%", fp, content));
111 return;
112 }
113
114 if FileUtils.file_exists(fp) {
115 log("Info: The file '%' already exists.", fp);
116 return;
117 }
118
119 if !File.write_entire_file(fp, content) {
120 log("Cannot create or write into file: %", fp);
121 exit(1);
122 }
123}
124
125create_files :: (root_fp: string, rules: Rules) {
126 for rules.files {
127 fp := tprint("%/%", root_fp, it.file_name);
128 create_file(fp, it.content);
129 }
130
131 build_template_fp := tprint("%/%", root_fp, rules.build_file_name);
132 create_file(build_template_fp, rules.template);
133}
134
135create_template :: (template_fp: string) -> string {
136 if !FileUtils.file_exists(template_fp) {
137 log_error("Could not find template: %", template_fp);
138 }
139
140 content, success := File.read_entire_file(template_fp);
141
142 if !success {
143 log_error("Cannot read file: %", template_fp);
144 exit(1);
145 }
146
147 return content;
148}
149
150template_rules_exec :: () -> []Rules {
151 buf: [TR_EXEC_BUF_MAX]Rules;
152 #insert TR_EXEC_BODY;
153 return buf;
154}
155
156rules_execute :: (template_rules: []Rules, root_fp: string, args: Arguments) {
157
158 create_project :: (template_name: string) #expand {
159 success := false;
160 match: Rules;
161
162 for `template_rules {
163 if it.arg_name == template_name {
164 success = true;
165 match = it;
166 }
167 }
168
169 if !success {
170 log("Cannot find template: %", template_name);
171 exit(1);
172 }
173
174 if `args.r {
175 template_placeholder_rename(*match, `args.r);
176 }
177
178 create_directories(root_fp, match);
179 create_files(root_fp, match);
180 }
181
182 if args.t {
183 create_project(args.t);
184 return;
185 }
186
187 log("Default Template: %", DEFAULT_TEMPLATE);
188 create_project(DEFAULT_TEMPLATE);
189}
190
191template_placeholder_rename :: (rules: *Rules, new_name: string) {
192
193 replace_new :: (arr: []string) -> []string #expand {
194 buf: [..]string;
195 buf.allocator = temp;
196
197 for * arr {
198 if it.* == RENAME_MARKER {
199 array_add(*buf, `new_name);
200 } else {
201 array_add(*buf, it.*);
202 }
203 }
204
205 return buf;
206 }
207
208 replace_new :: (s: string, new: *string) #expand {
209 if contains(s, RENAME_MARKER) {
210 `had_match = true;
211 new.* = replace(s, RENAME_MARKER, `new_name);
212 }
213 }
214
215 rules.template = replace(rules.template, RENAME_MARKER, new_name,, temp);
216 success := array_find(rules.dirs_in_workplace, RENAME_MARKER);
217
218 if success {
219 rules.dirs_in_workplace = replace_new(rules.dirs_in_workplace);
220 }
221
222 had_match := false;
223
224 buf: [..]File_Entity;
225 buf.allocator = temp;
226
227 for * rules.files {
228 file_name := it.file_name;
229 content := it.content;
230 replace_new(it.file_name, *file_name);
231 replace_new(it.content, *content);
232 array_add(*buf, .{ file_name, content });
233 }
234
235 if had_match {
236 rules.files = buf;
237 }
238}
239
240list_all_templates :: (template_rules: []Rules) {
241 log("Available Templates:");
242 for template_rules {
243 print(" %\t%\n", it.arg_name, it.description);
244 }
245 print("\n");
246}
247
248
249FILE_SCOPE :: #code {};
250
251
252main :: () {
253 success, args, is_set := Cli.parse_arguments(Arguments);
254
255 if !success {
256 log("Unknown arguments.");
257 exit(1);
258 }
259
260 template_rules := #run template_rules_exec();
261
262 if args.l {
263 list_all_templates(template_rules);
264 return;
265 }
266
267 current_path := get_working_directory();
268 if !current_path {
269 log("Cannot resolve project path");
270 exit(1);
271 }
272
273 rules_execute(template_rules, current_path, args);
274 create_scm_project(current_path, args);
275
276 log("[Jai Init]: Done");
277}
278
279
Copyright 2026  E766CB298A6D1E64 | Git-Thing heavily inspired by cgit