use rahti_build::Backend;
use crate::templates as t;
pub fn with_dependency(manifest: &str, name: &str, line: &str) -> Option<String> {
if declares(manifest, name) {
return None;
}
Some(append_to_dependencies(manifest, line))
}
pub fn declares(manifest: &str, name: &str) -> bool {
let table = format!("[dependencies.{name}]");
manifest.lines().any(|line| {
let line = line.trim();
line == table
|| line
.strip_prefix(name)
.is_some_and(|rest| rest.trim_start().starts_with('='))
})
}
fn append_to_dependencies(manifest: &str, line: &str) -> String {
let ends_with_newline = manifest.ends_with('\n');
let mut lines: Vec<String> = manifest.lines().map(str::to_string).collect();
match lines.iter().position(|l| l.trim() == "[dependencies]") {
Some(header) => {
let end = lines[header + 1..]
.iter()
.position(|l| l.trim_start().starts_with('['))
.map(|offset| header + 1 + offset)
.unwrap_or(lines.len());
let mut at = end;
while at > header + 1 && lines[at - 1].trim().is_empty() {
at -= 1;
}
lines.insert(at, line.to_string());
}
None => {
if lines.last().is_some_and(|l| !l.trim().is_empty()) {
lines.push(String::new());
}
lines.push("[dependencies]".to_string());
lines.push(line.to_string());
}
}
let mut out = lines.join("\n");
if ends_with_newline {
out.push('\n');
}
out
}
pub fn with_ws_feature(manifest: &str) -> Result<Option<String>, String> {
if manifest.lines().any(|l| l.trim() == "[dependencies.rahti]") {
return Err("`rahti` is declared as a [dependencies.rahti] table".to_string());
}
let at = manifest
.lines()
.position(|l| {
l.trim_start()
.strip_prefix("rahti")
.is_some_and(|rest| rest.trim_start().starts_with('='))
})
.ok_or_else(|| "Cargo.toml has no `rahti` dependency".to_string())?;
let mut lines: Vec<String> = manifest.lines().map(str::to_string).collect();
let line = &lines[at];
let (key, value) = line.split_once('=').expect("the line was found by its `=`");
let value = value.trim();
if value.starts_with('{') && !value.ends_with('}') {
return Err("the `rahti` dependency spans more than one line".to_string());
}
if declares_ws(value) {
return Ok(None);
}
let wired = format!("{key}= {}", t::with_ws_feature(value));
lines[at] = wired;
let mut out = lines.join("\n");
if manifest.ends_with('\n') {
out.push('\n');
}
Ok(Some(out))
}
fn declares_ws(value: &str) -> bool {
value.contains("\"ws\"")
}
pub fn with_database_url(env: &str, backend: Backend) -> Option<String> {
let present = env
.lines()
.any(|l| l.trim_start().starts_with("DATABASE_URL"));
if present {
return None;
}
let mut out = t::database_env(backend);
out.push_str(env);
Some(out)
}
#[cfg(test)]
#[path = "tests/wiring.rs"]
mod tests;