use proc_macro::TokenStream;
use syn::LitStr;
use super::parse::ServerDb;
pub(super) fn guard_server_datasource_provider(
schema_path: &LitStr,
schema: &cratestack_core::Schema,
db: ServerDb,
) -> Result<(), TokenStream> {
let Some(provider) = schema_datasource_provider(schema) else {
return Ok(());
};
let expected_provider = match db {
ServerDb::Postgres => "postgresql",
ServerDb::None => "none",
};
if provider == expected_provider {
return Ok(());
}
let db_arg = match db {
ServerDb::Postgres => "Postgres",
ServerDb::None => "None",
};
Err(TokenStream::from(
syn::Error::new(
schema_path.span(),
format!(
"include_server_schema!(..., db = {db_arg}) requires this schema's `datasource \
{{ provider = \"...\" }}` to be `\"{expected_provider}\"`, but found \
`\"{provider}\"` — the macro's `db` argument and the schema's own `datasource` \
declaration must agree. Either change `db = {db_arg}` to match the schema, or \
change the schema's `provider` to `\"{expected_provider}\"` (see \
https://github.com/cratestack/cratestack/issues/327)."
),
)
.to_compile_error(),
))
}
pub(super) fn guard_server_postgres_backend(
schema_path: &LitStr,
db: ServerDb,
) -> Result<(), TokenStream> {
if db != ServerDb::Postgres {
return Ok(());
}
if cfg!(feature = "postgres") {
return Ok(());
}
Err(TokenStream::from(
syn::Error::new(
schema_path.span(),
"include_server_schema!(..., db = Postgres) requires a facade crate with \
`cratestack-sqlx` support, but `cratestack-macros` was compiled without its \
`postgres` feature — this facade (e.g. `cratestack-api`) has no `cratestack-sqlx` \
dependency at all, under any feature, so there is no `sqlx::PgPool`/`SqlxRuntime` \
for this schema's generated code to use. Depend on \
`cratestack = { package = \"cratestack-pg\" }` instead for `db = Postgres` \
schemas, or switch this schema to `datasource { provider = \"none\" }` + \
`db = None` if it never actually needs a database (see \
https://github.com/cratestack/cratestack/issues/347).",
)
.to_compile_error(),
))
}
fn schema_datasource_provider(schema: &cratestack_core::Schema) -> Option<&str> {
schema
.datasource
.as_ref()?
.entries
.iter()
.find(|entry| entry.key == "provider")
.map(|entry| entry.value.trim_matches('"'))
}
#[cfg(test)]
mod tests {
use super::{ServerDb, schema_datasource_provider};
#[test]
fn reads_provider_from_datasource_block() {
let schema = cratestack_parser::parse_schema(
r#"
datasource db {
provider = "postgresql"
}
model Widget {
id Int @id
}
"#,
)
.expect("schema should parse");
assert_eq!(schema_datasource_provider(&schema), Some("postgresql"));
}
#[test]
fn reads_none_provider_from_datasource_block() {
let schema = cratestack_parser::parse_schema(
r#"
datasource db {
provider = "none"
}
"#,
)
.expect("schema should parse");
assert_eq!(schema_datasource_provider(&schema), Some("none"));
}
#[test]
fn no_datasource_block_yields_no_provider() {
let schema = cratestack_parser::parse_schema(
r#"
model Widget {
id Int @id
}
"#,
)
.expect("schema should parse");
assert_eq!(schema_datasource_provider(&schema), None);
}
#[test]
fn server_db_variants_are_distinct() {
assert_ne!(ServerDb::Postgres, ServerDb::None);
}
}