pub enum Plugin<Str, ArrayStr, MapStr, MapArrayStr, ArrayMatched> {
ListProperties(ListProperties<Str, ArrayStr, MapStr, MapArrayStr>),
ListValues(ListValues<Str, ArrayStr, MapStr>),
Spacing(Spacing<Str, ArrayStr, MapStr>),
Color(Color<Str, ArrayStr, MapStr>),
Number(Number<Str, ArrayStr, MapStr>),
Arbitrary(Arbitrary<Str, ArrayStr, MapStr, ArrayMatched>),
Functional(Functional<Str>),
}Expand description
A plugin is a structure capable of generating CSS styles from a CSS selector.
Several kinds of plugins exist and define what values are accepted as selector or modifier and what CSS is generated based on the input selector. The API is designed to be fully declarative (so that plugin declarations are serializable), except for the functional kind.
Each plugin kind has a set of required parameters and a set of default parameters which can be automatically filled in Rust using the struct update syntax.
It’s common to define several plugins to handle a single utility class, and to define static
plugins as constants (the default function on each plugin kind is a const fn).
After you have defined a plugin, you need to register it in the Config structure by calling
Config::register_plugin.
§Simple example (defines the static values of the font-family plugin)
use encre_css::prelude::build_plugin::*;
const PLUGIN: StaticPlugin = Plugin::ListValues(ListValues {
prop: SingleProp("font-family"),
values: map! {
"font-sans" => r#"ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont"#,
"font-serif" => r#"Georgia, Cambria, "Times New Roman", Times, serif"#,
"font-mono" => r#"Menlo, Monaco, Consolas, "Liberation Mono", monospace"#,
},
..ListValues::default()
});§More advanced example (defines the stroke-width plugin)
use encre_css::prelude::build_plugin::*;
const PLUGIN: StaticPlugin = Plugin::Number(Number {
namespace: "stroke",
prop: SingleProp("stroke-width"),
template: Some(SingleProp("{}px")),
..Number::default()
});
// There's also a plugin sharing the same `stroke` namespace (which helps changing the
// stroke color, e.g `stroke-red-500`), so it's required to define `hints` and `matchers`
const PLUGIN_ARBITRARY: StaticPlugin = Plugin::Arbitrary(Arbitrary {
namespace: "stroke",
prop: SingleProp("stroke-width"),
disambiguate: Some(ArbitraryDisambiguate {
matched: &[CssType::Length, CssType::Percentage, CssType::LineWidth, CssType::Number],
separation: ArbitraryDisambiguateSeparation::None,
}),
..Arbitrary::default()
});§More powerful usage
If you need to have full control over the CSS rule generated, you can use the Functional
plugin kind. It allows executing a full-blown Rust function for each selector having a specific
namespace. However, it’s (of course) not serializable, and thus cannot be used in, e.g a TOML
configuration.
§Example
use encre_css::Config;
use encre_css::prelude::build_plugin::*;
/// Reads the `emoji` extra field of the configuration to find the replacement emoji.
fn extract_emoji_value<'a>(config: &'a Config, value: &str) -> Option<&'a str> {
config.extra.get("emoji")
.and_then(|r| r.as_table())
.and_then(|r| r.get(value))
.and_then(|r| r.as_str())
}
const PLUGIN: StaticPlugin = Plugin::Functional(Functional {
namespace: "emoji",
can_handle: |context| matches!(context.modifier, Modifier::Builtin {
value,
..
} if extract_emoji_value(context.config, value).is_some()),
handle: |context| {
// Only accept static modifiers, and dynamically fetch them from the
// `emoji` extra field of the configuration
if let Modifier::Builtin { value, .. } = context.modifier
&& let Some(value) = extract_emoji_value(&context.config, value) {
generate_at_rules(context, |context| {
generate_class(
context,
|context| {
context.buffer.line(format_args!("content: \"{value}\";"));
},
"",
);
});
}
},
});Have a look at https://gitlab.com/encre-org/encre-css/tree/main/crates/encre-css/src/plugins for more examples.
§Define a plugin in TOML
Instead of defining plugins in Rust, you can also define them in encre-css’s TOML configuration
(or every other language that uses a serde deserializer).
The sole exception is plugins using the Functional kind which are not serializable.
To do that, you need to add a new entry in the custom_plugins list of the configuration.
You can then define plugins as you would do in Rust.
§Example
[[custom_plugins]]
[custom_plugins.Number]
namespace = "stroke"
prop = "stroke-width"
template = "{}px"
[[custom_plugins]]
[custom_plugins.Arbitrary]
namespace = "stroke"
prop = "stroke-width"
hints = ["Length", "Percentage"]
matchers = [["Length", "Percentage", "LineWidth", "Number"], "None"]§Advice
encre-css builds a trie structure based on the
namespace of the plugins to optimize matching a utility class to a specific plugin, so it’s
highly discouraged to leave the namespace of a plugin empty, otherwise the performances will
decrease heavily.
§Release a plugin as a crate
If you want to release your custom plugins as a crate, you can export a register function
taking a mutable reference to a Config structure and use the Config::register_plugin
function to register them.
pub fn register(config: &mut Config) {
config.register_plugin(&PLUGIN);
config.register_plugin(&PLUGIN_ARBITRARY);
}Variants§
ListProperties(ListProperties<Str, ArrayStr, MapStr, MapArrayStr>)
See ListProperties.
ListValues(ListValues<Str, ArrayStr, MapStr>)
See ListValues.
Spacing(Spacing<Str, ArrayStr, MapStr>)
See Spacing.
Color(Color<Str, ArrayStr, MapStr>)
See Color.
Number(Number<Str, ArrayStr, MapStr>)
See Number.
Arbitrary(Arbitrary<Str, ArrayStr, MapStr, ArrayMatched>)
See Arbitrary.
Functional(Functional<Str>)
See Functional.
Not serializable.