use std::fmt::Write as _;
use crate::coding::blueprint_programs::{
JAVASCRIPT_HTTP_JSON_STATS, PYTHON_HTTP_JSON_STATS, PYTHON_PERSONAL_BUDGET_REPORT,
RUST_HTTP_JSON_STATS,
};
use crate::language::Language;
use crate::solver::BlueprintComposition;
#[derive(Clone, Copy)]
pub struct Capability {
pub slug: &'static str,
pub label: &'static str,
pub keywords: &'static [&'static str],
}
#[derive(Clone, Copy)]
pub struct RecipeProgram {
pub language_slug: &'static str,
pub libraries: &'static [&'static str],
pub run_command: &'static str,
pub execution: BlueprintExecution,
pub code: &'static str,
}
#[derive(Clone, Copy)]
pub enum BlueprintExecution {
ExternalLibrariesAndNetwork,
ReviewDataAssumptions,
}
#[derive(Clone, Copy)]
pub struct BlueprintRecipe {
pub slug: &'static str,
pub label: &'static str,
pub required_capabilities: &'static [&'static str],
pub programs: &'static [RecipeProgram],
}
impl BlueprintRecipe {
fn program_for(&self, language_slug: &str) -> Option<&'static RecipeProgram> {
self.programs
.iter()
.find(|program| program.language_slug == language_slug)
}
}
pub struct Blueprint {
pub recipe: &'static BlueprintRecipe,
pub program: &'static RecipeProgram,
pub capabilities: Vec<&'static Capability>,
}
pub const CAPABILITIES: &[Capability] = &[
Capability {
slug: "http_request",
label: "Make an HTTP request",
keywords: &[
"http",
"https",
"url",
"get request",
"http get",
"fetch",
"download",
"запрос",
"ссылк",
"загруз",
"स्थानांतरण",
"अनुरोध",
"请求",
"下载",
"网址",
],
},
Capability {
slug: "json_parse",
label: "Parse the JSON response",
keywords: &[
"json",
"parse",
"parses",
"parsing",
"deserialize",
"разбор",
"разобрать",
"парсинг",
"जेसन",
"पार्स",
"解析",
],
},
Capability {
slug: "statistics",
label: "Calculate statistics (mean, median)",
keywords: &[
"statistics",
"statistic",
"mean",
"average",
"median",
"статистик",
"среднее",
"медиан",
"औसत",
"माध्यिका",
"सांख्यिकी",
"统计",
"平均",
"中位数",
],
},
Capability {
slug: "output_results",
label: "Output the results",
keywords: &[
"output",
"print",
"outputs",
"display",
"report",
"вывод",
"вывести",
"печат",
"आउटपुट",
"छाप",
"输出",
"打印",
"显示",
],
},
Capability {
slug: "error_handling",
label: "Handle errors",
keywords: &[
"error handling",
"error-handling",
"errors",
"error",
"exception",
"ошибк",
"обработк",
"त्रुटि",
"错误",
"异常",
],
},
Capability {
slug: "comments",
label: "Document the code with comments",
keywords: &[
"comments",
"comment",
"commented",
"documented",
"комментар",
"टिप्पणि",
"注释",
"评论",
],
},
Capability {
slug: "web_research",
label: "Research current source data",
keywords: &[
"search",
"research",
"sources",
"source",
"look up",
"current",
"average",
"поиск",
"источник",
"источники",
"искать",
"खोज",
"स्रोत",
"वर्तमान",
"搜索",
"来源",
],
},
Capability {
slug: "city_costs",
label: "Compare city living costs",
keywords: &[
"living costs",
"cost of living",
"average rent",
"rent",
"moscow",
"berlin",
"new york",
"city",
"cities",
"аренда",
"стоимость жизни",
"москва",
"берлин",
"нью-йорк",
"जीवन यापन",
"लागत",
"किराया",
"मास्को",
"बर्लिन",
"न्यूयॉर्क",
"租金",
"生活成本",
],
},
Capability {
slug: "budget_rule",
label: "Apply the 50/30/20 budget rule",
keywords: &[
"50/30/20",
"budget rule",
"monthly income",
"income",
"needs",
"wants",
"savings",
"бюджет",
"доход",
"сбереж",
"बजट",
"आय",
"बचत",
"预算",
"收入",
],
},
Capability {
slug: "compound_savings",
label: "Project compound savings",
keywords: &[
"annual return",
"return",
"8%",
"10 years",
"$3000",
"100,000",
"100000",
"years to save",
"накопить",
"доходность",
"वार्षिक रिटर्न",
"रिटर्न",
"साल",
"收益",
"年",
],
},
Capability {
slug: "markdown_report",
label: "Export a Markdown comparison report",
keywords: &[
"markdown",
"formatted markdown",
"report",
"comparison table",
"table",
"export",
"отчет",
"отчёт",
"таблица",
"экспорт",
"मार्कडाउन",
"रिपोर्ट",
"तालिका",
"निर्यात",
"报告",
"表格",
"导出",
],
},
];
pub const RECIPES: &[BlueprintRecipe] = &[
BlueprintRecipe {
slug: "http_json_stats",
label: "fetch JSON over HTTP and report the mean and median of its numbers",
required_capabilities: &["http_request", "json_parse", "statistics"],
programs: &[
RecipeProgram {
language_slug: "rust",
libraries: &["reqwest (blocking, json)", "serde_json"],
run_command: "cargo run -- <url-returning-json>",
execution: BlueprintExecution::ExternalLibrariesAndNetwork,
code: RUST_HTTP_JSON_STATS,
},
RecipeProgram {
language_slug: "python",
libraries: &["requests"],
run_command: "python stats.py <url-returning-json>",
execution: BlueprintExecution::ExternalLibrariesAndNetwork,
code: PYTHON_HTTP_JSON_STATS,
},
RecipeProgram {
language_slug: "javascript",
libraries: &["Node.js 18+ (built-in global fetch; no extra packages)"],
run_command: "node stats.js <url-returning-json>",
execution: BlueprintExecution::ExternalLibrariesAndNetwork,
code: JAVASCRIPT_HTTP_JSON_STATS,
},
],
},
BlueprintRecipe {
slug: "personal_budget_report",
label: "build a sourced 50/30/20 city budget calculator and Markdown report",
required_capabilities: &[
"web_research",
"city_costs",
"budget_rule",
"compound_savings",
"markdown_report",
],
programs: &[RecipeProgram {
language_slug: "python",
libraries: &["Python 3 standard library only"],
run_command: "python budget_report.py",
execution: BlueprintExecution::ReviewDataAssumptions,
code: PYTHON_PERSONAL_BUDGET_REPORT,
}],
},
];
#[must_use]
pub fn detect_capabilities(normalized: &str) -> Vec<&'static Capability> {
CAPABILITIES
.iter()
.filter(|capability| {
capability
.keywords
.iter()
.any(|keyword| contains_keyword(normalized, keyword))
})
.collect()
}
#[must_use]
pub fn select_blueprint(normalized: &str, language_slug: &str) -> Option<Blueprint> {
let detected = detect_capabilities(normalized);
let detected_slugs: Vec<&str> = detected.iter().map(|capability| capability.slug).collect();
let recipe = RECIPES.iter().find(|recipe| {
recipe
.required_capabilities
.iter()
.all(|required| detected_slugs.contains(required))
&& recipe.program_for(language_slug).is_some()
})?;
let program = recipe.program_for(language_slug)?;
Some(Blueprint {
recipe,
program,
capabilities: detected,
})
}
fn contains_keyword(normalized: &str, keyword: &str) -> bool {
if crate::coding::contains_cjk(keyword) {
return normalized.contains(keyword);
}
if keyword.contains(' ') {
return normalized.contains(keyword);
}
normalized
.split(|character: char| !character.is_alphanumeric())
.any(|token| token == keyword || (keyword.len() >= 4 && token.starts_with(keyword)))
}
fn comment_marker(language_slug: &str) -> &'static str {
match language_slug {
"python" | "ruby" => "#",
_ => "//",
}
}
const REGION_OPEN: &str = "region:";
const REGION_CLOSE: &str = "endregion:";
fn region_directive<'line>(trimmed: &'line str, marker: &str) -> Option<(bool, &'line str)> {
let rest = trimmed.strip_prefix(marker)?.trim_start();
if let Some(slug) = rest.strip_prefix(REGION_CLOSE) {
return Some((false, slug.trim()));
}
let slug = rest.strip_prefix(REGION_OPEN)?;
Some((true, slug.trim()))
}
#[must_use]
pub fn compose_program(blueprint: &Blueprint, strategy: BlueprintComposition) -> String {
let language_slug = blueprint.program.language_slug;
let marker = comment_marker(language_slug);
let compose = strategy == BlueprintComposition::Composed;
let requested = |slug: &str| blueprint.capabilities.iter().any(|c| c.slug == slug);
let mut kept: Vec<&str> = Vec::new();
let mut skipping = false;
for line in blueprint.program.code.lines() {
let trimmed = line.trim_start();
if let Some((is_open, slug)) = region_directive(trimmed, marker) {
skipping = is_open && compose && !requested(slug);
continue;
}
if !skipping {
kept.push(line);
}
}
if compose && !wants_comments(blueprint) {
strip_comment_lines(&kept, language_slug)
} else {
collapse_blank_runs(&kept)
}
}
fn strip_comment_lines(lines: &[&str], language_slug: &str) -> String {
let marker = comment_marker(language_slug);
let mut kept: Vec<&str> = Vec::new();
let mut in_docstring = false;
for &line in lines {
let trimmed = line.trim_start();
if language_slug == "python" {
if in_docstring {
if trimmed.contains("\"\"\"") {
in_docstring = false;
}
continue;
}
if let Some(rest) = trimmed.strip_prefix("\"\"\"") {
if !rest.contains("\"\"\"") {
in_docstring = true;
}
continue;
}
}
if trimmed.starts_with(marker) {
continue;
}
kept.push(line);
}
collapse_blank_runs(&kept)
}
fn collapse_blank_runs(lines: &[&str]) -> String {
let mut out = String::new();
let mut pending_blank = false;
let mut wrote_any = false;
for line in lines {
if line.trim().is_empty() {
if wrote_any {
pending_blank = true;
}
continue;
}
if pending_blank {
out.push('\n');
pending_blank = false;
}
out.push_str(line);
out.push('\n');
wrote_any = true;
}
if out.ends_with('\n') {
out.pop();
}
out
}
#[must_use]
pub fn wants_comments(blueprint: &Blueprint) -> bool {
blueprint
.capabilities
.iter()
.any(|capability| capability.slug == "comments")
}
#[must_use]
pub fn blueprint_intro(language_name: &str, recipe_label: &str, language: Language) -> String {
match language {
Language::Russian => format!(
"Вот программа на языке {language_name}, которая решает составную задачу \
({recipe_label}). Я разбил ваш запрос на следующие подзадачи:"
),
Language::Hindi => format!(
"यहाँ {language_name} में एक प्रोग्राम है जो इस संयुक्त कार्य को हल करता है \
({recipe_label})। मैंने आपके अनुरोध को इन उप-कार्यों में विभाजित किया है:"
),
Language::Chinese => format!(
"这是一个解决该复合任务的 {language_name} 程序({recipe_label})。\
我已将您的请求分解为以下子任务:"
),
_ => format!(
"Here is a {language_name} program for the requested composite task \
({recipe_label}). I decomposed your request into these sub-tasks:"
),
}
}
#[must_use]
pub fn capability_label(capability: &Capability, language: Language) -> &'static str {
match (capability.slug, language) {
("http_request", Language::Russian) => "Выполнить HTTP-запрос",
("http_request", Language::Hindi) => "HTTP अनुरोध करें",
("http_request", Language::Chinese) => "发起 HTTP 请求",
("json_parse", Language::Russian) => "Разобрать JSON-ответ",
("json_parse", Language::Hindi) => "JSON प्रतिक्रिया पार्स करें",
("json_parse", Language::Chinese) => "解析 JSON 响应",
("statistics", Language::Russian) => "Вычислить статистику (среднее, медиана)",
("statistics", Language::Hindi) => "सांख्यिकी (औसत, माध्यिका) की गणना करें",
("statistics", Language::Chinese) => "计算统计量(平均值、中位数)",
("output_results", Language::Russian) => "Вывести результаты",
("output_results", Language::Hindi) => "परिणाम आउटपुट करें",
("output_results", Language::Chinese) => "输出结果",
("error_handling", Language::Russian) => "Обработать ошибки",
("error_handling", Language::Hindi) => "त्रुटियाँ संभालें",
("error_handling", Language::Chinese) => "处理错误",
("comments", Language::Russian) => "Снабдить код комментариями",
("comments", Language::Hindi) => "कोड में टिप्पणियाँ जोड़ें",
("comments", Language::Chinese) => "为代码添加注释",
("web_research", Language::Russian) => "Найти актуальные исходные данные",
("web_research", Language::Hindi) => "वर्तमान स्रोत डेटा खोजें",
("web_research", Language::Chinese) => "检索当前来源数据",
("city_costs", Language::Russian) => "Сравнить стоимость жизни по городам",
("city_costs", Language::Hindi) => "शहरों की जीवन-यापन लागत की तुलना करें",
("city_costs", Language::Chinese) => "比较城市生活成本",
("budget_rule", Language::Russian) => "Применить правило бюджета 50/30/20",
("budget_rule", Language::Hindi) => "50/30/20 बजट नियम लागू करें",
("budget_rule", Language::Chinese) => "应用 50/30/20 预算规则",
("compound_savings", Language::Russian) => "Рассчитать накопления со сложным процентом",
("compound_savings", Language::Hindi) => "चक्रवृद्धि बचत का अनुमान लगाएँ",
("compound_savings", Language::Chinese) => "预测复利储蓄",
("markdown_report", Language::Russian) => "Экспортировать Markdown-отчёт со сравнением",
("markdown_report", Language::Hindi) => "Markdown तुलना रिपोर्ट निर्यात करें",
("markdown_report", Language::Chinese) => "导出 Markdown 比较报告",
_ => capability.label,
}
}
#[must_use]
pub const fn libraries_heading(language: Language) -> &'static str {
match language {
Language::Russian => "Необходимые библиотеки:",
Language::Hindi => "आवश्यक लाइब्रेरियाँ:",
Language::Chinese => "所需的库:",
_ => "Required libraries:",
}
}
#[must_use]
pub fn blueprint_execution_report(
run_command: &str,
execution: BlueprintExecution,
language: Language,
) -> String {
match (execution, language) {
(BlueprintExecution::ReviewDataAssumptions, Language::Russian) => format!(
"Статус выполнения: не запускалось — этот отчёт не выполнялся в офлайн-песочнице, \
а допущения о стоимости жизни нужно сверить с указанными источниками. Код \
приведён для проверки. Запустить самостоятельно: `{run_command}`."
),
(BlueprintExecution::ReviewDataAssumptions, Language::Hindi) => format!(
"निष्पादन स्थिति: नहीं चलाया गया — यह रिपोर्ट ऑफ़लाइन सैंडबॉक्स में नहीं चली, \
और शहर-लागत मानों को दिए गए स्रोतों से जाँचना चाहिए। कोड समीक्षा के लिए दिया \
गया है। स्वयं चलाएँ: `{run_command}`।"
),
(BlueprintExecution::ReviewDataAssumptions, Language::Chinese) => format!(
"执行状态:未运行 —— 该报告未在离线沙箱中执行,城市成本假设应先按列出的来源核对。\
代码仅供审阅。自行运行:`{run_command}`。"
),
(BlueprintExecution::ReviewDataAssumptions, _) => format!(
"Execution status: not run — this report blueprint was not executed in the \
offline sandbox, and the city-cost assumptions should be reviewed against the \
listed sources before use. The code is provided for review. Run it yourself: \
`{run_command}`."
),
(_, Language::Russian) => format!(
"Статус выполнения: не запускалось — программе нужны внешние библиотеки и \
доступ к сети, поэтому офлайн-песочница её не выполняет. Код приведён для \
проверки. Запустить самостоятельно: `{run_command}`."
),
(_, Language::Hindi) => format!(
"निष्पादन स्थिति: नहीं चलाया गया — प्रोग्राम को बाहरी लाइब्रेरियों और नेटवर्क पहुँच \
की आवश्यकता है, इसलिए ऑफ़लाइन सैंडबॉक्स इसे नहीं चलाता। कोड समीक्षा के लिए दिया \
गया है। स्वयं चलाएँ: `{run_command}`।"
),
(_, Language::Chinese) => format!(
"执行状态:未运行 —— 该程序需要外部库和网络访问,因此离线沙箱不会执行它。\
代码仅供审阅。自行运行:`{run_command}`。"
),
_ => format!(
"Execution status: not run — this program needs external libraries and network \
access, so the offline sandbox does not execute it. The code is provided for \
review. Run it yourself: `{run_command}`."
),
}
}
#[must_use]
pub fn recipe_summary(recipe: &BlueprintRecipe, language: Language) -> &'static str {
match (recipe.slug, language) {
("http_json_stats", Language::Russian) => {
"загрузить JSON по HTTP и вывести среднее и медиану его чисел"
}
("http_json_stats", Language::Hindi) => {
"HTTP के माध्यम से JSON प्राप्त करें और उसकी संख्याओं का औसत और माध्यिका दिखाएँ"
}
("http_json_stats", Language::Chinese) => {
"通过 HTTP 获取 JSON 并报告其中数字的平均值和中位数"
}
("personal_budget_report", Language::Russian) => {
"собрать бюджетный калькулятор 50/30/20 с городскими расходами, источниками и Markdown-отчётом"
}
("personal_budget_report", Language::Hindi) => {
"स्रोतों सहित 50/30/20 शहर बजट कैलकुलेटर और Markdown रिपोर्ट बनाएँ"
}
("personal_budget_report", Language::Chinese) => {
"生成带来源的 50/30/20 城市预算计算器和 Markdown 报告"
}
_ => recipe.label,
}
}
#[must_use]
const fn how_to_run_heading(language: Language) -> &'static str {
match language {
Language::Russian => "Как запустить самостоятельно:",
Language::Hindi => "इसे स्वयं कैसे चलाएँ:",
Language::Chinese => "如何自行运行:",
_ => "How to run it yourself:",
}
}
#[must_use]
pub fn render(blueprint: &Blueprint, language: Language, strategy: BlueprintComposition) -> String {
let catalog_language = crate::coding::program_language_by_slug(blueprint.program.language_slug);
let language_name =
catalog_language.map_or(blueprint.program.language_slug, |language| language.name);
let code_fence = catalog_language.map_or(blueprint.program.language_slug, |l| l.code_fence);
let summary = recipe_summary(blueprint.recipe, language);
let mut body = blueprint_intro(language_name, summary, language);
body.push_str("\n\n");
for (index, capability) in blueprint.capabilities.iter().enumerate() {
writeln!(
body,
"{}. {}",
index + 1,
capability_label(capability, language)
)
.expect("string write is infallible");
}
let program_code = compose_program(blueprint, strategy);
write!(
body,
"\n```{code_fence}\n{program_code}\n```\n\n{}\n",
libraries_heading(language)
)
.expect("string write is infallible");
for library in blueprint.program.libraries {
writeln!(body, "- {library}").expect("string write is infallible");
}
write!(
body,
"\n{}\n\n{}",
how_to_run_heading(language),
blueprint_execution_report(
blueprint.program.run_command,
blueprint.program.execution,
language
)
)
.expect("string write is infallible");
body
}