import { getMatches } from "../file-matching.js";
import { defineSource } from "../source.js";
import { nonEmptyString, optionalUrl, stringArray } from "../utilities/schema-primitives.js";
import { readFile } from "node:fs/promises";
import { resolve } from "node:path";
import is from "@sindresorhus/is";
import { z } from "zod";
import { parse } from "smol-toml";
//#region src/lib/sources/rust-cargo-toml.ts
/**
* Source for Rust `Cargo.toml` manifest files.
* These are TOML files used by the Cargo package manager. Metadata lives
* primarily in the `[package]` table, with dependencies in `[dependencies]`,
* `[dev-dependencies]`, and `[build-dependencies]`.
*
* Uses `smol-toml` for TOML parsing.
* @see https://doc.rust-lang.org/cargo/reference/manifest.html
*/
const cargoTomlAuthorEntrySchema = z.object({
	email: z.string().optional(),
	name: z.string()
});
const cargoTomlDependencyEntrySchema = z.object({
	name: z.string(),
	version: z.string().optional()
});
const cargoTomlSchema = z.object({
	authors: z.array(cargoTomlAuthorEntrySchema),
	buildDependencies: z.array(cargoTomlDependencyEntrySchema),
	categories: stringArray,
	dependencies: z.array(cargoTomlDependencyEntrySchema),
	description: nonEmptyString,
	devDependencies: z.array(cargoTomlDependencyEntrySchema),
	documentation: optionalUrl,
	edition: nonEmptyString,
	homepage: optionalUrl,
	keywords: stringArray,
	license: nonEmptyString,
	licenseFile: nonEmptyString,
	name: nonEmptyString,
	readme: nonEmptyString,
	repository: optionalUrl,
	rustVersion: nonEmptyString,
	version: nonEmptyString,
	workspaceMembers: stringArray
});
/**
* Parse a `Cargo.toml` content string into a structured object.
* Returns undefined if the TOML is malformed.
*/
function parse$1(content) {
	let data;
	try {
		const parsed = parse(content);
		if (!is.plainObject(parsed)) return void 0;
		data = parsed;
	} catch {
		return;
	}
	const packageData = is.plainObject(data.package) ? data.package : {};
	const workspace = is.plainObject(data.workspace) ? data.workspace : void 0;
	return cargoTomlSchema.parse({
		authors: parseAuthors(packageData.authors),
		buildDependencies: parseDependencies(is.plainObject(data["build-dependencies"]) ? data["build-dependencies"] : {}),
		categories: toStringArray(packageData.categories),
		dependencies: parseDependencies(is.plainObject(data.dependencies) ? data.dependencies : {}),
		description: nonEmpty(packageData.description),
		devDependencies: parseDependencies(is.plainObject(data["dev-dependencies"]) ? data["dev-dependencies"] : {}),
		documentation: nonEmpty(packageData.documentation),
		edition: nonEmpty(packageData.edition),
		homepage: nonEmpty(packageData.homepage),
		keywords: toStringArray(packageData.keywords),
		license: nonEmpty(packageData.license),
		licenseFile: nonEmpty(packageData["license-file"]),
		name: nonEmpty(packageData.name),
		readme: nonEmpty(packageData.readme),
		repository: nonEmpty(packageData.repository),
		rustVersion: nonEmpty(packageData["rust-version"]),
		version: nonEmpty(packageData.version),
		workspaceMembers: toStringArray(workspace?.members)
	});
}
/** Return a trimmed string, or undefined if not a non-empty string. */
function nonEmpty(value) {
	if (typeof value !== "string") return void 0;
	const trimmed = value.trim();
	return trimmed.length > 0 ? trimmed : void 0;
}
/** Convert an unknown value to a string array, filtering non-strings. */
function toStringArray(value) {
	if (!Array.isArray(value)) return [];
	return value.filter((v) => typeof v === "string" && v.trim().length > 0);
}
/**
* Parse `authors` field entries. Each entry is `"Name <email>"` or `"Name"`.
*/
function parseAuthors(value) {
	if (!Array.isArray(value)) return [];
	const results = [];
	for (const entry of value) {
		if (typeof entry !== "string") continue;
		const trimmed = entry.trim();
		if (trimmed.length === 0) continue;
		const bracketIndex = trimmed.indexOf("<");
		if (bracketIndex !== -1) {
			const closeBracket = trimmed.indexOf(">", bracketIndex);
			if (closeBracket !== -1) {
				const name = trimmed.slice(0, bracketIndex).trim();
				const email = trimmed.slice(bracketIndex + 1, closeBracket).trim();
				results.push({
					email: email.length > 0 ? email : void 0,
					name
				});
				continue;
			}
		}
		results.push({
			email: void 0,
			name: trimmed
		});
	}
	return results;
}
/**
* Parse a dependencies table into name/version entries.
* Handles both `dep = "version"` and `dep = { version = "..." }` forms.
*/
function parseDependencies(table) {
	const results = [];
	for (const [name, value] of Object.entries(table)) if (typeof value === "string") results.push({
		name,
		version: value
	});
	else if (is.plainObject(value)) results.push({
		name,
		version: nonEmpty(value.version)
	});
	else results.push({
		name,
		version: void 0
	});
	return results;
}
const rustCargoTomlSource = defineSource({
	async discover(context) {
		return getMatches(context.options, ["Cargo.toml"]);
	},
	key: "rustCargoToml",
	async parse(input, context) {
		const data = parse$1(await readFile(resolve(context.options.path, input), "utf8"));
		if (data !== void 0) return {
			data,
			source: input
		};
	},
	phase: 1
});
//#endregion
export { rustCargoTomlSource };
