openstack/
lib.rs

1// Copyright 2017-2022 Dmitry Tantsur <divius.inside@gmail.com>
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! OpenStack SDK in Rust.
16//!
17//! The goal of this project is to provide a simple API for working with
18//! OpenStack clouds.
19//!
20//! # Usage
21//!
22//! Start with [authentication](auth/index.html), then create a
23//! [Cloud](struct.Cloud.html) object and use it for OpenStack API calls.
24//!
25//! # Examples
26//!
27//! ## List servers
28//!
29//! Get authentication parameters from the environment and get UUIDs of all
30//! servers.
31//!
32//! ```rust,no_run
33//! async fn get_server_uuids() -> openstack::Result<Vec<String>> {
34//!     let os = openstack::Cloud::from_env().await?;
35//!     let server_names = os
36//!         .list_servers()
37//!         .await?
38//!         .into_iter()
39//!         .map(|server| server.id().clone())
40//!         .collect();
41//!     Ok(server_names)
42//! }
43//! # #[tokio::main(flavor = "current_thread")]
44//! # async fn main() { get_server_uuids().await.unwrap(); }
45//! ```
46//!
47//! ## Find images
48//!
49//! Find public images using Identity password authentication with the default region:
50//!
51//! ```rust,no_run
52//! use futures::TryStreamExt;
53//!
54//! async fn get_public_image_names() -> openstack::Result<Vec<String>> {
55//!     let scope = openstack::auth::Scope::Project {
56//!         project: openstack::IdOrName::from_name("project1"),
57//!         domain: Some(openstack::IdOrName::from_id("default")),
58//!     };
59//!     let auth = openstack::auth::Password::new(
60//!         "https://cloud.local/identity",
61//!         "admin",
62//!         "pa$$w0rd",
63//!         "Default"
64//!     )
65//!     .expect("Invalid auth_url")
66//!     .with_scope(scope);
67//!
68//!     let os = openstack::Cloud::new(auth).await?;
69//!     let image_names = os
70//!         .find_images()
71//!         .with_visibility(openstack::image::ImageVisibility::Public)
72//!         .into_stream()
73//!         // This `map_ok` comes from `futures::TryStreamExt`, thus the closure returns a `Future`.
74//!         .map_ok(|image| image.name().clone())
75//!         .try_collect()
76//!         .await?;
77//!     Ok(image_names)
78//! }
79//! # #[tokio::main(flavor = "current_thread")]
80//! # async fn main() { get_public_image_names().await.unwrap(); }
81//! ```
82//!
83//! Notice the difference between `list_*` methods (return a result with a vector) and `find_*`
84//! methods (return a query builder that can be used to create a stream).
85//!
86//! ## Create server
87//!
88//! Create a server with authentication from a `clouds.yaml` file:
89//!
90//! ```rust,no_run
91//! use openstack::waiter::Waiter;
92//!
93//! async fn create_server() -> openstack::Result<openstack::compute::Server> {
94//!     openstack::Cloud::from_config("my-cloud-1")
95//!         .await?
96//!         .new_server("test-server-1", "x-large")
97//!         .with_image("centos-7")
98//!         .with_network("private")
99//!         .with_keypair("default")
100//!         .create()
101//!         .await?
102//!         .wait()
103//!         .await
104//! }
105//! # #[tokio::main(flavor = "current_thread")]
106//! # async fn main() { create_server().await.unwrap(); }
107//! ```
108//!
109//! # Requirements
110//!
111//! This crate requires Rust 2022 edition and rustc version 1.76.0 or newer.
112
113#![crate_name = "openstack"]
114#![crate_type = "lib"]
115#![doc(html_root_url = "https://docs.rs/openstack/0.6.0")]
116// NOTE: we do not use generic deny(warnings) to avoid breakages with new
117// versions of the compiler. Add more warnings here as you discover them.
118// Taken from https://github.com/rust-unofficial/patterns/
119#![deny(
120    dead_code,
121    improper_ctypes,
122    missing_copy_implementations,
123    missing_debug_implementations,
124    missing_docs,
125    non_shorthand_field_patterns,
126    no_mangle_generic_items,
127    overflowing_literals,
128    path_statements,
129    patterns_in_fns_without_body,
130    trivial_casts,
131    trivial_numeric_casts,
132    unconditional_recursion,
133    unsafe_code,
134    unused,
135    unused_allocation,
136    unused_comparisons,
137    unused_doc_comments,
138    unused_import_braces,
139    unused_parens,
140    // FIXME(dtantsur): https://github.com/rust-lang/rust/issues/122533
141    // unused_qualifications,
142    unused_results,
143    while_true
144)]
145// TODO(dtantsur): revise these
146#![allow(unused_extern_crates)]
147#![allow(unused_macro_rules)]
148#![allow(
149    clippy::new_ret_no_self,
150    clippy::should_implement_trait,
151    clippy::wrong_self_convention
152)]
153
154#[macro_use]
155extern crate log;
156
157#[allow(unused_macros)]
158macro_rules! transparent_property {
159    ($(#[$attr:meta])* $name:ident: ref $type:ty) => (
160        $(#[$attr])*
161        #[inline]
162        pub fn $name(&self) -> &$type {
163            &self.inner.$name
164        }
165    );
166
167    ($(#[$attr:meta])* $name:ident: $type:ty) => (
168        $(#[$attr])*
169        #[inline]
170        pub fn $name(&self) -> $type {
171            self.inner.$name
172        }
173    );
174}
175
176#[allow(unused_macros)]
177macro_rules! query_filter {
178    ($(#[$attr:meta])* $func:ident -> $name:ident) => (
179        $(#[$attr])*
180        pub fn $func<T: Into<String>>(mut self, value: T) -> Self {
181            self.query.push_str(stringify!($name), value);
182            self
183        }
184    );
185
186    ($(#[$attr:meta])* $set_func:ident, $with_func:ident -> $name:ident) => (
187        $(#[$attr])*
188        pub fn $set_func<T: Into<String>>(&mut self, value: T)  {
189            self.query.push_str(stringify!($name), value);
190        }
191
192        $(#[$attr])*
193        #[inline]
194        pub fn $with_func<T: Into<String>>(mut self, value: T) -> Self {
195            self.$set_func(value);
196            self
197        }
198    );
199
200    ($(#[$attr:meta])* $func:ident -> $name:ident: $type:ty) => (
201        $(#[$attr])*
202        pub fn $func<T: Into<$type>>(mut self, value: T) -> Self {
203            self.query.push(stringify!($name), value.into());
204            self
205        }
206    );
207
208    ($(#[$attr:meta])* $set_func:ident, $with_func:ident -> $name:ident: $type:ty) => (
209        $(#[$attr])*
210        pub fn $set_func<T: Into<$type>>(&mut self, value: T)  {
211            self.query.push(stringify!($name), value.into());
212        }
213
214        $(#[$attr])*
215        #[inline]
216        pub fn $with_func<T: Into<$type>>(mut self, value: T) -> Self {
217            self.$set_func(value.into());
218            self
219        }
220    );
221}
222
223#[allow(unused_macros)]
224macro_rules! query_filter_ng {
225    ($(#[$attr:meta])* $func:ident -> $name:path) => (
226        $(#[$attr])*
227        pub fn $func<T: Into<String>>(mut self, value: T) -> Self {
228            self.query.push($name(value.into()));
229            self
230        }
231    );
232
233    ($(#[$attr:meta])* $set_func:ident, $with_func:ident -> $name:path) => (
234        $(#[$attr])*
235        pub fn $set_func<T: Into<String>>(&mut self, value: T)  {
236            self.query.push($name(value.into()));
237        }
238
239        $(#[$attr])*
240        #[inline]
241        pub fn $with_func<T: Into<String>>(mut self, value: T) -> Self {
242            self.$set_func(value);
243            self
244        }
245    );
246
247    ($(#[$attr:meta])* $func:ident -> $name:path: $type:ty) => (
248        $(#[$attr])*
249        pub fn $func<T: Into<$type>>(mut self, value: T) -> Self {
250            self.query.push($name(value.into()));
251            self
252        }
253    );
254
255    ($(#[$attr:meta])* $set_func:ident, $with_func:ident -> $name:path: $type:ty) => (
256        $(#[$attr])*
257        pub fn $set_func<T: Into<$type>>(&mut self, value: T)  {
258            self.query.push($name(value.into()));
259        }
260
261        $(#[$attr])*
262        #[inline]
263        pub fn $with_func<T: Into<$type>>(mut self, value: T) -> Self {
264            self.$set_func(value.into());
265            self
266        }
267    );
268}
269
270#[allow(unused_macros)]
271macro_rules! creation_field {
272
273    ($(#[$attr:meta])* $set_func:ident, $with_func:ident -> $name:ident) => (
274        $(#[$attr])*
275        #[inline]
276        pub fn $set_func<S: Into<String>>(&mut self, value: S)  {
277            self.$name = value.into();
278        }
279
280        $(#[$attr])*
281        #[inline]
282        pub fn $with_func<S: Into<String>>(mut self, value: S) -> Self {
283            self.$set_func(value);
284            self
285        }
286    );
287
288    ($(#[$attr:meta])* $set_func:ident, $with_func:ident -> $name:ident: $type:ty) => (
289        $(#[$attr])*
290        #[inline]
291        pub fn $set_func(&mut self, value: $type)  {
292            self.$name = value;
293        }
294
295        $(#[$attr])*
296        #[inline]
297        pub fn $with_func(mut self, value: $type) -> Self {
298            self.$set_func(value);
299            self
300        }
301    );
302
303    ($(#[$attr:meta])* $set_func:ident, $with_func:ident -> $name:ident: optional String) => (
304        $(#[$attr])*
305        #[inline]
306        pub fn $set_func<S: Into<String>>(&mut self, value: S)  {
307            self.$name = Some(value.into());
308        }
309
310        $(#[$attr])*
311        #[inline]
312        pub fn $with_func<S: Into<String>>(mut self, value: S) -> Self {
313            self.$set_func(value);
314            self
315        }
316    );
317
318    ($(#[$attr:meta])* $set_func:ident, $with_func:ident -> $name:ident: optional $type:ty) => (
319        $(#[$attr])*
320        #[inline]
321        pub fn $set_func(&mut self, value: $type)  {
322            self.$name = Some(value);
323        }
324
325        $(#[$attr])*
326        #[inline]
327        pub fn $with_func(mut self, value: $type) -> Self {
328            self.$set_func(value);
329            self
330        }
331    );
332
333}
334
335#[allow(unused_macros)]
336macro_rules! creation_inner_field {
337
338    ($(#[$attr:meta])* $set_func:ident, $with_func:ident -> $name:ident) => (
339        $(#[$attr])*
340        #[inline]
341        pub fn $set_func<S: Into<String>>(&mut self, value: S)  {
342            self.inner.$name = value.into();
343        }
344
345        $(#[$attr])*
346        #[inline]
347        pub fn $with_func<S: Into<String>>(mut self, value: S) -> Self {
348            self.$set_func(value);
349            self
350        }
351    );
352
353    ($(#[$attr:meta])* $set_func:ident, $with_func:ident -> $name:ident: $type:ty) => (
354        $(#[$attr])*
355        #[inline]
356        pub fn $set_func(&mut self, value: $type)  {
357            self.inner.$name = value;
358        }
359
360        $(#[$attr])*
361        #[inline]
362        pub fn $with_func(mut self, value: $type) -> Self {
363            self.$set_func(value);
364            self
365        }
366    );
367
368    ($(#[$attr:meta])* $set_func:ident, $with_func:ident -> $name:ident: optional String) => (
369        $(#[$attr])*
370        #[inline]
371        pub fn $set_func<S: Into<String>>(&mut self, value: S)  {
372            self.inner.$name = Some(value.into());
373        }
374
375        $(#[$attr])*
376        #[inline]
377        pub fn $with_func<S: Into<String>>(mut self, value: S) -> Self {
378            self.$set_func(value);
379            self
380        }
381    );
382
383    ($(#[$attr:meta])* $set_func:ident, $with_func:ident -> $name:ident: optional $type:ty) => (
384        $(#[$attr])*
385        #[inline]
386        pub fn $set_func(&mut self, value: $type)  {
387            self.inner.$name = Some(value);
388        }
389
390        $(#[$attr])*
391        #[inline]
392        pub fn $with_func(mut self, value: $type) -> Self {
393            self.$set_func(value);
394            self
395        }
396    );
397
398}
399
400#[allow(unused_macros)]
401macro_rules! creation_inner_vec {
402
403    ($(#[$attr:meta])* $add_func:ident, $with_func:ident -> $name:ident) => (
404        $(#[$attr])*
405        pub fn $add_func<S: Into<String>>(&mut self, value: S)  {
406            self.inner.$name.push(value.into());
407        }
408
409        $(#[$attr])*
410        #[inline]
411        pub fn $name(&mut self) -> &mut Vec<String> {
412            &mut self.inner.$name
413        }
414
415        $(#[$attr])*
416        #[inline]
417        pub fn $with_func<S: Into<String>>(mut self, value: S) -> Self {
418            self.$add_func(value);
419            self
420        }
421    );
422
423    ($(#[$attr:meta])* $add_func:ident, $with_func:ident -> $name:ident: $type:ty) => (
424        $(#[$attr])*
425        pub fn $add_func(&mut self, value: $type)  {
426            self.inner.$name.push(value);
427        }
428
429        $(#[$attr])*
430        #[inline]
431        pub fn $name(&mut self) -> &mut Vec<$type> {
432            &mut self.inner.$name
433        }
434
435        $(#[$attr])*
436        #[inline]
437        pub fn $with_func(mut self, value: $type) -> Self {
438            self.$add_func(value);
439            self
440        }
441    );
442
443    ($(#[$attr:meta])* $add_func:ident, $with_func:ident -> $name:ident: into $type:ty) => (
444        $(#[$attr])*
445        pub fn $add_func<S: Into<$type>>(&mut self, value: S)  {
446            self.inner.$name.push(value.into());
447        }
448
449        $(#[$attr])*
450        #[inline]
451        pub fn $name(&mut self) -> &mut Vec<$type> {
452            &mut self.inner.$name
453        }
454
455        $(#[$attr])*
456        #[inline]
457        pub fn $with_func<S: Into<$type>>(mut self, value: S) -> Self {
458            self.$add_func(value);
459            self
460        }
461    );
462
463
464}
465
466#[allow(unused_macros)]
467macro_rules! update_field {
468
469    ($(#[$attr:meta])* $set_func:ident, $with_func:ident -> $name:ident) => (
470        $(#[$attr])*
471        pub fn $set_func<S: Into<String>>(&mut self, value: S)  {
472            self.inner.$name = value.into();
473            self.dirty.insert(stringify!($name));
474        }
475
476        $(#[$attr])*
477        #[inline]
478        pub fn $with_func<S: Into<String>>(mut self, value: S) -> Self {
479            self.$set_func(value);
480            self
481        }
482    );
483
484    ($(#[$attr:meta])* $set_func:ident, $with_func:ident -> $name:ident: $type:ty) => (
485        $(#[$attr])*
486        #[allow(unused_results)]
487        pub fn $set_func(&mut self, value: $type)  {
488            self.inner.$name = value;
489            self.dirty.insert(stringify!($name));
490        }
491
492        $(#[$attr])*
493        #[inline]
494        pub fn $with_func(mut self, value: $type) -> Self {
495            self.$set_func(value);
496            self
497        }
498    );
499
500    ($(#[$attr:meta])* $set_func:ident, $with_func:ident -> $name:ident: optional String) => (
501        $(#[$attr])*
502        #[allow(unused_results)]
503        pub fn $set_func<S: Into<String>>(&mut self, value: S)  {
504            self.inner.$name = Some(value.into());
505            self.dirty.insert(stringify!($name));
506        }
507
508        $(#[$attr])*
509        #[inline]
510        pub fn $with_func<S: Into<String>>(mut self, value: S) -> Self {
511            self.$set_func(value);
512            self
513        }
514    );
515
516    ($(#[$attr:meta])* $set_func:ident, $with_func:ident -> $name:ident: optional $type:ty) => (
517        $(#[$attr])*
518        #[allow(unused_results)]
519        pub fn $set_func(&mut self, value: $type)  {
520            self.inner.$name = Some(value);
521            self.dirty.insert(stringify!($name));
522        }
523
524        $(#[$attr])*
525        #[inline]
526        pub fn $with_func(mut self, value: $type) -> Self {
527            self.$set_func(value);
528            self
529        }
530    );
531
532}
533
534#[allow(unused_macros)]
535macro_rules! update_field_mut {
536
537    ($(#[$attr:meta])* $mut_func:ident, $set_func:ident, $with_func:ident -> $name:ident: $type:ty) => (
538        $(#[$attr])*
539        #[allow(unused_results)]
540        pub fn $mut_func(&mut self) -> &mut $type {
541            self.dirty.insert(stringify!($name));
542            &mut self.inner.$name
543        }
544
545        $(#[$attr])*
546        #[allow(unused_results)]
547        pub fn $set_func(&mut self, value: $type)  {
548            self.inner.$name = value;
549            self.dirty.insert(stringify!($name));
550        }
551
552        $(#[$attr])*
553        #[inline]
554        pub fn $with_func(mut self, value: $type) -> Self {
555            self.$set_func(value);
556            self
557        }
558    );
559
560}
561
562#[allow(unused_macros)]
563macro_rules! save_option_fields {
564    ($self:ident -> $target:ident: $($field:ident)+) => {
565        $($target.$field = if $self.dirty.contains(stringify!($field)) {
566            $self.inner.$field.clone()
567        } else {
568            None
569        };)+
570    }
571}
572
573#[allow(unused_macros)]
574macro_rules! save_fields {
575    ($self:ident -> $target:ident: $($field:ident)+) => {
576        $($target.$field = if $self.dirty.contains(stringify!($field)) {
577            Some($self.inner.$field.clone())
578        } else {
579            None
580        };)+
581    }
582}
583
584#[allow(unused_macros)]
585macro_rules! protocol_enum {
586    {$(#[$attr:meta])* enum $name:ident: $carrier:ty {
587        $($(#[$iattr:meta])* $item:ident = $val:expr),+
588    }} => (
589        $(#[$attr])*
590        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
591        #[non_exhaustive]
592        pub enum $name {
593            $($(#[$iattr])* $item),+,
594        }
595
596        impl<'de> ::serde::de::Deserialize<'de> for $name {
597            fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
598                    where D: ::serde::de::Deserializer<'de> {
599                let value: $carrier = ::serde::de::Deserialize::deserialize(
600                    deserializer)?;
601                match value {
602                    $($val => Ok($name::$item)),+,
603                    other => {
604                        use ::serde::de::Error;
605                        let err = format!("Unexpected {}: {}",
606                                          stringify!($name), other);
607                        Err(D::Error::custom(err))
608                    }
609                }
610            }
611        }
612
613        impl ::serde::ser::Serialize for $name {
614            fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
615                    where S: ::serde::ser::Serializer {
616                match self {
617                    $($name::$item => $val),+,
618                }.serialize(serializer)
619            }
620        }
621
622        impl From<$name> for $carrier {
623            fn from(value: $name) -> $carrier {
624                match value {
625                    $($name::$item => $val),+,
626                }
627            }
628        }
629    );
630
631    {$(#[$attr:meta])* enum $name:ident {
632        $($(#[$iattr:meta])* $item:ident = $val:expr),+
633    }} => (
634        $(#[$attr])*
635        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
636        #[non_exhaustive]
637        pub enum $name {
638            $($(#[$iattr])* $item),+,
639        }
640
641        impl $name {
642            fn as_ref(&self) -> &'static str {
643                match *self {
644                    $($name::$item => $val),+,
645                }
646            }
647        }
648
649        impl<'de> ::serde::de::Deserialize<'de> for $name {
650            fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
651                    where D: ::serde::de::Deserializer<'de> {
652                match String::deserialize(deserializer)?.as_ref() {
653                    $($val => Ok($name::$item)),+,
654                    other => {
655                        use ::serde::de::Error;
656                        let err = format!("Unexpected {}: {}",
657                                          stringify!($name), other);
658                        Err(D::Error::custom(err))
659                    }
660                }
661            }
662        }
663
664        impl ::std::fmt::Display for $name {
665            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
666                f.write_str(self.as_ref())
667            }
668        }
669
670        impl ::serde::ser::Serialize for $name {
671            fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
672                    where S: ::serde::ser::Serializer {
673                serializer.serialize_str(self.as_ref())
674            }
675        }
676
677        impl From<$name> for String {
678            fn from(value: $name) -> String {
679                String::from(value.as_ref())
680            }
681        }
682    );
683
684    {$(#[$attr:meta])* enum $name:ident = $def:ident {
685        $($(#[$iattr:meta])* $item:ident = $val:expr),+
686    }} => (
687        $(#[$attr])*
688        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
689        #[non_exhaustive]
690        pub enum $name {
691            $($(#[$iattr])* $item),+,
692        }
693
694        impl $name {
695            fn as_ref(&self) -> &'static str {
696                match *self {
697                    $($name::$item => $val),+,
698                }
699            }
700        }
701
702        impl<'de> ::serde::de::Deserialize<'de> for $name {
703            fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
704                    where D: ::serde::de::Deserializer<'de> {
705                Ok(match String::deserialize(deserializer)?.as_str() {
706                    $($val => $name::$item),+,
707                    _ => $name::$def,
708                })
709            }
710        }
711
712        impl ::std::fmt::Display for $name {
713            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
714                f.write_str(self.as_ref())
715            }
716        }
717
718        impl ::serde::ser::Serialize for $name {
719            fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
720                    where S: ::serde::ser::Serializer {
721                if *self == $name::$def {
722                        use ::serde::ser::Error;
723                        let err = format!("Cannot serialize default value {}::{}",
724                                          stringify!($name), stringify!($def));
725                        return Err(S::Error::custom(err));
726                }
727
728                serializer.serialize_str(self.as_ref())
729            }
730        }
731
732        impl From<$name> for String {
733            fn from(value: $name) -> String {
734                String::from(value.as_ref())
735            }
736        }
737    );
738}
739
740/// Reimports of authentication bits from `osauth`.
741///
742/// See [osauth documentation](https://docs.rs/osauth/) for details.
743pub mod auth {
744    pub use osauth::identity::{Password, Scope, Token};
745    pub use osauth::{AuthType, NoAuth};
746}
747#[cfg(feature = "baremetal")]
748pub mod baremetal;
749#[cfg(feature = "block-storage")]
750pub mod block_storage;
751mod cloud;
752pub mod common;
753#[cfg(feature = "compute")]
754pub mod compute;
755#[cfg(feature = "image")]
756pub mod image;
757#[cfg(feature = "network")]
758pub mod network;
759#[cfg(feature = "object-storage")]
760pub mod object_storage;
761/// Synchronous sessions based on one from [osauth](https://docs.rs/osauth/).
762pub mod session {
763    pub use osauth::services::ServiceType;
764    pub use osauth::Session;
765}
766mod utils;
767pub mod waiter;
768
769pub use osauth::common::IdOrName;
770pub use osauth::{EndpointFilters, Error, ErrorKind, InterfaceType, ValidInterfaces};
771
772/// A result of an OpenStack operation.
773pub type Result<T> = std::result::Result<T, Error>;
774
775pub use crate::cloud::Cloud;
776pub use crate::common::Refresh;
777
778/// Sorting request.
779#[derive(Debug, Clone)]
780pub enum Sort<T> {
781    /// Sorting by given field in ascendant order.
782    Asc(T),
783    /// Sorting by given field in descendant order.
784    Desc(T),
785}
786
787impl<T> Sort<T> {
788    #[allow(unused)]
789    fn unwrap(self) -> (T, utils::SortDir) {
790        match self {
791            Sort::Asc(val) => (val, utils::SortDir::Asc),
792            Sort::Desc(val) => (val, utils::SortDir::Desc),
793        }
794    }
795}
796
797impl<T: Into<String>> From<Sort<T>> for (String, String) {
798    fn from(other: Sort<T>) -> (String, String) {
799        match other {
800            Sort::Asc(val) => (val.into(), String::from("asc")),
801            Sort::Desc(val) => (val.into(), String::from("desc")),
802        }
803    }
804}