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