float_pigment_css/
ffi.rs

1#![doc(hidden)]
2#![cfg(feature = "ffi")]
3
4use alloc::{
5    boxed::Box,
6    ffi::CString,
7    string::{String, ToString},
8    vec::Vec,
9};
10use bit_set::BitSet;
11use core::ffi::{c_char, CStr};
12use core::ptr::{null, null_mut};
13use hashbrown::HashMap;
14
15use crate::parser::parse_color_to_rgba;
16use crate::property::{Property, PropertyMeta};
17use crate::sheet::borrow::{InlineRule, Selector};
18use crate::typing::ImportantBitSet;
19
20use crate::group;
21use crate::parser;
22use crate::parser::property_value::var::{
23    CustomPropertyContext, CustomPropertyGetter, CustomPropertySetter,
24};
25use crate::sheet;
26use group::drop_css_extension;
27use group::StyleSheetImportIndex as StyleSheetImportIndexImpl;
28use parser::Warning;
29use sheet::borrow::{Array, StyleSheet};
30use sheet::str_store::StrRef;
31
32#[cfg(feature = "deserialize")]
33use sheet::borrow::de_static_ref_zero_copy_env;
34
35#[macro_export]
36macro_rules! check_null {
37    ($arg:expr, $error:expr, $default:expr) => {
38        if $arg.is_null() {
39            return FfiResult::error($error, $default);
40        }
41    };
42}
43
44#[macro_export]
45macro_rules! raw_ptr_as_mut_ref {
46    ($from:expr, $type:ty) => {
47        &mut *($from as *mut $type)
48    };
49}
50
51pub type RawMutPtr = *mut ();
52
53pub type NullPtr = *const ();
54
55#[repr(C)]
56pub enum FfiErrorCode {
57    None,
58    ThisNullPointer,
59    PathNullPointer,
60    PrefixNullPointer,
61    SourceNullPointer,
62    BufferNullPointer,
63    ExprPtrNullPointer,
64    StrNullPointer,
65    InlineStyleTextNullPointer,
66    InlineRuleNullPointer,
67    StyleTextNullPointer,
68    SelectorTextNullPointer,
69    InvalidPath,
70    JsonNullPointer,
71    ArrayNullPointer,
72    SelectorNullPointer,
73    StyleSheetNullPointer,
74    MapNullPointer,
75    Unknown,
76}
77
78#[repr(C)]
79pub struct FfiResult<T> {
80    pub value: T,
81    pub err: FfiErrorCode,
82}
83
84impl<T> FfiResult<T> {
85    pub fn ok(value: T) -> Self {
86        Self {
87            value,
88            err: FfiErrorCode::None,
89        }
90    }
91    pub fn error(err: FfiErrorCode, default: T) -> Self {
92        Self {
93            value: default,
94            err,
95        }
96    }
97}
98
99/// # Safety
100///
101/// Create a new style sheet resource.
102///
103/// # Arguments
104/// * `this` - A raw pointer to a [`StyleSheetResource`] instance
105/// * `path` - C string pointer to the style sheet path (UTF-8 encoded)
106/// * `source` - C string pointer to the CSS source content (UTF-8 encoded)
107/// * `warnings` - Optional output parameter to receive warnings array pointer
108///
109/// # Examples
110///
111/// ```c
112/// FfiResult result = FPStyleSheetResourceNew();
113/// if (result.err != FfiErrorCode::None) {
114///     // handle error
115/// }
116/// RawMutPtr resource = result.value;
117/// ```
118///
119#[export_name = "FPStyleSheetResourceNew"]
120pub unsafe extern "C" fn style_sheet_resource_new() -> FfiResult<RawMutPtr> {
121    FfiResult::ok(Box::into_raw(Box::new(group::StyleSheetResource::new())) as RawMutPtr)
122}
123
124/// # Safety
125///
126/// Free the style sheet resource.
127///
128/// # Arguments
129/// * `this` - A raw pointer to a [`StyleSheetResource`] instance
130///
131/// # Examples
132///
133/// ```c
134/// FfiResult result = FPStyleSheetResourceFree(resource);
135/// if (result.err != FfiErrorCode::None) {
136///     // handle error
137/// }
138/// ```
139///
140#[export_name = "FPStyleSheetResourceFree"]
141pub unsafe extern "C" fn style_sheet_resource_free(this: RawMutPtr) -> FfiResult<NullPtr> {
142    check_null!(this, FfiErrorCode::ThisNullPointer, null());
143    drop(Box::from_raw(this as *mut group::StyleSheetResource));
144    FfiResult::ok(null())
145}
146
147/// # Safety
148/// Add a tag name prefix to the resource.
149///
150/// # Arguments
151/// * `this` - A raw pointer to a [`StyleSheetResource`] instance
152/// * `path` - C string pointer to the stylesheet path (UTF-8 encoded)
153/// * `prefix` - C string pointer to the prefix to add to the tag name (UTF-8 encoded)
154///
155/// # Examples
156///
157/// ```c
158/// FfiResult result = FPStyleSheetResourceAddTagNamePrefix(resource, path, prefix);
159/// if (result.err != FfiErrorCode::None) {
160///     // handle error
161/// }
162/// ```
163///
164#[export_name = "FPStyleSheetResourceAddTagNamePrefix"]
165pub unsafe extern "C" fn style_sheet_resource_add_tag_name_prefix(
166    this: RawMutPtr,
167    path: *const c_char,
168    prefix: *const c_char,
169) -> FfiResult<NullPtr> {
170    check_null!(this, FfiErrorCode::ThisNullPointer, null());
171    check_null!(path, FfiErrorCode::PathNullPointer, null());
172    check_null!(prefix, FfiErrorCode::PrefixNullPointer, null());
173    let res = raw_ptr_as_mut_ref!(this, group::StyleSheetResource);
174    let path = CStr::from_ptr(path).to_string_lossy();
175    let prefix = CStr::from_ptr(prefix).to_string_lossy();
176    res.add_tag_name_prefix(&path, &prefix);
177    FfiResult::ok(null())
178}
179
180/// # Safety
181/// Serialize the specified style sheet to the JSON format.
182///
183/// # Arguments
184/// * `this` - A raw pointer to a [`StyleSheetResource`] instance
185/// * `path` - C string pointer to the stylesheet path (UTF-8 encoded)
186/// * `ret_buffer_len` - Pointer to a variable to store the length of the serialized data
187///
188/// # Examples
189///
190/// ```c
191/// FfiResult result = FPStyleSheetResourceSerializeJson(resource, path, &mut buffer_len);
192/// if (result.err != FfiErrorCode::None) {
193///     // handle error
194/// }
195/// ```
196///
197#[cfg(all(feature = "serialize", feature = "serialize_json"))]
198#[export_name = "FPStyleSheetResourceSerializeJson"]
199pub unsafe extern "C" fn style_sheet_resource_serialize_json(
200    this: RawMutPtr,
201    path: *const c_char,
202    ret_buffer_len: &mut usize,
203) -> FfiResult<*mut u8> {
204    check_null!(this, FfiErrorCode::ThisNullPointer, null_mut());
205    check_null!(path, FfiErrorCode::PathNullPointer, null_mut());
206    let res = raw_ptr_as_mut_ref!(this, group::StyleSheetResource);
207    let path = CStr::from_ptr(path).to_string_lossy();
208    let serial = res.serialize_json(&path).unwrap_or_default();
209    *ret_buffer_len = serial.len();
210    let ret = Box::into_raw(serial.into_boxed_str());
211    FfiResult::ok(ret as *mut u8)
212}
213
214/// # Safety
215///
216/// Serialize the specified style sheet to the binary format.
217///
218/// # Arguments
219/// * `this` - A raw pointer to a [`StyleSheetResource`] instance
220/// * `path` - C string pointer to the stylesheet path (UTF-8 encoded)
221/// * `ret_buffer_len` - Pointer to a variable to store the length of the serialized data
222///
223/// # Examples
224///
225/// ```c
226/// FfiResult result = FPStyleSheetResourceSerializeBincode(resource, path, &mut buffer_len);
227/// if (result.err != FfiErrorCode::None) {
228///     // handle error
229/// }
230/// ```
231///
232#[cfg(feature = "serialize")]
233#[export_name = "FPStyleSheetResourceSerializeBincode"]
234pub unsafe extern "C" fn style_sheet_resource_serialize_bincode(
235    this: RawMutPtr,
236    path: *const c_char,
237    ret_buffer_len: &mut usize,
238) -> FfiResult<*mut u8> {
239    check_null!(this, FfiErrorCode::ThisNullPointer, null_mut());
240    check_null!(path, FfiErrorCode::PathNullPointer, null_mut());
241    let res = raw_ptr_as_mut_ref!(this, group::StyleSheetResource);
242    let path = CStr::from_ptr(path).to_string_lossy();
243    let serial = res.serialize_bincode(&path).unwrap_or_default();
244    *ret_buffer_len = serial.len();
245    let ret = Box::into_raw(serial.into_boxed_slice());
246    FfiResult::ok(ret as *mut u8)
247}
248/// # Safety
249///
250/// Add a style sheet to the resource manager.
251///
252/// # Arguments
253/// * `this` - A raw pointer to a [`StyleSheetResource`] instance
254/// * `path` - C string pointer to the style sheet path (UTF-8 encoded)
255/// * `source` - C string pointer to the CSS source content (UTF-8 encoded)
256/// * `warnings` - Optional output parameter to receive warnings array pointer
257///
258/// # Examples
259///
260/// ```c
261/// FPStyleSheetResourceAddSource(resource, path, source, &mut warnings);
262/// ```
263///
264#[export_name = "FPStyleSheetResourceAddSource"]
265pub unsafe extern "C" fn style_sheet_resource_add_source(
266    this: RawMutPtr,
267    path: *const c_char,
268    source: *const c_char,
269    warnings: *mut *mut Array<Warning>,
270) -> FfiResult<NullPtr> {
271    check_null!(this, FfiErrorCode::ThisNullPointer, null());
272    check_null!(path, FfiErrorCode::PathNullPointer, null());
273    check_null!(source, FfiErrorCode::SourceNullPointer, null());
274    let res = raw_ptr_as_mut_ref!(this, group::StyleSheetResource);
275    let path = CStr::from_ptr(path).to_string_lossy();
276    let source = CStr::from_ptr(source).to_string_lossy();
277    let w = res.add_source(&path, &source);
278    if !warnings.is_null() {
279        *warnings = Box::into_raw(Box::new(w.into()));
280    }
281    FfiResult::ok(null())
282}
283
284/// # Safety
285///
286/// Add a style sheet to the resource manager with hooks.
287///
288/// # Arguments
289/// * `this` - A raw pointer to a [`StyleSheetResource`] instance
290/// * `hooks` - A parser hooks
291/// * `path` - C string pointer to the style sheet path (UTF-8 encoded)
292/// * `source` - C string pointer to the CSS source content (UTF-8 encoded)
293/// * `warnings` - Optional output parameter to receive warnings array pointer
294///
295/// # Examples
296///
297/// ```c
298/// FPStyleSheetResourceAddSourceWithHooks(resource, hooks, path, source, &mut warnings);
299/// ```
300///
301#[export_name = "FPStyleSheetResourceAddSourceWithHooks"]
302pub unsafe extern "C" fn style_sheet_resource_add_source_with_hooks(
303    this: RawMutPtr,
304    hooks: parser::hooks::CParserHooks,
305    path: *const c_char,
306    source: *const c_char,
307    warnings: *mut *mut Array<Warning>,
308) -> FfiResult<NullPtr> {
309    check_null!(this, FfiErrorCode::ThisNullPointer, null());
310    check_null!(path, FfiErrorCode::PathNullPointer, null());
311    check_null!(source, FfiErrorCode::SourceNullPointer, null());
312    let res = raw_ptr_as_mut_ref!(this, group::StyleSheetResource);
313    let path = CStr::from_ptr(path).to_string_lossy();
314    let source = CStr::from_ptr(source).to_string_lossy();
315    let w = res.add_source_with_hooks(&path, &source, Some(Box::new(hooks)));
316    if !warnings.is_null() {
317        *warnings = Box::into_raw(Box::new(w.into()));
318    }
319    FfiResult::ok(null())
320}
321
322/// # Safety
323/// Add a style sheet to the resource manager from binary format.
324///
325/// # Arguments
326/// * `this` - A raw pointer to a [`StyleSheetResource`] instance
327/// * `path` - C string pointer to the stylesheet path (UTF-8 encoded)
328/// * `buffer_ptr` - Pointer to the buffer to store the serialized data
329/// * `buffer_len` - Length of the buffer
330/// * `drop_fn` - Optional drop function
331/// * `drop_args` - Pointer to the drop argument
332/// * `warnings` - Optional output parameter to receive warnings array pointer
333///
334/// # Examples
335///
336/// ```c
337/// FPStyleSheetResourceAddBincode(resource, path, buffer_ptr, buffer_len, drop_fn, drop_args, &mut warnings);
338/// ```
339///
340#[cfg(feature = "deserialize")]
341#[export_name = "FPStyleSheetResourceAddBincode"]
342pub unsafe extern "C" fn style_sheet_resource_add_bincode(
343    this: RawMutPtr,
344    path: *const c_char,
345    buffer_ptr: *mut u8,
346    buffer_len: usize,
347    drop_fn: Option<unsafe extern "C" fn(RawMutPtr)>,
348    drop_args: RawMutPtr,
349    warnings: *mut *mut Array<Warning>,
350) -> FfiResult<NullPtr> {
351    check_null!(this, FfiErrorCode::ThisNullPointer, null());
352    check_null!(path, FfiErrorCode::PathNullPointer, null());
353    check_null!(buffer_ptr, FfiErrorCode::BufferNullPointer, null());
354    let res = raw_ptr_as_mut_ref!(this, group::StyleSheetResource);
355    let bincode: *mut [u8] = core::slice::from_raw_parts_mut(buffer_ptr, buffer_len);
356    let path = CStr::from_ptr(path).to_string_lossy();
357    let w = res.add_bincode_zero_copy(&path, bincode, move || {
358        if let Some(drop_fn) = drop_fn {
359            drop_fn(drop_args);
360        }
361    });
362    if !warnings.is_null() {
363        *warnings = Box::into_raw(Box::new(w.into()));
364    }
365    FfiResult::ok(null())
366}
367
368/// # Safety
369/// Get the direct dependencies of the specified style sheet.
370///
371/// # Arguments
372/// * `this` - A raw pointer to a [`StyleSheetResource`] instance
373/// * `path` - C string pointer to the stylesheet path (UTF-8 encoded)
374///
375/// # Examples
376///
377/// ```c
378/// FPStyleSheetResourceDirectDependencies(resource, path);
379/// ```
380///
381#[export_name = "FPStyleSheetResourceDirectDependencies"]
382pub unsafe extern "C" fn style_sheet_resource_direct_dependencies(
383    this: RawMutPtr,
384    path: *const c_char,
385) -> FfiResult<*mut Array<StrRef>> {
386    check_null!(this, FfiErrorCode::ThisNullPointer, null_mut());
387    check_null!(path, FfiErrorCode::PathNullPointer, null_mut());
388    let res = raw_ptr_as_mut_ref!(this, group::StyleSheetResource);
389    let path = CStr::from_ptr(path).to_string_lossy();
390    let deps = res.direct_dependencies(&path);
391    let deps: Vec<_> = deps.into_iter().map(StrRef::from).collect();
392    FfiResult::ok(Box::into_raw(Box::new(deps.into())))
393}
394
395/// # Safety
396///
397/// Generate the import index of the resource.
398///
399/// # Arguments
400/// * `this` - A raw pointer to a [`StyleSheetResource`] instance
401///
402/// # Examples
403///
404/// ```c
405/// FPStyleSheetResourceGenerateImportIndex(resource);
406/// ```
407///
408#[export_name = "FPStyleSheetResourceGenerateImportIndex"]
409pub unsafe extern "C" fn style_sheet_resource_generate_import_index(
410    this: RawMutPtr,
411) -> FfiResult<RawMutPtr> {
412    check_null!(this, FfiErrorCode::ThisNullPointer, null_mut());
413    let res = raw_ptr_as_mut_ref!(this, group::StyleSheetResource);
414    FfiResult::ok(
415        StyleSheetImportIndex {
416            inner: res.generate_import_indexes(),
417            map: StyleSheetMap::default(),
418        }
419        .into_raw(),
420    )
421}
422
423type StyleSheetMap = HashMap<String, StyleSheet>;
424
425struct StyleSheetImportIndex {
426    inner: StyleSheetImportIndexImpl,
427    map: StyleSheetMap,
428}
429
430impl StyleSheetImportIndex {
431    fn into_raw(self) -> RawMutPtr {
432        Box::into_raw(Box::new(self)) as RawMutPtr
433    }
434}
435
436/// # Safety
437/// Create a new style sheet import index.
438///
439/// # Examples
440///
441/// ```c
442/// RawMutPtr import_index = FPStyleSheetImportIndexNew();
443/// ```
444///
445#[export_name = "FPStyleSheetImportIndexNew"]
446pub unsafe extern "C" fn style_sheet_import_index_new() -> FfiResult<RawMutPtr> {
447    FfiResult::ok(
448        StyleSheetImportIndex {
449            inner: StyleSheetImportIndexImpl::new(),
450            map: StyleSheetMap::default(),
451        }
452        .into_raw(),
453    )
454}
455
456/// # Safety
457/// Free the style sheet import index.
458///
459/// # Arguments
460/// * `this` - A raw pointer to a [`StyleSheetImportIndex`] instance
461///
462/// # Examples
463///
464/// ```c
465/// FPStyleSheetImportIndexFree(import_index);
466/// ```
467///
468#[export_name = "FPStyleSheetImportIndexFree"]
469pub unsafe extern "C" fn style_sheet_import_index_free(this: RawMutPtr) -> FfiResult<NullPtr> {
470    check_null!(this, FfiErrorCode::ThisNullPointer, null());
471    drop(Box::from_raw(this as *mut StyleSheetImportIndex));
472    FfiResult::ok(null())
473}
474
475/// # Safety
476/// Query and mark the dependencies of the specified style sheet.
477///
478/// # Arguments
479/// * `this` - A raw pointer to a [`StyleSheetImportIndex`] instance
480/// * `path` - C string pointer to the style sheet path (UTF-8 encoded)
481///
482/// # Examples
483///
484/// ```c
485/// FPStyleSheetImportIndexQueryAndMarkDependencies(import_index, path);
486/// ```
487///
488#[export_name = "FPStyleSheetImportIndexQueryAndMarkDependencies"]
489pub unsafe extern "C" fn style_sheet_import_index_query_and_mark_dependencies(
490    this: RawMutPtr,
491    path: *const c_char,
492) -> FfiResult<*mut Array<StrRef>> {
493    check_null!(this, FfiErrorCode::ThisNullPointer, null_mut());
494    check_null!(path, FfiErrorCode::PathNullPointer, null_mut());
495    let style_sheet_import_index = raw_ptr_as_mut_ref!(this, StyleSheetImportIndex);
496    let path = CStr::from_ptr(path).to_string_lossy();
497    let deps = style_sheet_import_index
498        .inner
499        .query_and_mark_dependencies(&path);
500    let deps: Vec<_> = deps.into_iter().map(StrRef::from).collect();
501    FfiResult::ok(Box::into_raw(Box::new(deps.into())))
502}
503
504/// # Safety
505/// List the dependencies of the specified style sheet.
506///
507/// # Arguments
508/// * `this` - A raw pointer to a [`StyleSheetImportIndex`] instance
509/// * `path` - C string pointer to the style sheet path (UTF-8 encoded)
510///
511/// # Examples
512///
513/// ```c
514/// FPStyleSheetImportIndexListDependencies(import_index, path);
515/// ```
516///
517#[export_name = "FPStyleSheetImportIndexListDependencies"]
518pub unsafe extern "C" fn style_sheet_import_index_list_dependencies(
519    this: RawMutPtr,
520    path: *const c_char,
521) -> FfiResult<*mut Array<StrRef>> {
522    check_null!(this, FfiErrorCode::ThisNullPointer, null_mut());
523    check_null!(path, FfiErrorCode::PathNullPointer, null_mut());
524    let style_sheet_import_index = raw_ptr_as_mut_ref!(this, StyleSheetImportIndex);
525    let path = CStr::from_ptr(path).to_string_lossy();
526    let deps = style_sheet_import_index
527        .inner
528        .list_dependencies(&path, true);
529    let deps: Vec<_> = deps.into_iter().map(StrRef::from).collect();
530    FfiResult::ok(Box::into_raw(Box::new(deps.into())))
531}
532
533/// # Safety
534/// List the dependency of the specified style sheet.
535///
536/// # Arguments
537/// * `this` - A raw pointer to a [`StyleSheetImportIndex`] instance
538/// * `path` - C string pointer to the style sheet path (UTF-8 encoded)
539///
540/// # Examples
541///
542/// ```c
543/// FPStyleSheetImportIndexListDependency(import_index, path);
544/// ```
545///
546#[export_name = "FPStyleSheetImportIndexListDependency"]
547pub unsafe extern "C" fn style_sheet_import_index_list_dependency(
548    this: RawMutPtr,
549    path: *const c_char,
550) -> FfiResult<*mut Array<StrRef>> {
551    check_null!(this, FfiErrorCode::ThisNullPointer, null_mut());
552    check_null!(path, FfiErrorCode::PathNullPointer, null_mut());
553    let style_sheet_import_index = raw_ptr_as_mut_ref!(this, StyleSheetImportIndex);
554    let path = CStr::from_ptr(path).to_string_lossy();
555    let deps = style_sheet_import_index
556        .inner
557        .list_dependencies(&path, false);
558    let deps: Vec<_> = deps.into_iter().map(StrRef::from).collect();
559    FfiResult::ok(Box::into_raw(Box::new(deps.into())))
560}
561
562/// # Safety
563/// Add a style sheet to the import index from binary format.
564///
565/// # Arguments
566/// * `this` - A raw pointer to a [`StyleSheetImportIndex`] instance
567/// * `path` - C string pointer to the style sheet path (UTF-8 encoded)
568/// * `buffer_ptr` - Pointer to the buffer to store the serialized data
569/// * `buffer_len` - Length of the buffer
570/// * `drop_fn` - Optional drop function
571/// * `drop_args` - Pointer to the drop argument
572/// * `warnings` - Optional output parameter to receive warnings array pointer
573///
574/// # Examples
575///
576/// ```c
577/// FPStyleSheetImportIndexAddBincode(import_index, path, buffer_ptr, buffer_len, drop_fn, drop_args, &mut warnings);
578/// ```
579///
580#[cfg(feature = "deserialize")]
581#[export_name = "FPStyleSheetImportIndexAddBincode"]
582pub unsafe extern "C" fn style_sheet_import_index_add_bincode(
583    this: RawMutPtr,
584    path: *const c_char,
585    buffer_ptr: *mut u8,
586    buffer_len: usize,
587    drop_fn: Option<unsafe extern "C" fn(RawMutPtr)>,
588    drop_args: RawMutPtr,
589    warnings: *mut *mut Array<Warning>,
590) -> FfiResult<NullPtr> {
591    use float_pigment_consistent_bincode::Options;
592    use parser::WarningKind;
593    check_null!(this, FfiErrorCode::ThisNullPointer, null_mut());
594    check_null!(path, FfiErrorCode::PathNullPointer, null_mut());
595    check_null!(buffer_ptr, FfiErrorCode::BufferNullPointer, null_mut());
596    let path = CStr::from_ptr(path).to_string_lossy();
597    let sheet = de_static_ref_zero_copy_env(
598        core::slice::from_raw_parts_mut(buffer_ptr, buffer_len),
599        |s| {
600            let s: Result<StyleSheet, _> = float_pigment_consistent_bincode::DefaultOptions::new()
601                .allow_trailing_bytes()
602                .deserialize(s);
603            match s {
604                Ok(ss) => ss,
605                Err(err) => {
606                    let w = vec![Warning {
607                        kind: WarningKind::DeserializationFailed,
608                        message: format!(
609                            "failed to deserialize bincode formatted style sheet: {err}"
610                        )
611                        .into(),
612                        start_line: 0,
613                        start_col: 0,
614                        end_line: 0,
615                        end_col: 0,
616                    }];
617                    if !warnings.is_null() {
618                        *warnings = Box::into_raw(Box::new(w.into()));
619                    }
620                    StyleSheet::from_sheet(&sheet::CompiledStyleSheet::new())
621                }
622            }
623        },
624        move || {
625            if let Some(drop_fn) = drop_fn {
626                drop_fn(drop_args);
627            }
628        },
629    );
630    let path = drop_css_extension(&path).into();
631    let style_sheet_import_index = raw_ptr_as_mut_ref!(this, StyleSheetImportIndex);
632    style_sheet_import_index.map.insert(path, sheet);
633    FfiResult::ok(null())
634}
635
636/// # Safety
637/// Remove a style sheet from the style sheet import index.
638///
639/// # Arguments
640/// * `this` - A raw pointer to a [`StyleSheetImportIndex`] instance
641/// * `path` - C string pointer to the style sheet path (UTF-8 encoded)
642///
643/// # Examples
644///
645/// ```c
646/// FPStyleSheetImportIndexRemoveBincode(import_index, path);
647/// ```
648///
649#[export_name = "FPStyleSheetImportIndexRemoveBincode"]
650pub unsafe extern "C" fn style_sheet_import_index_remove_bincode(
651    this: RawMutPtr,
652    path: *const c_char,
653) -> FfiResult<NullPtr> {
654    check_null!(this, FfiErrorCode::ThisNullPointer, null());
655    check_null!(path, FfiErrorCode::PathNullPointer, null());
656    let path = CStr::from_ptr(path).to_string_lossy();
657    let path = drop_css_extension(&path);
658    let style_sheet_import_index = raw_ptr_as_mut_ref!(this, StyleSheetImportIndex);
659    style_sheet_import_index.map.remove(path);
660    FfiResult::ok(null())
661}
662/// # Safety
663/// Get the style sheet from the style sheet import index.
664///
665/// # Arguments
666/// * `this` - A raw pointer to a [`StyleSheetImportIndex`] instance
667/// * `path` - C string pointer to the style sheet path (UTF-8 encoded)
668///
669/// # Examples
670///
671/// ```c
672/// FPStyleSheetImportIndexGetStyleSheet(import_index, path);
673/// ```
674///
675#[export_name = "FPStyleSheetImportIndexGetStyleSheet"]
676pub unsafe extern "C" fn style_sheet_import_index_get_style_sheet(
677    this: RawMutPtr,
678    path: *const c_char,
679) -> FfiResult<*mut StyleSheet> {
680    check_null!(this, FfiErrorCode::ThisNullPointer, null_mut());
681    check_null!(path, FfiErrorCode::PathNullPointer, null_mut());
682    let path = CStr::from_ptr(path).to_string_lossy();
683    let path = drop_css_extension(&path);
684    let style_sheet_import_index = raw_ptr_as_mut_ref!(this, StyleSheetImportIndex);
685    match style_sheet_import_index.map.get_mut(path) {
686        None => FfiResult::error(FfiErrorCode::InvalidPath, null_mut()),
687        Some(x) => FfiResult::ok(x as *mut StyleSheet),
688    }
689}
690/// # Safety
691/// Serialize the style sheet import index to the JSON format.
692///
693/// # Arguments
694/// * `this` - A raw pointer to a [`StyleSheetImportIndex`] instance
695/// * `ret_buffer_len` - Pointer to a variable to store the length of the serialized data
696///
697/// # Examples
698///
699/// ```c
700/// FPStyleSheetImportIndexSerializeJson(import_index, &buffer_len);
701/// ```
702///
703#[cfg(all(feature = "serialize", feature = "serialize_json"))]
704#[export_name = "FPStyleSheetImportIndexSerializeJson"]
705pub unsafe extern "C" fn style_sheet_import_index_serialize_json(
706    this: RawMutPtr,
707    ret_buffer_len: &mut usize,
708) -> FfiResult<*mut u8> {
709    check_null!(this, FfiErrorCode::ThisNullPointer, null_mut());
710    let style_sheet_import_index = raw_ptr_as_mut_ref!(this, StyleSheetImportIndex);
711    let serial = style_sheet_import_index.inner.serialize_json();
712    *ret_buffer_len = serial.len();
713    let ret = Box::into_raw(serial.into_boxed_str());
714    FfiResult::ok(ret as *mut u8)
715}
716/// # Safety
717/// Serialize the style sheet import index to the binary format.
718///
719/// # Arguments
720/// * `this` - A raw pointer to a [`StyleSheetImportIndex`] instance
721/// * `ret_buffer_len` - Pointer to a variable to store the length of the serialized data
722///
723/// # Examples
724///
725/// ```c
726/// FPStyleSheetImportIndexSerializeBincode(import_index, &buffer_len);
727/// ```
728///
729#[cfg(feature = "serialize")]
730#[export_name = "FPStyleSheetImportIndexSerializeBincode"]
731pub unsafe extern "C" fn style_sheet_import_index_serialize_bincode(
732    this: RawMutPtr,
733    ret_buffer_len: &mut usize,
734) -> FfiResult<*mut u8> {
735    check_null!(this, FfiErrorCode::ThisNullPointer, null_mut());
736    let style_sheet_import_index = raw_ptr_as_mut_ref!(this, StyleSheetImportIndex);
737    let serial = style_sheet_import_index.inner.serialize_bincode();
738    *ret_buffer_len = serial.len();
739    let ret = Box::into_raw(serial.into_boxed_slice());
740    FfiResult::ok(ret as *mut u8)
741}
742/// # Safety
743/// Deserialize the style sheet import index from the JSON format.
744///
745/// # Arguments
746/// * `json` - C string pointer to the JSON data
747///
748/// # Examples
749///
750/// ```c
751/// FPStyleSheetImportIndexDeserializeJson(json, &import_index);
752/// ```
753///
754#[cfg(all(feature = "deserialize", feature = "deserialize_json"))]
755#[export_name = "FPStyleSheetImportIndexDeserializeJson"]
756pub unsafe extern "C" fn style_sheet_import_index_deserialize_json(
757    json: *const c_char,
758) -> FfiResult<RawMutPtr> {
759    check_null!(json, FfiErrorCode::JsonNullPointer, null_mut());
760    let json = CStr::from_ptr(json).to_string_lossy();
761    FfiResult::ok(
762        StyleSheetImportIndex {
763            inner: StyleSheetImportIndexImpl::deserialize_json(&json),
764            map: StyleSheetMap::default(),
765        }
766        .into_raw(),
767    )
768}
769/// # Safety
770/// Deserialize the style sheet import index from the binary format.
771///
772/// # Arguments
773/// * `buffer_ptr` - Pointer to the binary data
774/// * `buffer_len` - Length of the binary data
775/// * `drop_fn` - Optional drop function
776/// * `drop_args` - Pointer to the drop argument
777///
778/// # Examples
779///
780/// ```c
781/// FPStyleSheetImportIndexDeserializeBincode(buffer_ptr, buffer_len, drop_fn, drop_args);
782/// ```
783///
784#[cfg(feature = "deserialize")]
785#[export_name = "FPStyleSheetImportIndexDeserializeBincode"]
786pub unsafe extern "C" fn style_sheet_import_index_deserialize_bincode(
787    buffer_ptr: *mut u8,
788    buffer_len: usize,
789    drop_fn: Option<unsafe extern "C" fn(RawMutPtr)>,
790    drop_args: RawMutPtr,
791) -> FfiResult<RawMutPtr> {
792    check_null!(buffer_ptr, FfiErrorCode::BufferNullPointer, null_mut());
793    let bincode: *mut [u8] = core::slice::from_raw_parts_mut(buffer_ptr, buffer_len);
794    FfiResult::ok(
795        StyleSheetImportIndex {
796            inner: StyleSheetImportIndexImpl::deserialize_bincode_zero_copy(bincode, move || {
797                if let Some(drop_fn) = drop_fn {
798                    drop_fn(drop_args);
799                }
800            }),
801            map: StyleSheetMap::default(),
802        }
803        .into_raw(),
804    )
805}
806/// # Safety
807/// Merge the style sheet import index from binary format.
808///
809/// # Arguments
810/// * `this` - A raw pointer to a [`StyleSheetImportIndex`] instance
811/// * `buffer_ptr` - Pointer to the binary data
812/// * `buffer_len` - Length of the binary data
813/// * `drop_fn` - Optional drop function
814/// * `drop_args` - Pointer to the drop argument
815///
816/// # Examples
817///
818/// ```c
819/// FPStyleSheetImportIndexMergeBincode(import_index, buffer_ptr, buffer_len, drop_fn, drop_args);
820/// ```
821///
822#[cfg(feature = "deserialize")]
823#[export_name = "FPStyleSheetImportIndexMergeBincode"]
824pub unsafe extern "C" fn style_sheet_import_index_merge_bincode(
825    this: RawMutPtr,
826    buffer_ptr: *mut u8,
827    buffer_len: usize,
828    drop_fn: Option<unsafe extern "C" fn(*mut ())>,
829    drop_args: *mut (),
830) -> FfiResult<NullPtr> {
831    check_null!(this, FfiErrorCode::ThisNullPointer, null());
832    check_null!(buffer_ptr, FfiErrorCode::BufferNullPointer, null());
833    let style_sheet_import_index = raw_ptr_as_mut_ref!(this, StyleSheetImportIndex);
834    let bincode: *mut [u8] = core::slice::from_raw_parts_mut(buffer_ptr, buffer_len);
835    style_sheet_import_index
836        .inner
837        .merge_bincode_zero_copy(bincode, move || {
838            if let Some(drop_fn) = drop_fn {
839                drop_fn(drop_args);
840            }
841        });
842    FfiResult::ok(null())
843}
844
845/// # Safety
846/// Free the buffer.
847///
848/// # Arguments
849/// * `buffer_ptr` - Pointer to the buffer
850/// * `buffer_len` - Length of the buffer
851///
852/// # Examples
853///
854/// ```c
855/// FPBufferFree(buffer_ptr, buffer_len);
856/// ```
857///
858#[export_name = "FPBufferFree"]
859pub unsafe extern "C" fn buffer_free(buffer_ptr: *mut u8, buffer_len: usize) -> FfiResult<NullPtr> {
860    check_null!(buffer_ptr, FfiErrorCode::BufferNullPointer, null());
861    let x: *mut [u8] = core::slice::from_raw_parts_mut(buffer_ptr, buffer_len);
862    drop(Box::from_raw(x));
863    FfiResult::ok(null())
864}
865
866/// # Safety
867/// Free the array of string references.
868///
869/// # Arguments
870/// * `x` - Pointer to the array of string references
871///
872/// # Examples
873///
874/// ```c
875/// FPArrayStrRefFree(x);
876/// ```
877///
878#[export_name = "FPArrayStrRefFree"]
879pub unsafe extern "C" fn array_str_ref_free(x: *mut Array<StrRef>) -> FfiResult<NullPtr> {
880    check_null!(x, FfiErrorCode::ArrayNullPointer, null());
881    drop(Box::from_raw(x));
882    FfiResult::ok(null())
883}
884
885/// # Safety
886/// Free the array of warnings.
887///
888/// # Arguments
889/// * `warnings` - Pointer to the array of warnings
890///
891/// # Examples
892///
893/// ```c
894/// FPArrayWarningFree(warnings);
895/// ```
896///
897#[export_name = "FPArrayWarningFree"]
898pub unsafe extern "C" fn array_warning_free(
899    warnings: *mut Array<parser::Warning>,
900) -> FfiResult<NullPtr> {
901    check_null!(warnings, FfiErrorCode::ArrayNullPointer, null());
902    drop(Box::from_raw(warnings));
903    FfiResult::ok(null())
904}
905
906/// # Safety
907/// Parse the inline style from the string.
908///
909/// # Arguments
910/// * `inline_style_text_ptr` - Pointer to the inline style text
911/// * `warnings` - Optional output parameter to receive warnings array pointer
912///
913/// # Examples
914///
915/// ```c
916/// FPParseInlineStyle(inline_style_text_ptr, warnings);
917/// ```
918///
919#[export_name = "FPParseInlineStyle"]
920pub unsafe extern "C" fn parse_inline_style(
921    inline_style_text_ptr: *const c_char,
922    warnings: *mut *mut Array<parser::Warning>,
923) -> FfiResult<*mut InlineRule> {
924    check_null!(
925        inline_style_text_ptr,
926        FfiErrorCode::InlineStyleTextNullPointer,
927        null_mut()
928    );
929    let inline_style_text = CStr::from_ptr(inline_style_text_ptr).to_string_lossy();
930    let (prop, w) =
931        parser::parse_inline_style(&inline_style_text, parser::StyleParsingDebugMode::None);
932    if !warnings.is_null() {
933        *warnings = Box::into_raw(Box::new(w.into()));
934    }
935    let mut important = BitSet::new();
936    let mut bs_empty = true;
937    let properties = prop
938        .into_iter()
939        .enumerate()
940        .map(|(index, x)| match x {
941            PropertyMeta::Normal { property } => property,
942            PropertyMeta::Important { property } => {
943                important.insert(index);
944                bs_empty = false;
945                property
946            }
947            PropertyMeta::DebugGroup { .. } => Property::Unknown,
948        })
949        .collect();
950    let important = if bs_empty {
951        ImportantBitSet::None
952    } else {
953        ImportantBitSet::Array(important.into_bit_vec().to_bytes().into())
954    };
955    let inline_rule = InlineRule::new(properties, important);
956    FfiResult::ok(Box::into_raw(Box::new(inline_rule)))
957}
958
959/// # Safety
960/// Free the inline style.
961///
962/// # Arguments
963/// * `inline_rule` - Pointer to the inline style
964///
965/// # Examples
966///
967/// ```c
968/// FPInlineStyleFree(inline_rule);
969/// ```
970///
971#[export_name = "FPInlineStyleFree"]
972pub unsafe extern "C" fn inline_style_free(inline_rule: *mut InlineRule) -> FfiResult<NullPtr> {
973    check_null!(inline_rule, FfiErrorCode::InlineRuleNullPointer, null());
974    drop(Box::from_raw(inline_rule));
975    FfiResult::ok(null())
976}
977
978/// # Safety
979/// Parse the style sheet from the string.
980///
981/// # Arguments
982/// * `style_text_ptr` - Pointer to the style sheet text
983///
984/// # Examples
985///
986/// ```c
987/// FPStyleSheetFromString(style_text_ptr);
988/// ```
989///
990#[export_name = "FPParseStyleSheetFromString"]
991pub unsafe extern "C" fn parse_style_sheet_from_string(
992    style_text_ptr: *const c_char,
993) -> FfiResult<*mut StyleSheet> {
994    check_null!(
995        style_text_ptr,
996        FfiErrorCode::StyleTextNullPointer,
997        null_mut()
998    );
999    let style_text = CStr::from_ptr(style_text_ptr).to_string_lossy();
1000    let (compiled_style_sheet, _) = parser::parse_style_sheet("string", &style_text);
1001    let style_sheet = StyleSheet::from_sheet(&compiled_style_sheet);
1002    FfiResult::ok(Box::into_raw(Box::new(style_sheet)))
1003}
1004
1005/// # Safety
1006/// Parse the selector from the string.
1007///
1008/// # Arguments
1009/// * `selector_text_ptr` - Pointer to the selector text
1010///
1011/// # Examples
1012///
1013/// ```c
1014/// FPParseSelectorFromString(selector_text_ptr);
1015/// ```
1016///
1017#[export_name = "FPParseSelectorFromString"]
1018pub unsafe extern "C" fn parse_selector_from_string(
1019    selector_text_ptr: *const c_char,
1020) -> FfiResult<*mut Selector> {
1021    check_null!(
1022        selector_text_ptr,
1023        FfiErrorCode::SelectorTextNullPointer,
1024        null_mut()
1025    );
1026    let selector_text = CStr::from_ptr(selector_text_ptr).to_string_lossy();
1027    let selector = Selector::from_string(&selector_text);
1028    FfiResult::ok(Box::into_raw(Box::new(selector)))
1029}
1030
1031/// # Safety
1032/// Free the selector.
1033///
1034/// # Arguments
1035/// * `selector` - Pointer to the selector
1036///
1037/// # Examples
1038///
1039/// ```c
1040/// FPSelectorFree(selector);
1041/// ```
1042///
1043#[export_name = "FPSelectorFree"]
1044pub unsafe extern "C" fn selector_free(selector: *mut Selector) -> FfiResult<NullPtr> {
1045    check_null!(selector, FfiErrorCode::SelectorNullPointer, null());
1046    drop(Box::from_raw(selector));
1047    FfiResult::ok(null())
1048}
1049
1050/// # Safety
1051/// Free the style sheet.
1052///
1053/// # Arguments
1054/// * `style_sheet` - Pointer to the style sheet
1055///
1056/// # Examples
1057///
1058/// ```c
1059/// FPStyleSheetFree(style_sheet);
1060/// ```
1061///
1062#[export_name = "FPStyleSheetFree"]
1063pub unsafe extern "C" fn style_sheet_free(style_sheet: *mut StyleSheet) -> FfiResult<NullPtr> {
1064    check_null!(style_sheet, FfiErrorCode::StyleSheetNullPointer, null());
1065    drop(Box::from_raw(style_sheet));
1066    FfiResult::ok(null())
1067}
1068
1069/// # Safety
1070/// Get the version of the style sheet in the binary format.
1071///
1072/// # Arguments
1073/// * `buffer_ptr` - Pointer to the buffer
1074/// * `buffer_len` - Length of the buffer
1075///
1076/// # Examples
1077///
1078/// ```c
1079/// FPStyleSheetBincodeVersion(buffer_ptr, buffer_len);
1080/// ```
1081///
1082#[cfg(feature = "deserialize")]
1083#[export_name = "FPStyleSheetBincodeVersion"]
1084pub unsafe extern "C" fn style_sheet_bincode_version(
1085    buffer_ptr: *mut u8,
1086    buffer_len: usize,
1087) -> FfiResult<*mut StrRef> {
1088    use float_pigment_consistent_bincode::Options;
1089    check_null!(buffer_ptr, FfiErrorCode::BufferNullPointer, null_mut());
1090    let sheet = de_static_ref_zero_copy_env(
1091        core::slice::from_raw_parts_mut(buffer_ptr, buffer_len),
1092        |s| {
1093            let s: Result<StyleSheet, _> = float_pigment_consistent_bincode::DefaultOptions::new()
1094                .allow_trailing_bytes()
1095                .deserialize(s);
1096            match s {
1097                Ok(ss) => ss,
1098                Err(err) => {
1099                    let mut ss = StyleSheet::from_sheet(&sheet::CompiledStyleSheet::new());
1100                    if let StyleSheet::V1(ssv) = &mut ss {
1101                        ssv.version = Box::new(
1102                            format!("Failed to deserialize bincode formatted style sheet: {err}")
1103                                .into(),
1104                        );
1105                    }
1106                    ss
1107                }
1108            }
1109        },
1110        move || {},
1111    );
1112    let version = match sheet {
1113        StyleSheet::V1(v1) => v1.version,
1114        _ => Box::new("unknown version".into()),
1115    };
1116    FfiResult::ok(Box::into_raw(version))
1117}
1118
1119/// # Safety
1120/// Get the version of the CSS parser.
1121///
1122/// # Examples
1123///
1124/// ```c
1125/// FPCssParserVersion();
1126/// ```
1127#[export_name = "FPCssParserVersion"]
1128pub unsafe extern "C" fn css_parser_version() -> FfiResult<*mut StrRef> {
1129    let version = env!("CARGO_PKG_VERSION").to_string().into();
1130    FfiResult::ok(Box::into_raw(Box::new(version)))
1131}
1132
1133#[repr(C)]
1134#[derive(Debug, Default)]
1135pub struct ColorValue {
1136    red: u8,
1137    green: u8,
1138    blue: u8,
1139    alpha: u8,
1140}
1141
1142/// # Safety
1143/// Parse the color from the string.
1144///
1145/// # Arguments
1146/// * `source` - Pointer to the source string
1147///
1148/// # Examples
1149///
1150/// ```c
1151/// FPParseColorFromString(source);
1152/// ```
1153///
1154#[export_name = "FPParseColorFromString"]
1155pub unsafe extern "C" fn parse_color_from_string(source: *const c_char) -> FfiResult<ColorValue> {
1156    check_null!(
1157        source,
1158        FfiErrorCode::SourceNullPointer,
1159        ColorValue::default()
1160    );
1161    let source = CStr::from_ptr(source).to_string_lossy();
1162    let ret = parse_color_to_rgba(&source);
1163    FfiResult::ok(ColorValue {
1164        red: ret.0,
1165        green: ret.1,
1166        blue: ret.2,
1167        alpha: ret.3,
1168    })
1169}
1170
1171/// # Safety
1172/// Substitute the variable in the expression.
1173///
1174/// # Arguments
1175/// * `expr_ptr` - Pointer to the expression
1176/// * `map` - Pointer to the map
1177/// * `getter` - Custom property getter
1178/// * `setter` - Custom property setter
1179///
1180/// # Examples
1181///
1182/// ```c
1183/// FPSubstituteVariable(expr_ptr, map, getter, setter);
1184/// ```
1185///
1186#[export_name = "FPSubstituteVariable"]
1187pub unsafe extern "C" fn substitute_variable(
1188    expr_ptr: *const c_char,
1189    map: RawMutPtr,
1190    getter: CustomPropertyGetter,
1191    setter: CustomPropertySetter,
1192) -> FfiResult<*const c_char> {
1193    check_null!(expr_ptr, FfiErrorCode::ExprPtrNullPointer, null());
1194    check_null!(map, FfiErrorCode::MapNullPointer, null());
1195    let expr = CStr::from_ptr(expr_ptr).to_string_lossy();
1196    let context = CustomPropertyContext::create(map, getter, setter);
1197    if let Some(ret) = parser::property_value::var::substitute_variable(&expr, &context) {
1198        if let Ok(r) = CString::new(ret) {
1199            return FfiResult::ok(r.into_raw());
1200        }
1201    }
1202    FfiResult::ok(null())
1203}
1204
1205/// # Safety
1206/// Free the string.
1207///
1208/// # Arguments
1209/// * `ptr` - Pointer to the string
1210///
1211/// # Examples
1212///
1213/// ```c
1214/// FPStrFree(ptr);
1215/// ```
1216///
1217#[export_name = "FPStrFree"]
1218pub unsafe extern "C" fn str_free(ptr: *const c_char) -> FfiResult<NullPtr> {
1219    check_null!(ptr, FfiErrorCode::StrNullPointer, null());
1220    drop(CString::from_raw(ptr as *mut c_char));
1221    FfiResult::ok(null())
1222}