1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use std::str::FromStr;

use bonsaidb_core::connection::{AsyncStorageConnection, StorageConnection};
use bonsaidb_core::schema::{
    CollectionName, InvalidNameError, SchemaName, SchemaSummary, ViewName,
};
use clap::Parser;

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

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

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

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

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

            if let Some(item) = self.item {
                match item {
                    CollectionOrView::View(view) => {
                        let Some(collection) = schema.collection(&view.collection) else {
                            return Err(crate::Error::Core(
                                bonsaidb_core::Error::CollectionNotFound,
                            ));
                        };
                        let Some(view) = collection.view(&view) else {
                            return Err(crate::Error::Core(bonsaidb_core::Error::ViewNotFound));
                        };
                        println!("Version: {}", view.version);
                        println!("Policy: {}", view.policy);
                    }
                    CollectionOrView::Collection(collection) => {
                        let Some(collection) = schema.collection(&collection) else {
                            return Err(crate::Error::Core(
                                bonsaidb_core::Error::CollectionNotFound,
                            ));
                        };
                        let mut views = collection.views().collect::<Vec<_>>();
                        views.sort_by(|v1, v2| v1.name.cmp(&v2.name));
                        for view in views {
                            println!("{}", view.name);
                        }
                    }
                }
            } else {
                print_collection_list(&schema);
            }
        } else if let Some(item) = self.item {
            eprintln!("missing `schema` for inspecting {item:?}");
            std::process::exit(-1);
        } else {
            print_schema_list(schemas);
        }
        Ok(())
    }
}

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

    for schema in schemas {
        println!("{}", schema.name);
    }
}

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

    for collection in collections {
        println!("{}", collection.name);
    }
}

/// A name that is either a [`CollectionName`] or [`ViewName`].
#[derive(Debug, Clone)]
pub enum CollectionOrView {
    /// A view name.
    View(ViewName),
    /// A collection name.
    Collection(CollectionName),
}

impl FromStr for CollectionOrView {
    type Err = InvalidNameError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if let Ok(view) = ViewName::from_str(s) {
            Ok(Self::View(view))
        } else {
            CollectionName::from_str(s).map(Self::Collection)
        }
    }
}