ndg_commonmark/syntax/
mod.rs1pub mod error;
9pub mod types;
10
11pub use error::{SyntaxError, SyntaxResult};
13pub use types::{SyntaxConfig, SyntaxHighlighter, SyntaxManager};
14
15#[cfg(all(feature = "syntastica", feature = "syntect"))]
17compile_error!(
18 "Cannot enable both 'syntastica' and 'syntect' features simultaneously. \
19 They are mutually exclusive."
20);
21
22#[cfg(feature = "syntastica")] mod syntastica;
24#[cfg(feature = "syntastica")] pub use syntastica::*;
25
26#[cfg(feature = "syntect")] mod syntect;
28#[cfg(feature = "syntect")] pub use syntect::*;
29
30pub fn create_default_manager(
46 syntax_queries_dir: Option<&std::path::Path>,
47) -> SyntaxResult<SyntaxManager> {
48 #[cfg(not(feature = "syntastica"))]
49 let _ = syntax_queries_dir;
50
51 #[cfg(all(feature = "syntastica", feature = "syntect"))]
53 {
54 return Err(SyntaxError::MutuallyExclusiveBackends);
55 }
56
57 #[cfg(feature = "syntastica")]
58 {
59 create_syntastica_manager(syntax_queries_dir)
60 }
61
62 #[cfg(feature = "syntect")]
63 {
64 return create_syntect_manager();
65 }
66
67 #[cfg(not(any(feature = "syntastica", feature = "syntect")))]
68 {
69 Err(SyntaxError::NoBackendAvailable)
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::{types::*, *};
76
77 #[test]
78 fn test_syntax_config_default() {
79 let config = SyntaxConfig::default();
80 assert!(config.fallback_to_plain);
81 assert!(config.language_aliases.contains_key("js"));
82 assert_eq!(config.language_aliases["js"], "javascript");
83 }
84
85 #[test]
86 fn test_language_resolution() {
87 let manager =
88 SyntaxManager::new(Box::new(NoopHighlighter), SyntaxConfig::default());
89
90 assert_eq!(manager.resolve_language("js"), "javascript");
91 assert_eq!(manager.resolve_language("py"), "python");
92 assert_eq!(manager.resolve_language("ts"), "typescript");
93 assert_eq!(manager.resolve_language("rust"), "rust");
94 assert_eq!(manager.resolve_language("nix"), "nix");
95 }
96
97 #[test]
98 fn test_modular_access_to_syntax_types() {
99 use super::{
101 error::{SyntaxError, SyntaxResult},
102 types::SyntaxConfig,
103 };
104
105 let error = SyntaxError::UnsupportedLanguage("test".to_string());
107 assert!(matches!(error, SyntaxError::UnsupportedLanguage(_)));
108
109 let result: SyntaxResult<String> = Err(SyntaxError::NoBackendAvailable);
111 assert!(result.is_err());
112
113 let config = SyntaxConfig::default();
115 assert!(config.fallback_to_plain);
116 assert!(config.language_aliases.contains_key("js"));
117
118 let _config2: SyntaxConfig = SyntaxConfig::default();
120 let _error2: SyntaxError = SyntaxError::BackendError("test".to_string());
121 }
122
123 #[cfg(feature = "syntect")]
124 #[test]
125 fn test_nix_language_support() {
126 let manager = create_default_manager(None)
127 .expect("Failed to create default syntax manager");
128 let languages = manager.highlighter().supported_languages();
129
130 assert!(
132 languages.contains(&"nix".to_string()),
133 "Expected Nix language support via two-face"
134 );
135
136 let nix_code = r#"
140{ pkgs ? import <nixpkgs> {} }:
141
142pkgs.stdenv.mkDerivation rec {
143 pname = "hello";
144 version = "2.12";
145
146 src = pkgs.fetchurl {
147 url = "mirror://gnu/hello/${pname}-${version}.tar.gz";
148 sha256 = "1ayhp9v4m4rdhjmnl2bq3cibrbqqkgjbl3s7yk2nhlh8vj3ay16g";
149 };
150}
151"#;
152
153 let result = manager.highlight_code(nix_code, "nix", Some("Nord"));
154 assert!(
155 result.is_ok(),
156 "Failed to highlight Nix code: {:?}",
157 result.err()
158 );
159 }
160
161 struct NoopHighlighter;
162
163 impl SyntaxHighlighter for NoopHighlighter {
164 fn name(&self) -> &'static str {
165 "noop"
166 }
167
168 fn supported_languages(&self) -> Vec<String> {
169 Vec::new()
170 }
171
172 fn available_themes(&self) -> Vec<String> {
173 Vec::new()
174 }
175
176 fn highlight(
177 &self,
178 _code: &str,
179 _language: &str,
180 _theme: Option<&str>,
181 ) -> SyntaxResult<String> {
182 Ok(String::new())
183 }
184
185 fn language_from_extension(&self, _extension: &str) -> Option<String> {
186 None
187 }
188 }
189}