selene_gql/flagger/
mod.rs1mod call;
4mod ddl;
5mod expr;
6mod mutation;
7mod query;
8
9use selene_core::feature_register::{FeatureId, is_supported, name_of, non_supported_rationale};
10
11use crate::{SourceSpan, Statement, error::ParserError};
12
13#[derive(Clone, Copy, Debug, Eq, PartialEq)]
15pub struct FeatureUse {
16 pub feature_id: FeatureId,
18 pub span: SourceSpan,
20}
21
22#[must_use]
24pub fn feature_walk(statement: &Statement) -> Vec<FeatureUse> {
25 let mut uses = Vec::new();
26 query::statement(statement, &mut uses);
27 uses
28}
29
30pub(crate) fn flag(statement: &Statement) -> Result<(), ParserError> {
31 reject_unimplemented(statement)?;
32 for feature in feature_walk(statement) {
33 check_feature(feature.feature_id, feature.span)?;
34 }
35 Ok(())
36}
37
38pub(super) fn record_feature(uses: &mut Vec<FeatureUse>, id: FeatureId, span: SourceSpan) {
39 uses.push(FeatureUse {
40 feature_id: id,
41 span,
42 });
43}
44
45fn check_feature(id: FeatureId, span: SourceSpan) -> Result<(), ParserError> {
46 if is_supported(id) {
47 return Ok(());
48 }
49 Err(ParserError::UnsupportedFeature {
50 feature_id: id,
51 display_name: name_of(id).unwrap_or("unnamed feature"),
52 span,
53 hint: non_supported_rationale(id)
54 .unwrap_or("feature is outside the selene-db D1 claim list"),
55 })
56}
57
58fn reject_unimplemented(statement: &Statement) -> Result<(), ParserError> {
59 if let Statement::Ddl(
60 crate::DdlStatement::CreateGraph {
61 or_replace: true,
62 span,
63 ..
64 }
65 | crate::DdlStatement::CreateNodeType {
66 or_replace: true,
67 span,
68 ..
69 }
70 | crate::DdlStatement::CreateEdgeType {
71 or_replace: true,
72 span,
73 ..
74 },
75 ) = statement
76 {
77 return Err(ParserError::not_implemented(
78 "OR REPLACE is not part of ISO/IEC 39075:2024 catalog DDL",
79 *span,
80 Some("OR REPLACE has no ISO feature ID; drop the modifier or DROP+CREATE explicitly"),
81 ));
82 }
83 Ok(())
84}