1#![doc = include_str!("../docs.md")]
2
3use pest::Parser;
4use pest_derive::Parser;
5use thiserror::Error;
6
7#[derive(Parser)]
11#[grammar = "src/grammar.pest"] pub struct MotogarageParser;
13
14#[derive(Error, Debug)]
18pub enum MotoError {
19 #[error("Parsing error: {0}")]
21 ParseError(#[from] pest::error::Error<Rule>),
22
23 #[error("Interp error: {0}")]
25 InterpreterError(String),
26}
27
28#[derive(Debug, Clone, PartialEq)]
35pub enum Command {
36 Definition(Motorcycle), Get(Query), Count(Query), }
40
41#[derive(Debug, Clone, Default, PartialEq)]
43pub struct Motorcycle {
44 pub name: String,
45 pub year: Option<u32>, pub engine_cc: Option<u32>,
47 pub bike_type: Option<String>,
48}
49
50#[derive(Debug, Clone, PartialEq)]
53pub struct Query {
54 pub condition: Option<Condition>,
55}
56
57#[derive(Debug, Clone, PartialEq)]
59pub struct Condition {
60 pub field: String, pub operator: String, pub value: Value, }
64
65#[derive(Debug, Clone, PartialEq)]
67pub enum Value {
68 Number(u32), StringType(String), StringLiteral(String), }
72
73impl Value {
75 pub fn value_as_number(&self) -> u32 {
77 match self {
78 Value::Number(n) => *n,
79 _ => 0,
80 }
81 }
82 pub fn value_as_string(&self) -> String {
84 match self {
85 Value::StringType(s) => s.clone(),
86 Value::StringLiteral(s) => s.clone(),
87 _ => String::new(),
88 }
89 }
90}
91
92#[derive(Debug, Default)]
96pub struct Garage {
97 bikes: Vec<Motorcycle>, }
99
100impl Garage {
101 pub fn new() -> Self {
102 Self::default()
103 }
104
105 pub fn execute(&mut self, program: Vec<Command>) -> Result<Vec<String>, MotoError> {
107 let mut results = Vec::new(); for command in program {
110 match command {
111 Command::Definition(bike) => {
113 self.bikes.push(bike);
114 }
115 Command::Get(query) => {
117 results.extend(self.run_query_get(query));
118 }
119 Command::Count(query) => {
121 let count = self.filter_bikes(&query).count();
122 results.push(format!("Bikes found: {}", count));
123 }
124 }
125 }
126 Ok(results) }
128
129 fn filter_bikes<'a>(&'a self, query: &'a Query) -> impl Iterator<Item = &'a Motorcycle> {
132 self.bikes
133 .iter()
134 .filter(move |bike| query.condition.as_ref().map_or(true, |c| bike.matches(c)))
137 }
138
139 fn run_query_get(&self, query: Query) -> Vec<String> {
141 self.filter_bikes(&query)
142 .map(|bike| bike.name.clone()) .collect() }
145}
146
147impl Motorcycle {
149 fn matches(&self, condition: &Condition) -> bool {
150 match condition.field.as_str() { "type" => self
152 .bike_type
153 .as_ref() .map_or(false, |t| *t == condition.value.value_as_string()),
156 "year" => self.year.map_or(false, |y| { compare(y, &condition.operator, condition.value.value_as_number())
158 }),
159 "engine" => self.engine_cc.map_or(false, |e| { compare(e, &condition.operator, condition.value.value_as_number())
161 }),
162 _ => false, }
164 }
165}
166fn compare(a: u32, op: &str, b: u32) -> bool {
168 match op {
169 "=" => a == b,
170 ">" => a > b,
171 "<" => a < b,
172 _ => false, }
174}
175impl Condition {} pub fn parse_moto_file(input: &str) -> Result<Vec<Command>, MotoError> {
182 let pairs = MotogarageParser::parse(Rule::file, input)?; let mut ast = Vec::new();
185
186 for pair in pairs.into_iter().next().unwrap().into_inner() {
189 match pair.as_rule() {
190 Rule::command => ast.push(parse_command(pair)), Rule::EOI => (), _ => unreachable!(), }
194 }
195 Ok(ast) }
197
198fn parse_command(pair: pest::iterators::Pair<Rule>) -> Command {
200 let inner = pair.into_inner().next().unwrap();
202 match inner.as_rule() {
203 Rule::definition => Command::Definition(parse_definition(inner)),
204 Rule::query_get => Command::Get(parse_query(inner)),
205 Rule::query_count => Command::Count(parse_query(inner)),
206 _ => unreachable!(),
207 }
208}
209
210fn parse_definition(pair: pest::iterators::Pair<Rule>) -> Motorcycle {
212 let mut inner_pairs = pair.into_inner();
213 let name = parse_string_literal(inner_pairs.next().unwrap());
215
216 let mut bike = Motorcycle {
217 name,
218 ..Default::default() };
220
221 let properties_pairs = inner_pairs.next().unwrap().into_inner();
223 for prop_pair in properties_pairs { let mut prop_inner = prop_pair.into_inner();
225 let field_name = prop_inner.next().unwrap().as_str(); let value_pair = prop_inner.next().unwrap(); match field_name {
230 "year" => bike.year = Some(parse_value(value_pair).value_as_number()),
231 "engine" => bike.engine_cc = Some(parse_value(value_pair).value_as_number()),
232 "type" => bike.bike_type = Some(parse_value(value_pair).value_as_string()),
233 _ => {} }
235 }
236 bike
237}
238
239fn parse_query(pair: pest::iterators::Pair<Rule>) -> Query {
242 let where_clause_pair = pair.into_inner().next();
244
245 let condition_pair =
247 where_clause_pair.map(|where_pair| where_pair.into_inner().next().unwrap());
248
249 let condition = condition_pair.map(parse_condition);
251
252 Query { condition } }
254
255fn parse_condition(pair: pest::iterators::Pair<Rule>) -> Condition {
257 let mut inner = pair.into_inner();
259 let field = inner.next().unwrap().as_str().to_string();
260 let operator = inner.next().unwrap().as_str().to_string();
261 let value = parse_value(inner.next().unwrap());
262 Condition {
263 field,
264 operator,
265 value,
266 }
267}
268
269fn parse_value(pair: pest::iterators::Pair<Rule>) -> Value {
271 let inner = pair.into_inner().next().unwrap();
273 match inner.as_rule() {
274 Rule::number => Value::Number(inner.as_str().parse().unwrap_or(0)),
275 Rule::number_with_unit => {
276 Value::Number(inner.as_str().replace("cc", "").parse().unwrap_or(0))
278 }
279 Rule::ident => Value::StringType(inner.as_str().to_string()),
280 Rule::string_literal => Value::StringLiteral(parse_string_literal(inner)),
281 _ => unreachable!(),
282 }
283}
284
285fn parse_string_literal(pair: pest::iterators::Pair<Rule>) -> String {
287 pair.as_str().trim_matches('"').to_string() }