Skip to main content

selene_gql/flagger/
mod.rs

1//! ISO optional-feature gate over parsed AST.
2
3mod 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/// One optional feature observed while walking an AST.
14#[derive(Clone, Copy, Debug, Eq, PartialEq)]
15pub struct FeatureUse {
16    /// ISO feature identifier.
17    pub feature_id: FeatureId,
18    /// Source span that exercises the feature.
19    pub span: SourceSpan,
20}
21
22/// Return every optional feature surface reached by `statement`.
23#[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}