import type { GoogleDriveFile } from "./types";
export function escapeXml(str: string): string {
return str.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'");
}
export function generateListBucketResult(files: GoogleDriveFile[], bucket: string): string {
const contents = files
.map(
(f) => `
${escapeXml(f.name)}
${f.modifiedTime || new Date().toISOString()}
"${f.md5Checksum || f.id}"
${f.size || 0}
STANDARD
`,
)
.join("");
return `
${escapeXml(bucket)}
1000
false
${contents}
`;
}
export function initiateMultipartUploadResult(bucket: string, key: string, uploadId: string): string {
return `
${escapeXml(bucket)}${escapeXml(key)}${escapeXml(uploadId)}`;
}
export function completeMultipartUploadResult(bucket: string, key: string, etag: string): string {
return `
/${escapeXml(bucket)}/${escapeXml(key)}${escapeXml(bucket)}${escapeXml(key)}"${escapeXml(etag)}"`;
}
export function listPartsResult(bucket: string, key: string, uploadId: string, result: { parts: Array<{ partNumber: number; size: number; etag: string; ts: number }>; nextMarker: number; truncated: boolean }): string {
const parts = result.parts.map((part) => `${part.partNumber}${new Date(part.ts).toISOString()}"${escapeXml(part.etag)}"${part.size}`).join("");
return `
${escapeXml(bucket)}${escapeXml(key)}${escapeXml(uploadId)}${result.nextMarker}${result.truncated}${parts}`;
}
export function listMultipartUploadsResult(bucket: string): string {
return `
${escapeXml(bucket)}1000false`;
}
const PART_RE = /]*>([\s\S]*?)<\/Part>/g;
const NUM_RE = /\s*(\d+)\s*<\/PartNumber>/;
const ETAG_RE = /\s*(?:"|")?([^<"&]*)(?:"|")?\s*<\/ETag>/;
export function parseCompleteMultipartUpload(xml: string): Array<{ partNumber: number; etag: string }> {
const parts: Array<{ partNumber: number; etag: string }> = [];
for (const match of xml.matchAll(PART_RE)) {
const numberMatch = NUM_RE.exec(match[1]);
const etagMatch = ETAG_RE.exec(match[1]);
if (!numberMatch || !etagMatch || parts.length >= 10_000) throw new Error("MalformedXML");
parts.push({ partNumber: Number(numberMatch[1]), etag: etagMatch[1].trim() });
}
if (parts.length === 0 && !/]*>\s*<\/CompleteMultipartUpload>/.test(xml)) throw new Error("MalformedXML");
return parts;
}