flams_system/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2//#![feature(file_buffered)]
3//#![feature(lazy_type_alias)]
4
5pub mod backend;
6//pub mod formats;
7pub mod building;
8#[cfg(feature = "tokio")]
9pub mod logging;
10pub mod settings;
11
12pub fn span_capture<R>(f: impl FnOnce() -> R) -> (String, R) {
13    use tracing_subscriber::{fmt, layer::SubscriberExt};
14    #[derive(Clone)]
15    struct Buffer(std::sync::Arc<std::sync::Mutex<Vec<u8>>>);
16    impl std::io::Write for &Buffer {
17        fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
18            if let Ok(mut l) = self.0.lock() {
19                l.extend_from_slice(buf);
20            }
21            Ok(buf.len())
22        }
23        #[inline]
24        fn flush(&mut self) -> std::io::Result<()> {
25            Ok(())
26        }
27    }
28    impl<'a> fmt::MakeWriter<'a> for Buffer {
29        type Writer = &'a Self;
30        #[inline]
31        fn make_writer(&'a self) -> Self::Writer {
32            self
33        }
34    }
35    let buffer = Buffer(std::sync::Arc::new(std::sync::Mutex::new(Vec::<u8>::new())));
36    let bufcl = buffer.clone();
37    let sub = tracing_subscriber::registry().with(fmt::layer().with_writer(bufcl).with_ansi(false));
38    let ret = tracing::subscriber::with_default(sub, f);
39    let s = buffer
40        .0
41        .lock()
42        .map(|mut l| String::from_utf8_lossy(&std::mem::take(&mut *l)).into_owned())
43        .unwrap_or_default();
44    (s, ret)
45}
46
47#[cfg(feature = "zip")]
48pub mod zip;
49
50use building::queue_manager::QueueManager;
51use flams_math_archives::{
52    artifacts::Artifact, backend::AnyBackend, utils::AsyncEngine, LocalArchive,
53};
54use ftml_uris::{DocumentUri, UriPath};
55use settings::SettingsSpec;
56
57pub use inventory::submit as register_exension;
58
59pub struct FlamsExtension {
60    pub name: &'static str,
61    pub on_start: fn(),
62    pub on_build_result: fn(&AnyBackend, &DocumentUri, &UriPath, &dyn Artifact),
63}
64
65#[cfg(feature = "tokio")]
66pub struct TokioEngine;
67#[cfg(feature = "tokio")]
68impl AsyncEngine for TokioEngine {
69    fn background(f: impl FnOnce() + Send + 'static) {
70        let span = tracing::Span::current();
71        tokio::task::spawn_blocking(move || span.in_scope(f));
72    }
73    async fn block_on<R: Send + Sync + 'static>(
74        f: impl FnOnce() -> R + Send + Sync + 'static,
75    ) -> R {
76        tokio::task::spawn_blocking(f).await.expect("this is a bug")
77    }
78    fn exec_after(delay: std::time::Duration, f: impl FnOnce() + Send + 'static) {
79        tokio::task::spawn(async move {
80            tokio::time::sleep(delay).await;
81            f();
82        });
83    }
84}
85
86inventory::collect!(FlamsExtension);
87
88/// #### Panics
89pub fn initialize<A: AsyncEngine>(settings: SettingsSpec) {
90    settings::Settings::initialize(settings);
91    let settings = settings::Settings::get();
92    #[cfg(feature = "rocksdb")]
93    if let Some(p) = &settings.rdf_database {
94        flams_math_archives::backend::set_global(p);
95    }
96    if settings.lsp {
97        use tracing::Level;
98        use tracing_subscriber::layer::SubscriberExt;
99        use tracing_subscriber::Layer;
100        #[cfg(feature = "tokio")]
101        let logger = logging::LogStore::new();
102        let debug = settings.debug;
103        let level = if debug { Level::TRACE } else { Level::INFO };
104
105        let l = tracing_subscriber::fmt::layer()
106            //.with_max_level(Level::INFO)//(if debug {Level::TRACE} else {Level::INFO})
107            .with_ansi(false)
108            .with_target(true)
109            .with_writer(std::io::stderr)
110            .with_filter(tracing::level_filters::LevelFilter::from(Level::INFO)); //.init();
111        #[cfg(feature = "tokio")]
112        let sub = tracing_subscriber::registry()
113            .with(logger.with_filter(tracing::level_filters::LevelFilter::from(level)))
114            .with(l);
115        #[cfg(not(feature = "tokio"))]
116        let sub = tracing_subscriber::registry().with(l);
117        tracing::subscriber::set_global_default(sub)
118            .expect("Failed to set global default logging subscriber");
119    } else {
120        #[cfg(feature = "tokio")]
121        logging::LogStore::initialize();
122    }
123    tracing::info_span!(target:"initializing",parent:None,"initializing").in_scope(move || {
124        #[cfg(feature = "gitlab")]
125        {
126            if let Some(url) = &settings.gitlab_url {
127                let cfg = flams_git::gl::GitlabConfig::new(
128                    url.clone(),
129                    settings.gitlab_token.as_ref().map(ToString::to_string),
130                    settings.gitlab_app_id.as_ref().map(ToString::to_string),
131                    settings.gitlab_app_secret.as_ref().map(ToString::to_string),
132                );
133                flams_git::gl::GLInstance::global().clone().load(cfg);
134            }
135        }
136        backend::initialize::<A>();
137        QueueManager::initialize(settings.num_threads);
138        for e in inventory::iter::<FlamsExtension>() {
139            A::background(|| {
140                tracing::info_span!("Initializing", extension = e.name).in_scope(|| (e.on_start)());
141            });
142        }
143    });
144}
145
146#[cfg(feature = "gitlab")]
147pub trait LocalArchiveExt {
148    fn is_managed(&self) -> Option<&flams_git::GitUrl>;
149}
150
151#[cfg(feature = "gitlab")]
152impl LocalArchiveExt for LocalArchive {
153    fn is_managed(&self) -> Option<&flams_git::GitUrl> {
154        let gl = crate::settings::Settings::get().gitlab_url.as_ref()?;
155        self.git_url(gl)
156    }
157}