-- YAML header parsing module local M = {} local lyaml = require('lyaml') local utils = require('notex.utils') -- Extract YAML header from markdown content function M.extract_yaml_header(content) if not content or content == "" then return nil, "Empty content provided" end -- Check for YAML header delimiters local start_pos = content:find("^%s*%-%-%-%s*\n") if not start_pos then return nil, "No YAML header found" end local end_pos = content:find("\n%s*%-%-%-%s*\n", start_pos + 4) if not end_pos then return nil, "Unclosed YAML header" end -- Extract YAML content local yaml_content = content:sub(start_pos + 4, end_pos - 1) return yaml_content, nil end -- Parse YAML header content function M.parse_yaml(yaml_content) if not yaml_content or yaml_content == "" then return {}, nil end local ok, data = pcall(lyaml.load, yaml_content) if not ok then return nil, "YAML parsing failed: " .. tostring(data) end if type(data) ~= "table" then return {}, nil end return data, nil end -- Parse markdown file and extract YAML header function M.parse_markdown_file(file_path) -- Validate file exists if not utils.file_exists(file_path) then return nil, "File not found: " .. file_path end -- Validate UTF-8 encoding if not utils.is_utf8(file_path) then return nil, "File is not valid UTF-8: " .. file_path end -- Read file content local content, err = utils.read_file(file_path) if not content then return nil, err end -- Extract YAML header local yaml_content, extract_err = M.extract_yaml_header(content) if not yaml_content then return nil, extract_err end -- Parse YAML local yaml_data, parse_err = M.parse_yaml(yaml_content) if not yaml_data then return nil, parse_err end return yaml_data, nil end -- Flatten YAML data into key-value pairs function M.flatten_yaml(data, prefix) local flattened = {} prefix = prefix or "" for key, value in pairs(data) do local full_key = prefix .. (prefix ~= "" and "." or "") .. key if type(value) == "table" then -- Recursively flatten nested tables local nested = M.flatten_yaml(value, full_key) for nested_key, nested_value in pairs(nested) do flattened[nested_key] = nested_value end else flattened[full_key] = value end end return flattened end -- Validate YAML structure function M.validate_yaml(yaml_data) local errors = {} if type(yaml_data) ~= "table" then table.insert(errors, "YAML data must be a table") return errors end -- Check for required fields (if any) local required_fields = {} -- Add required fields as needed for _, field in ipairs(required_fields) do if yaml_data[field] == nil then table.insert(errors, string.format("Required field '%s' is missing", field)) end end -- Validate field types local field_types = { -- Define expected types for specific fields } for field, expected_type in pairs(field_types) do if yaml_data[field] ~= nil and type(yaml_data[field]) ~= expected_type then table.insert(errors, string.format("Field '%s' should be %s, got %s", field, expected_type, type(yaml_data[field]))) end end return errors end -- Detect and convert property types function M.detect_property_type(value) local value_type = type(value) if value_type == "boolean" then return "boolean", value elseif value_type == "number" then return "number", value elseif value_type == "string" then -- Check for ISO 8601 date format if value:match("^%d%d%d%d%-%d%d%-%d%d$") or value:match("^%d%d%d%d%-%d%d%-%d%dT%d%d:%d%d:%d%dZ?$") then return "date", value end -- Check for numeric strings local num = tonumber(value) if num and value:match("^%-?%d+%.?%d*$") then return "number", num end -- Check for boolean strings local lower = value:lower() if lower == "true" then return "boolean", true elseif lower == "false" then return "boolean", false end return "string", value elseif value_type == "table" then return "array", vim.json.encode(value) else return "string", tostring(value) end end -- Process YAML data into property format function M.process_properties(yaml_data) local flattened = M.flatten_yaml(yaml_data) local properties = {} for key, value in pairs(flattened) do local prop_type, processed_value = M.detect_property_type(value) table.insert(properties, { key = key, value = processed_value, value_type = prop_type }) end return properties end return M