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