| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- namespace Spry.Tools {
- public class Mkconst : Object {
- private static bool show_version = false;
- private static string? output_file = null;
- private static string? namespace_name = null;
- private static string? const_name_override = null;
- private const OptionEntry[] options = {
- { "output", 'o', 0, OptionArg.FILENAME, ref output_file, "Output file path (default: stdout)", "FILE" },
- { "ns", '\0', 0, OptionArg.STRING, ref namespace_name, "Wrap const in namespace", "NAMESPACE" },
- { "name", 'n', 0, OptionArg.STRING, ref const_name_override, "Override const name (default: derived from filename)", "NAME" },
- { "version", 'v', 0, OptionArg.NONE, ref show_version, "Show version information", null },
- { null }
- };
- public static int main(string[] args) {
- try {
- var opt_context = new OptionContext("<INPUT_FILE> - Convert file content to a Vala string constant");
- opt_context.set_help_enabled(true);
- opt_context.add_main_entries(options, null);
- opt_context.set_description(
- "Arguments:\n" +
- " INPUT_FILE Input file (typically an HTML template file)\n" +
- "\n" +
- "The const name is derived from the input filename by converting to CAPITAL_SNAKE_CASE.\n" +
- "For example, 'UserContentComponent.vala.html' becomes USER_CONTENT_COMPONENT_VALA_HTML.\n" +
- "\n" +
- "Output goes to stdout by default, or use -o to specify an output file."
- );
- opt_context.parse(ref args);
- } catch (OptionError e) {
- stderr.printf("Error: %s\n", e.message);
- stderr.printf("Run '%s --help' for more information.\n", args[0]);
- return 1;
- }
- if (show_version) {
- stdout.printf("spry-mkconst 0.1\n");
- return 0;
- }
- // Get input file from remaining arguments
- string? input_file = null;
- if (args.length > 1) {
- input_file = args[1];
- }
- if (input_file == null) {
- stderr.printf("Error: No input file specified.\n");
- stderr.printf("Run '%s --help' for more information.\n", args[0]);
- return 1;
- }
- try {
- var tool = new Mkconst();
- tool.process(input_file, output_file, namespace_name, const_name_override);
- return 0;
- } catch (Error e) {
- stderr.printf("%s\n", e.message);
- return 1;
- }
- }
- public void process(string input_path, string? output_path, string? ns_name, string? const_name) throws Error {
- // Check if input file exists
- var input_file = File.new_for_path(input_path);
- if (!input_file.query_exists()) {
- throw new IOError.NOT_FOUND(@"Error: Input file '$(input_path)' does not exist");
- }
- // Read input file
- string content = read_file_content(input_path);
- // Normalize line endings to Unix style
- content = content.replace("\r\n", "\n").replace("\r", "\n");
- // Derive const name from filename if not provided
- string actual_const_name;
- if (const_name != null) {
- actual_const_name = const_name;
- } else {
- actual_const_name = filename_to_const_name(input_path);
- }
- // Escape any triple quotes in content by replacing """ with \"\"\"
- string escaped_content = content.replace("\"\"\"", "\\\"\\\"\\\"");
- // Determine indentation based on namespace
- string indent = ns_name != null ? " " : "";
- // Build output
- var output = new StringBuilder();
- // Write namespace if provided
- if (ns_name != null) {
- output.append(@"namespace $(ns_name) {\n\n");
- }
- // Write the const declaration
- // Remove trailing newline from content before wrapping in triple quotes
- string trimmed_content = escaped_content;
- if (trimmed_content.has_suffix("\n")) {
- trimmed_content = trimmed_content.substring(0, trimmed_content.length - 1);
- }
- output.append(@"$(indent)const string $(actual_const_name) = \"\"\"$(trimmed_content)\"\"\";\n");
- // Close namespace if provided
- if (ns_name != null) {
- output.append("}\n");
- }
- // Write output
- if (output_path != null) {
- write_file_content(output_path, output.str);
- } else {
- stdout.printf("%s", output.str);
- }
- }
- private string filename_to_const_name(string filepath) {
- // Get just the filename (without directory path)
- string basename = Path.get_basename(filepath);
- // Convert to CAPITAL_SNAKE_CASE
- // Assumes input is PascalCase with possible extensions
- // e.g., "UserContentComponent.vala.html" -> "USER_CONTENT_COMPONENT_VALA_HTML"
- var result = new StringBuilder();
- var prev_was_lower = false;
- var prev_was_upper = false;
- foreach (var c in basename.to_utf8()) {
- if (c == '.') {
- // Convert dots to underscores
- result.append("_");
- prev_was_lower = false;
- prev_was_upper = false;
- } else if (c == '-') {
- // Convert dashes to underscores
- result.append("_");
- prev_was_lower = false;
- prev_was_upper = false;
- } else if (c == '_') {
- result.append("_");
- prev_was_lower = false;
- prev_was_upper = false;
- } else if (c.isupper()) {
- if (prev_was_lower) {
- // Transition from lower to upper, insert underscore
- result.append("_");
- } else if (result.len > 0 && prev_was_upper) {
- // Check if this is end of an acronym (next char would be lower)
- // We'll handle this in the next iteration
- }
- result.append(c.to_string());
- prev_was_lower = false;
- prev_was_upper = true;
- } else if (c.islower()) {
- if (prev_was_upper && result.len > 1) {
- // Check if previous char was part of an acronym
- // e.g., "HTMLFile" -> we want "HTML_FILE"
- // Look back to see if we have multiple uppercase before this
- string current = result.str;
- if (current.length >= 2) {
- char prev_char = (char)current.get(current.length - 1);
- char prev_prev_char = (char)current.get(current.length - 2);
- if (prev_char.isupper() && prev_prev_char.isupper()) {
- // Insert underscore before the last uppercase char
- result.erase(result.len - 1);
- result.append("_");
- result.append(prev_char.to_string());
- }
- }
- }
- result.append(c.toupper().to_string());
- prev_was_lower = true;
- prev_was_upper = false;
- } else if (c.isdigit()) {
- result.append(c.to_string());
- prev_was_lower = false;
- prev_was_upper = false;
- }
- // Ignore other characters
- }
- return result.str;
- }
- private string read_file_content(string path) throws Error {
- var file = File.new_for_path(path);
- var input_stream = new DataInputStream(file.read());
- var builder = new StringBuilder();
- string line;
- while ((line = input_stream.read_line(null)) != null) {
- builder.append(line);
- builder.append("\n");
- }
- input_stream.close();
- return builder.str;
- }
- private void write_file_content(string path, string content) throws Error {
- var file = File.new_for_path(path);
-
- // Ensure parent directory exists
- var parent = file.get_parent();
- if (parent != null && !parent.query_exists()) {
- parent.make_directory_with_parents();
- }
- var output_stream = new DataOutputStream(file.replace(null, false, FileCreateFlags.NONE));
- output_stream.put_string(content);
- output_stream.close();
- }
- }
- }
|