♻️ Major code restructuring

This commit is contained in:
Manuel Cillero 2024-02-09 14:05:38 +01:00
parent a96e203bb3
commit fa66d628a0
221 changed files with 228 additions and 315 deletions

54
src/html/assets.rs Normal file
View file

@ -0,0 +1,54 @@
pub mod headscript;
pub mod headstyles;
pub mod javascript;
pub mod stylesheet;
use crate::html::{html, Markup};
use crate::{SmartDefault, Weight};
pub trait AssetsTrait {
fn path(&self) -> &str;
fn weight(&self) -> Weight;
fn prepare(&self) -> Markup;
}
#[derive(SmartDefault)]
pub struct Assets<T>(Vec<T>);
impl<T: AssetsTrait> Assets<T> {
pub fn new() -> Self {
Assets::<T>(Vec::<T>::new())
}
pub fn add(&mut self, asset: T) -> &mut Self {
match self.0.iter().position(|x| x.path() == asset.path()) {
Some(index) => {
if self.0[index].weight() > asset.weight() {
self.0.remove(index);
self.0.push(asset);
}
}
_ => self.0.push(asset),
};
self
}
pub fn remove(&mut self, path: &'static str) -> &mut Self {
if let Some(index) = self.0.iter().position(|x| x.path() == path) {
self.0.remove(index);
};
self
}
pub fn prepare(&mut self) -> Markup {
let assets = &mut self.0;
assets.sort_by_key(|a| a.weight());
html! {
@for a in assets {
(a.prepare())
}
}
}
}

View file

@ -0,0 +1,44 @@
use crate::html::assets::AssetsTrait;
use crate::html::{html, Markup};
use crate::{SmartDefault, Weight};
#[rustfmt::skip]
#[derive(SmartDefault)]
pub struct HeadScript {
path : String,
code : String,
weight: Weight,
}
impl AssetsTrait for HeadScript {
fn path(&self) -> &str {
self.path.as_str()
}
fn weight(&self) -> Weight {
self.weight
}
fn prepare(&self) -> Markup {
html! { script { (self.code) }; }
}
}
impl HeadScript {
pub fn named(path: impl Into<String>) -> Self {
HeadScript {
path: path.into(),
..Default::default()
}
}
pub fn with_code(mut self, code: impl Into<String>) -> Self {
self.code = code.into().trim().to_owned();
self
}
pub fn with_weight(mut self, value: Weight) -> Self {
self.weight = value;
self
}
}

View file

@ -0,0 +1,44 @@
use crate::html::assets::AssetsTrait;
use crate::html::{html, Markup};
use crate::{SmartDefault, Weight};
#[rustfmt::skip]
#[derive(SmartDefault)]
pub struct HeadStyles {
path : String,
styles: String,
weight: Weight,
}
impl AssetsTrait for HeadStyles {
fn path(&self) -> &str {
self.path.as_str()
}
fn weight(&self) -> Weight {
self.weight
}
fn prepare(&self) -> Markup {
html! { styles { (self.styles) }; }
}
}
impl HeadStyles {
pub fn named(path: impl Into<String>) -> Self {
HeadStyles {
path: path.into(),
..Default::default()
}
}
pub fn with_styles(mut self, styles: impl Into<String>) -> Self {
self.styles = styles.into().trim().to_owned();
self
}
pub fn with_weight(mut self, value: Weight) -> Self {
self.weight = value;
self
}
}

View file

@ -0,0 +1,69 @@
use crate::html::assets::AssetsTrait;
use crate::html::{html, Markup};
use crate::{SmartDefault, Weight};
#[derive(Default, Eq, PartialEq)]
pub enum ModeJS {
Async,
#[default]
Defer,
Normal,
}
#[rustfmt::skip]
#[derive(SmartDefault)]
pub struct JavaScript {
path : String,
prefix : &'static str,
version: &'static str,
weight : Weight,
mode : ModeJS,
}
impl AssetsTrait for JavaScript {
fn path(&self) -> &str {
self.path.as_str()
}
fn weight(&self) -> Weight {
self.weight
}
fn prepare(&self) -> Markup {
html! {
script type="text/javascript"
src=(crate::concat_string!(self.path, self.prefix, self.version))
async[self.mode == ModeJS::Async]
defer[self.mode == ModeJS::Defer]
{};
}
}
}
impl JavaScript {
pub fn at(path: impl Into<String>) -> Self {
JavaScript {
path: path.into(),
..Default::default()
}
}
pub fn with_version(mut self, version: &'static str) -> Self {
(self.prefix, self.version) = if version.is_empty() {
("", "")
} else {
("?v=", version)
};
self
}
pub fn with_weight(mut self, value: Weight) -> Self {
self.weight = value;
self
}
pub fn with_mode(mut self, mode: ModeJS) -> Self {
self.mode = mode;
self
}
}

View file

@ -0,0 +1,73 @@
use crate::html::assets::AssetsTrait;
use crate::html::{html, Markup};
use crate::{SmartDefault, Weight};
pub enum TargetMedia {
Default,
Print,
Screen,
Speech,
}
#[rustfmt::skip]
#[derive(SmartDefault)]
pub struct StyleSheet {
path : String,
prefix : &'static str,
version: &'static str,
media : Option<&'static str>,
weight : Weight,
}
impl AssetsTrait for StyleSheet {
fn path(&self) -> &str {
self.path.as_str()
}
fn weight(&self) -> Weight {
self.weight
}
fn prepare(&self) -> Markup {
html! {
link
rel="stylesheet"
href=(crate::concat_string!(self.path, self.prefix, self.version))
media=[self.media];
}
}
}
impl StyleSheet {
pub fn at(path: impl Into<String>) -> Self {
StyleSheet {
path: path.into(),
..Default::default()
}
}
pub fn with_version(mut self, version: &'static str) -> Self {
(self.prefix, self.version) = if version.is_empty() {
("", "")
} else {
("?v=", version)
};
self
}
pub fn with_weight(mut self, value: Weight) -> Self {
self.weight = value;
self
}
#[rustfmt::skip]
pub fn for_media(mut self, media: TargetMedia) -> Self {
self.media = match media {
TargetMedia::Print => Some("print"),
TargetMedia::Screen => Some("screen"),
TargetMedia::Speech => Some("speech"),
_ => None,
};
self
}
}

93
src/html/favicon.rs Normal file
View file

@ -0,0 +1,93 @@
use crate::html::{html, Markup};
use crate::SmartDefault;
#[derive(SmartDefault)]
pub struct Favicon(Vec<Markup>);
impl Favicon {
pub fn new() -> Self {
Favicon::default()
}
// Favicon BUILDER.
pub fn with_icon(self, image: &str) -> Self {
self.add_icon_item("icon", image, None, None)
}
pub fn with_icon_for_sizes(self, image: &str, sizes: &str) -> Self {
self.add_icon_item("icon", image, Some(sizes), None)
}
pub fn with_apple_touch_icon(self, image: &str, sizes: &str) -> Self {
self.add_icon_item("apple-touch-icon", image, Some(sizes), None)
}
pub fn with_mask_icon(self, image: &str, color: &str) -> Self {
self.add_icon_item("mask-icon", image, None, Some(color))
}
pub fn with_manifest(self, file: &str) -> Self {
self.add_icon_item("manifest", file, None, None)
}
pub fn with_theme_color(mut self, color: &str) -> Self {
self.0.push(html! {
meta name="theme-color" content=(color);
});
self
}
pub fn with_ms_tile_color(mut self, color: &str) -> Self {
self.0.push(html! {
meta name="msapplication-TileColor" content=(color);
});
self
}
pub fn with_ms_tile_image(mut self, image: &str) -> Self {
self.0.push(html! {
meta name="msapplication-TileImage" content=(image);
});
self
}
fn add_icon_item(
mut self,
icon_rel: &str,
icon_source: &str,
icon_sizes: Option<&str>,
icon_color: Option<&str>,
) -> Self {
let icon_type = match icon_source.rfind('.') {
Some(i) => match icon_source[i..].to_owned().to_lowercase().as_str() {
".gif" => Some("image/gif"),
".ico" => Some("image/x-icon"),
".jpg" => Some("image/jpg"),
".png" => Some("image/png"),
".svg" => Some("image/svg+xml"),
_ => None,
},
_ => None,
};
self.0.push(html! {
link
rel=(icon_rel)
type=[(icon_type)]
sizes=[(icon_sizes)]
color=[(icon_color)]
href=(icon_source);
});
self
}
// Favicon PREPARE.
pub(crate) fn prepare(&self) -> Markup {
html! {
@for item in &self.0 {
(item)
}
}
}
}

350
src/html/maud.rs Normal file
View file

@ -0,0 +1,350 @@
//#![no_std]
//! A macro for writing HTML templates.
//!
//! This documentation only describes the runtime API. For a general
//! guide, check out the [book] instead.
//!
//! [book]: https://maud.lambda.xyz/
//#![doc(html_root_url = "https://docs.rs/maud/0.25.0")]
extern crate alloc;
use alloc::{borrow::Cow, boxed::Box, string::String};
use core::fmt::{self, Arguments, Display, Write};
pub use pagetop_macros::html;
mod escape;
/// An adapter that escapes HTML special characters.
///
/// The following characters are escaped:
///
/// * `&` is escaped as `&amp;`
/// * `<` is escaped as `&lt;`
/// * `>` is escaped as `&gt;`
/// * `"` is escaped as `&quot;`
///
/// All other characters are passed through unchanged.
///
/// **Note:** In versions prior to 0.13, the single quote (`'`) was
/// escaped as well.
///
/// # Example
///
/// ```rust
/// use maud::Escaper;
/// use std::fmt::Write;
/// let mut s = String::new();
/// write!(Escaper::new(&mut s), "<script>launchMissiles()</script>").unwrap();
/// assert_eq!(s, "&lt;script&gt;launchMissiles()&lt;/script&gt;");
/// ```
pub struct Escaper<'a>(&'a mut String);
impl<'a> Escaper<'a> {
/// Creates an `Escaper` from a `String`.
pub fn new(buffer: &'a mut String) -> Escaper<'a> {
Escaper(buffer)
}
}
impl<'a> fmt::Write for Escaper<'a> {
fn write_str(&mut self, s: &str) -> fmt::Result {
escape::escape_to_string(s, self.0);
Ok(())
}
}
/// Represents a type that can be rendered as HTML.
///
/// To implement this for your own type, override either the `.render()`
/// or `.render_to()` methods; since each is defined in terms of the
/// other, you only need to implement one of them. See the example below.
///
/// # Minimal implementation
///
/// An implementation of this trait must override at least one of
/// `.render()` or `.render_to()`. Since the default definitions of
/// these methods call each other, not doing this will result in
/// infinite recursion.
///
/// # Example
///
/// ```rust
/// use maud::{html, Markup, Render};
///
/// /// Provides a shorthand for linking to a CSS stylesheet.
/// pub struct Stylesheet(&'static str);
///
/// impl Render for Stylesheet {
/// fn render(&self) -> Markup {
/// html! {
/// link rel="stylesheet" type="text/css" href=(self.0);
/// }
/// }
/// }
/// ```
pub trait Render {
/// Renders `self` as a block of `Markup`.
fn render(&self) -> Markup {
let mut buffer = String::new();
self.render_to(&mut buffer);
PreEscaped(buffer)
}
/// Appends a representation of `self` to the given buffer.
///
/// Its default implementation just calls `.render()`, but you may
/// override it with something more efficient.
///
/// Note that no further escaping is performed on data written to
/// the buffer. If you override this method, you must make sure that
/// any data written is properly escaped, whether by hand or using
/// the [`Escaper`](struct.Escaper.html) wrapper struct.
fn render_to(&self, buffer: &mut String) {
buffer.push_str(&self.render().into_string());
}
}
impl Render for str {
fn render_to(&self, w: &mut String) {
escape::escape_to_string(self, w);
}
}
impl Render for String {
fn render_to(&self, w: &mut String) {
str::render_to(self, w);
}
}
impl<'a> Render for Cow<'a, str> {
fn render_to(&self, w: &mut String) {
str::render_to(self, w);
}
}
impl<'a> Render for Arguments<'a> {
fn render_to(&self, w: &mut String) {
let _ = Escaper::new(w).write_fmt(*self);
}
}
impl<'a, T: Render + ?Sized> Render for &'a T {
fn render_to(&self, w: &mut String) {
T::render_to(self, w);
}
}
impl<'a, T: Render + ?Sized> Render for &'a mut T {
fn render_to(&self, w: &mut String) {
T::render_to(self, w);
}
}
impl<T: Render + ?Sized> Render for Box<T> {
fn render_to(&self, w: &mut String) {
T::render_to(self, w);
}
}
macro_rules! impl_render_with_display {
($($ty:ty)*) => {
$(
impl Render for $ty {
fn render_to(&self, w: &mut String) {
// TODO: remove the explicit arg when Rust 1.58 is released
format_args!("{self}", self = self).render_to(w);
}
}
)*
};
}
impl_render_with_display! {
char f32 f64
}
macro_rules! impl_render_with_itoa {
($($ty:ty)*) => {
$(
impl Render for $ty {
fn render_to(&self, w: &mut String) {
w.push_str(itoa::Buffer::new().format(*self));
}
}
)*
};
}
impl_render_with_itoa! {
i8 i16 i32 i64 i128 isize
u8 u16 u32 u64 u128 usize
}
/// Renders a value using its [`Display`] impl.
///
/// # Example
///
/// ```rust
/// use maud::html;
/// use std::net::Ipv4Addr;
///
/// let ip_address = Ipv4Addr::new(127, 0, 0, 1);
///
/// let markup = html! {
/// "My IP address is: "
/// (maud::display(ip_address))
/// };
///
/// assert_eq!(markup.into_string(), "My IP address is: 127.0.0.1");
/// ```
pub fn display(value: impl Display) -> impl Render {
struct DisplayWrapper<T>(T);
impl<T: Display> Render for DisplayWrapper<T> {
fn render_to(&self, w: &mut String) {
format_args!("{0}", self.0).render_to(w);
}
}
DisplayWrapper(value)
}
/// A wrapper that renders the inner value without escaping.
#[derive(Debug, Clone, Copy)]
pub struct PreEscaped<T: AsRef<str>>(pub T);
impl<T: AsRef<str>> Render for PreEscaped<T> {
fn render_to(&self, w: &mut String) {
w.push_str(self.0.as_ref());
}
}
/// A block of markup is a string that does not need to be escaped.
///
/// The `html!` macro expands to an expression of this type.
pub type Markup = PreEscaped<String>;
impl Markup {
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl<T: AsRef<str> + Into<String>> PreEscaped<T> {
/// Converts the inner value to a string.
pub fn into_string(self) -> String {
self.0.into()
}
}
impl<T: AsRef<str> + Into<String>> From<PreEscaped<T>> for String {
fn from(value: PreEscaped<T>) -> String {
value.into_string()
}
}
impl<T: AsRef<str> + Default> Default for PreEscaped<T> {
fn default() -> Self {
Self(Default::default())
}
}
/// The literal string `<!DOCTYPE html>`.
///
/// # Example
///
/// A minimal web page:
///
/// ```rust
/// use maud::{DOCTYPE, html};
///
/// let markup = html! {
/// (DOCTYPE)
/// html {
/// head {
/// meta charset="utf-8";
/// title { "Test page" }
/// }
/// body {
/// p { "Hello, world!" }
/// }
/// }
/// };
/// ```
pub const DOCTYPE: PreEscaped<&'static str> = PreEscaped("<!DOCTYPE html>");
mod actix_support {
extern crate alloc;
use crate::html::PreEscaped;
use actix_web::{http::header, HttpRequest, HttpResponse, Responder};
use alloc::string::String;
impl Responder for PreEscaped<String> {
type Body = String;
fn respond_to(self, _req: &HttpRequest) -> HttpResponse<Self::Body> {
HttpResponse::Ok()
.content_type(header::ContentType::html())
.message_body(self.0)
.unwrap()
}
}
}
#[doc(hidden)]
pub mod html_private {
extern crate alloc;
use super::{display, Render};
use alloc::string::String;
use core::fmt::Display;
#[doc(hidden)]
#[macro_export]
macro_rules! render_to {
($x:expr, $buffer:expr) => {{
use $crate::html::html_private::*;
match ChooseRenderOrDisplay($x) {
x => (&&x).implements_render_or_display().render_to(x.0, $buffer),
}
}};
}
pub use render_to;
pub struct ChooseRenderOrDisplay<T>(pub T);
pub struct ViaRenderTag;
pub struct ViaDisplayTag;
pub trait ViaRender {
fn implements_render_or_display(&self) -> ViaRenderTag {
ViaRenderTag
}
}
pub trait ViaDisplay {
fn implements_render_or_display(&self) -> ViaDisplayTag {
ViaDisplayTag
}
}
impl<T: Render> ViaRender for &ChooseRenderOrDisplay<T> {}
impl<T: Display> ViaDisplay for ChooseRenderOrDisplay<T> {}
impl ViaRenderTag {
pub fn render_to<T: Render + ?Sized>(self, value: &T, buffer: &mut String) {
value.render_to(buffer);
}
}
impl ViaDisplayTag {
pub fn render_to<T: Display + ?Sized>(self, value: &T, buffer: &mut String) {
display(value).render_to(buffer);
}
}
}

34
src/html/maud/escape.rs Normal file
View file

@ -0,0 +1,34 @@
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!! PLEASE KEEP THIS IN SYNC WITH `maud_macros/src/escape.rs` !!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
extern crate alloc;
use alloc::string::String;
pub fn escape_to_string(input: &str, output: &mut String) {
for b in input.bytes() {
match b {
b'&' => output.push_str("&amp;"),
b'<' => output.push_str("&lt;"),
b'>' => output.push_str("&gt;"),
b'"' => output.push_str("&quot;"),
_ => unsafe { output.as_mut_vec().push(b) },
}
}
}
#[cfg(test)]
mod test {
extern crate alloc;
use super::escape_to_string;
use alloc::string::String;
#[test]
fn it_works() {
let mut s = String::new();
escape_to_string("<script>launchMissiles()</script>", &mut s);
assert_eq!(s, "&lt;script&gt;launchMissiles()&lt;/script&gt;");
}
}

106
src/html/opt_classes.rs Normal file
View file

@ -0,0 +1,106 @@
//! **OptionClasses** implements a *helper* for dynamically adding class names to components.
//!
//! This *helper* differentiates between default classes (generally associated with styles provided
//! by the theme) and user classes (for customizing components based on application styles).
//!
//! Classes can be added using [Add]. Operations to [Remove], [Replace] or [Toggle] a class, as well
//! as [Clear] all classes, are also provided.
//!
//! **OptionClasses** assumes that the order of the classes is irrelevant
//! (<https://stackoverflow.com/a/1321712>), and duplicate classes will not be allowed.
use crate::{fn_with, SmartDefault};
pub enum ClassesOp {
Add,
Prepend,
Remove,
Replace(String),
Toggle,
Set,
}
#[derive(SmartDefault)]
pub struct OptionClasses(Vec<String>);
impl OptionClasses {
pub fn new(classes: impl Into<String>) -> Self {
OptionClasses::default().with_value(ClassesOp::Prepend, classes)
}
// OptionClasses BUILDER.
#[fn_with]
pub fn alter_value(&mut self, op: ClassesOp, classes: impl Into<String>) -> &mut Self {
let classes: String = classes.into();
let classes: Vec<&str> = classes.split_ascii_whitespace().collect();
match op {
ClassesOp::Add => {
self.add(&classes, self.0.len());
}
ClassesOp::Prepend => {
self.add(&classes, 0);
}
ClassesOp::Remove => {
for class in classes {
self.0.retain(|c| c.ne(&class.to_string()));
}
}
ClassesOp::Replace(classes_to_replace) => {
let mut pos = self.0.len();
let replace: Vec<&str> = classes_to_replace.split_ascii_whitespace().collect();
for class in replace {
if let Some(replace_pos) = self.0.iter().position(|c| c.eq(class)) {
self.0.remove(replace_pos);
if pos > replace_pos {
pos = replace_pos;
}
}
}
self.add(&classes, pos);
}
ClassesOp::Toggle => {
for class in classes {
if !class.is_empty() {
if let Some(pos) = self.0.iter().position(|c| c.eq(class)) {
self.0.remove(pos);
} else {
self.0.push(class.to_string());
}
}
}
}
ClassesOp::Set => {
self.0.clear();
self.add(&classes, 0);
}
}
self
}
#[inline]
fn add(&mut self, classes: &Vec<&str>, mut pos: usize) {
for class in classes {
if !class.is_empty() && !self.0.iter().any(|c| c.eq(class)) {
self.0.insert(pos, class.to_string());
pos += 1;
}
}
}
// OptionClasses GETTERS.
pub fn get(&self) -> Option<String> {
if self.0.is_empty() {
None
} else {
Some(self.0.join(" "))
}
}
pub fn contains(&self, class: impl Into<String>) -> bool {
let class: String = class.into();
self.0.iter().any(|c| c.eq(&class))
}
}

45
src/html/opt_component.rs Normal file
View file

@ -0,0 +1,45 @@
use crate::core::component::{ArcTypedComponent, ComponentTrait, Context};
use crate::fn_with;
use crate::html::{html, Markup};
pub struct OptionComponent<C: ComponentTrait>(Option<ArcTypedComponent<C>>);
impl<C: ComponentTrait> Default for OptionComponent<C> {
fn default() -> Self {
OptionComponent(None)
}
}
impl<C: ComponentTrait> OptionComponent<C> {
pub fn new(component: C) -> Self {
OptionComponent::default().with_value(Some(component))
}
// OptionComponent BUILDER.
#[fn_with]
pub fn alter_value(&mut self, component: Option<C>) -> &mut Self {
if let Some(component) = component {
self.0 = Some(ArcTypedComponent::new(component));
} else {
self.0 = None;
}
self
}
// OptionComponent GETTERS.
pub fn get(&self) -> Option<ArcTypedComponent<C>> {
if let Some(value) = &self.0 {
return Some(value.clone());
}
None
}
pub fn render(&self, cx: &mut Context) -> Markup {
match &self.0 {
Some(component) => component.render(cx),
_ => html! {},
}
}
}

29
src/html/opt_id.rs Normal file
View file

@ -0,0 +1,29 @@
use crate::{fn_with, SmartDefault};
#[derive(SmartDefault)]
pub struct OptionId(Option<String>);
impl OptionId {
pub fn new(value: impl Into<String>) -> Self {
OptionId::default().with_value(value)
}
// OptionId BUILDER.
#[fn_with]
pub fn alter_value(&mut self, value: impl Into<String>) -> &mut Self {
self.0 = Some(value.into().trim().replace(' ', "_"));
self
}
// OptionId GETTERS.
pub fn get(&self) -> Option<String> {
if let Some(value) = &self.0 {
if !value.is_empty() {
return Some(value.to_owned());
}
}
None
}
}

29
src/html/opt_name.rs Normal file
View file

@ -0,0 +1,29 @@
use crate::{fn_with, SmartDefault};
#[derive(SmartDefault)]
pub struct OptionName(Option<String>);
impl OptionName {
pub fn new(value: impl Into<String>) -> Self {
OptionName::default().with_value(value)
}
// OptionName BUILDER.
#[fn_with]
pub fn alter_value(&mut self, value: impl Into<String>) -> &mut Self {
self.0 = Some(value.into().trim().replace(' ', "_"));
self
}
// OptionName GETTERS.
pub fn get(&self) -> Option<String> {
if let Some(value) = &self.0 {
if !value.is_empty() {
return Some(value.to_owned());
}
}
None
}
}

29
src/html/opt_string.rs Normal file
View file

@ -0,0 +1,29 @@
use crate::{fn_with, SmartDefault};
#[derive(SmartDefault)]
pub struct OptionString(Option<String>);
impl OptionString {
pub fn new(value: impl Into<String>) -> Self {
OptionString::default().with_value(value)
}
// OptionString BUILDER.
#[fn_with]
pub fn alter_value(&mut self, value: impl Into<String>) -> &mut Self {
self.0 = Some(value.into().trim().to_owned());
self
}
// OptionString GETTERS.
pub fn get(&self) -> Option<String> {
if let Some(value) = &self.0 {
if !value.is_empty() {
return Some(value.to_owned());
}
}
None
}
}

View file

@ -0,0 +1,30 @@
use crate::html::Markup;
use crate::locale::{L10n, LanguageIdentifier};
use crate::{fn_with, SmartDefault};
#[derive(SmartDefault)]
pub struct OptionTranslated(L10n);
impl OptionTranslated {
pub fn new(value: L10n) -> Self {
OptionTranslated(value)
}
// OptionTranslated BUILDER.
#[fn_with]
pub fn alter_value(&mut self, value: L10n) -> &mut Self {
self.0 = value;
self
}
// OptionTranslated GETTERS.
pub fn using(&self, langid: &LanguageIdentifier) -> Option<String> {
self.0.using(langid)
}
pub fn escaped(&self, langid: &LanguageIdentifier) -> Markup {
self.0.escaped(langid)
}
}

54
src/html/unit.rs Normal file
View file

@ -0,0 +1,54 @@
use crate::{concat_string, SmartDefault};
// About pixels: Pixels (px) are relative to the viewing device. For low-dpi devices, 1px is one
// device pixel (dot) of the display. For printers and high resolution screens 1px implies multiple
// device pixels.
// About em: 2em means 2 times the size of the current font. The em and rem units are practical in
// creating perfectly scalable layout!
// About viewport: If the browser window size is 50cm wide, 1vw = 0.5cm.
#[rustfmt::skip]
#[derive(SmartDefault)]
pub enum Value {
#[default]
None,
Auto,
Cm(isize), // Centimeters.
In(isize), // Inches (1in = 96px = 2.54cm).
Mm(isize), // Millimeters.
Pc(isize), // Picas (1pc = 12pt).
Pt(isize), // Points (1pt = 1/72 of 1in).
Px(isize), // Pixels (1px = 1/96th of 1in).
RelEm(f32), // Relative to the font-size of the element.
RelPct(f32), // Percentage relative to the parent element.
RelRem(f32), // Relative to font-size of the root element.
RelVh(f32), // Relative to 1% of the height of the viewport.
RelVw(f32), // Relative to 1% of the value of the viewport.
}
#[rustfmt::skip]
impl ToString for Value {
fn to_string(&self) -> String {
match self {
Value::None => "".to_owned(),
Value::Auto => "auto".to_owned(),
// Absolute value.
Value::Cm(av) => concat_string!(av.to_string(), "cm"),
Value::In(av) => concat_string!(av.to_string(), "in"),
Value::Mm(av) => concat_string!(av.to_string(), "mm"),
Value::Pc(av) => concat_string!(av.to_string(), "pc"),
Value::Pt(av) => concat_string!(av.to_string(), "pt"),
Value::Px(av) => concat_string!(av.to_string(), "px"),
// Relative value.
Value::RelEm(rv) => concat_string!(rv.to_string(), "em"),
Value::RelPct(rv) => concat_string!(rv.to_string(), "%"),
Value::RelRem(rv) => concat_string!(rv.to_string(), "rem"),
Value::RelVh(rv) => concat_string!(rv.to_string(), "vh"),
Value::RelVw(rv) => concat_string!(rv.to_string(), "vw"),
}
}
}