scylladb_parse/statements/
trigger.rs

1use super::*;
2
3#[derive(ParseFromStr, Clone, Debug, TryInto, From, ToTokens, PartialEq, Eq)]
4#[parse_via(TaggedTriggerStatement)]
5pub enum TriggerStatement {
6    Create(CreateTriggerStatement),
7    Drop(DropTriggerStatement),
8}
9
10impl TryFrom<TaggedTriggerStatement> for TriggerStatement {
11    type Error = anyhow::Error;
12
13    fn try_from(value: TaggedTriggerStatement) -> Result<Self, Self::Error> {
14        Ok(match value {
15            TaggedTriggerStatement::Create(value) => TriggerStatement::Create(value.try_into()?),
16            TaggedTriggerStatement::Drop(value) => TriggerStatement::Drop(value.try_into()?),
17        })
18    }
19}
20
21#[derive(ParseFromStr, Clone, Debug, TryInto, From, ToTokens, PartialEq, Eq)]
22#[tokenize_as(TriggerStatement)]
23pub enum TaggedTriggerStatement {
24    Create(TaggedCreateTriggerStatement),
25    Drop(TaggedDropTriggerStatement),
26}
27
28impl Parse for TaggedTriggerStatement {
29    type Output = Self;
30    fn parse(s: &mut StatementStream<'_>) -> anyhow::Result<Self::Output> {
31        Ok(if s.check::<CREATE>() {
32            Self::Create(s.parse()?)
33        } else if s.check::<DROP>() {
34            Self::Drop(s.parse()?)
35        } else {
36            anyhow::bail!("Expected a trigger statement, found {}", s.info())
37        })
38    }
39}
40
41impl Display for TriggerStatement {
42    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
43        match self {
44            Self::Create(stmt) => stmt.fmt(f),
45            Self::Drop(stmt) => stmt.fmt(f),
46        }
47    }
48}
49
50#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq, Eq)]
51#[parse_via(TaggedCreateTriggerStatement)]
52pub struct CreateTriggerStatement {
53    #[builder(setter(name = "set_if_not_exists"), default)]
54    pub if_not_exists: bool,
55    #[builder(setter(into))]
56    pub name: Name,
57    #[builder(setter(into))]
58    pub table: KeyspaceQualifiedName,
59    #[builder(setter(into))]
60    pub using: LitStr,
61}
62
63impl TryFrom<TaggedCreateTriggerStatement> for CreateTriggerStatement {
64    type Error = anyhow::Error;
65
66    fn try_from(value: TaggedCreateTriggerStatement) -> Result<Self, Self::Error> {
67        Ok(Self {
68            if_not_exists: value.if_not_exists,
69            name: value.name.into_value()?,
70            table: value.table.try_into()?,
71            using: value.using.into_value()?,
72        })
73    }
74}
75
76#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq, Eq)]
77#[tokenize_as(CreateTriggerStatement)]
78pub struct TaggedCreateTriggerStatement {
79    #[builder(setter(name = "set_if_not_exists"), default)]
80    pub if_not_exists: bool,
81    pub name: Tag<Name>,
82    pub table: TaggedKeyspaceQualifiedName,
83    pub using: Tag<LitStr>,
84}
85
86impl CreateTriggerStatementBuilder {
87    /// Set IF NOT EXISTS on the statement.
88    /// To undo this, use `set_if_not_exists(false)`.
89    pub fn if_not_exists(&mut self) -> &mut Self {
90        self.if_not_exists.replace(true);
91        self
92    }
93}
94
95impl Parse for TaggedCreateTriggerStatement {
96    type Output = Self;
97    fn parse(s: &mut StatementStream<'_>) -> anyhow::Result<Self::Output> {
98        s.parse::<(CREATE, TRIGGER)>()?;
99        let mut res = TaggedCreateTriggerStatementBuilder::default();
100        res.set_if_not_exists(s.parse::<Option<(IF, NOT, EXISTS)>>()?.is_some())
101            .name(s.parse()?)
102            .table(s.parse::<(ON, _)>()?.1)
103            .using(s.parse::<(USING, _)>()?.1);
104        s.parse::<Option<Semicolon>>()?;
105        Ok(res
106            .build()
107            .map_err(|e| anyhow::anyhow!("Invalid CREATE TRIGGER statement: {}", e))?)
108    }
109}
110
111impl Display for CreateTriggerStatement {
112    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
113        write!(
114            f,
115            "CREATE TRIGGER{} {} ON {} USING {}",
116            if self.if_not_exists { " IF NOT EXISTS" } else { "" },
117            self.name,
118            self.table,
119            self.using
120        )
121    }
122}
123
124#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq, Eq)]
125#[parse_via(TaggedDropTriggerStatement)]
126pub struct DropTriggerStatement {
127    #[builder(setter(name = "set_if_exists"), default)]
128    pub if_exists: bool,
129    #[builder(setter(into))]
130    pub name: Name,
131    #[builder(setter(into))]
132    pub table: KeyspaceQualifiedName,
133}
134
135impl TryFrom<TaggedDropTriggerStatement> for DropTriggerStatement {
136    type Error = anyhow::Error;
137    fn try_from(value: TaggedDropTriggerStatement) -> Result<Self, Self::Error> {
138        Ok(Self {
139            if_exists: value.if_exists,
140            name: value.name.into_value()?,
141            table: value.table.try_into()?,
142        })
143    }
144}
145
146#[derive(ParseFromStr, Builder, Clone, Debug, ToTokens, PartialEq, Eq)]
147#[tokenize_as(DropTriggerStatement)]
148pub struct TaggedDropTriggerStatement {
149    #[builder(setter(name = "set_if_exists"), default)]
150    pub if_exists: bool,
151    pub name: Tag<Name>,
152    pub table: TaggedKeyspaceQualifiedName,
153}
154
155impl DropTriggerStatementBuilder {
156    /// Set IF EXISTS on the statement.
157    /// To undo this, use `set_if_exists(false)`.
158    pub fn if_exists(&mut self) -> &mut Self {
159        self.if_exists.replace(true);
160        self
161    }
162}
163
164impl Parse for TaggedDropTriggerStatement {
165    type Output = Self;
166    fn parse(s: &mut StatementStream<'_>) -> anyhow::Result<Self::Output> {
167        s.parse::<(DROP, TRIGGER)>()?;
168        let mut res = TaggedDropTriggerStatementBuilder::default();
169        res.set_if_exists(s.parse::<Option<(IF, EXISTS)>>()?.is_some())
170            .name(s.parse()?)
171            .table(s.parse::<(ON, _)>()?.1);
172        s.parse::<Option<Semicolon>>()?;
173        Ok(res
174            .build()
175            .map_err(|e| anyhow::anyhow!("Invalid DROP TRIGGER statement: {}", e))?)
176    }
177}
178
179impl Display for DropTriggerStatement {
180    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
181        write!(
182            f,
183            "DROP TRIGGER{} {} ON {}",
184            if self.if_exists { " IF EXISTS" } else { "" },
185            self.name,
186            self.table
187        )
188    }
189}
190
191#[cfg(test)]
192mod test {
193    use super::*;
194    use crate::KeyspaceQualifyExt;
195
196    #[test]
197    fn test_parse_create_trigger() {
198        let mut builder = CreateTriggerStatementBuilder::default();
199        builder.name("test_trigger");
200        assert!(builder.build().is_err());
201        builder.table("test_keyspace".dot("my_table"));
202        assert!(builder.build().is_err());
203        builder.using("test_trigger_function");
204        let statement = builder.build().unwrap().to_string();
205        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
206        builder.if_not_exists();
207        let statement = builder.build().unwrap().to_string();
208        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
209    }
210
211    #[test]
212    fn test_parse_drop_trigger() {
213        let mut builder = DropTriggerStatementBuilder::default();
214        builder.name("test_trigger");
215        assert!(builder.build().is_err());
216        builder.table("test_keyspace".dot("my_table"));
217        let statement = builder.build().unwrap().to_string();
218        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
219        builder.if_exists();
220        let statement = builder.build().unwrap().to_string();
221        assert_eq!(builder.build().unwrap(), statement.parse().unwrap());
222    }
223}