| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 |
- namespace Spry.Tools {
- public class Mkcomponent : Object {
- private static bool show_version = false;
- private static string? output_file = null;
- private static string? html_file = null;
- private const OptionEntry[] options = {
- { "output", 'o', 0, OptionArg.FILENAME, ref output_file, "Output file path (alternative to positional OUTPUT_FILE)", "FILE" },
- { "html", 'H', 0, OptionArg.FILENAME, ref html_file, "HTML template file (default: <input_vala>.html)", "FILE" },
- { "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_VALA_FILE> [OUTPUT_FILE] - Inject HTML markup into Vala component files");
- opt_context.set_help_enabled(true);
- opt_context.add_main_entries(options, null);
- opt_context.set_description(
- "Arguments:\n" +
- " INPUT_VALA_FILE Input Vala component file\n" +
- " OUTPUT_FILE Output Vala file (optional, default: input filename in build directory)\n" +
- "\n" +
- "The output file can be specified either as a positional argument or via -o/--output."
- );
- 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-mkcomponent 0.1\n");
- return 0;
- }
- // Get input file and optional output file from remaining arguments
- string? input_file = null;
- string? positional_output = null;
-
- if (args.length > 1) {
- input_file = args[1];
- }
- if (args.length > 2) {
- positional_output = args[2];
- }
- 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;
- }
- // Determine output file: -o flag takes precedence over positional argument
- string actual_output;
- if (output_file != null) {
- actual_output = output_file;
- } else if (positional_output != null) {
- actual_output = positional_output;
- } else {
- // Default: use the input filename (will be written to build directory by meson)
- actual_output = input_file;
- }
- // Determine HTML file
- string actual_html;
- if (html_file != null) {
- actual_html = html_file;
- } else {
- // Default: append .html to the input vala file path
- actual_html = input_file + ".html";
- }
- try {
- var tool = new Mkcomponent();
- tool.process(input_file, actual_output, actual_html);
- return 0;
- } catch (Error e) {
- stderr.printf("%s\n", e.message);
- return 1;
- }
- }
- public void process(string input_path, string output_path, string html_path) 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");
- }
- // Check if HTML file exists
- var html_file_obj = File.new_for_path(html_path);
- if (!html_file_obj.query_exists()) {
- throw new IOError.NOT_FOUND(@"Error: HTML template file '$(html_path)' does not exist");
- }
- // Read input Vala file
- string vala_content = read_file_content(input_path);
- // Read HTML template
- string html_content = read_file_content(html_path);
- // Normalize line endings to Unix style
- vala_content = vala_content.replace("\r\n", "\n").replace("\r", "\n");
- html_content = html_content.replace("\r\n", "\n").replace("\r", "\n");
- // First check if there's a markup property with a getter body (to give specific error)
- Regex getter_with_body_regex;
- try {
- // This pattern matches a markup property with a getter that has a body
- getter_with_body_regex = new Regex(
- @"public\\s+override\\s+string\\s+markup\\s*\\{\\s*get\\s*\\{",
- RegexCompileFlags.MULTILINE
- );
- } catch (RegexError e) {
- throw new IOError.FAILED(@"Internal error: Failed to compile regex: $(e.message)");
- }
- if (getter_with_body_regex.match(vala_content)) {
- throw new IOError.FAILED(@"Error: The markup property in '$(input_path)' already has a getter defined. Expected empty getter: { get; }");
- }
- // Now check for the empty getter pattern
- MatchInfo match_info;
- Regex markup_property_regex;
- try {
- // Pattern to match: public override string markup { get; }
- // Allow for various whitespace patterns
- markup_property_regex = new Regex(
- @"public\\s+override\\s+string\\s+markup\\s*\\{\\s*get\\s*;\\s*\\}",
- RegexCompileFlags.MULTILINE
- );
- } catch (RegexError e) {
- throw new IOError.FAILED(@"Internal error: Failed to compile regex: $(e.message)");
- }
- if (!markup_property_regex.match(vala_content, 0, out match_info)) {
- throw new IOError.NOT_FOUND(@"Error: No 'public override string markup' property found in '$(input_path)'");
- }
- // Escape any triple quotes in HTML content by replacing """ with \"\"\"
- string escaped_html = html_content.replace("\"\"\"", "\\\"\\\"\\\"");
- // Determine the indentation to use for the HTML content
- // Find the line containing the markup property and extract its indentation
- string indentation = extract_indentation(vala_content);
- // Generate the replacement - we replace just the "{ get; }" part
- // Regex to match just the getter part: { get; }
- Regex getter_regex;
- try {
- getter_regex = new Regex(
- @"\\{\\s*get\\s*;\\s*\\}",
- 0
- );
- } catch (RegexError e) {
- throw new IOError.FAILED(@"Internal error: Failed to compile getter regex: $(e.message)");
- }
- // Build the replacement getter - HTML content placed verbatim between triple quotes
- string replacement = @"{ get {\n$(indentation)$(indentation)return \"\"\"$(escaped_html)\"\"\";\n$(indentation)}}";
- // Perform the replacement
- string output_content;
- try {
- output_content = getter_regex.replace(vala_content, -1, 0, replacement);
- } catch (RegexError e) {
- throw new IOError.FAILED(@"Internal error: Failed to replace markup property: $(e.message)");
- }
- // Write output file
- write_file_content(output_path, output_content);
- }
- 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();
- }
- private string extract_indentation(string content) {
- // Find the line containing "public override string markup" and extract its leading whitespace
- Regex indent_regex;
- try {
- indent_regex = new Regex(
- @"^([ \\t]*)public\\s+override\\s+string\\s+markup",
- RegexCompileFlags.MULTILINE
- );
- } catch (RegexError e) {
- return " "; // Default to 4 spaces
- }
- MatchInfo match;
- if (indent_regex.match(content, 0, out match)) {
- return match.fetch(1) ?? " ";
- }
- return " "; // Default to 4 spaces
- }
- }
- }
|