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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
use educe::Educe;
use std::collections::BTreeMap;
use std::ops::{Deref, DerefMut};
use std::sync::{Arc, Mutex};
use maplit::btreemap;
use once_cell::sync::OnceCell;
use teo_parser::ast::schema::Schema;
use teo_result:: Result;
use teo_runtime::connection;
use teo_runtime::namespace::Namespace;
use crate::app::callbacks::callback::AsyncCallback;
use crate::cli::command::CLI;
use crate::cli::entrance::Entrance;
use crate::cli::runtime_version::RuntimeVersion;

#[derive(Educe)]
#[educe(Debug)]
pub struct Ctx {
    loaded: bool,
    pub(crate) argv: Option<Vec<String>>,
    pub(crate) runtime_version: RuntimeVersion,
    pub(crate) entrance: Entrance,
    pub(crate) main_namespace: Namespace,
    pub(crate) cli: Option<CLI>,
    #[educe(Debug(ignore))]
    pub(crate) schema: Option<Schema>,
    #[educe(Debug(ignore))]
    pub(crate) setup: Option<Arc<dyn AsyncCallback>>,
    #[educe(Debug(ignore))]
    pub(crate) programs: BTreeMap<String, Arc<dyn AsyncCallback>>,
    #[educe(Debug(ignore))]
    pub(crate) conn_ctx: Option<connection::Ctx>,
}

impl Ctx {

    fn new() -> Self {
        Self {
            loaded: true,
            argv: None,
            runtime_version: RuntimeVersion::Rust(env!("TEO_RUSTC_VERSION")),
            entrance: Entrance::APP,
            main_namespace: Namespace::main(),
            cli: None,
            schema: None,
            setup: None,
            programs: btreemap!{},
            conn_ctx: None,
        }
    }

    pub(in crate::app) fn create() -> bool {
        if CURRENT.get().is_none() {
            CURRENT.set(Arc::new(Mutex::new(Self::new()))).unwrap();
            true
        } else {
            false
        }
    }

    pub(in crate::app) fn drop() -> Result<()> {
        Ok(Self::get_mut().reset())
    }

    pub fn get() -> &'static Ctx {
        match CURRENT.get() {
            Some(ctx) => {
                let retval = ctx.lock().unwrap();
                unsafe {
                    &*(retval.deref() as * const Ctx)
                }
            },
            None => panic!("app ctx is accessed when it's not created"),
        }
    }

    pub fn get_mut() -> &'static mut Ctx {
        match CURRENT.get() {
            Some(ctx) => {
                let mut retval = ctx.lock().unwrap();
                unsafe {
                    &mut *(retval.deref_mut() as * mut Ctx)
                }
            },
            None => panic!("app ctx is accessed mutably when it's not created"),
        }
    }

    fn reset(&mut self) {
        self.loaded = false;
    }

    fn reload(&mut self) {
        self.main_namespace = Namespace::main();
        self.loaded = true;
    }

    pub fn main_namespace() -> &'static Namespace {
        &Ctx::get().main_namespace
    }

    pub fn main_namespace_mut() -> &'static mut Namespace {
        &mut Ctx::get_mut().main_namespace
    }

    pub fn set_cli(cli: CLI) {
        Ctx::get_mut().cli = Some(cli)
    }

    pub fn cli() -> &'static CLI {
        Ctx::get().cli.as_ref().unwrap()
    }

    pub fn argv() -> Option<Vec<String>> {
        Ctx::get().argv.clone()
    }

    pub fn set_argv(argv: Option<Vec<String>>) {
        Ctx::get_mut().argv = argv;
    }

    pub fn set_schema(schema: Schema) {
        Ctx::get_mut().schema = Some(schema)
    }

    pub fn schema() -> &'static Schema {
        Ctx::get().schema.as_ref().unwrap()
    }

    pub fn set_entrance(entrance: Entrance) {
        Ctx::get_mut().entrance = entrance;
    }

    pub fn set_runtime_version(runtime_version: RuntimeVersion) {
        Ctx::get_mut().runtime_version = runtime_version;
    }


    pub fn conn_ctx() -> &'static connection::Ctx {
        Ctx::get().conn_ctx.as_ref().unwrap()
    }

    pub fn setup() -> Option<&'static Arc<dyn AsyncCallback>> {
        Ctx::get().setup.as_ref()
    }

    pub fn set_setup<F>(f: F) where F: AsyncCallback + 'static {
        Ctx::get_mut().setup = Some(Arc::new(f));
    }

    pub fn insert_program<F>(name: &str, f: F) where F: AsyncCallback + 'static {
        Ctx::get_mut().programs.insert(name.to_owned(), Arc::new(f));
    }
}

static CURRENT: OnceCell<Arc<Mutex<Ctx>>> = OnceCell::new();