UNPKG

tusktsk

Version:

TuskTsk - The Freedom Configuration Language. Query databases, use any syntax, never bow to any king!

213 lines (180 loc) 5.94 kB
/** * TuskLang for JavaScript/Node.js * ================================ * The freedom configuration language * * "We don't bow to any king" - Use any syntax you prefer! */ const TuskLangEnhanced = require('./tsk-enhanced.js'); // Database adapters const adapters = { sqlite: null, postgres: null, mysql: null, mongodb: null, redis: null }; // Try to load optional database adapters try { adapters.sqlite = require('./adapters/sqlite.js'); } catch (e) {} try { adapters.postgres = require('./adapters/postgres.js'); } catch (e) {} try { adapters.mysql = require('./adapters/mysql.js'); } catch (e) {} try { adapters.mongodb = require('./adapters/mongodb.js'); } catch (e) {} try { adapters.redis = require('./adapters/redis.js'); } catch (e) {} /** * Main TuskLang class */ class TuskLang { constructor(options = {}) { this.parser = new TuskLangEnhanced(); // Set database adapter if provided if (options.database) { this.setDatabase(options.database); } } /** * Parse TuskLang content * @param {string} content - TuskLang formatted content * @returns {object} Parsed configuration object */ parse(content) { return this.parser.parse(content); } /** * Parse TuskLang file * @param {string} filename - Path to .tsk file * @returns {object} Parsed configuration object */ parseFile(filename) { const fs = require('fs'); const content = fs.readFileSync(filename, 'utf8'); return this.parse(content); } /** * Parse TuskLang file asynchronously * @param {string} filename - Path to .tsk file * @returns {Promise<object>} Parsed configuration object */ async parseFileAsync(filename) { const fs = require('fs').promises; const content = await fs.readFile(filename, 'utf8'); return this.parse(content); } /** * Set database adapter * @param {object} config - Database configuration */ setDatabase(config) { const { type, ...options } = config; if (!type) { throw new Error('Database type is required'); } const AdapterClass = adapters[type]; if (!AdapterClass) { throw new Error(`Database adapter '${type}' not found. Install the required package.`); } const adapter = new AdapterClass(options); this.parser.setDatabaseAdapter(adapter); } /** * Convert object to TuskLang format * @param {object} data - Data to serialize * @returns {string} TuskLang formatted string */ static stringify(data, options = {}) { const indent = options.indent || ' '; const style = options.style || 'mixed'; // 'flat', 'sections', 'objects', 'mixed' let output = '# Generated by TuskLang\n'; output += `# ${new Date().toISOString()}\n\n`; // Serialize based on style preference output += TuskLang.serializeValue(data, 0, indent, style); return output; } /** * Serialize a value to TuskLang format */ static serializeValue(value, depth = 0, indent = ' ', style = 'mixed') { const spacing = indent.repeat(depth); if (value === null) return 'null'; if (typeof value === 'boolean') return String(value); if (typeof value === 'number') return String(value); if (typeof value === 'string') { // Quote if contains special characters if (/[\s"'{}[\]:,#]/.test(value) || value === 'true' || value === 'false' || value === 'null') { return `"${value.replace(/"/g, '\\"')}"`; } return value; } if (Array.isArray(value)) { if (value.length === 0) return '[]'; if (value.length <= 3 && value.every(v => typeof v !== 'object')) { // Inline small arrays return '[' + value.map(v => TuskLang.serializeValue(v)).join(', ') + ']'; } // Multiline arrays let output = '[\n'; for (const item of value) { output += spacing + indent + TuskLang.serializeValue(item, depth + 1, indent, style) + '\n'; } output += spacing + ']'; return output; } if (typeof value === 'object') { const keys = Object.keys(value); if (keys.length === 0) return '{}'; let output = ''; // Choose style based on preference and content const useSection = style === 'sections' || (style === 'mixed' && depth === 0); const useBraces = style === 'objects' || (style === 'mixed' && depth > 0); if (useSection && depth === 0) { // Use [section] style for top level for (const key of keys) { if (typeof value[key] === 'object' && !Array.isArray(value[key])) { output += `\n[${key}]\n`; const subKeys = Object.keys(value[key]); for (const subKey of subKeys) { output += `${subKey}: ${TuskLang.serializeValue(value[key][subKey], depth + 1, indent, style)}\n`; } } else { output += `${key}: ${TuskLang.serializeValue(value[key], depth + 1, indent, style)}\n`; } } } else if (useBraces) { // Use {} style output = '{\n'; for (const key of keys) { output += spacing + indent + `${key}: ${TuskLang.serializeValue(value[key], depth + 1, indent, style)}\n`; } output += spacing + '}'; } else { // Flat style for (const key of keys) { output += spacing + `${key}: ${TuskLang.serializeValue(value[key], depth + 1, indent, style)}\n`; } } return output.trim(); } return String(value); } /** * Get available database adapters */ static getAvailableAdapters() { return Object.keys(adapters).filter(key => adapters[key] !== null); } } // Export the main class module.exports = TuskLang; // Also export the parser directly module.exports.TuskLangParser = TuskLangEnhanced; // Export adapter classes module.exports.adapters = adapters;