Skip to main content

rust_query/
value.rs

1pub mod aggregate;
2mod operations;
3pub mod optional;
4pub mod trivial;
5
6use std::{cell::OnceCell, fmt::Debug, marker::PhantomData, ops::Deref, rc::Rc};
7
8use sea_query::{Alias, JoinType, Nullable, SelectStatement};
9
10use crate::{
11    IntoSelect, Lazy, Select, Table, Transaction,
12    alias::{Field, JoinableTable, MyAlias, Scope},
13    ast::{MySelect, Source},
14    db::{TableRow, TableRowInner},
15    mutable::Mutable,
16    mymap::MyMap,
17    private::IntoJoinable,
18    schema::canonical,
19};
20
21#[derive(Default)]
22pub struct ValueBuilder {
23    pub(crate) from: Rc<MySelect>,
24    // only used for tables
25    pub(super) scope: Scope,
26    // implicit joins
27    pub(super) extra: MyMap<Source, MyAlias>,
28    // calculating these results
29    pub(super) forwarded: MyMap<MyTableRef, MyAlias>,
30}
31
32impl ValueBuilder {
33    pub(crate) fn get_aggr(
34        &mut self,
35        aggr: Rc<SelectStatement>,
36        conds: Vec<MyTableRef>,
37    ) -> MyAlias {
38        let source = Source {
39            kind: crate::ast::SourceKind::Aggregate(aggr),
40            conds: conds
41                .into_iter()
42                .enumerate()
43                .map(|(idx, join)| {
44                    let alias = Alias::new(join.table_name.main_column());
45                    (
46                        Field::U64(MyAlias::new(idx)),
47                        sea_query::Expr::col((self.get_table(join), alias)),
48                    )
49                })
50                .collect(),
51        };
52        let new_alias = || self.scope.new_alias();
53        *self.extra.get_or_init(source, new_alias)
54    }
55
56    pub(crate) fn get_join<T: Table>(
57        &mut self,
58        expr: sea_query::Expr,
59        possible_null: bool,
60        new_col: &'static str,
61    ) -> sea_query::Expr {
62        match &expr {
63            // we could use our own type instead of `sea_query::Expr` to make it much easier
64            // to know if we joined the table explicitly, but that would add much complexity
65            // everywhere else
66            sea_query::Expr::Column(sea_query::ColumnRef::Column(sea_query::ColumnName(
67                Some(sea_query::TableName(None, table)),
68                col,
69            ))) => {
70                // check if this table has been joined explicitly
71                if let Some(alias) = MyAlias::try_from(table)
72                    && let Some(from) = self.from.tables.get(alias.idx)
73                    && from.main_column() == col.inner().as_ref()
74                {
75                    // No need to join the table again
76                    return sea_query::Expr::col((alias, new_col));
77                }
78            }
79            _ => (),
80        };
81
82        let join_type = if possible_null {
83            JoinType::LeftJoin
84        } else {
85            JoinType::Join
86        };
87        let source = Source {
88            kind: crate::ast::SourceKind::Implicit(T::NAME.to_owned(), join_type),
89            conds: vec![(Field::Str(T::ID), expr)],
90        };
91        let new_alias = || self.scope.new_alias();
92
93        // TODO: possible optimization to unify the join_type?
94        // e.g. join + left join = join
95        let alias = *self.extra.get_or_init(source, new_alias);
96
97        sea_query::Expr::col((alias, new_col))
98    }
99
100    pub fn get_unique<T: Table>(
101        &mut self,
102        conds: Box<[(&'static str, sea_query::Expr)]>,
103    ) -> sea_query::Expr {
104        let source = Source {
105            kind: crate::ast::SourceKind::Implicit(T::NAME.to_owned(), JoinType::LeftJoin),
106            conds: conds.into_iter().map(|x| (Field::Str(x.0), x.1)).collect(),
107        };
108
109        let new_alias = || self.scope.new_alias();
110        let table = self.extra.get_or_init(source, new_alias);
111        sea_query::Expr::col((*table, Alias::new(T::ID))).into()
112    }
113
114    pub fn get_table(&mut self, table: MyTableRef) -> MyAlias {
115        if Rc::ptr_eq(&self.from.scope_rc, &table.scope_rc) {
116            MyAlias::new(table.idx)
117        } else {
118            *self.forwarded.get_or_init(table, || self.scope.new_alias())
119        }
120    }
121}
122
123/// This references a particular user specified join,
124/// so not any of the forwarded joins.
125/// We use this to know if the current scope has the original join or needs to forward it.
126#[derive(Clone)]
127pub struct MyTableRef {
128    // one Rc exists for each scope, so we can check if we have the right
129    // scope by comparing the Rc ptr.
130    pub(crate) scope_rc: Rc<()>,
131    pub(crate) idx: usize,
132    pub(crate) table_name: JoinableTable,
133}
134
135impl PartialEq for MyTableRef {
136    fn eq(&self, other: &Self) -> bool {
137        Rc::ptr_eq(&self.scope_rc, &other.scope_rc) && self.idx == other.idx
138    }
139}
140
141pub trait NumTyp: OrdTyp + Clone + Copy {
142    const ZERO: sea_query::Value;
143}
144
145impl NumTyp for i64 {
146    const ZERO: sea_query::Value = sea_query::Value::BigInt(Some(0));
147}
148impl NumTyp for f64 {
149    const ZERO: sea_query::Value = sea_query::Value::Double(Some(0.));
150}
151
152pub trait OrdTyp: EqTyp {}
153impl OrdTyp for String {}
154impl OrdTyp for Vec<u8> {}
155impl OrdTyp for i64 {}
156impl OrdTyp for f64 {}
157impl OrdTyp for bool {}
158
159pub trait BuffTyp: MyTyp {}
160impl BuffTyp for String {}
161impl BuffTyp for Vec<u8> {}
162
163#[diagnostic::on_unimplemented(
164    message = "Columns with type `{Self}` can not be checked for equality",
165    note = "`EqTyp` is also implemented for all table types"
166)]
167pub trait EqTyp: MyTyp {}
168
169impl EqTyp for String {}
170impl EqTyp for Vec<u8> {}
171impl EqTyp for i64 {}
172impl EqTyp for f64 {}
173impl EqTyp for bool {}
174#[diagnostic::do_not_recommend]
175impl<T: Table> EqTyp for T {}
176
177/// Trait for all values that can be used as expressions in queries.
178///
179/// There is a hierarchy of types that can be used to build queries.
180/// - [TableRow], [i64], [f64], [bool], `&[u8]`, `&str`:
181///   These are the base types for building expressions. They all
182///   implement [IntoExpr] and are [Copy]. Note that [TableRow] is special
183///   because it refers to a table row that is guaranteed to exist.
184/// - [Expr] is the type that all [IntoExpr] values can be converted into.
185///   It has a lot of methods to combine expressions into more complicated expressions.
186///   Next to those, it implements [std::ops::Deref], if the expression is a table expression.
187///   This can be used to get access to the columns of the table, which can themselves be table expressions.
188///   Note that combinators like [crate::optional] and [crate::aggregate] also have [Expr] as return type.
189///
190/// Note that while [Expr] implements [IntoExpr], you may want to use `&Expr` instead.
191/// Using a reference lets you reuse [Expr] without calling [Clone] explicitly.
192pub trait IntoExpr<'column, S> {
193    /// The type of the expression.
194    type Typ: MyTyp;
195
196    /// Turn this value into an [Expr].
197    fn into_expr(self) -> Expr<'column, S, Self::Typ>;
198}
199impl<'column, S, T: IntoExpr<'column, S, Typ = X>, X: MyTyp> IntoExpr<'column, S> for Option<T> {
200    type Typ = Option<X>;
201    fn into_expr(self) -> Expr<'column, S, Self::Typ> {
202        let this = self.map(|x| x.into_expr().inner);
203        Expr::adhoc(move |b| {
204            this.as_ref()
205                .map(|x| (x.func)(b))
206                .unwrap_or(<X::Sql as Nullable>::null().into())
207        })
208    }
209}
210
211impl<'column, S> IntoExpr<'column, S> for String {
212    type Typ = String;
213    fn into_expr(self) -> Expr<'column, S, Self::Typ> {
214        Expr::adhoc(move |_| sea_query::Expr::from(self.clone()))
215    }
216}
217
218impl<'column, S> IntoExpr<'column, S> for &str {
219    type Typ = String;
220    fn into_expr(self) -> Expr<'column, S, Self::Typ> {
221        self.to_owned().into_expr()
222    }
223}
224
225impl<'column, S> IntoExpr<'column, S> for Vec<u8> {
226    type Typ = Vec<u8>;
227    fn into_expr(self) -> Expr<'column, S, Self::Typ> {
228        Expr::adhoc(move |_| sea_query::Expr::from(self.clone()))
229    }
230}
231
232impl<'column, S> IntoExpr<'column, S> for &[u8] {
233    type Typ = Vec<u8>;
234    fn into_expr(self) -> Expr<'column, S, Self::Typ> {
235        self.to_owned().into_expr()
236    }
237}
238
239impl<'column, S> IntoExpr<'column, S> for bool {
240    type Typ = bool;
241    fn into_expr(self) -> Expr<'column, S, Self::Typ> {
242        Expr::adhoc(move |_| sea_query::Expr::from(self))
243    }
244}
245
246impl<'column, S> IntoExpr<'column, S> for i64 {
247    type Typ = i64;
248    fn into_expr(self) -> Expr<'column, S, Self::Typ> {
249        Expr::adhoc(move |_| sea_query::Expr::from(self))
250    }
251}
252impl<'column, S> IntoExpr<'column, S> for f64 {
253    type Typ = f64;
254    fn into_expr(self) -> Expr<'column, S, Self::Typ> {
255        Expr::adhoc(move |_| sea_query::Expr::from(self))
256    }
257}
258
259impl<'column, S, T> IntoExpr<'column, S> for &T
260where
261    T: IntoExpr<'column, S> + Clone,
262{
263    type Typ = T::Typ;
264    fn into_expr(self) -> Expr<'column, S, Self::Typ> {
265        T::into_expr(self.clone())
266    }
267}
268
269/// Use this a value in a query to get the current datetime as a number of seconds.
270#[derive(Clone, Copy)]
271#[deprecated = "Use `Expr::unix_epoch` instead"]
272pub struct UnixEpoch;
273
274#[expect(deprecated)]
275impl<'column, S> IntoExpr<'column, S> for UnixEpoch {
276    type Typ = i64;
277    fn into_expr(self) -> Expr<'column, S, Self::Typ> {
278        Expr::adhoc(|_| sea_query::Expr::cust("unixepoch('now')"))
279    }
280}
281
282pub trait OptTable: MyTyp {
283    type Schema;
284    type Select;
285    type Mutable<'t>;
286    fn select_opt_mutable(
287        val: Expr<'_, Self::Schema, Self>,
288    ) -> Select<'_, Self::Schema, Self::Select>;
289
290    fn into_mutable<'t>(val: Self::Select) -> Self::Mutable<'t>;
291}
292
293impl<T: Table> OptTable for T {
294    type Schema = T::Schema;
295    type Select = (T::Mutable, TableRow<T>);
296    type Mutable<'t> = Mutable<'t, T>;
297    fn select_opt_mutable(
298        val: Expr<'_, Self::Schema, Self>,
299    ) -> Select<'_, Self::Schema, Self::Select> {
300        (T::select_mutable(val.clone()), val).into_select()
301    }
302
303    fn into_mutable<'t>((inner, row_id): Self::Select) -> Self::Mutable<'t> {
304        Mutable::new(inner, row_id)
305    }
306}
307
308impl<T: Table> OptTable for Option<T> {
309    type Schema = T::Schema;
310    type Select = Option<(T::Mutable, TableRow<T>)>;
311    type Mutable<'t> = Option<Mutable<'t, T>>;
312    fn select_opt_mutable(
313        val: Expr<'_, Self::Schema, Self>,
314    ) -> Select<'_, Self::Schema, Self::Select> {
315        crate::optional(|row| {
316            let val = row.and(val);
317            row.then_select((T::select_mutable(val.clone()), val))
318        })
319    }
320
321    fn into_mutable<'t>(val: Self::Select) -> Self::Mutable<'t> {
322        val.map(T::into_mutable)
323    }
324}
325
326pub trait MyTyp: Sized + 'static {
327    type Prev: MyTyp;
328    const NULLABLE: bool = false;
329    const TYP: canonical::ColumnType;
330    const FK: Option<(&'static str, &'static str)> = None;
331    type Out: SecretFromSql;
332    type Lazy<'t>;
333    type Ext<'t>;
334    type Sql: Nullable;
335    fn out_to_lazy<'t>(val: Self::Out) -> Self::Lazy<'t>;
336    fn out_to_value(val: Self::Out) -> sea_query::Value;
337}
338
339pub(crate) trait SecretFromSql: Sized {
340    fn from_sql(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self>;
341}
342
343#[diagnostic::do_not_recommend]
344impl<T: Table> MyTyp for T {
345    type Prev = T::MigrateFrom;
346    const TYP: canonical::ColumnType = canonical::ColumnType::Integer;
347    const FK: Option<(&'static str, &'static str)> = Some((T::NAME, T::ID));
348    type Out = TableRow<T>;
349    type Lazy<'t> = Lazy<'t, T>;
350    type Ext<'t> = T::Ext2<'t>;
351    type Sql = i64;
352    fn out_to_lazy<'t>(val: Self::Out) -> Self::Lazy<'t> {
353        Lazy {
354            id: val,
355            lazy: OnceCell::new(),
356            txn: Transaction::new_ref(),
357        }
358    }
359    fn out_to_value(val: Self::Out) -> sea_query::Value {
360        val.inner.idx.into()
361    }
362}
363
364impl<T: Table> SecretFromSql for TableRow<T> {
365    fn from_sql(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
366        Ok(TableRow {
367            _local: PhantomData,
368            inner: TableRowInner {
369                _p: PhantomData,
370                idx: value.as_i64()?,
371            },
372        })
373    }
374}
375
376impl MyTyp for i64 {
377    type Prev = Self;
378    const TYP: canonical::ColumnType = canonical::ColumnType::Integer;
379    type Out = Self;
380    type Lazy<'t> = Self;
381    type Ext<'t> = ();
382    type Sql = i64;
383    fn out_to_lazy<'t>(val: Self::Out) -> Self::Lazy<'t> {
384        val
385    }
386    fn out_to_value(val: Self::Out) -> sea_query::Value {
387        val.into()
388    }
389}
390
391impl SecretFromSql for i64 {
392    fn from_sql(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
393        value.as_i64()
394    }
395}
396
397impl MyTyp for f64 {
398    type Prev = Self;
399    const TYP: canonical::ColumnType = canonical::ColumnType::Real;
400    type Out = Self;
401    type Lazy<'t> = Self;
402    type Ext<'t> = ();
403    type Sql = f64;
404    fn out_to_lazy<'t>(val: Self::Out) -> Self::Lazy<'t> {
405        val
406    }
407    fn out_to_value(val: Self::Out) -> sea_query::Value {
408        val.into()
409    }
410}
411
412impl SecretFromSql for f64 {
413    fn from_sql(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
414        value.as_f64()
415    }
416}
417
418impl MyTyp for bool {
419    type Prev = Self;
420    const TYP: canonical::ColumnType = canonical::ColumnType::Integer;
421    type Out = Self;
422    type Lazy<'t> = Self;
423    type Ext<'t> = ();
424    type Sql = bool;
425    fn out_to_lazy<'t>(val: Self::Out) -> Self::Lazy<'t> {
426        val
427    }
428    fn out_to_value(val: Self::Out) -> sea_query::Value {
429        val.into()
430    }
431}
432
433impl SecretFromSql for bool {
434    fn from_sql(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
435        Ok(value.as_i64()? != 0)
436    }
437}
438
439impl MyTyp for String {
440    type Prev = Self;
441    const TYP: canonical::ColumnType = canonical::ColumnType::Text;
442    type Out = Self;
443    type Lazy<'t> = Self;
444    type Ext<'t> = ();
445    type Sql = String;
446    fn out_to_lazy<'t>(val: Self::Out) -> Self::Lazy<'t> {
447        val
448    }
449    fn out_to_value(val: Self::Out) -> sea_query::Value {
450        val.into()
451    }
452}
453assert_impl_all!(String: Nullable);
454
455impl SecretFromSql for String {
456    fn from_sql(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
457        Ok(value.as_str()?.to_owned())
458    }
459}
460
461impl MyTyp for Vec<u8> {
462    type Prev = Self;
463    const TYP: canonical::ColumnType = canonical::ColumnType::Blob;
464    type Out = Self;
465    type Lazy<'t> = Self;
466    type Ext<'t> = ();
467    type Sql = Vec<u8>;
468    fn out_to_lazy<'t>(val: Self::Out) -> Self::Lazy<'t> {
469        val
470    }
471    fn out_to_value(val: Self::Out) -> sea_query::Value {
472        val.into()
473    }
474}
475assert_impl_all!(Vec<u8>: Nullable);
476
477impl SecretFromSql for Vec<u8> {
478    fn from_sql(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
479        Ok(value.as_blob()?.to_owned())
480    }
481}
482
483impl<T: MyTyp> MyTyp for Option<T> {
484    type Prev = Option<T::Prev>;
485    const TYP: canonical::ColumnType = T::TYP;
486    const NULLABLE: bool = true;
487    const FK: Option<(&'static str, &'static str)> = T::FK;
488    type Out = Option<T::Out>;
489    type Lazy<'t> = Option<T::Lazy<'t>>;
490    type Ext<'t> = ();
491    type Sql = T::Sql;
492    fn out_to_lazy<'t>(val: Self::Out) -> Self::Lazy<'t> {
493        val.map(T::out_to_lazy)
494    }
495    fn out_to_value(val: Self::Out) -> sea_query::Value {
496        val.map(T::out_to_value)
497            .unwrap_or_else(<T::Sql as Nullable>::null)
498    }
499}
500
501impl<T: SecretFromSql> SecretFromSql for Option<T> {
502    fn from_sql(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
503        if value.data_type() == rusqlite::types::Type::Null {
504            Ok(None)
505        } else {
506            Ok(Some(T::from_sql(value)?))
507        }
508    }
509}
510
511/// This is an expression that can be used in queries.
512///
513/// - The lifetime parameter `'column` specifies which columns need to be in scope.
514/// - The type parameter `S` specifies the expected schema of the query.
515/// - And finally the type paramter `T` specifies the type of the expression.
516///
517/// [Expr] implements [Deref] to have column fields in case the expression has a table type.
518pub struct Expr<'column, S, T: MyTyp> {
519    pub(crate) _local: PhantomData<*const ()>,
520    pub(crate) inner: Rc<AdHoc<dyn Fn(&mut ValueBuilder) -> sea_query::Expr, T>>,
521    pub(crate) _p: PhantomData<&'column ()>,
522    pub(crate) _p2: PhantomData<S>,
523    pub(crate) ext: OnceCell<Box<T::Ext<'static>>>,
524}
525
526impl<S, T: MyTyp> Debug for Expr<'_, S, T> {
527    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
528        write!(f, "Expr of type {}", std::any::type_name::<T>())
529    }
530}
531
532impl<'column, S, T: MyTyp> Expr<'column, S, T> {
533    /// Extremely easy to use API. Should only be used by the macro to implement migrations.
534    #[doc(hidden)]
535    pub fn _migrate<OldS>(prev: impl IntoExpr<'column, OldS>) -> Self {
536        let prev = DynTypedExpr::erase(prev);
537        Self::adhoc(move |b| (prev.func)(b))
538    }
539}
540
541pub fn adhoc_expr<S, T: MyTyp>(
542    f: impl 'static + Fn(&mut ValueBuilder) -> sea_query::Expr,
543) -> Expr<'static, S, T> {
544    Expr::adhoc(f)
545}
546
547pub fn new_column<'x, S, C: MyTyp, T: Table>(
548    table: impl IntoExpr<'x, S, Typ = T>,
549    name: &'static str,
550) -> Expr<'x, S, C> {
551    let table = table.into_expr().inner;
552    let possible_null = table.maybe_optional;
553    Expr::adhoc_promise(
554        move |b| {
555            let main_column = table.build_expr(b);
556            b.get_join::<T>(main_column, table.maybe_optional, name)
557        },
558        possible_null,
559    )
560}
561
562pub fn unique_from_joinable<'inner, T: Table>(
563    j: impl IntoJoinable<'inner, T::Schema, Typ = T>,
564) -> Expr<'inner, T::Schema, Option<T>> {
565    let list = j.into_joinable().conds;
566    ::rust_query::private::adhoc_expr(move |_b| {
567        let list = list
568            .iter()
569            .map(|(name, col)| (*name, (col.func)(_b)))
570            .collect();
571        _b.get_unique::<T>(list)
572    })
573}
574
575pub struct AdHoc<F: ?Sized, T> {
576    maybe_optional: bool,
577    _p: PhantomData<T>,
578    func: F,
579}
580
581impl<F: ?Sized + Fn(&mut ValueBuilder) -> sea_query::Expr, T> AdHoc<F, T> {
582    pub fn build_expr(&self, b: &mut ValueBuilder) -> sea_query::Expr {
583        (self.func)(b)
584    }
585}
586
587impl<S, T: MyTyp> Expr<'_, S, T> {
588    pub(crate) fn adhoc(f: impl 'static + Fn(&mut ValueBuilder) -> sea_query::Expr) -> Self {
589        Self::adhoc_promise(f, true)
590    }
591
592    /// Only set `maybe_optional` to `false` if you are absolutely sure that the
593    /// value is not null. The [crate::optional] combinator makes this more difficult.
594    /// There is no reason to use this for values that can not be foreign keys.
595    /// This is used to optimize implicit joins from LEFT JOIN to just JOIN.
596    pub(crate) fn adhoc_promise(
597        f: impl 'static + Fn(&mut ValueBuilder) -> sea_query::Expr,
598        maybe_optional: bool,
599    ) -> Self {
600        Self::new(Rc::new(AdHoc {
601            func: f,
602            maybe_optional,
603            _p: PhantomData,
604        }))
605    }
606
607    pub(crate) fn new(val: Rc<AdHoc<dyn Fn(&mut ValueBuilder) -> sea_query::Expr, T>>) -> Self {
608        Self {
609            _local: PhantomData,
610            inner: val,
611            _p: PhantomData,
612            _p2: PhantomData,
613            ext: OnceCell::new(),
614        }
615    }
616}
617
618impl<S, T: MyTyp> Clone for Expr<'_, S, T> {
619    fn clone(&self) -> Self {
620        Self {
621            _local: PhantomData,
622            inner: self.inner.clone(),
623            _p: self._p,
624            _p2: self._p2,
625            ext: OnceCell::new(),
626        }
627    }
628}
629
630#[derive(Clone)]
631pub struct DynTypedExpr {
632    pub func: Rc<dyn Fn(&mut ValueBuilder) -> sea_query::Expr>,
633}
634
635impl DynTypedExpr {
636    pub fn new(f: impl 'static + Fn(&mut ValueBuilder) -> sea_query::Expr) -> Self {
637        Self { func: Rc::new(f) }
638    }
639    pub fn erase<'x, S>(expr: impl IntoExpr<'x, S>) -> Self {
640        let typed = expr.into_expr().inner;
641        Self::new(move |b| typed.build_expr(b))
642    }
643}
644
645impl<'column, S, T: MyTyp> IntoExpr<'column, S> for Expr<'column, S, T> {
646    type Typ = T;
647    fn into_expr(self) -> Expr<'column, S, Self::Typ> {
648        self
649    }
650}
651
652impl<'t, T: Table> Deref for Expr<'t, T::Schema, T> {
653    type Target = T::Ext2<'t>;
654
655    fn deref(&self) -> &Self::Target {
656        T::covariant_ext(self.ext.get_or_init(|| {
657            let expr = Expr {
658                _local: PhantomData,
659                inner: self.inner.clone(),
660                _p: PhantomData::<&'static ()>,
661                _p2: PhantomData,
662                ext: OnceCell::new(),
663            };
664            Box::new(T::build_ext2(&expr))
665        }))
666    }
667}