| import type { CodingQuestion } from '@/data/codingQuestions'; |
| import { apiFetch } from '@/lib/authClient'; |
|
|
| type DSADifficulty = CodingQuestion['difficulty']; |
| type StarterCode = CodingQuestion['starterCode']; |
|
|
| export type DSAMongoQuestion = { |
| id: string; |
| source?: string; |
| questionNumber: number; |
| title: string; |
| difficulty: string; |
| category: string; |
| topics: string[]; |
| companies?: string[]; |
| hint?: string; |
| problemStatement: string; |
| examples?: { |
| input: string; |
| expectedOutput: string; |
| explanation?: string; |
| }[]; |
| constraints?: string[]; |
| }; |
|
|
| type DSAQuestionsResponse = { |
| questions?: DSAMongoQuestion[]; |
| }; |
|
|
| const emptyStarterCode: StarterCode = { |
| javascript: '', |
| python: '', |
| cpp: '', |
| java: '', |
| go: '', |
| }; |
|
|
| function splitTopLevel(value: string) { |
| const parts: string[] = []; |
| let current = ''; |
| let depth = 0; |
| let quote: '"' | "'" | null = null; |
| let escaped = false; |
|
|
| for (const char of value) { |
| if (escaped) { |
| current += char; |
| escaped = false; |
| continue; |
| } |
|
|
| if (char === '\\') { |
| current += char; |
| escaped = true; |
| continue; |
| } |
|
|
| if (quote) { |
| current += char; |
| if (char === quote) quote = null; |
| continue; |
| } |
|
|
| if (char === '"' || char === "'") { |
| current += char; |
| quote = char; |
| continue; |
| } |
|
|
| if (char === '[' || char === '{' || char === '(') depth += 1; |
| if (char === ']' || char === '}' || char === ')') depth = Math.max(0, depth - 1); |
|
|
| if (char === ',' && depth === 0) { |
| parts.push(current.trim()); |
| current = ''; |
| continue; |
| } |
|
|
| current += char; |
| } |
|
|
| if (current.trim()) parts.push(current.trim()); |
| return parts; |
| } |
|
|
| function parseExampleValue(raw: string): unknown { |
| const value = raw.trim().replace(/^(?:Input|Output):\s*/i, ''); |
| if (!value) return ''; |
|
|
| const jsonLike = value |
| .replace(/\bTrue\b/g, 'true') |
| .replace(/\bFalse\b/g, 'false') |
| .replace(/\bNone\b/g, 'null') |
| .replace(/\bNULL\b/g, 'null') |
| .replace(/\bnull\b/g, 'null') |
| .replace(/'/g, '"'); |
|
|
| try { |
| return JSON.parse(jsonLike); |
| } catch { |
| if (/^-?\d+(?:\.\d+)?$/.test(value)) return Number(value); |
| if (/^(true|false)$/i.test(value)) return value.toLowerCase() === 'true'; |
| return value.replace(/^["']|["']$/g, ''); |
| } |
| } |
|
|
| function parseExampleParams(input: string) { |
| const value = input.trim().replace(/^Input:\s*/i, ''); |
| if (!value) return []; |
|
|
| const parts = splitTopLevel(value); |
| const assignments = parts |
| .map((part) => part.match(/^[A-Za-z_]\w*\s*=\s*([\s\S]+)$/)?.[1]) |
| .filter((part): part is string => Boolean(part)); |
|
|
| if (assignments.length > 0) { |
| return assignments.map(parseExampleValue); |
| } |
|
|
| return [parseExampleValue(value)]; |
| } |
|
|
| function normalizeDifficulty(value: string): DSADifficulty { |
| const normalized = value.trim().toLowerCase(); |
| if (normalized === 'medium' || normalized === 'med.') return 'Medium'; |
| if (normalized === 'hard') return 'Hard'; |
| return 'Easy'; |
| } |
|
|
| function formatDSADescription(question: DSAMongoQuestion) { |
| const sections = [question.problemStatement.trim()].filter(Boolean); |
|
|
| if (question.examples?.length) { |
| sections.push( |
| [ |
| 'Examples:', |
| ...question.examples.map((example, index) => { |
| const lines = [`${index + 1}. Input: ${example.input}`]; |
| if (example.expectedOutput) lines.push(` Output: ${example.expectedOutput}`); |
| if (example.explanation) lines.push(` Explanation: ${example.explanation}`); |
| return lines.join('\n'); |
| }), |
| ].join('\n'), |
| ); |
| } |
|
|
| if (question.constraints?.length) { |
| sections.push(['Constraints:', ...question.constraints.map((constraint) => `- ${constraint}`)].join('\n')); |
| } |
|
|
| if (question.hint) { |
| sections.push(`Hint: ${question.hint}`); |
| } |
|
|
| if (question.companies?.length) { |
| sections.push(`Asked by: ${question.companies.join(', ')}`); |
| } |
|
|
| return sections.join('\n\n'); |
| } |
|
|
| function toTestCases(question: DSAMongoQuestion): CodingQuestion['testCases'] { |
| return (question.examples ?? []).map((example) => ({ |
| input: example.input, |
| output: example.expectedOutput, |
| params: parseExampleParams(example.input), |
| expected: parseExampleValue(example.expectedOutput), |
| })); |
| } |
|
|
| export function mapDSAQuestionToCodingQuestion(question: DSAMongoQuestion): CodingQuestion { |
| return { |
| id: question.id || `dsa-${question.questionNumber}`, |
| title: question.title, |
| difficulty: normalizeDifficulty(question.difficulty), |
| category: question.category || question.topics?.[0] || 'DSA', |
| companies: question.companies ?? [], |
| description: formatDSADescription(question), |
| starterCode: emptyStarterCode, |
| testCases: toTestCases(question), |
| hiddenTestCases: [], |
| solution: { |
| javascript: '', |
| python: '', |
| cpp: '', |
| java: '', |
| go: '', |
| explanation: question.hint || 'A full solution is not available for this MongoDB question yet.', |
| }, |
| }; |
| } |
|
|
| export async function fetchDSAQuestions(): Promise<CodingQuestion[]> { |
| const res = await apiFetch('/api/dsa/questions'); |
| if (!res.ok) { |
| const text = await res.text(); |
| throw new Error(text.trim() || `Failed to fetch DSA questions (${res.status})`); |
| } |
|
|
| const data = (await res.json()) as DSAQuestionsResponse; |
| return (data.questions ?? []).map(mapDSAQuestionToCodingQuestion); |
| } |
|
|