/* * MIT License * * Copyright (c) 2025 dev@ptrace.dev * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ /* +Version: 2.3.1 */ #import "Basic"; #import "String"; File :: #import "File"; FileUtils :: #import "File_Utilities"; Process :: #import "Process"; Cli :: #import "Command_Line"; // if enabled, it does not create any directories, files or SCM repositories DRY_RUN :: false; // if enabled, the dry run won't emit any logs DONT_BE_CHATTY :: false; Rules :: struct { arg_name: string; description: string; template: string; dirs_in_workplace: []string; files: []File_Entity; build_file_name: string = BUILD_FN; } File_Entity :: struct { file_name: string; content: string; } Arguments :: struct { g: bool; @"?If provided, no SCM repository will be created" l: bool; @"?List available template types" t: string; @"?Pass a template type" r: string; @"?Replace the replace marker with a given string" } log_dry :: (msg: string) { #if DONT_BE_CHATTY return; log("[Dry Run]: %", msg); } create_scm_project :: (root_fp: string, args: Arguments) { if SCM_IS_TURNED_OFF || args.g return; #if DRY_RUN { log_dry("Creating SCM Project"); return; } if FileUtils.file_exists(SCM_DOT_DIR) return; command := SCM_COMMANDS; process_result, output_str, error_str := Process.run_command( ..command, working_directory = root_fp, capture_and_return_output = true ); if process_result.exit_code != 0 { if error_str log("Error: %", error_str); if output_str log("Error: %", output_str); exit(1); } } create_directories :: (root_fp: string, rules: Rules) { if !rules.dirs_in_workplace return; for rules.dirs_in_workplace { #if DRY_RUN { log_dry(tprint("mkdir: %/%", root_fp, it)); return; } File.make_directory_if_it_does_not_exist(tprint("%/%", root_fp, it)); } } create_file :: (fp: string, content: string = "") { #if DRY_RUN { log_dry(tprint("file_write: % -> content:\n%", fp, content)); return; } if FileUtils.file_exists(fp) { log("Info: The file '%' already exists.", fp); return; } if !File.write_entire_file(fp, content) { log("Cannot create or write into file: %", fp); exit(1); } } create_files :: (root_fp: string, rules: Rules) { for rules.files { fp := tprint("%/%", root_fp, it.file_name); create_file(fp, it.content); } build_template_fp := tprint("%/%", root_fp, rules.build_file_name); create_file(build_template_fp, rules.template); } create_template :: (template_fp: string) -> string { if !FileUtils.file_exists(template_fp) { log_error("Could not find template: %", template_fp); } content, success := File.read_entire_file(template_fp); if !success { log_error("Cannot read file: %", template_fp); exit(1); } return content; } template_rules_exec :: () -> []Rules { buf: [TR_EXEC_BUF_MAX]Rules; #insert TR_EXEC_BODY; return buf; } rules_execute :: (template_rules: []Rules, root_fp: string, args: Arguments) { create_project :: (template_name: string) #expand { success := false; match: Rules; for `template_rules { if it.arg_name == template_name { success = true; match = it; } } if !success { log("Cannot find template: %", template_name); exit(1); } if `args.r { template_placeholder_rename(*match, `args.r); } create_directories(root_fp, match); create_files(root_fp, match); } if args.t { create_project(args.t); return; } log("Default Template: %", DEFAULT_TEMPLATE); create_project(DEFAULT_TEMPLATE); } template_placeholder_rename :: (rules: *Rules, new_name: string) { replace_new :: (arr: []string) -> []string #expand { buf: [..]string; buf.allocator = temp; for * arr { if it.* == RENAME_MARKER { array_add(*buf, `new_name); } else { array_add(*buf, it.*); } } return buf; } replace_new :: (s: string, new: *string) #expand { if contains(s, RENAME_MARKER) { `had_match = true; new.* = replace(s, RENAME_MARKER, `new_name); } } rules.template = replace(rules.template, RENAME_MARKER, new_name,, temp); success := array_find(rules.dirs_in_workplace, RENAME_MARKER); if success { rules.dirs_in_workplace = replace_new(rules.dirs_in_workplace); } had_match := false; buf: [..]File_Entity; buf.allocator = temp; for * rules.files { file_name := it.file_name; content := it.content; replace_new(it.file_name, *file_name); replace_new(it.content, *content); array_add(*buf, .{ file_name, content }); } if had_match { rules.files = buf; } } list_all_templates :: (template_rules: []Rules) { log("Available Templates:"); for template_rules { print(" %\t%\n", it.arg_name, it.description); } print("\n"); } FILE_SCOPE :: #code {}; main :: () { success, args, is_set := Cli.parse_arguments(Arguments); if !success { log("Unknown arguments."); exit(1); } template_rules := #run template_rules_exec(); if args.l { list_all_templates(template_rules); return; } current_path := get_working_directory(); if !current_path { log("Cannot resolve project path"); exit(1); } rules_execute(template_rules, current_path, args); create_scm_project(current_path, args); log("[Jai Init]: Done"); }