Better value representations, type function 🌐

This commit is contained in:
Laurenz 2021-01-04 21:29:15 +01:00
parent 77c06ebc24
commit 2e77b1c836
16 changed files with 227 additions and 173 deletions

View File

@ -1,6 +1,6 @@
//! Color handling. //! Color handling.
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Display, Formatter};
use std::str::FromStr; use std::str::FromStr;
/// A color in a dynamic format. /// A color in a dynamic format.
@ -10,10 +10,18 @@ pub enum Color {
Rgba(RgbaColor), Rgba(RgbaColor),
} }
impl Display for Color {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Rgba(c) => Display::fmt(c, f),
}
}
}
impl Debug for Color { impl Debug for Color {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
Self::Rgba(c) => c.fmt(f), Self::Rgba(c) => Debug::fmt(c, f),
} }
} }
} }
@ -76,6 +84,16 @@ impl FromStr for RgbaColor {
} }
} }
impl Display for RgbaColor {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "#{:02x}{:02x}{:02x}", self.r, self.g, self.b)?;
if self.a != 255 {
write!(f, "{:02x}", self.a)?;
}
Ok(())
}
}
impl Debug for RgbaColor { impl Debug for RgbaColor {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if f.alternate() { if f.alternate() {
@ -83,15 +101,11 @@ impl Debug for RgbaColor {
f, f,
"rgba({:02}, {:02}, {:02}, {:02})", "rgba({:02}, {:02}, {:02}, {:02})",
self.r, self.g, self.b, self.a, self.r, self.g, self.b, self.a,
)?; )
} else { } else {
write!(f, "#{:02x}{:02x}{:02x}", self.r, self.g, self.b)?; Display::fmt(self, f)
if self.a != 255 {
write!(f, "{:02x}", self.a)?;
} }
} }
Ok(())
}
} }
/// The error when parsing an [`RgbaColor`] from a string fails. /// The error when parsing an [`RgbaColor`] from a string fails.

View File

@ -119,15 +119,17 @@ impl EvalContext {
if !children.is_empty() || keep_empty(group.softness) { if !children.is_empty() || keep_empty(group.softness) {
self.runs.push(NodePages { self.runs.push(NodePages {
size: group.size, size: group.size,
child: Node::any(NodePad { child: NodePad {
padding: group.padding, padding: group.padding,
child: Node::any(NodeStack { child: NodeStack {
dirs: group.dirs, dirs: group.dirs,
align: group.align, align: group.align,
expansion: Gen::uniform(Expansion::Fill), expansion: Gen::uniform(Expansion::Fill),
children, children,
}), }
}), .into(),
}
.into(),
}) })
} }
group.softness group.softness

View File

@ -128,23 +128,8 @@ impl StateFont {
impl Default for StateFont { impl Default for StateFont {
fn default() -> Self { fn default() -> Self {
Self { Self {
families: Rc::new(default_font_families()),
variant: FontVariant {
style: FontStyle::Normal,
weight: FontWeight::REGULAR,
stretch: FontStretch::Normal,
},
size: Length::pt(11.0),
scale: Linear::ONE,
strong: false,
emph: false,
}
}
}
/// The default tree of font fallbacks. /// The default tree of font fallbacks.
fn default_font_families() -> FallbackTree { families: Rc::new(fallback! {
fallback! {
list: ["sans-serif"], list: ["sans-serif"],
classes: { classes: {
"serif" => ["source serif pro", "noto serif"], "serif" => ["source serif pro", "noto serif"],
@ -158,5 +143,16 @@ fn default_font_families() -> FallbackTree {
"noto emoji", "noto emoji",
"latin modern math", "latin modern math",
], ],
}),
variant: FontVariant {
style: FontStyle::Normal,
weight: FontWeight::REGULAR,
stretch: FontStretch::Normal,
},
size: Length::pt(11.0),
scale: Linear::ONE,
strong: false,
emph: false,
}
} }
} }

View File

@ -1,6 +1,6 @@
use std::any::Any; use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Display, Formatter};
use std::ops::Deref; use std::ops::Deref;
use std::rc::Rc; use std::rc::Rc;
@ -45,14 +45,6 @@ pub enum Value {
} }
impl Value { impl Value {
/// Create a new dynamic value.
pub fn any<T>(any: T) -> Self
where
T: Type + Debug + Clone + PartialEq + 'static,
{
Self::Any(ValueAny::new(any))
}
/// Try to cast the value into a specific type. /// Try to cast the value into a specific type.
pub fn cast<T>(self) -> CastResult<T, Self> pub fn cast<T>(self) -> CastResult<T, Self>
where where
@ -68,8 +60,8 @@ impl Value {
Self::Bool(_) => bool::TYPE_NAME, Self::Bool(_) => bool::TYPE_NAME,
Self::Int(_) => i64::TYPE_NAME, Self::Int(_) => i64::TYPE_NAME,
Self::Float(_) => f64::TYPE_NAME, Self::Float(_) => f64::TYPE_NAME,
Self::Relative(_) => Relative::TYPE_NAME,
Self::Length(_) => Length::TYPE_NAME, Self::Length(_) => Length::TYPE_NAME,
Self::Relative(_) => Relative::TYPE_NAME,
Self::Linear(_) => Linear::TYPE_NAME, Self::Linear(_) => Linear::TYPE_NAME,
Self::Color(_) => Color::TYPE_NAME, Self::Color(_) => Color::TYPE_NAME,
Self::Str(_) => String::TYPE_NAME, Self::Str(_) => String::TYPE_NAME,
@ -88,16 +80,24 @@ impl Eval for &Value {
/// Evaluate everything contained in this value. /// Evaluate everything contained in this value.
fn eval(self, ctx: &mut EvalContext) -> Self::Output { fn eval(self, ctx: &mut EvalContext) -> Self::Output {
match self { ctx.push(ctx.make_text_node(match self {
// Don't print out none values. Value::None => return,
Value::None => {} Value::Bool(v) => v.to_string(),
Value::Int(v) => v.to_string(),
// Pass through. Value::Float(v) => v.to_string(),
Value::Content(tree) => tree.eval(ctx), Value::Length(v) => v.to_string(),
Value::Relative(v) => v.to_string(),
// Format with debug. Value::Linear(v) => v.to_string(),
val => ctx.push(ctx.make_text_node(format!("{:?}", val))), Value::Color(v) => v.to_string(),
} Value::Str(v) => v.clone(),
// TODO: Find good representation for composite types.
Value::Array(_v) => "(array)".into(),
Value::Dict(_v) => "(dictionary)".into(),
Value::Content(tree) => return tree.eval(ctx),
Value::Func(v) => v.to_string(),
Value::Any(v) => v.to_string(),
Value::Error => "(error)".into(),
}));
} }
} }
@ -110,21 +110,21 @@ impl Default for Value {
impl Debug for Value { impl Debug for Value {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
Self::None => f.pad("none"), Self::None => f.pad("None"),
Self::Bool(v) => v.fmt(f), Self::Bool(v) => Debug::fmt(v, f),
Self::Int(v) => v.fmt(f), Self::Int(v) => Debug::fmt(v, f),
Self::Float(v) => v.fmt(f), Self::Float(v) => Debug::fmt(v, f),
Self::Length(v) => v.fmt(f), Self::Length(v) => Debug::fmt(v, f),
Self::Relative(v) => v.fmt(f), Self::Relative(v) => Debug::fmt(v, f),
Self::Linear(v) => v.fmt(f), Self::Linear(v) => Debug::fmt(v, f),
Self::Color(v) => v.fmt(f), Self::Color(v) => Debug::fmt(v, f),
Self::Str(v) => v.fmt(f), Self::Str(v) => Debug::fmt(v, f),
Self::Array(v) => v.fmt(f), Self::Array(v) => Debug::fmt(v, f),
Self::Dict(v) => v.fmt(f), Self::Dict(v) => Debug::fmt(v, f),
Self::Content(v) => v.fmt(f), Self::Content(v) => Debug::fmt(v, f),
Self::Func(v) => v.fmt(f), Self::Func(v) => Debug::fmt(v, f),
Self::Any(v) => v.fmt(f), Self::Any(v) => Debug::fmt(v, f),
Self::Error => f.pad("<error>"), Self::Error => f.pad("Error"),
} }
} }
} }
@ -140,15 +140,18 @@ pub type ValueContent = Tree;
/// A wrapper around a reference-counted executable function. /// A wrapper around a reference-counted executable function.
#[derive(Clone)] #[derive(Clone)]
pub struct ValueFunc(Rc<dyn Fn(&mut EvalContext, &mut Args) -> Value>); pub struct ValueFunc {
name: String,
f: Rc<dyn Fn(&mut EvalContext, &mut Args) -> Value>,
}
impl ValueFunc { impl ValueFunc {
/// Create a new function value from a rust function or closure. /// Create a new function value from a rust function or closure.
pub fn new<F>(func: F) -> Self pub fn new<F>(name: impl Into<String>, f: F) -> Self
where where
F: Fn(&mut EvalContext, &mut Args) -> Value + 'static, F: Fn(&mut EvalContext, &mut Args) -> Value + 'static,
{ {
Self(Rc::new(func)) Self { name: name.into(), f: Rc::new(f) }
} }
} }
@ -162,13 +165,18 @@ impl Deref for ValueFunc {
type Target = dyn Fn(&mut EvalContext, &mut Args) -> Value; type Target = dyn Fn(&mut EvalContext, &mut Args) -> Value;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
self.0.as_ref() self.f.as_ref()
} }
} }
impl Display for ValueFunc {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "<function {}>", self.name)
}
}
impl Debug for ValueFunc { impl Debug for ValueFunc {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad("<function>") Display::fmt(self, f)
} }
} }
@ -179,7 +187,7 @@ impl ValueAny {
/// Create a new instance from any value that satisifies the required bounds. /// Create a new instance from any value that satisifies the required bounds.
pub fn new<T>(any: T) -> Self pub fn new<T>(any: T) -> Self
where where
T: Type + Debug + Clone + PartialEq + 'static, T: Type + Debug + Display + Clone + PartialEq + 'static,
{ {
Self(Box::new(any)) Self(Box::new(any))
} }
@ -221,13 +229,19 @@ impl PartialEq for ValueAny {
} }
} }
impl Debug for ValueAny { impl Display for ValueAny {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.0.fmt(f) Display::fmt(&self.0, f)
} }
} }
trait Bounds: Debug + 'static { impl Debug for ValueAny {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Debug::fmt(&self.0, f)
}
}
trait Bounds: Debug + Display + 'static {
fn as_any(&self) -> &dyn Any; fn as_any(&self) -> &dyn Any;
fn into_any(self: Box<Self>) -> Box<dyn Any>; fn into_any(self: Box<Self>) -> Box<dyn Any>;
fn dyn_eq(&self, other: &ValueAny) -> bool; fn dyn_eq(&self, other: &ValueAny) -> bool;
@ -237,7 +251,7 @@ trait Bounds: Debug + 'static {
impl<T> Bounds for T impl<T> Bounds for T
where where
T: Type + Debug + Clone + PartialEq + 'static, T: Type + Debug + Display + Clone + PartialEq + 'static,
{ {
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
@ -304,6 +318,16 @@ impl<T, V> CastResult<T, V> {
} }
} }
impl Type for Value {
const TYPE_NAME: &'static str = "value";
}
impl Cast<Value> for Value {
fn cast(value: Value) -> CastResult<Self, Value> {
CastResult::Ok(value)
}
}
impl<T> Cast<Spanned<Value>> for T impl<T> Cast<Spanned<Value>> for T
where where
T: Cast<Value>, T: Cast<Value>,
@ -390,15 +414,6 @@ impl From<&str> for Value {
} }
} }
impl<F> From<F> for Value
where
F: Fn(&mut EvalContext, &mut Args) -> Value + 'static,
{
fn from(func: F) -> Self {
Self::Func(ValueFunc::new(func))
}
}
impl From<ValueAny> for Value { impl From<ValueAny> for Value {
fn from(v: ValueAny) -> Self { fn from(v: ValueAny) -> Self {
Self::Any(v) Self::Any(v)
@ -407,9 +422,8 @@ impl From<ValueAny> for Value {
/// Make a type usable as a [`Value`]. /// Make a type usable as a [`Value`].
/// ///
/// Given a type `T`, this implements the following traits: /// Given a type `T`, this always implements the following traits:
/// - [`Type`] for `T`, /// - [`Type`] for `T`,
/// - [`From<T>`](From) for [`Value`],
/// - [`Cast<Value>`](Cast) for `T`. /// - [`Cast<Value>`](Cast) for `T`.
#[macro_export] #[macro_export]
macro_rules! impl_type { macro_rules! impl_type {
@ -423,12 +437,6 @@ macro_rules! impl_type {
const TYPE_NAME: &'static str = $type_name; const TYPE_NAME: &'static str = $type_name;
} }
impl From<$type> for $crate::eval::Value {
fn from(any: $type) -> Self {
$crate::eval::Value::any(any)
}
}
impl $crate::eval::Cast<$crate::eval::Value> for $type { impl $crate::eval::Cast<$crate::eval::Value> for $type {
fn cast( fn cast(
value: $crate::eval::Value, value: $crate::eval::Value,

View File

@ -90,7 +90,7 @@ impl Display for Length {
} else { } else {
(self.to_cm(), Unit::Cm) (self.to_cm(), Unit::Cm)
}; };
write!(f, "{:.2}{}", val, unit) write!(f, "{}{}", (val * 100.0).round() / 100.0, unit)
} }
} }
@ -214,8 +214,8 @@ mod tests {
#[test] #[test]
fn test_length_formatting() { fn test_length_formatting() {
assert_eq!(Length::pt(-28.34).to_string(), "-1.00cm".to_string()); assert_eq!(Length::pt(-28.34).to_string(), "-1cm".to_string());
assert_eq!(Length::pt(23.0).to_string(), "23.00pt".to_string()); assert_eq!(Length::pt(23.0).to_string(), "23pt".to_string());
assert_eq!(Length::cm(12.728).to_string(), "12.73cm".to_string()); assert_eq!(Length::cm(12.728).to_string(), "12.73cm".to_string());
} }
} }

View File

@ -32,7 +32,7 @@ impl Relative {
impl Display for Relative { impl Display for Relative {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{:.2}%", self.0) write!(f, "{}%", (self.0 * 10000.0).round() / 100.0)
} }
} }

View File

@ -25,8 +25,8 @@ impl Layout for NodeFixed {
} }
} }
impl From<NodeFixed> for Node { impl From<NodeFixed> for NodeAny {
fn from(fixed: NodeFixed) -> Self { fn from(fixed: NodeFixed) -> Self {
Self::any(fixed) Self::new(fixed)
} }
} }

View File

@ -14,16 +14,6 @@ pub enum Node {
Any(NodeAny), Any(NodeAny),
} }
impl Node {
/// Create a new dynamic node.
pub fn any<T>(any: T) -> Self
where
T: Layout + Debug + Clone + PartialEq + 'static,
{
Self::Any(NodeAny::new(any))
}
}
impl Layout for Node { impl Layout for Node {
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted { fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted {
match self { match self {
@ -81,9 +71,12 @@ impl Debug for NodeAny {
} }
} }
impl From<NodeAny> for Node { impl<T> From<T> for Node
fn from(dynamic: NodeAny) -> Self { where
Self::Any(dynamic) T: Into<NodeAny>,
{
fn from(t: T) -> Self {
Self::Any(t.into())
} }
} }

View File

@ -29,9 +29,9 @@ impl Layout for NodePad {
} }
} }
impl From<NodePad> for Node { impl From<NodePad> for NodeAny {
fn from(pad: NodePad) -> Self { fn from(pad: NodePad) -> Self {
Self::any(pad) Self::new(pad)
} }
} }

View File

@ -36,9 +36,9 @@ impl Layout for NodePar {
} }
} }
impl From<NodePar> for Node { impl From<NodePar> for NodeAny {
fn from(par: NodePar) -> Self { fn from(par: NodePar) -> Self {
Self::any(par) Self::new(par)
} }
} }

View File

@ -34,9 +34,9 @@ impl Layout for NodeStack {
} }
} }
impl From<NodeStack> for Node { impl From<NodeStack> for NodeAny {
fn from(stack: NodeStack) -> Self { fn from(stack: NodeStack) -> Self {
Self::any(stack) Self::new(stack)
} }
} }

16
src/library/extend.rs Normal file
View File

@ -0,0 +1,16 @@
use crate::prelude::*;
/// `type`: Find out the name of a value's type.
///
/// # Positional arguments
/// - Any value.
///
/// # Return value
/// The name of the value's type as a string.
pub fn type_(ctx: &mut EvalContext, args: &mut Args) -> Value {
if let Some(value) = args.require::<Value>(ctx, "value") {
value.type_name().into()
} else {
Value::Error
}
}

View File

@ -83,8 +83,8 @@ impl Layout for NodeImage {
} }
} }
impl From<NodeImage> for Node { impl From<NodeImage> for NodeAny {
fn from(image: NodeImage) -> Self { fn from(image: NodeImage) -> Self {
Self::any(image) Self::new(image)
} }
} }

View File

@ -1,3 +1,5 @@
use std::fmt::{self, Display, Formatter};
use crate::eval::Softness; use crate::eval::Softness;
use crate::layout::{Expansion, NodeFixed, NodeSpacing, NodeStack}; use crate::layout::{Expansion, NodeFixed, NodeSpacing, NodeStack};
use crate::paper::{Paper, PaperClass}; use crate::paper::{Paper, PaperClass};
@ -153,7 +155,19 @@ impl Switch for Alignment {
} }
impl_type! { impl_type! {
Alignment: "alignment" Alignment: "alignment",
}
impl Display for Alignment {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(match self {
Self::Left => "left",
Self::Center => "center",
Self::Right => "right",
Self::Top => "top",
Self::Bottom => "bottom",
})
}
} }
/// `box`: Layout content into a box. /// `box`: Layout content into a box.
@ -161,7 +175,7 @@ impl_type! {
/// # Named arguments /// # Named arguments
/// - Width of the box: `width`, of type `linear` relative to parent width. /// - Width of the box: `width`, of type `linear` relative to parent width.
/// - Height of the box: `height`, of type `linear` relative to parent height. /// - Height of the box: `height`, of type `linear` relative to parent height.
pub fn boxed(ctx: &mut EvalContext, args: &mut Args) -> Value { pub fn box_(ctx: &mut EvalContext, args: &mut Args) -> Value {
let snapshot = ctx.state.clone(); let snapshot = ctx.state.clone();
let width = args.get(ctx, "width"); let width = args.get(ctx, "width");
@ -189,7 +203,7 @@ pub fn boxed(ctx: &mut EvalContext, args: &mut Args) -> Value {
ctx.push(NodeFixed { ctx.push(NodeFixed {
width, width,
height, height,
child: Node::any(NodeStack { dirs, align, expansion, children }), child: NodeStack { dirs, align, expansion, children }.into(),
}); });
ctx.state = snapshot; ctx.state = snapshot;

View File

@ -1,67 +1,78 @@
//! The standard library. //! The standard library.
mod extend;
mod insert; mod insert;
mod layout; mod layout;
mod style; mod style;
pub use extend::*;
pub use insert::*; pub use insert::*;
pub use layout::*; pub use layout::*;
pub use style::*; pub use style::*;
use fontdock::{FontStretch, FontStyle, FontWeight}; use fontdock::{FontStretch, FontStyle, FontWeight};
use crate::eval::Scope; use crate::eval::{Scope, ValueAny, ValueFunc};
use crate::geom::Dir; use crate::geom::Dir;
/// The scope containing the standard library. /// The scope containing the standard library.
pub fn _std() -> Scope { pub fn _std() -> Scope {
let mut std = Scope::new(); let mut std = Scope::new();
macro_rules! set {
(func: $name:expr, $func:expr) => {
std.set($name, ValueFunc::new($name, $func))
};
(any: $var:expr, $any:expr) => {
std.set($var, ValueAny::new($any))
};
}
// Functions. // Functions.
std.set("align", align); set!(func: "align", align);
std.set("box", boxed); set!(func: "box", box_);
std.set("font", font); set!(func: "font", font);
std.set("h", h); set!(func: "h", h);
std.set("image", image); set!(func: "image", image);
std.set("page", page); set!(func: "page", page);
std.set("pagebreak", pagebreak); set!(func: "pagebreak", pagebreak);
std.set("rgb", rgb); set!(func: "rgb", rgb);
std.set("v", v); set!(func: "type", type_);
set!(func: "v", v);
// Constants. // Constants.
std.set("left", Alignment::Left); set!(any: "left", Alignment::Left);
std.set("center", Alignment::Center); set!(any: "center", Alignment::Center);
std.set("right", Alignment::Right); set!(any: "right", Alignment::Right);
std.set("top", Alignment::Top); set!(any: "top", Alignment::Top);
std.set("bottom", Alignment::Bottom); set!(any: "bottom", Alignment::Bottom);
std.set("ltr", Dir::LTR); set!(any: "ltr", Dir::LTR);
std.set("rtl", Dir::RTL); set!(any: "rtl", Dir::RTL);
std.set("ttb", Dir::TTB); set!(any: "ttb", Dir::TTB);
std.set("btt", Dir::BTT); set!(any: "btt", Dir::BTT);
std.set("serif", FontFamily::Serif); set!(any: "serif", FontFamily::Serif);
std.set("sans-serif", FontFamily::SansSerif); set!(any: "sans-serif", FontFamily::SansSerif);
std.set("monospace", FontFamily::Monospace); set!(any: "monospace", FontFamily::Monospace);
std.set("normal", FontStyle::Normal); set!(any: "normal", FontStyle::Normal);
std.set("italic", FontStyle::Italic); set!(any: "italic", FontStyle::Italic);
std.set("oblique", FontStyle::Oblique); set!(any: "oblique", FontStyle::Oblique);
std.set("thin", FontWeight::THIN); set!(any: "thin", FontWeight::THIN);
std.set("extralight", FontWeight::EXTRALIGHT); set!(any: "extralight", FontWeight::EXTRALIGHT);
std.set("light", FontWeight::LIGHT); set!(any: "light", FontWeight::LIGHT);
std.set("regular", FontWeight::REGULAR); set!(any: "regular", FontWeight::REGULAR);
std.set("medium", FontWeight::MEDIUM); set!(any: "medium", FontWeight::MEDIUM);
std.set("semibold", FontWeight::SEMIBOLD); set!(any: "semibold", FontWeight::SEMIBOLD);
std.set("bold", FontWeight::BOLD); set!(any: "bold", FontWeight::BOLD);
std.set("extrabold", FontWeight::EXTRABOLD); set!(any: "extrabold", FontWeight::EXTRABOLD);
std.set("black", FontWeight::BLACK); set!(any: "black", FontWeight::BLACK);
std.set("ultra-condensed", FontStretch::UltraCondensed); set!(any: "ultra-condensed", FontStretch::UltraCondensed);
std.set("extra-condensed", FontStretch::ExtraCondensed); set!(any: "extra-condensed", FontStretch::ExtraCondensed);
std.set("condensed", FontStretch::Condensed); set!(any: "condensed", FontStretch::Condensed);
std.set("semi-condensed", FontStretch::SemiCondensed); set!(any: "semi-condensed", FontStretch::SemiCondensed);
std.set("normal", FontStretch::Normal); set!(any: "normal", FontStretch::Normal);
std.set("semi-expanded", FontStretch::SemiExpanded); set!(any: "semi-expanded", FontStretch::SemiExpanded);
std.set("expanded", FontStretch::Expanded); set!(any: "expanded", FontStretch::Expanded);
std.set("extra-expanded", FontStretch::ExtraExpanded); set!(any: "extra-expanded", FontStretch::ExtraExpanded);
std.set("ultra-expanded", FontStretch::UltraExpanded); set!(any: "ultra-expanded", FontStretch::UltraExpanded);
std std
} }

View File

@ -269,14 +269,14 @@ impl Location {
} }
} }
impl Debug for Location {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self, f)
}
}
impl Display for Location { impl Display for Location {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}:{}", self.line, self.column) write!(f, "{}:{}", self.line, self.column)
} }
} }
impl Debug for Location {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self, f)
}
}