tsrs 0.18.0-aarch64-linux-gnu

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +177 -0
  4. data/ext/tsrs/extconf.rb +121 -0
  5. data/ext/tsrs/include/tsrs.h +113 -0
  6. data/ext/tsrs/tsrs.c +261 -0
  7. data/lib/tsrs/3.2/tsrs.so +0 -0
  8. data/lib/tsrs/3.3/tsrs.so +0 -0
  9. data/lib/tsrs/3.4/tsrs.so +0 -0
  10. data/lib/tsrs/4.0/tsrs.so +0 -0
  11. data/lib/tsrs/backend.rb +66 -0
  12. data/lib/tsrs/check_result.rb +61 -0
  13. data/lib/tsrs/diagnosed.rb +21 -0
  14. data/lib/tsrs/diagnostic.rb +112 -0
  15. data/lib/tsrs/errors.rb +25 -0
  16. data/lib/tsrs/options.rb +62 -0
  17. data/lib/tsrs/session.rb +57 -0
  18. data/lib/tsrs/transpile_result.rb +54 -0
  19. data/lib/tsrs/version.rb +5 -0
  20. data/lib/tsrs.rb +80 -0
  21. data/licenses/README.md +11 -0
  22. data/licenses/tsrs-Apache-2.0.txt +55 -0
  23. data/licenses/tsrs-NOTICE.txt +416 -0
  24. data/rust/.cargo/config.toml +5 -0
  25. data/rust/Cargo.lock +1301 -0
  26. data/rust/Cargo.toml +39 -0
  27. data/rust/build.rs +77 -0
  28. data/rust/cbindgen.toml +25 -0
  29. data/rust/rust-toolchain.toml +5 -0
  30. data/rust/rustfmt.toml +3 -0
  31. data/rust/src/check.rs +136 -0
  32. data/rust/src/lib.rs +213 -0
  33. data/rust/src/options.rs +60 -0
  34. data/rust/src/result.rs +52 -0
  35. data/rust/src/session.rs +194 -0
  36. data/rust/src/status.rs +26 -0
  37. data/rust/src/transpile.rs +66 -0
  38. data/rust/src/worker.rs +84 -0
  39. data/sig/tsrs/backend.rbs +44 -0
  40. data/sig/tsrs/check_result.rbs +33 -0
  41. data/sig/tsrs/diagnosed.rbs +15 -0
  42. data/sig/tsrs/diagnostic.rbs +66 -0
  43. data/sig/tsrs/errors.rbs +37 -0
  44. data/sig/tsrs/options.rbs +19 -0
  45. data/sig/tsrs/session.rbs +26 -0
  46. data/sig/tsrs/transpile_result.rbs +29 -0
  47. data/sig/tsrs/types.rbs +5 -0
  48. data/sig/tsrs/version.rbs +5 -0
  49. data/sig/tsrs.rbs +27 -0
  50. data/tsrs.gemspec +45 -0
  51. metadata +102 -0
data/rust/Cargo.toml ADDED
@@ -0,0 +1,39 @@
1
+ [workspace]
2
+
3
+ [package]
4
+ name = "tsrs-ruby-ffi"
5
+ edition = "2021"
6
+ authors = ["Marco Roth <marco.roth@intergga.ch>"]
7
+ description = "C FFI bindings for tsrs, used by the tsrs gem"
8
+ license = "MIT"
9
+ repository = "https://github.com/marcoroth/tsrs-ruby"
10
+ publish = false
11
+
12
+ [lib]
13
+ name = "tsrs_ffi"
14
+ path = "src/lib.rs"
15
+ crate-type = ["cdylib", "staticlib", "rlib"]
16
+
17
+ # tsrs does not publish its crates, so they come from the release tag. See .github/workflows/upstream.yml.
18
+ [dependencies]
19
+ tsrs_api = { git = "https://github.com/oxc-project/tsrs", tag = "v0.18.0" }
20
+ tsrs_ast = { git = "https://github.com/oxc-project/tsrs", tag = "v0.18.0" }
21
+ tsrs_compiler = { git = "https://github.com/oxc-project/tsrs", tag = "v0.18.0" }
22
+ tsrs_contentmapper = { git = "https://github.com/oxc-project/tsrs", tag = "v0.18.0" }
23
+ tsrs_core = { git = "https://github.com/oxc-project/tsrs", tag = "v0.18.0" }
24
+ tsrs_execute = { git = "https://github.com/oxc-project/tsrs", tag = "v0.18.0" }
25
+ tsrs_project = { git = "https://github.com/oxc-project/tsrs", tag = "v0.18.0" }
26
+ tsrs_tsoptions = { git = "https://github.com/oxc-project/tsrs", tag = "v0.18.0" }
27
+ tsrs_vfs = { git = "https://github.com/oxc-project/tsrs", tag = "v0.18.0" }
28
+ serde = { version = "1", features = ["derive"] }
29
+ serde_json = "1"
30
+
31
+ [build-dependencies]
32
+ cbindgen = "0.29"
33
+
34
+ [profile.release]
35
+ opt-level = 3
36
+ lto = "fat"
37
+ codegen-units = 1
38
+ strip = "symbols"
39
+ panic = "unwind"
data/rust/build.rs ADDED
@@ -0,0 +1,77 @@
1
+ use std::env;
2
+ use std::fs;
3
+ use std::path::PathBuf;
4
+
5
+ fn main() {
6
+ let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
7
+ let header_path = PathBuf::from(&crate_dir).join("../ext/tsrs/include/tsrs.h");
8
+
9
+ if let Ok(bindings) = cbindgen::generate(&crate_dir) {
10
+ if let Some(parent) = header_path.parent() {
11
+ let _ = std::fs::create_dir_all(parent);
12
+ }
13
+
14
+ bindings.write_to_file(&header_path);
15
+ }
16
+
17
+ let lock_path = PathBuf::from(&crate_dir).join("Cargo.lock");
18
+
19
+ println!("cargo:rerun-if-changed={}", lock_path.display());
20
+
21
+ println!(
22
+ "cargo:rerun-if-changed={}",
23
+ PathBuf::from(&crate_dir).join("src").display()
24
+ );
25
+
26
+ println!(
27
+ "cargo:rerun-if-changed={}",
28
+ PathBuf::from(&crate_dir).join("cbindgen.toml").display()
29
+ );
30
+
31
+ let version_rb_path = PathBuf::from(&crate_dir).join("../lib/tsrs/version.rb");
32
+
33
+ println!("cargo:rerun-if-changed={}", version_rb_path.display());
34
+
35
+ println!(
36
+ "cargo:rustc-env=TSRS_VERSION={}",
37
+ locked_version(&lock_path, "tsrs_api")
38
+ );
39
+
40
+ println!("cargo:rustc-env=GEM_VERSION={}", gem_version(&version_rb_path));
41
+ }
42
+
43
+ fn gem_version(version_rb_path: &PathBuf) -> String {
44
+ let Ok(source) = fs::read_to_string(version_rb_path) else {
45
+ return "unknown".to_string();
46
+ };
47
+
48
+ for line in source.lines() {
49
+ let Some(rest) = line.trim().strip_prefix("VERSION = ") else {
50
+ continue;
51
+ };
52
+
53
+ return rest.trim().trim_matches('"').to_string();
54
+ }
55
+
56
+ "unknown".to_string()
57
+ }
58
+
59
+ fn locked_version(lock_path: &PathBuf, package: &str) -> String {
60
+ let Ok(lock) = fs::read_to_string(lock_path) else {
61
+ return "unknown".to_string();
62
+ };
63
+
64
+ let mut lines = lock.lines();
65
+
66
+ while let Some(line) = lines.next() {
67
+ if line.trim() != format!("name = \"{package}\"") {
68
+ continue;
69
+ }
70
+
71
+ if let Some(version) = lines.next().and_then(|next| next.trim().strip_prefix("version = ")) {
72
+ return version.trim_matches('"').to_string();
73
+ }
74
+ }
75
+
76
+ "unknown".to_string()
77
+ }
@@ -0,0 +1,25 @@
1
+ language = "C"
2
+ header = """/* Generated by cbindgen, do not edit manually */
3
+
4
+ #include <stdbool.h>
5
+ #include <stdint.h>
6
+ #include <stddef.h>"""
7
+ include_guard = "TSRS_H"
8
+ no_includes = true
9
+
10
+ [export]
11
+ include = [
12
+ "TsrsResult",
13
+ "TsrsErrorCode",
14
+ "TsrsSession",
15
+ ]
16
+
17
+ [enum]
18
+ rename_variants = "ScreamingSnakeCase"
19
+ prefix_with_name = true
20
+
21
+ [fn]
22
+ sort_by = "None"
23
+
24
+ [parse]
25
+ parse_deps = false
@@ -0,0 +1,5 @@
1
+ # tsrs requires the Rust version its workspace pins (rust-version in its Cargo.toml).
2
+ [toolchain]
3
+ channel = "1.99.1"
4
+ components = ["clippy", "rustfmt"]
5
+ profile = "minimal"
data/rust/rustfmt.toml ADDED
@@ -0,0 +1,3 @@
1
+ tab_spaces = 2
2
+ max_width = 120
3
+ edition = "2021"
data/rust/src/check.rs ADDED
@@ -0,0 +1,136 @@
1
+ use std::io::Write;
2
+ use std::sync::{Arc, Mutex};
3
+ use std::time::{Duration, Instant};
4
+
5
+ use serde::Serialize;
6
+ use serde_json::value::RawValue;
7
+ use tsrs_ast::Diagnostic;
8
+ use tsrs_core::{tspath, P};
9
+ use tsrs_execute::tsc::{ReadWriteCloser, System};
10
+ use tsrs_vfs::{bundled, osvfs, FS};
11
+
12
+ use crate::options::CheckRequest;
13
+ use crate::result::{TsrsErrorCode, TsrsResult};
14
+ use crate::status::Status;
15
+
16
+ #[derive(Serialize)]
17
+ struct CheckPayload {
18
+ status: Status,
19
+ output: String,
20
+ diagnostics: Box<RawValue>,
21
+ }
22
+
23
+ struct RubySys {
24
+ output: Mutex<String>,
25
+ sink: Box<dyn Fn(P<Diagnostic>) + Send + Sync>,
26
+ fs: Arc<dyn FS>,
27
+ default_library_path: String,
28
+ cwd: String,
29
+ start: Instant,
30
+ }
31
+
32
+ impl System for RubySys {
33
+ fn fs(&self) -> Arc<dyn FS> {
34
+ Arc::clone(&self.fs)
35
+ }
36
+
37
+ fn default_library_path(&self) -> &str {
38
+ &self.default_library_path
39
+ }
40
+
41
+ fn get_current_directory(&self) -> &str {
42
+ &self.cwd
43
+ }
44
+
45
+ fn write(&self, text: &str) {
46
+ if let Ok(mut output) = self.output.lock() {
47
+ output.push_str(text);
48
+ }
49
+ }
50
+
51
+ fn flush(&self) {}
52
+
53
+ fn write_output_is_tty(&self) -> bool {
54
+ false
55
+ }
56
+
57
+ fn get_environment_variable(&self, name: &str) -> Option<String> {
58
+ std::env::var(name).ok()
59
+ }
60
+
61
+ fn spawn(
62
+ &self,
63
+ command: &[String],
64
+ dir: &str,
65
+ stderr: Option<Box<dyn Write + Send>>,
66
+ ) -> Result<ReadWriteCloser, String> {
67
+ tsrs_contentmapper::spawn_process(command, dir, stderr)
68
+ }
69
+
70
+ fn now(&self) -> Instant {
71
+ Instant::now()
72
+ }
73
+
74
+ fn since_start(&self) -> Duration {
75
+ self.start.elapsed()
76
+ }
77
+
78
+ fn diagnostic_sink(&self) -> Option<&(dyn Fn(P<Diagnostic>) + Sync)> {
79
+ Some(&*self.sink as &(dyn Fn(P<Diagnostic>) + Sync))
80
+ }
81
+ }
82
+
83
+ pub fn check(request_json: String) -> TsrsResult {
84
+ let request: CheckRequest = match serde_json::from_str(&request_json) {
85
+ Ok(request) => request,
86
+ Err(error) => return TsrsResult::error(TsrsErrorCode::Option, error.to_string()),
87
+ };
88
+
89
+ if request.args.iter().any(|arg| is_build_flag(arg)) {
90
+ return TsrsResult::error(TsrsErrorCode::Option, "build mode (--build) is not supported");
91
+ }
92
+
93
+ // The parsed command line keeps referring to the system, so it is leaked, as tsrs's CLI does.
94
+ let collected: Arc<Mutex<Vec<P<Diagnostic>>>> = Arc::new(Mutex::new(Vec::new()));
95
+ let sink_target = Arc::clone(&collected);
96
+
97
+ let sys: &'static RubySys = Box::leak(Box::new(RubySys {
98
+ output: Mutex::new(String::new()),
99
+ sink: Box::new(move |diagnostic| {
100
+ if let Ok(mut list) = sink_target.lock() {
101
+ list.push(diagnostic);
102
+ }
103
+ }),
104
+ fs: Arc::new(bundled::wrap_fs(osvfs::fs())),
105
+ default_library_path: bundled::default_lib_path(),
106
+ cwd: tspath::normalize_path(&request.cwd),
107
+ start: Instant::now(),
108
+ }));
109
+
110
+ let result = tsrs_execute::execute::command_line(sys, request.args);
111
+
112
+ let diagnostics = std::mem::take(&mut *collected.lock().unwrap_or_else(|poisoned| poisoned.into_inner()));
113
+ let output = std::mem::take(&mut *sys.output.lock().unwrap_or_else(|poisoned| poisoned.into_inner()));
114
+
115
+ let diagnostics = tsrs_api::diagnostics::diagnostic_responses(&diagnostics);
116
+
117
+ let diagnostics = match tsrs_core::json::marshal(&diagnostics).map(RawValue::from_string) {
118
+ Ok(Ok(diagnostics)) => diagnostics,
119
+ _ => return TsrsResult::error(TsrsErrorCode::Internal, "the diagnostics could not be serialized"),
120
+ };
121
+
122
+ let payload = CheckPayload {
123
+ status: result.status.into(),
124
+ output,
125
+ diagnostics,
126
+ };
127
+
128
+ match serde_json::to_string(&payload) {
129
+ Ok(json) => TsrsResult::value(json),
130
+ Err(error) => TsrsResult::error(TsrsErrorCode::Internal, error.to_string()),
131
+ }
132
+ }
133
+
134
+ fn is_build_flag(arg: &str) -> bool {
135
+ matches!(arg.to_lowercase().as_str(), "-b" | "--b" | "-build" | "--build")
136
+ }
data/rust/src/lib.rs ADDED
@@ -0,0 +1,213 @@
1
+ use std::ffi::{CStr, CString};
2
+ use std::os::raw::c_char;
3
+ use std::panic::{catch_unwind, AssertUnwindSafe};
4
+
5
+ mod check;
6
+ mod options;
7
+ mod result;
8
+ mod session;
9
+ mod status;
10
+ mod transpile;
11
+ mod worker;
12
+
13
+ use result::{TsrsErrorCode, TsrsResult};
14
+ use session::TsrsSession;
15
+
16
+ /// # Safety
17
+ ///
18
+ /// `source` and `options_json` must be valid, nul-terminated UTF-8 strings.
19
+ #[no_mangle]
20
+ pub unsafe extern "C" fn tsrs_transpile(source: *const c_char, options_json: *const c_char) -> TsrsResult {
21
+ transpile_with(source, options_json, false)
22
+ }
23
+
24
+ /// # Safety
25
+ ///
26
+ /// `source` and `options_json` must be valid, nul-terminated UTF-8 strings.
27
+ #[no_mangle]
28
+ pub unsafe extern "C" fn tsrs_transpile_declaration(source: *const c_char, options_json: *const c_char) -> TsrsResult {
29
+ transpile_with(source, options_json, true)
30
+ }
31
+
32
+ /// # Safety
33
+ ///
34
+ /// `options_json` must be a valid, nul-terminated UTF-8 string.
35
+ #[no_mangle]
36
+ pub unsafe extern "C" fn tsrs_check(options_json: *const c_char) -> TsrsResult {
37
+ let Some(options) = borrow(options_json) else {
38
+ return TsrsResult::error(TsrsErrorCode::Encoding, "options were not valid UTF-8");
39
+ };
40
+
41
+ let options = options.to_string();
42
+
43
+ guard(move || worker::run(move || check::check(options)))
44
+ }
45
+
46
+ /// # Safety
47
+ ///
48
+ /// `cwd` must be a valid, nul-terminated UTF-8 string.
49
+ #[no_mangle]
50
+ pub unsafe extern "C" fn tsrs_session_new(cwd: *const c_char) -> *mut TsrsSession {
51
+ let Some(cwd) = borrow(cwd) else {
52
+ return std::ptr::null_mut();
53
+ };
54
+
55
+ let cwd = cwd.to_string();
56
+
57
+ catch_unwind(AssertUnwindSafe(|| Box::into_raw(Box::new(session::new(&cwd))))).unwrap_or(std::ptr::null_mut())
58
+ }
59
+
60
+ /// Taken before a check that runs without the GVL, so that freeing the original meanwhile cannot free the session.
61
+ ///
62
+ /// # Safety
63
+ ///
64
+ /// `session` must be a live pointer from `tsrs_session_new` or `tsrs_session_retain`.
65
+ #[no_mangle]
66
+ pub unsafe extern "C" fn tsrs_session_retain(session: *const TsrsSession) -> *mut TsrsSession {
67
+ match session.as_ref() {
68
+ Some(session) => Box::into_raw(Box::new(session.retain())),
69
+ None => std::ptr::null_mut(),
70
+ }
71
+ }
72
+
73
+ /// # Safety
74
+ ///
75
+ /// `session` must be a live pointer from `tsrs_session_new` or `tsrs_session_retain`, and `options_json` a valid,
76
+ /// nul-terminated UTF-8 string.
77
+ #[no_mangle]
78
+ pub unsafe extern "C" fn tsrs_session_check(session: *const TsrsSession, options_json: *const c_char) -> TsrsResult {
79
+ let Some(session) = session.as_ref() else {
80
+ return TsrsResult::error(TsrsErrorCode::Internal, "the session is gone");
81
+ };
82
+
83
+ let Some(options) = borrow(options_json) else {
84
+ return TsrsResult::error(TsrsErrorCode::Encoding, "options were not valid UTF-8");
85
+ };
86
+
87
+ let options = options.to_string();
88
+ let session = session.session();
89
+
90
+ guard(move || worker::run(move || session::check(&session, &options)))
91
+ }
92
+
93
+ /// The pointer stays valid until `tsrs_session_free`.
94
+ ///
95
+ /// # Safety
96
+ ///
97
+ /// `session` must be a live pointer from `tsrs_session_new` or `tsrs_session_retain`, or null.
98
+ #[no_mangle]
99
+ pub unsafe extern "C" fn tsrs_session_close(session: *const TsrsSession) {
100
+ if let Some(session) = session.as_ref() {
101
+ let _ = catch_unwind(AssertUnwindSafe(|| session.close()));
102
+ }
103
+ }
104
+
105
+ /// # Safety
106
+ ///
107
+ /// `session` must be a pointer from `tsrs_session_new` or `tsrs_session_retain` that is not used afterwards, or null.
108
+ #[no_mangle]
109
+ pub unsafe extern "C" fn tsrs_session_free(session: *mut TsrsSession) {
110
+ if session.is_null() {
111
+ return;
112
+ }
113
+
114
+ let _ = catch_unwind(AssertUnwindSafe(|| drop(Box::from_raw(session))));
115
+ }
116
+
117
+ #[no_mangle]
118
+ pub extern "C" fn tsrs_version() -> *mut c_char {
119
+ into_c_string(env!("GEM_VERSION"))
120
+ }
121
+
122
+ #[no_mangle]
123
+ pub extern "C" fn tsrs_tsrs_version() -> *mut c_char {
124
+ into_c_string(env!("TSRS_VERSION"))
125
+ }
126
+
127
+ #[no_mangle]
128
+ pub extern "C" fn tsrs_typescript_version() -> *mut c_char {
129
+ into_c_string(tsrs_core::version())
130
+ }
131
+
132
+ /// # Safety
133
+ ///
134
+ /// `value` must be a pointer this library returned, and must not be used afterwards.
135
+ #[no_mangle]
136
+ pub unsafe extern "C" fn tsrs_string_free(value: *mut c_char) {
137
+ if value.is_null() {
138
+ return;
139
+ }
140
+
141
+ drop(CString::from_raw(value));
142
+ }
143
+
144
+ /// # Safety
145
+ ///
146
+ /// `result` must be one this library returned, and must not be used afterwards.
147
+ #[no_mangle]
148
+ pub unsafe extern "C" fn tsrs_result_free(result: TsrsResult) {
149
+ if !result.value.is_null() {
150
+ drop(CString::from_raw(result.value));
151
+ }
152
+
153
+ if !result.error.is_null() {
154
+ drop(CString::from_raw(result.error));
155
+ }
156
+ }
157
+
158
+ #[no_mangle]
159
+ pub extern "C" fn tsrs_panic_for_test() -> TsrsResult {
160
+ let previous = std::panic::take_hook();
161
+
162
+ std::panic::set_hook(Box::new(|_| {}));
163
+
164
+ let caught = worker::run(|| -> TsrsResult { panic!("a deliberate panic") });
165
+
166
+ std::panic::set_hook(previous);
167
+
168
+ caught
169
+ }
170
+
171
+ unsafe fn transpile_with(source: *const c_char, options_json: *const c_char, declaration: bool) -> TsrsResult {
172
+ let Some(source) = borrow(source) else {
173
+ return TsrsResult::error(TsrsErrorCode::Encoding, "source was not valid UTF-8");
174
+ };
175
+
176
+ let Some(options) = borrow(options_json) else {
177
+ return TsrsResult::error(TsrsErrorCode::Encoding, "options were not valid UTF-8");
178
+ };
179
+
180
+ let source = source.to_string();
181
+ let options = options.to_string();
182
+
183
+ guard(move || worker::run(move || transpile::transpile(source, options, declaration)))
184
+ }
185
+
186
+ fn guard(call: impl FnOnce() -> TsrsResult) -> TsrsResult {
187
+ match catch_unwind(AssertUnwindSafe(call)) {
188
+ Ok(result) => result,
189
+ Err(payload) => TsrsResult::error(TsrsErrorCode::Panic, panic_message(&payload)),
190
+ }
191
+ }
192
+
193
+ unsafe fn borrow<'a>(value: *const c_char) -> Option<&'a str> {
194
+ if value.is_null() {
195
+ return None;
196
+ }
197
+
198
+ CStr::from_ptr(value).to_str().ok()
199
+ }
200
+
201
+ pub(crate) fn panic_message(payload: &Box<dyn std::any::Any + Send>) -> String {
202
+ payload
203
+ .downcast_ref::<&str>()
204
+ .map(|message| (*message).to_string())
205
+ .or_else(|| payload.downcast_ref::<String>().cloned())
206
+ .unwrap_or_else(|| "tsrs panicked".to_string())
207
+ }
208
+
209
+ fn into_c_string(value: &str) -> *mut c_char {
210
+ CString::new(value)
211
+ .unwrap_or_else(|_| CString::new("unknown").expect("a string with no interior nul"))
212
+ .into_raw()
213
+ }
@@ -0,0 +1,60 @@
1
+ use serde::Deserialize;
2
+ use tsrs_core::CompilerOptions;
3
+ use tsrs_tsoptions::ParseConfigHost;
4
+ use tsrs_vfs::FS;
5
+
6
+ #[derive(Deserialize)]
7
+ #[serde(deny_unknown_fields, rename_all = "camelCase")]
8
+ pub struct TranspileRequest {
9
+ #[serde(default)]
10
+ pub file_name: String,
11
+ #[serde(default)]
12
+ pub args: Vec<String>,
13
+ #[serde(default = "yes")]
14
+ pub report_diagnostics: bool,
15
+ }
16
+
17
+ #[derive(Deserialize)]
18
+ #[serde(deny_unknown_fields, rename_all = "camelCase")]
19
+ pub struct CheckRequest {
20
+ pub cwd: String,
21
+ #[serde(default)]
22
+ pub args: Vec<String>,
23
+ }
24
+
25
+ fn yes() -> bool {
26
+ true
27
+ }
28
+
29
+ struct OptionsHost;
30
+
31
+ impl ParseConfigHost for OptionsHost {
32
+ fn fs(&self) -> &dyn FS {
33
+ tsrs_vfs::osvfs::fs()
34
+ }
35
+
36
+ fn get_current_directory(&self) -> &str {
37
+ "/"
38
+ }
39
+ }
40
+
41
+ static OPTIONS_HOST: OptionsHost = OptionsHost;
42
+
43
+ pub fn compiler_options(args: &[String]) -> Result<CompilerOptions, String> {
44
+ let parsed = tsrs_tsoptions::parse_command_line(args, &OPTIONS_HOST);
45
+
46
+ if let Some(error) = parsed.errors.first() {
47
+ return Err(error.localize());
48
+ }
49
+
50
+ if let Some(file) = parsed.file_names().first() {
51
+ return Err(format!("{file:?} is a file name, not a compiler option"));
52
+ }
53
+
54
+ Ok(
55
+ parsed
56
+ .compiler_options()
57
+ .map(|options| (*options).clone())
58
+ .unwrap_or_default(),
59
+ )
60
+ }
@@ -0,0 +1,52 @@
1
+ use std::ffi::CString;
2
+ use std::os::raw::c_char;
3
+
4
+ #[repr(C)]
5
+ pub enum TsrsErrorCode {
6
+ None = 0,
7
+ Option,
8
+ Encoding,
9
+ Transpile,
10
+ Check,
11
+ Internal,
12
+ Panic,
13
+ }
14
+
15
+ #[repr(C)]
16
+ pub struct TsrsResult {
17
+ pub value: *mut c_char,
18
+ pub value_len: usize,
19
+ pub error: *mut c_char,
20
+ pub code: TsrsErrorCode,
21
+ }
22
+
23
+ // SAFETY: the pointers are owned CStrings that nothing else refers to.
24
+ unsafe impl Send for TsrsResult {}
25
+
26
+ impl TsrsResult {
27
+ pub fn value(payload: String) -> Self {
28
+ let len = payload.len();
29
+
30
+ match CString::new(payload) {
31
+ Ok(value) => Self {
32
+ value: value.into_raw(),
33
+ value_len: len,
34
+ error: std::ptr::null_mut(),
35
+ code: TsrsErrorCode::None,
36
+ },
37
+ Err(_) => Self::error(TsrsErrorCode::Encoding, "the payload contained a null byte"),
38
+ }
39
+ }
40
+
41
+ pub fn error(code: TsrsErrorCode, message: impl Into<String>) -> Self {
42
+ let message = CString::new(message.into())
43
+ .unwrap_or_else(|_| CString::new("an error with no printable message").expect("no interior nul"));
44
+
45
+ Self {
46
+ value: std::ptr::null_mut(),
47
+ value_len: 0,
48
+ error: message.into_raw(),
49
+ code,
50
+ }
51
+ }
52
+ }