@syntropysoft/praetorian
Version:
Praetorian CLI – A universal multi-environment configuration validator for DevSecOps teams. Validate, compare, and secure YAML/ENV files with ease.
257 lines • 7.64 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parsePropertiesValue = exports.parsePropertiesContent = exports.PropertiesFileAdapter = void 0;
const AbstractFileAdapter_1 = require("../base/AbstractFileAdapter");
/**
* Properties File Adapter - Functional Programming
*
* Single Responsibility: Parse Java Properties files only
* Pure functions, no state, no side effects
*/
class PropertiesFileAdapter extends AbstractFileAdapter_1.AbstractFileAdapter {
canHandle(filePath) {
// Guard clause: no file path
if (!filePath || typeof filePath !== 'string') {
return false;
}
return filePath.endsWith('.properties');
}
async read(filePath) {
// Guard clause: no file path
if (!filePath || typeof filePath !== 'string') {
throw new Error('File path is required');
}
this.validateFileExists(filePath);
try {
const content = await this.readFileContent(filePath);
return (0, exports.parsePropertiesContent)(content);
}
catch (error) {
throw new Error(`Failed to parse Properties file ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
getFormat() {
return 'properties';
}
getSupportedExtensions() {
return ['.properties'];
}
}
exports.PropertiesFileAdapter = PropertiesFileAdapter;
/**
* Pure function to parse properties content
*/
const parsePropertiesContent = (content) => {
// Guard clause: no content
if (!content || typeof content !== 'string') {
return {};
}
const lines = splitIntoLines(content);
const processedLines = processMultilineProperties(lines);
const keyValuePairs = extractKeyValuePairs(processedLines);
return keyValuePairs.reduce((result, pair) => {
if (pair.key) {
result[pair.key] = (0, exports.parsePropertiesValue)(pair.value);
}
return result;
}, {});
};
exports.parsePropertiesContent = parsePropertiesContent;
/**
* Pure function to split content into lines
*/
const splitIntoLines = (content) => {
// Guard clause: empty content
if (!content) {
return [];
}
return content.split('\n');
};
/**
* Pure function to process multi-line properties
*/
const processMultilineProperties = (lines) => {
// Guard clause: no lines
if (!lines || lines.length === 0) {
return [];
}
return lines.reduce((processed, line) => {
const trimmedLine = line.trim();
// Skip empty lines and comments
if (isCommentOrEmpty(trimmedLine)) {
return processed;
}
// Handle continuation from previous line
if (processed.length > 0 && processed[processed.length - 1].endsWith('\\')) {
const lastLine = processed[processed.length - 1];
processed[processed.length - 1] = lastLine.slice(0, -1) + trimmedLine;
}
else {
processed.push(trimmedLine);
}
return processed;
}, []);
};
/**
* Pure function to check if line is comment or empty
*/
const isCommentOrEmpty = (line) => {
// Guard clause: no line
if (!line) {
return true;
}
return line === '' || line.startsWith('#') || line.startsWith('!');
};
/**
* Pure function to extract key-value pairs from lines
*/
const extractKeyValuePairs = (lines) => {
// Guard clause: no lines
if (!lines || lines.length === 0) {
return [];
}
return lines
.map(extractKeyValuePair)
.filter(pair => pair !== null);
};
/**
* Pure function to extract key-value pair from a single line
*/
const extractKeyValuePair = (line) => {
// Guard clause: no line
if (!line) {
return null;
}
const separatorInfo = findSeparator(line);
if (separatorInfo.index > 0) {
const key = line.substring(0, separatorInfo.index).trim();
const value = line.substring(separatorInfo.index + separatorInfo.separator.length).trim();
if (key) {
return { key, value };
}
}
else {
// Handle cases where there's no clear separator - treat as key with empty value
// This handles cases like "key " (key with trailing space)
const trimmedLine = line.trim();
if (trimmedLine && !trimmedLine.includes('=') && !trimmedLine.includes(':')) {
return { key: trimmedLine, value: '' };
}
}
return null;
};
/**
* Pure function to find the best separator in a line
*/
const findSeparator = (line) => {
// Guard clause: no line
if (!line) {
return { index: -1, separator: '' };
}
const separators = ['=', ':', ' '];
for (const separator of separators) {
const index = line.indexOf(separator);
if (index >= 0) {
return { index, separator };
}
}
return { index: -1, separator: '' };
};
/**
* Pure function to check if line has any separator
*/
const hasSeparator = (line) => {
// Guard clause: no line
if (!line) {
return false;
}
return line.includes('=') || line.includes(':') || line.includes(' ');
};
/**
* Pure function to parse a property value
*/
const parsePropertiesValue = (value) => {
// Guard clause: no value
if (!value || value === null || value === undefined) {
return '';
}
// Check if value is quoted - if so, return as string without quotes
const isQuoted = isQuotedValue(value);
if (isQuoted) {
return removeQuotes(value);
}
// Parse boolean values (only for unquoted values)
const booleanValue = parseBoolean(value);
if (booleanValue !== null) {
return booleanValue;
}
// Parse numeric values (only for unquoted values)
const numericValue = parseNumber(value);
if (numericValue !== null) {
return numericValue;
}
// Return as string
return value;
};
exports.parsePropertiesValue = parsePropertiesValue;
/**
* Pure function to check if value is quoted
*/
const isQuotedValue = (value) => {
// Guard clause: no value
if (!value || value.length < 2) {
return false;
}
return (value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"));
};
/**
* Pure function to remove quotes from value
*/
const removeQuotes = (value) => {
// Guard clause: no value
if (!value) {
return value;
}
if (isQuotedValue(value)) {
return value.slice(1, -1);
}
return value;
};
/**
* Pure function to parse boolean values
*/
const parseBoolean = (value) => {
// Guard clause: no value
if (!value) {
return null;
}
const lowerValue = value.toLowerCase();
if (lowerValue === 'true' || lowerValue === 'yes' || lowerValue === 'on') {
return true;
}
if (lowerValue === 'false' || lowerValue === 'no' || lowerValue === 'off') {
return false;
}
return null;
};
/**
* Pure function to parse numeric values
*/
const parseNumber = (value) => {
// Guard clause: no value or empty string
if (!value || value === '') {
return null;
}
// Guard clause: values starting with 0 but not "0" or "0.x" are not valid numbers
if (value.length > 1 && value.startsWith('0') && !value.includes('.')) {
return null;
}
const numericValue = Number(value);
// Guard clause: not a valid number
if (isNaN(numericValue)) {
return null;
}
return numericValue;
};
//# sourceMappingURL=PropertiesFileAdapter.js.map