sass_embedded/
embedded.rs

1use std::{ffi::OsStr, path::Path};
2
3use atty::Stream;
4
5use crate::{
6  channel::Channel,
7  host::ImporterRegistry,
8  host::{Host, LoggerRegistry},
9  protocol::{
10    self,
11    inbound_message::{
12      compile_request::{Input, StringInput},
13      CompileRequest,
14    },
15  },
16  CompileResult, Options, Result, StringOptions,
17};
18#[cfg(feature = "legacy")]
19use crate::{
20  legacy::LEGACY_IMPORTER_PROTOCOL, protocol::inbound_message::compile_request,
21};
22
23/// The sass-embedded compiler for rust host.
24#[derive(Debug)]
25pub struct Embedded {
26  channel: Channel,
27}
28
29impl Embedded {
30  /// Creates a sass-embedded compiler and connects with the dart-sass-embedded.
31  ///
32  /// ```no_run
33  /// let mut sass = sass_embedded::Sass::new("path/to/sass_embedded").unwrap();
34  /// ```
35  pub fn new(exe_path: impl AsRef<OsStr>) -> Result<Self> {
36    Ok(Self {
37      channel: Channel::new(exe_path)?,
38    })
39  }
40
41  /// Compiles the Sass file at path to CSS. If it succeeds it returns a [CompileResult],
42  /// and if it fails it throws an [Exception].
43  ///
44  /// ```no_run
45  /// use sass_embedded::{Sass, Options};
46  ///
47  /// let mut sass = Sass::new("path/to/sass_embedded").unwrap();
48  /// let res = sass.compile("../styles/a.scss", Options::default()).unwrap();
49  /// ```
50  ///
51  /// More information: [Sass documentation](https://sass-lang.com/documentation/js-api/modules#compile)
52  pub fn compile(
53    &mut self,
54    path: impl AsRef<Path>,
55    options: Options,
56  ) -> Result<CompileResult> {
57    let mut logger_registry = LoggerRegistry::default();
58    let mut importer_registry = ImporterRegistry::default();
59    let importers = importer_registry
60      .register_all(options.importers, options.load_paths)
61      .collect();
62    if let Some(l) = options.logger {
63      logger_registry.register(l);
64    }
65
66    let request = CompileRequest {
67      style: protocol::OutputStyle::from(options.style) as i32,
68      source_map: options.source_map,
69      alert_color: options
70        .alert_color
71        .unwrap_or_else(|| atty::is(Stream::Stdout)),
72      alert_ascii: options.alert_ascii,
73      verbose: options.verbose,
74      quiet_deps: options.quiet_deps,
75      source_map_include_sources: options.source_map_include_sources,
76      charset: options.charset,
77      importers,
78      input: Some(Input::Path(path.as_ref().to_str().unwrap().to_string())),
79      // id: set in compile_request
80      // global_functions: not implemented
81      ..Default::default()
82    };
83
84    let host = Host::new(importer_registry, logger_registry);
85    let conn = self.channel.connect(host)?;
86    let response = conn
87      .compile_request(request)
88      .map_err(|e| Box::new(e.into()))?;
89    CompileResult::try_from(response)
90  }
91
92  /// Compiles a stylesheet whose contents is source to CSS. If it succeeds it returns
93  /// a [CompileResult], and if it fails it throws an [Exception].
94  ///
95  /// ```no_run
96  /// use sass_embedded::{Sass, StringOptions};
97  ///
98  /// let mut sass = Sass::new("path/to/sass_embedded").unwrap();
99  /// let res = sass.compile_string("a {b: c}", StringOptions::default()).unwrap();
100  /// ```
101  ///
102  /// More information: [Sass documentation](https://sass-lang.com/documentation/js-api/modules#compileString)
103  pub fn compile_string(
104    &mut self,
105    source: impl Into<String>,
106    options: StringOptions,
107  ) -> Result<CompileResult> {
108    let mut logger_registry = LoggerRegistry::default();
109    let mut importer_registry = ImporterRegistry::default();
110    let importers = importer_registry
111      .register_all(options.common.importers, options.common.load_paths)
112      .collect();
113    if let Some(l) = options.common.logger {
114      logger_registry.register(l);
115    }
116
117    #[cfg(feature = "legacy")]
118    let importer = if let Some(input_importer) = options.input_importer {
119      Some(importer_registry.register(input_importer))
120    } else if matches!(&options.url, Some(u) if u.to_string() == LEGACY_IMPORTER_PROTOCOL)
121    {
122      Some(compile_request::Importer {
123        importer: Some(compile_request::importer::Importer::Path(
124          std::env::current_dir()
125            .unwrap()
126            .to_str()
127            .unwrap()
128            .to_string(),
129        )),
130      })
131    } else {
132      None
133    };
134
135    #[cfg(feature = "legacy")]
136    let url = options
137      .url
138      .map(|url| url.to_string())
139      .filter(|url| url != LEGACY_IMPORTER_PROTOCOL)
140      .unwrap_or_default();
141
142    #[cfg(not(feature = "legacy"))]
143    let importer = options
144      .input_importer
145      .map(|i| importer_registry.register(i));
146
147    #[cfg(not(feature = "legacy"))]
148    let url = options.url.map(|url| url.to_string()).unwrap_or_default();
149
150    let request = CompileRequest {
151      style: protocol::OutputStyle::from(options.common.style) as i32,
152      source_map: options.common.source_map,
153      alert_color: options
154        .common
155        .alert_color
156        .unwrap_or_else(|| atty::is(Stream::Stdout)),
157      alert_ascii: options.common.alert_ascii,
158      verbose: options.common.verbose,
159      quiet_deps: options.common.quiet_deps,
160      source_map_include_sources: options.common.source_map_include_sources,
161      charset: options.common.charset,
162      importers,
163      input: Some(Input::String(StringInput {
164        source: source.into(),
165        url,
166        syntax: protocol::Syntax::from(options.syntax) as i32,
167        importer,
168      })),
169      // id: set in compile_request
170      // global_functions: not implemented
171      ..Default::default()
172    };
173
174    let host = Host::new(importer_registry, logger_registry);
175    let conn = self.channel.connect(host)?;
176    let response = conn
177      .compile_request(request)
178      .map_err(|e| Box::new(e.into()))?;
179    CompileResult::try_from(response)
180  }
181
182  /// Gets the version of the sass-embedded compiler.
183  pub fn info(&mut self) -> Result<String> {
184    let logger_registry = LoggerRegistry::default();
185    let importer_registry = ImporterRegistry::default();
186    let host = Host::new(importer_registry, logger_registry);
187    let conn = self.channel.connect(host)?;
188    let response = conn.version_request().map_err(|e| Box::new(e.into()))?;
189    Ok(format!(
190      "sass-embedded\t#{}",
191      response.implementation_version
192    ))
193  }
194}