1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3use flams_lsp::state::{DocData, UrlOrFile};
4
5use flams_lsp::documents::LSPDocument;
6use flams_lsp::state::DocData::{Data, Doc};
7use flams_lsp::state::UrlOrFile::File;
8use flams_math_archives::backend::GlobalBackend;
9use flams_math_archives::source_files::SourceEntry;
10use flams_math_archives::{Archive, MathArchive};
11use ftml_ontology::utils::RefTree;
12use ftml_uris::DocumentUri;
13use std::ffi::{CStr, CString};
14use std::path::Path;
15use std::sync::{Arc, LazyLock, Mutex};
16
17use flams_lsp::LSPStore;
18use flams_utils::prelude::HMap;
19use serde::Serialize;
20
21extern crate tokio;
22
23#[unsafe(no_mangle)]
24pub extern "C" fn hello_world(arg: usize) {
25 println!("Hi from Rust! arg: {}", arg);
27}
28
29#[no_mangle]
30pub static FFI_VERSION: usize = 1;
31
32pub fn to_json<T: Serialize>(data: &T) -> *const libc::c_char {
33 CString::new(serde_json::to_string(data).unwrap())
34 .unwrap()
35 .into_raw()
36}
37
38#[unsafe(no_mangle)]
39pub unsafe extern "C" fn free_string(s: *mut libc::c_char) {
40 unsafe {
41 if s.is_null() {
42 return;
43 }
44 drop(CString::from_raw(s));
45 }
46}
47
48static GLOBAL_STATE: LazyLock<Mutex<HMap<UrlOrFile, DocData>>> =
49 LazyLock::new(|| Mutex::new(HMap::default()));
50
51#[unsafe(no_mangle)]
52pub extern "C" fn initialize() {
53 tracing_subscriber::fmt().init();
54 let _ce = color_eyre::install();
55 let spec = flams_system::settings::SettingsSpec::default();
56 tokio::runtime::Builder::new_multi_thread()
58 .enable_all()
59 .build()
60 .expect("Failed to initialize Tokio runtime")
61 .block_on(async {
62 flams_system::settings::Settings::initialize(spec);
63 GlobalBackend::initialize::<flams_system::TokioEngine>();
64 });
65}
66
67fn _get_all_files() -> Vec<(Arc<Path>, DocumentUri)> {
68 let mut files: Vec<(Arc<Path>, DocumentUri)> = Vec::new();
69 for a in GlobalBackend.all_archives().iter() {
70 if let Archive::Local(a) = a {
71 a.with_sources(|d| {
72 for e in d.dfs() {
73 if let SourceEntry::File(f) = e {
74 let Ok(uri) = DocumentUri::from_archive_relpath(
75 a.uri().clone(),
76 f.relative_path.as_ref(),
77 ) else {
78 continue;
79 };
80 files.push((
81 f.relative_path
82 .steps()
83 .fold(a.source_dir(), |p, s| p.join(s))
84 .into(),
85 uri,
86 ));
87 }
88 }
89 });
90 }
91 }
92 files
93}
94
95#[unsafe(no_mangle)]
96pub extern "C" fn load_all_files() {
97 let files = _get_all_files();
98 let len = files.len();
99 tracing::info!("Linting {len} files");
100
101 let mut state = GLOBAL_STATE.lock().unwrap();
102 state.clear();
103 for (p, uri) in files {
105 if let Some(ret) = LSPStore::<true>::new(&mut state).load(p.as_ref(), &uri) {
106 state.insert(File(p.clone()), Data(ret, true));
107 }
108 }
109 tracing::info!("Finished linting {len} files");
110}
111
112#[unsafe(no_mangle)]
113pub unsafe extern "C" fn list_of_all_files() -> *const libc::c_char {
114 to_json(
115 &_get_all_files()
116 .into_iter()
117 .map(|(p, _)| p.as_ref().to_str().unwrap().to_string())
118 .collect::<Vec<_>>(),
119 )
120}
121
122#[unsafe(no_mangle)]
123pub unsafe extern "C" fn list_of_loaded_files() -> *const libc::c_char {
124 let state = GLOBAL_STATE.lock().unwrap();
125 let paths: Vec<&str> = state
126 .keys()
127 .map(|k| match k {
128 File(p) => p.as_ref().to_str().unwrap(),
129 x => {
130 tracing::warn!("Unexpected key: {:?}", x);
131 ""
132 }
133 })
134 .filter(|s| !s.is_empty())
135 .collect();
136 to_json(&paths)
137}
138
139#[unsafe(no_mangle)]
140pub unsafe extern "C" fn get_file_annotations(path: *const libc::c_char) -> *const libc::c_char {
141 let path_str: &str = unsafe { CStr::from_ptr(path).to_str().unwrap() };
142 let state = GLOBAL_STATE.lock().unwrap();
143 let doc = state.get(&File(Path::new(path_str).into()));
144 match doc {
145 Some(Data(data, _)) => to_json(&data.lock().annotations),
146 Some(Doc(_lspdoc)) => CString::new("").unwrap().into_raw(),
147 None => CString::new("").unwrap().into_raw(),
148 }
149}
150
151#[unsafe(no_mangle)]
152pub unsafe extern "C" fn unload_file(path: *const libc::c_char) {
153 let path_str: &str = unsafe { CStr::from_ptr(path).to_str().unwrap() };
154 let mut state = GLOBAL_STATE.lock().unwrap();
155 state.remove(&File(Path::new(path_str).into()));
156}
157
158#[unsafe(no_mangle)]
159pub unsafe extern "C" fn load_file(path: *const libc::c_char) {
160 let path_str: &str = unsafe { CStr::from_ptr(path).to_str().unwrap() };
161 let mut state = GLOBAL_STATE.lock().unwrap();
162
163 let lspdoc = LSPDocument::new("".to_string(), File(Path::new(path_str).into()));
164 let p = Path::new(path_str);
165 let uri: &DocumentUri = lspdoc.document_uri().unwrap();
166 if let Some(ret) = LSPStore::<true>::new(&mut state).load(p.as_ref(), &uri) {
167 state.insert(File(p.into()), Data(ret, true));
168 }
169}
170
171#[unsafe(no_mangle)]
172pub extern "C" fn unload_all_files() {
173 let mut state = GLOBAL_STATE.lock().unwrap();
174 state.clear();
175}
176
177#[unsafe(no_mangle)]
178pub extern "C" fn reset_global_backend() {
179 tokio::runtime::Builder::new_multi_thread()
180 .enable_all()
181 .build()
182 .expect("Failed to initialize Tokio runtime")
183 .block_on(async {
184 GlobalBackend.reset::<flams_system::TokioEngine>();
185 });
186}