Line data Source code
1 : -- lua/yoda/yaml_parser.lua
2 : -- Pure Lua YAML parser for ingress-mapping.yaml structure
3 : -- Uses unified logging system for trace debugging
4 :
5 1 : local logger = require("yoda-logging.logger")
6 : -- Refactored for low cyclomatic complexity (all functions ≤ 7)
7 :
8 1 : local M = {}
9 :
10 : -- ============================================================================
11 : -- Constants (configurable via vim.g.yoda_yaml_*)
12 : -- ============================================================================
13 :
14 : -- Allow users to extend environments without modifying source (OCP principle)
15 : local function get_known_environments()
16 26 : return vim.g.yoda_yaml_environments or { fastly = true, qa = true, prod = true }
17 1 : end
18 :
19 : -- Allow users to override YAML indentation if their file format differs
20 2 : local ENVIRONMENT_INDENT = vim.g.yoda_yaml_env_indent or 2
21 2 : local REGION_INDENT = vim.g.yoda_yaml_region_indent or 6
22 :
23 : -- ============================================================================
24 : -- Helper Functions (Low Complexity)
25 : -- ============================================================================
26 :
27 : --- Read YAML file content
28 : --- Complexity: 2 (1 if + 1 early return)
29 : --- @param yaml_path string Path to YAML file
30 : --- @return boolean, string|nil Success status, content or error
31 : local function read_yaml_file(yaml_path)
32 9 : local Path = require("plenary.path")
33 27 : local ok, content = pcall(Path.new(yaml_path).read, Path.new(yaml_path))
34 9 : if not ok then
35 1 : vim.notify("Failed to read YAML file: " .. yaml_path, vim.log.levels.ERROR)
36 1 : return false, nil
37 : end
38 8 : return true, content
39 1 : end
40 :
41 : --- Check if line is empty or comment
42 : --- Complexity: 1 (1 or condition)
43 : --- @param trimmed string Trimmed line content
44 : --- @return boolean
45 : local function is_skip_line(trimmed)
46 67 : return trimmed == "" or trimmed:match("^#") ~= nil
47 1 : end
48 :
49 : --- Extract environment name from line
50 : --- Complexity: 3 (1 match + 1 and + 1 table lookup)
51 : --- @param trimmed string Trimmed line content
52 : --- @param indent number Line indentation
53 : --- @return string|nil Environment name if valid
54 : local function extract_environment_name(trimmed, indent)
55 54 : if indent ~= ENVIRONMENT_INDENT then
56 41 : return nil
57 : end
58 :
59 13 : local env_name = trimmed:match("^-%s*name:%s*(.+)")
60 13 : local known_envs = get_known_environments()
61 13 : if env_name and known_envs[env_name] then
62 13 : return env_name
63 : end
64 : return nil
65 1 : end
66 :
67 : --- Extract region name from line
68 : --- Complexity: 3 (2 and conditions + 1 match)
69 : --- @param trimmed string Trimmed line content
70 : --- @param indent number Line indentation
71 : --- @param current_env string|nil Current environment context
72 : --- @return string|nil Region name if valid
73 : local function extract_region_name(trimmed, indent, current_env)
74 41 : if indent ~= REGION_INDENT or not current_env then
75 20 : return nil
76 : end
77 :
78 21 : return trimmed:match("^-%s*name:%s*(.+)")
79 1 : end
80 :
81 : --- Create debug log entry for line
82 : --- Complexity: 1 (simple format)
83 : --- @param i number Line number
84 : --- @param indent number Indentation
85 : --- @param trimmed string Content
86 : --- @return string Debug log entry
87 : local function create_line_log(i, indent, trimmed)
88 : return string.format("Line %d: indent=%d content='%s'", i, indent, trimmed)
89 1 : end
90 :
91 : --- Save current environment to results
92 : --- Complexity: 2 (1 if + simple operations)
93 : --- @param environments table Environments map
94 : --- @param current_env string|nil Current environment
95 : --- @param current_regions table Current regions array
96 : local function save_environment(environments, current_env, current_regions)
97 21 : if not current_env then
98 8 : return
99 : end
100 :
101 13 : environments[current_env] = current_regions
102 13 : logger.trace("Saved environment", { env = current_env, regions = #current_regions })
103 14 : end
104 :
105 : --- Process environment line
106 : --- Complexity: 1 (simple operations)
107 : --- @param env_order table Environment order array
108 : --- @param env_name string Environment name
109 : --- @param line_num number Line number
110 : local function log_new_environment(env_order, env_name, line_num)
111 13 : table.insert(env_order, env_name)
112 13 : logger.trace("Found environment", { env = env_name, line = line_num })
113 14 : end
114 :
115 : --- Process region line
116 : --- Complexity: 1 (simple operations)
117 : --- @param current_regions table Regions array
118 : --- @param region_name string Region name
119 : --- @param current_env string Current environment
120 : local function add_region(current_regions, region_name, current_env)
121 21 : table.insert(current_regions, region_name)
122 21 : logger.trace("Found region", { region = region_name, env = current_env })
123 22 : end
124 :
125 : --- Removed: write_debug_log - now handled by unified logger with file strategy
126 :
127 : --- Process single YAML line
128 : --- Complexity: 6 (1 skip + 2 env checks + 2 region checks + 1 log)
129 : --- @param line string Line content
130 : --- @param line_num number Line number
131 : --- @param state table Parser state {current_env, current_regions, environments, env_order}
132 : local function process_line(line, line_num, state)
133 67 : local trimmed = line:match("^%s*(.-)%s*$")
134 :
135 : -- Skip empty/comment lines
136 134 : if is_skip_line(trimmed) then
137 13 : return
138 : end
139 :
140 54 : local indent = #(line:match("^(%s*)") or "")
141 108 : logger.trace(function()
142 : return create_line_log(line_num, indent, trimmed)
143 54 : end)
144 :
145 : -- Check for environment definition
146 54 : local env_name = extract_environment_name(trimmed, indent)
147 54 : if env_name then
148 13 : save_environment(state.environments, state.current_env, state.current_regions)
149 13 : state.current_env = env_name
150 13 : state.current_regions = {}
151 13 : log_new_environment(state.env_order, env_name, line_num)
152 13 : return
153 : end
154 :
155 : -- Check for region definition
156 41 : local region_name = extract_region_name(trimmed, indent, state.current_env)
157 41 : if region_name then
158 21 : add_region(state.current_regions, region_name, state.current_env)
159 : end
160 42 : end
161 :
162 : --- Add final debug information
163 : --- Complexity: 2 (1 loop + simple operations)
164 : --- @param env_order table Environment order
165 : --- @param environments table Environments map
166 : local function finalize_debug_log(env_order, environments)
167 8 : logger.trace("YAML parsing complete", { env_order = env_order })
168 :
169 21 : for env_name, regions in pairs(environments) do
170 13 : logger.trace("Environment result", { env = env_name, regions = regions })
171 : end
172 9 : end
173 :
174 : --- Parse YAML file and extract environments and regions
175 : --- Complexity: 4 (1 read check + 1 loop + 1 save + 1 finalize)
176 : --- @param yaml_path string Path to the YAML file
177 : --- @return table|nil Table with environment names as keys and region arrays as values
178 1 : function M.parse_ingress_mapping(yaml_path)
179 : -- Input validation
180 9 : assert(type(yaml_path) == "string" and yaml_path ~= "", "yaml_path must be a non-empty string")
181 :
182 : -- Read file
183 9 : local ok, content = read_yaml_file(yaml_path)
184 9 : if not ok then
185 1 : return nil
186 : end
187 :
188 : -- Initialize parser state
189 8 : local state = {
190 : current_env = nil,
191 8 : current_regions = {},
192 8 : environments = {},
193 8 : env_order = {},
194 : }
195 :
196 : -- Setup debug logging with unified logger
197 8 : local lines = vim.split(content, "\n")
198 8 : logger.debug("Starting YAML parse", { file = yaml_path, lines = #lines })
199 :
200 : -- Process each line
201 75 : for i, line in ipairs(lines) do
202 67 : process_line(line, i, state)
203 : end
204 :
205 : -- Save final environment
206 8 : save_environment(state.environments, state.current_env, state.current_regions)
207 :
208 : -- Finalize debug logging
209 8 : finalize_debug_log(state.env_order, state.environments)
210 :
211 16 : logger.info("YAML parsing complete", {
212 8 : file = yaml_path,
213 8 : environments = #state.env_order,
214 : })
215 :
216 8 : return {
217 8 : environments = state.environments,
218 8 : env_order = state.env_order,
219 8 : }
220 1 : end
221 :
222 1 : return M
|