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(all(feature = "syntastica", feature = "syntect"))]
50 {
51 return Err(SyntaxError::MutuallyExclusiveBackends);
52 }
53
54 #[cfg(feature = "syntastica")]
55 {
56 create_syntastica_manager(syntax_queries_dir)
57 }
58
59 #[cfg(feature = "syntect")]
60 {
61 return create_syntect_manager();
62 }
63
64 #[cfg(not(any(feature = "syntastica", feature = "syntect")))]
65 {
66 Err(SyntaxError::NoBackendAvailable)
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::{types::*, *};
73
74 #[test]
75 fn test_syntax_config_default() {
76 let config = SyntaxConfig::default();
77 assert!(config.fallback_to_plain);
78 assert!(config.language_aliases.contains_key("js"));
79 assert_eq!(config.language_aliases["js"], "javascript");
80 }
81
82 #[test]
83 fn test_language_resolution() {
84 let manager =
85 SyntaxManager::new(Box::new(NoopHighlighter), SyntaxConfig::default());
86
87 assert_eq!(manager.resolve_language("js"), "javascript");
88 assert_eq!(manager.resolve_language("py"), "python");
89 assert_eq!(manager.resolve_language("ts"), "typescript");
90 assert_eq!(manager.resolve_language("rust"), "rust");
91 assert_eq!(manager.resolve_language("nix"), "nix");
92 }
93
94 #[test]
95 fn test_modular_access_to_syntax_types() {
96 use super::{
98 error::{SyntaxError, SyntaxResult},
99 types::SyntaxConfig,
100 };
101
102 let error = SyntaxError::UnsupportedLanguage("test".to_string());
104 assert!(matches!(error, SyntaxError::UnsupportedLanguage(_)));
105
106 let result: SyntaxResult<String> = Err(SyntaxError::NoBackendAvailable);
108 assert!(result.is_err());
109
110 let config = SyntaxConfig::default();
112 assert!(config.fallback_to_plain);
113 assert!(config.language_aliases.contains_key("js"));
114
115 let _config2: SyntaxConfig = SyntaxConfig::default();
117 let _error2: SyntaxError = SyntaxError::BackendError("test".to_string());
118 }
119
120 #[cfg(feature = "syntect")]
121 #[test]
122 fn test_nix_language_support() {
123 let manager = create_default_manager(None)
124 .expect("Failed to create default syntax manager");
125 let languages = manager.highlighter().supported_languages();
126
127 assert!(
129 languages.contains(&"nix".to_string()),
130 "Expected Nix language support via two-face"
131 );
132
133 let nix_code = r#"
137{ pkgs ? import <nixpkgs> {} }:
138
139pkgs.stdenv.mkDerivation rec {
140 pname = "hello";
141 version = "2.12";
142
143 src = pkgs.fetchurl {
144 url = "mirror://gnu/hello/${pname}-${version}.tar.gz";
145 sha256 = "1ayhp9v4m4rdhjmnl2bq3cibrbqqkgjbl3s7yk2nhlh8vj3ay16g";
146 };
147}
148"#;
149
150 let result = manager.highlight_code(nix_code, "nix", Some("Nord"));
151 assert!(
152 result.is_ok(),
153 "Failed to highlight Nix code: {:?}",
154 result.err()
155 );
156 }
157
158 struct NoopHighlighter;
159
160 impl SyntaxHighlighter for NoopHighlighter {
161 fn name(&self) -> &'static str {
162 "noop"
163 }
164
165 fn supported_languages(&self) -> Vec<String> {
166 Vec::new()
167 }
168
169 fn available_themes(&self) -> Vec<String> {
170 Vec::new()
171 }
172
173 fn highlight(
174 &self,
175 _code: &str,
176 _language: &str,
177 _theme: Option<&str>,
178 ) -> SyntaxResult<String> {
179 Ok(String::new())
180 }
181
182 fn language_from_extension(&self, _extension: &str) -> Option<String> {
183 None
184 }
185 }
186}