Skip to main content

bwq_lint/
ast.rs

1use crate::error::Span;
2
3#[derive(Debug, Clone, PartialEq)]
4pub struct Query {
5    pub expression: Expression,
6    pub span: Span,
7}
8
9#[derive(Debug, Clone, PartialEq)]
10pub enum Expression {
11    BooleanOp {
12        operator: BooleanOperator,
13        left: Box<Expression>,
14        right: Option<Box<Expression>>,
15        span: Span,
16    },
17
18    Group {
19        expression: Box<Expression>,
20        span: Span,
21    },
22
23    Proximity {
24        operator: ProximityOperator,
25        terms: Vec<Expression>,
26        span: Span,
27    },
28
29    Field {
30        field: FieldType,
31        value: Box<Expression>,
32        span: Span,
33    },
34
35    Range {
36        field: Option<FieldType>,
37        start: String,
38        end: String,
39        span: Span,
40    },
41
42    Term {
43        term: Term,
44        span: Span,
45    },
46
47    Comment {
48        text: String,
49        span: Span,
50    },
51}
52
53#[derive(Debug, Clone, PartialEq)]
54pub enum BooleanOperator {
55    And,
56    Or,
57    Not,
58}
59
60impl BooleanOperator {
61    pub fn parse(s: &str) -> Option<Self> {
62        match s {
63            "AND" => Some(Self::And),
64            "OR" => Some(Self::Or),
65            "NOT" => Some(Self::Not),
66            _ => None,
67        }
68    }
69
70    pub fn as_str(&self) -> &'static str {
71        match self {
72            Self::And => "AND",
73            Self::Or => "OR",
74            Self::Not => "NOT",
75        }
76    }
77}
78
79#[derive(Debug, Clone, PartialEq)]
80pub enum ProximityOperator {
81    Proximity { distance: Option<u32> },
82    Near { distance: u32 },
83    NearForward { distance: u32 },
84}
85
86#[derive(Debug, Clone, PartialEq)]
87pub enum FieldType {
88    Title,
89    Site,
90    Url,
91    Author,
92    Links,
93
94    Continent,
95    Country,
96    Region,
97    City,
98    Latitude,
99    Longitude,
100
101    Language,
102    ChannelId,
103
104    AuthorGender,
105    AuthorVerified,
106    AuthorVerifiedType,
107    AuthorFollowers,
108    BlogName,
109    ParentBlogName,
110    RootBlogName,
111    ParentPostId,
112    RootPostId,
113    Tags,
114    BrandIds,
115    Objects,
116    EngagementType,
117    EngagingWith,
118    EngagingWithGuid,
119    Guid,
120    ImageType,
121    ItemReview,
122    Rating,
123    MinuteOfDay,
124    PubType,
125    PublisherSubType,
126    Publication,
127    RedditAuthorFlair,
128    RedditPostFlair,
129    RedditSpoiler,
130    SensitiveContent,
131    Subreddit,
132    SubredditNSFW,
133    SubredditTopics,
134    TopLevelDomain,
135    WeblogTitle,
136    Sentiment,
137}
138
139impl FieldType {
140    pub fn parse(s: &str) -> Option<Self> {
141        match s.to_lowercase().as_str() {
142            "title" => Some(Self::Title),
143            "site" => Some(Self::Site),
144            "url" => Some(Self::Url),
145            "author" => Some(Self::Author),
146            "links" => Some(Self::Links),
147            "continent" => Some(Self::Continent),
148            "country" => Some(Self::Country),
149            "region" => Some(Self::Region),
150            "city" => Some(Self::City),
151            "latitude" => Some(Self::Latitude),
152            "longitude" => Some(Self::Longitude),
153            "language" => Some(Self::Language),
154            "channelid" => Some(Self::ChannelId),
155            "authorgender" => Some(Self::AuthorGender),
156            "authorverified" => Some(Self::AuthorVerified),
157            "authorverifiedtype" => Some(Self::AuthorVerifiedType),
158            "authorfollowers" => Some(Self::AuthorFollowers),
159            "blogname" => Some(Self::BlogName),
160            "parentblogname" => Some(Self::ParentBlogName),
161            "rootblogname" => Some(Self::RootBlogName),
162            "parentpostid" => Some(Self::ParentPostId),
163            "rootpostid" => Some(Self::RootPostId),
164            "tags" => Some(Self::Tags),
165            "brandids" => Some(Self::BrandIds),
166            "objects" => Some(Self::Objects),
167            "engagementtype" => Some(Self::EngagementType),
168            "engagingwith" => Some(Self::EngagingWith),
169            "engagingwithguid" => Some(Self::EngagingWithGuid),
170            "guid" => Some(Self::Guid),
171            "imagetype" => Some(Self::ImageType),
172            "itemreview" => Some(Self::ItemReview),
173            "rating" => Some(Self::Rating),
174            "minuteofday" => Some(Self::MinuteOfDay),
175            "pubtype" => Some(Self::PubType),
176            "publishersubtype" => Some(Self::PublisherSubType),
177            "publication" => Some(Self::Publication),
178            "redditauthorflair" => Some(Self::RedditAuthorFlair),
179            "redditpostflair" => Some(Self::RedditPostFlair),
180            "redditspoiler" => Some(Self::RedditSpoiler),
181            "sensitivecontent" => Some(Self::SensitiveContent),
182            "subreddit" => Some(Self::Subreddit),
183            "subredditnsfw" => Some(Self::SubredditNSFW),
184            "subreddittopics" => Some(Self::SubredditTopics),
185            "topleveldomain" => Some(Self::TopLevelDomain),
186            "weblogtitle" => Some(Self::WeblogTitle),
187            "sentiment" => Some(Self::Sentiment),
188            _ => None,
189        }
190    }
191
192    pub fn as_str(&self) -> &'static str {
193        match self {
194            Self::Title => "title",
195            Self::Site => "site",
196            Self::Url => "url",
197            Self::Author => "author",
198            Self::Links => "links",
199            Self::Continent => "continent",
200            Self::Country => "country",
201            Self::Region => "region",
202            Self::City => "city",
203            Self::Latitude => "latitude",
204            Self::Longitude => "longitude",
205            Self::Language => "language",
206            Self::ChannelId => "channelId",
207            Self::AuthorGender => "authorGender",
208            Self::AuthorVerified => "authorVerified",
209            Self::AuthorVerifiedType => "authorVerifiedType",
210            Self::AuthorFollowers => "authorFollowers",
211            Self::BlogName => "blogName",
212            Self::ParentBlogName => "parentBlogName",
213            Self::RootBlogName => "rootBlogName",
214            Self::ParentPostId => "parentPostId",
215            Self::RootPostId => "rootPostId",
216            Self::Tags => "tags",
217            Self::BrandIds => "brandIds",
218            Self::Objects => "objects",
219            Self::EngagementType => "engagementType",
220            Self::EngagingWith => "engagingWith",
221            Self::EngagingWithGuid => "engagingWithGuid",
222            Self::Guid => "guid",
223            Self::ImageType => "imageType",
224            Self::ItemReview => "itemReview",
225            Self::Rating => "rating",
226            Self::MinuteOfDay => "minuteOfDay",
227            Self::PubType => "pubType",
228            Self::PublisherSubType => "publisherSubType",
229            Self::Publication => "publication",
230            Self::RedditAuthorFlair => "redditAuthorFlair",
231            Self::RedditPostFlair => "redditPostFlair",
232            Self::RedditSpoiler => "redditSpoiler",
233            Self::SensitiveContent => "sensitiveContent",
234            Self::Subreddit => "subreddit",
235            Self::SubredditNSFW => "subredditNSFW",
236            Self::SubredditTopics => "subredditTopics",
237            Self::TopLevelDomain => "topLevelDomain",
238            Self::WeblogTitle => "weblogTitle",
239            Self::Sentiment => "sentiment",
240        }
241    }
242}
243
244#[derive(Debug, Clone, PartialEq)]
245pub enum Term {
246    Word { value: String },
247    Phrase { value: String },
248    Wildcard { value: String },
249    Replacement { value: String },
250    CaseSensitive { value: String },
251    Hashtag { value: String },
252    Mention { value: String },
253    Emoji { value: String },
254}