sass_embedded/
embedded.rs1use 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#[derive(Debug)]
25pub struct Embedded {
26  channel: Channel,
27}
28
29impl Embedded {
30  pub fn new(exe_path: impl AsRef<OsStr>) -> Result<Self> {
36    Ok(Self {
37      channel: Channel::new(exe_path)?,
38    })
39  }
40
41  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      ..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  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      ..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  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}