sass_rs/bindings/
context.rs

1//! Allow access to the various flavours of sass contexts:
2//! <https://github.com/sass/libsass/wiki/API-Sass-Context>
3
4use std::ffi;
5use std::sync::{Arc, RwLock};
6use std::path::Path;
7use libc::strdup;
8
9use sass_sys;
10use bindings::util;
11use bindings::ptr::Unique;
12use options::{OutputStyle, Options};
13use bindings::SassOptions;
14
15
16pub struct SassContext {
17    // Need Unique to send between threads, libsass is thread safe
18    pub raw: Unique<sass_sys::Sass_Context>,
19    pub options: Arc<RwLock<SassOptions>>
20}
21
22pub struct SassFileContext {
23    // Need Unique to send between threads, libsass is thread safe
24    context: Unique<sass_sys::Sass_File_Context>,
25    pub sass_context: SassContext
26}
27
28pub struct SassDataContext {
29    // Need Unique to send between threads, libsass is thread safe
30    context: Unique<sass_sys::Sass_Data_Context>,
31    pub sass_context: SassContext
32}
33
34pub enum Context {
35    File(SassFileContext),
36    Data(SassDataContext),
37}
38
39
40impl Context {
41    fn make_sass_context(c_sass_context: *mut sass_sys::Sass_Context) -> SassContext {;
42        let options = unsafe { sass_sys::sass_context_get_options(c_sass_context) };
43        let sass_options = Arc::new(RwLock::new(SassOptions {
44            raw: unsafe { Unique::new(options) }
45        }));
46
47        SassContext {
48            raw: unsafe { Unique::new(c_sass_context) },
49            options: sass_options
50        }
51    }
52
53    pub fn new_data(data: &str) -> Context {
54        let c_str = ffi::CString::new(data).unwrap();
55        let data_context = unsafe { sass_sys::sass_make_data_context(strdup(c_str.as_ptr())) };
56        let data_sass_context = unsafe { sass_sys::sass_data_context_get_context(data_context) };
57        let sass_context = Context::make_sass_context(data_sass_context);
58
59        Context::Data(SassDataContext {
60            context: unsafe { Unique::new(data_context) },
61            sass_context,
62        })
63    }
64
65    pub fn new_file<P: AsRef<Path>>(path: P) -> Result<Context, String> {
66        let c_str = ffi::CString::new(
67            path.as_ref().to_str().ok_or_else(|| "str conversation failed".to_string())?
68        ).map_err(|e| format!("Failed to create CString: {}", e))?;
69        let file_context = unsafe { sass_sys::sass_make_file_context(c_str.as_ptr()) };
70        let file_sass_context = unsafe { sass_sys::sass_file_context_get_context(file_context) };
71        let sass_context = Context::make_sass_context(file_sass_context);
72
73        Ok(Context::File(SassFileContext {
74            context: unsafe { Unique::new(file_context) },
75            sass_context,
76        }))
77    }
78
79    pub fn set_options(&mut self, options: Options) {
80        let mut sass_options = match *self {
81            Context::File(ref mut s) => {
82                (*s.sass_context.options).write().unwrap()
83            },
84            Context::Data(ref mut s) => {
85                (*s.sass_context.options).write().unwrap()
86            },
87        };
88        sass_options.set_output_style(options.output_style);
89        sass_options.set_precision(options.precision);
90        if options.indented_syntax {
91            sass_options.set_is_indented_syntax();
92        }
93        if !options.include_paths.is_empty() {
94            sass_options.set_include_path(options.include_paths);
95        }
96    }
97
98    pub fn compile(&mut self) -> Result<String, String> {
99        let ctx_out = match *self {
100            Context::File(ref mut s) => unsafe {
101                sass_sys::sass_compile_file_context(s.context.get_mut());
102                s.sass_context.raw.get_mut()
103            },
104            Context::Data(ref mut s) => unsafe {
105                sass_sys::sass_compile_data_context(s.context.get_mut());
106                s.sass_context.raw.get_mut()
107            },
108        };
109
110        let error_status = unsafe { sass_sys::sass_context_get_error_status(ctx_out) };
111        let error_message = unsafe { sass_sys::sass_context_get_error_message(ctx_out) };
112        let output_string = unsafe { sass_sys::sass_context_get_output_string(ctx_out) };
113
114        if error_status != 0 {
115            if !error_message.is_null() {
116                Result::Err(util::to_string(error_message))
117            } else {
118                Result::Err("An error occurred; no error message available.".to_string())
119            }
120        } else {
121            Result::Ok(util::to_string(output_string))
122        }
123    }
124
125    pub fn set_output_style(&mut self, output_style: OutputStyle) {
126        match *self {
127            Context::File(ref mut s) => {
128                let mut options = (*s.sass_context.options).write().unwrap();
129                options.set_output_style(output_style);
130            },
131            Context::Data(ref mut s) => {
132                let mut options = (*s.sass_context.options).write().unwrap();
133                options.set_output_style(output_style);
134            },
135        };
136    }
137}
138
139impl Drop for Context {
140    fn drop(&mut self) {
141        match *self {
142            Context::File(ref mut s) => unsafe {
143                sass_sys::sass_delete_file_context(s.context.get_mut())
144            },
145            Context::Data(ref mut s) => unsafe {
146                sass_sys::sass_delete_data_context(s.context.get_mut())
147            },
148        };
149    }
150}