1
use std::str::FromStr;
2

            
3
use bonsaidb_core::connection::{AsyncStorageConnection, StorageConnection};
4
use bonsaidb_core::schema::{
5
    CollectionName, InvalidNameError, SchemaName, SchemaSummary, ViewName,
6
};
7
use clap::Parser;
8

            
9
/// A schema query against a storage instance.
10
#[derive(Parser, Debug)]
11
pub struct Command {
12
    /// The name of the schema to query.
13
    pub name: Option<SchemaName>,
14

            
15
    /// The item in the schema to query.
16
    pub item: Option<CollectionOrView>,
17
}
18

            
19
impl Command {
20
    /// Executes the command on `storage`.
21
    pub fn execute<SC: StorageConnection>(self, storage: &SC) -> Result<(), crate::Error> {
22
        let schemas = storage.list_available_schemas()?;
23
        self.handle_schema_command(schemas)
24
    }
25

            
26
    /// Executes the command on `storage`.
27
    pub async fn execute_async<SC: AsyncStorageConnection>(
28
        self,
29
        storage: &SC,
30
    ) -> Result<(), crate::Error> {
31
        let schemas = storage.list_available_schemas().await?;
32
        self.handle_schema_command(schemas)
33
    }
34

            
35
    fn handle_schema_command(self, schemas: Vec<SchemaSummary>) -> Result<(), crate::Error> {
36
        if let Some(name) = self.name {
37
            let Some(schema) = schemas.into_iter().find(|s| s.name == name) else {
38
                return Err(crate::Error::Core(
39
                    bonsaidb_core::Error::SchemaNotRegistered(name),
40
                ));
41
            };
42

            
43
            if let Some(item) = self.item {
44
                match item {
45
                    CollectionOrView::View(view) => {
46
                        let Some(collection) = schema.collection(&view.collection) else {
47
                            return Err(crate::Error::Core(
48
                                bonsaidb_core::Error::CollectionNotFound,
49
                            ));
50
                        };
51
                        let Some(view) = collection.view(&view) else {
52
                            return Err(crate::Error::Core(bonsaidb_core::Error::ViewNotFound));
53
                        };
54
                        println!("Version: {}", view.version);
55
                        println!("Policy: {}", view.policy);
56
                    }
57
                    CollectionOrView::Collection(collection) => {
58
                        let Some(collection) = schema.collection(&collection) else {
59
                            return Err(crate::Error::Core(
60
                                bonsaidb_core::Error::CollectionNotFound,
61
                            ));
62
                        };
63
                        let mut views = collection.views().collect::<Vec<_>>();
64
                        views.sort_by(|v1, v2| v1.name.cmp(&v2.name));
65
                        for view in views {
66
                            println!("{}", view.name);
67
                        }
68
                    }
69
                }
70
            } else {
71
                print_collection_list(&schema);
72
            }
73
        } else if let Some(item) = self.item {
74
            eprintln!("missing `schema` for inspecting {item:?}");
75
            std::process::exit(-1);
76
        } else {
77
            print_schema_list(schemas);
78
        }
79
        Ok(())
80
    }
81
}
82

            
83
fn print_schema_list(mut schemas: Vec<SchemaSummary>) {
84
    schemas.sort_by(|s1, s2| s1.name.cmp(&s2.name));
85

            
86
    for schema in schemas {
87
        println!("{}", schema.name);
88
    }
89
}
90

            
91
fn print_collection_list(schema: &SchemaSummary) {
92
    let mut collections = schema.collections().collect::<Vec<_>>();
93
    collections.sort_by(|c1, c2| c1.name.cmp(&c2.name));
94

            
95
    for collection in collections {
96
        println!("{}", collection.name);
97
    }
98
}
99

            
100
/// A name that is either a [`CollectionName`] or [`ViewName`].
101
#[derive(Debug, Clone)]
102
pub enum CollectionOrView {
103
    /// A view name.
104
    View(ViewName),
105
    /// A collection name.
106
    Collection(CollectionName),
107
}
108

            
109
impl FromStr for CollectionOrView {
110
    type Err = InvalidNameError;
111

            
112
    fn from_str(s: &str) -> Result<Self, Self::Err> {
113
        if let Ok(view) = ViewName::from_str(s) {
114
            Ok(Self::View(view))
115
        } else {
116
            CollectionName::from_str(s).map(Self::Collection)
117
        }
118
    }
119
}