Switch to ecow
This commit is contained in:
parent
6e65ebf236
commit
a1d47695a2
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -304,6 +304,11 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ecow"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/typst/ecow#36b624de42f12f90ebad3e0498d770e1700f2e90"
|
||||
|
||||
[[package]]
|
||||
name = "elsa"
|
||||
version = "1.7.0"
|
||||
@ -1166,6 +1171,7 @@ dependencies = [
|
||||
"bitflags",
|
||||
"bytemuck",
|
||||
"comemo",
|
||||
"ecow",
|
||||
"flate2",
|
||||
"if_chain",
|
||||
"image",
|
||||
@ -1238,6 +1244,7 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"comemo",
|
||||
"csv",
|
||||
"ecow",
|
||||
"hypher",
|
||||
"kurbo",
|
||||
"lipsum",
|
||||
@ -1569,7 +1576,8 @@ checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd"
|
||||
[[package]]
|
||||
name = "xmp-writer"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/typst/xmp-writer#0bce4e38395877dad229ea4518d4f78038738155"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fd742bbbb930fc972b28bf66b7546dfbc7bb9a4c7924299df0ae6a5641fcadf"
|
||||
|
||||
[[package]]
|
||||
name = "yaml-front-matter"
|
||||
|
@ -16,6 +16,7 @@ typst-macros = { path = "macros" }
|
||||
bitflags = "1"
|
||||
bytemuck = "1"
|
||||
comemo = { git = "https://github.com/typst/comemo" }
|
||||
ecow = { git = "https://github.com/typst/ecow" }
|
||||
flate2 = "1"
|
||||
if_chain = "1"
|
||||
image = { version = "0.24", default-features = false, features = ["png", "jpeg", "gif"] }
|
||||
@ -40,7 +41,7 @@ unicode-segmentation = "1"
|
||||
unicode-xid = "0.2"
|
||||
unscanny = "0.1"
|
||||
usvg = { version = "0.22", default-features = false }
|
||||
xmp-writer = { git = "https://github.com/typst/xmp-writer" }
|
||||
xmp-writer = "0.1"
|
||||
|
||||
[profile.dev]
|
||||
debug = 0
|
||||
|
@ -13,6 +13,7 @@ bench = false
|
||||
typst = { path = ".." }
|
||||
comemo = { git = "https://github.com/typst/comemo" }
|
||||
csv = "1"
|
||||
ecow = { git = "https://github.com/typst/ecow" }
|
||||
hypher = "0.1"
|
||||
kurbo = "0.8"
|
||||
lipsum = { git = "https://github.com/reknih/lipsum" }
|
||||
|
@ -458,12 +458,12 @@ pub fn range(args: &mut Args) -> SourceResult<Value> {
|
||||
};
|
||||
|
||||
let mut x = start;
|
||||
let mut seq = vec![];
|
||||
let mut array = Array::new();
|
||||
|
||||
while x.cmp(&end) == 0.cmp(&step) {
|
||||
seq.push(Value::Int(x));
|
||||
array.push(Value::Int(x));
|
||||
x += step;
|
||||
}
|
||||
|
||||
Ok(Value::Array(Array::from_vec(seq)))
|
||||
Ok(Value::Array(array))
|
||||
}
|
||||
|
@ -86,15 +86,15 @@ pub fn csv(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
}
|
||||
|
||||
let mut reader = builder.from_reader(data.as_slice());
|
||||
let mut vec = vec![];
|
||||
let mut array = Array::new();
|
||||
|
||||
for result in reader.records() {
|
||||
let row = result.map_err(format_csv_error).at(span)?;
|
||||
let array = row.iter().map(|field| Value::Str(field.into())).collect();
|
||||
vec.push(Value::Array(array))
|
||||
let sub = row.iter().map(|field| Value::Str(field.into())).collect();
|
||||
array.push(Value::Array(sub))
|
||||
}
|
||||
|
||||
Ok(Value::Array(Array::from_vec(vec)))
|
||||
Ok(Value::Array(array))
|
||||
}
|
||||
|
||||
/// The delimiter to use when parsing CSV files.
|
||||
|
@ -8,6 +8,8 @@ pub use std::num::NonZeroUsize;
|
||||
#[doc(no_inline)]
|
||||
pub use comemo::{Track, Tracked, TrackedMut};
|
||||
#[doc(no_inline)]
|
||||
pub use ecow::{format_eco, EcoString};
|
||||
#[doc(no_inline)]
|
||||
pub use typst::diag::{bail, error, At, SourceResult, StrResult};
|
||||
#[doc(no_inline)]
|
||||
pub use typst::doc::*;
|
||||
@ -23,8 +25,6 @@ pub use typst::model::{
|
||||
#[doc(no_inline)]
|
||||
pub use typst::syntax::{Span, Spanned};
|
||||
#[doc(no_inline)]
|
||||
pub use typst::util::{format_eco, EcoString};
|
||||
#[doc(no_inline)]
|
||||
pub use typst::World;
|
||||
|
||||
#[doc(no_inline)]
|
||||
|
@ -18,7 +18,6 @@ use std::borrow::Cow;
|
||||
|
||||
use rustybuzz::Tag;
|
||||
use typst::font::{FontMetrics, FontStretch, FontStyle, FontWeight, VerticalFontMetric};
|
||||
use typst::util::EcoString;
|
||||
|
||||
use crate::layout::ParNode;
|
||||
use crate::prelude::*;
|
||||
|
@ -1,5 +1,4 @@
|
||||
use typst::model::SequenceNode;
|
||||
use typst::util::EcoString;
|
||||
|
||||
use super::{variant, SpaceNode, TextNode, TextSize};
|
||||
use crate::prelude::*;
|
||||
|
@ -8,9 +8,9 @@ use std::str::Utf8Error;
|
||||
use std::string::FromUtf8Error;
|
||||
|
||||
use comemo::Tracked;
|
||||
use ecow::EcoString;
|
||||
|
||||
use crate::syntax::{ErrorPos, Span, Spanned};
|
||||
use crate::util::{format_eco, EcoString};
|
||||
use crate::World;
|
||||
|
||||
/// Early-return with a [`SourceError`].
|
||||
@ -38,12 +38,14 @@ macro_rules! __error {
|
||||
};
|
||||
|
||||
($span:expr, $fmt:expr, $($arg:expr),+ $(,)?) => {
|
||||
$crate::diag::error!($span, $crate::util::format_eco!($fmt, $($arg),+))
|
||||
$crate::diag::error!($span, $crate::diag::format_eco!($fmt, $($arg),+))
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(inline)]
|
||||
pub use crate::__error as error;
|
||||
#[doc(hidden)]
|
||||
pub use ecow::format_eco;
|
||||
|
||||
/// A result that can carry multiple source errors.
|
||||
pub type SourceResult<T> = Result<T, Box<Vec<SourceError>>>;
|
||||
|
@ -5,6 +5,8 @@ use std::num::NonZeroUsize;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use ecow::EcoString;
|
||||
|
||||
use crate::font::Font;
|
||||
use crate::geom::{
|
||||
self, rounded_rect, Abs, Align, Axes, Color, Corners, Dir, Em, Geometry, Numeric,
|
||||
@ -14,7 +16,6 @@ use crate::image::Image;
|
||||
use crate::model::{
|
||||
capable, dict, node, Content, Dict, Fold, StableId, StyleChain, Value,
|
||||
};
|
||||
use crate::util::EcoString;
|
||||
|
||||
/// A finished document with metadata and page frames.
|
||||
#[derive(Debug, Default, Clone, Hash)]
|
||||
|
@ -1,11 +1,12 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use ecow::format_eco;
|
||||
use pdf_writer::types::{CidFontType, FontFlags, SystemInfo, UnicodeCmap};
|
||||
use pdf_writer::{Filter, Finish, Name, Rect, Str};
|
||||
use ttf_parser::{name_id, GlyphId, Tag};
|
||||
|
||||
use super::{deflate, EmExt, PdfContext, RefExt};
|
||||
use crate::util::{format_eco, SliceExt};
|
||||
use crate::util::SliceExt;
|
||||
|
||||
/// Embed all used fonts into the PDF.
|
||||
pub fn write_fonts(ctx: &mut PdfContext) {
|
||||
|
@ -1,8 +1,8 @@
|
||||
use ecow::EcoString;
|
||||
use pdf_writer::{Finish, Ref, TextStr};
|
||||
|
||||
use super::{AbsExt, PdfContext, RefExt};
|
||||
use crate::geom::{Abs, Point};
|
||||
use crate::util::EcoString;
|
||||
|
||||
/// A heading in the outline panel.
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -1,3 +1,4 @@
|
||||
use ecow::format_eco;
|
||||
use pdf_writer::types::{ActionType, AnnotationType, ColorSpaceOperand};
|
||||
use pdf_writer::writers::ColorSpace;
|
||||
use pdf_writer::{Content, Filter, Finish, Name, Rect, Ref, Str};
|
||||
@ -10,7 +11,6 @@ use crate::geom::{
|
||||
Transform,
|
||||
};
|
||||
use crate::image::Image;
|
||||
use crate::util::format_eco;
|
||||
|
||||
/// Construct page objects.
|
||||
pub fn construct_pages(ctx: &mut PdfContext, frames: &[Frame]) {
|
||||
|
@ -1,11 +1,11 @@
|
||||
use std::collections::{BTreeSet, HashSet};
|
||||
|
||||
use ecow::{format_eco, EcoString};
|
||||
use if_chain::if_chain;
|
||||
|
||||
use super::{analyze_expr, analyze_import, plain_docs_sentence, summarize_font_family};
|
||||
use crate::model::{methods_on, CastInfo, Scope, Value};
|
||||
use crate::syntax::{ast, LinkedNode, Source, SyntaxKind};
|
||||
use crate::util::{format_eco, EcoString};
|
||||
use crate::World;
|
||||
|
||||
/// Autocomplete a cursor position in a source file.
|
||||
|
@ -1,31 +1,33 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::{self, Debug, Formatter, Write};
|
||||
use std::ops::{Add, AddAssign};
|
||||
use std::sync::Arc;
|
||||
|
||||
use ecow::{format_eco, EcoString, EcoVec};
|
||||
|
||||
use super::{ops, Args, Func, Value, Vm};
|
||||
use crate::diag::{bail, At, SourceResult, StrResult};
|
||||
use crate::util::{format_eco, ArcExt, EcoString};
|
||||
|
||||
/// Create a new [`Array`] from values.
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __array {
|
||||
($value:expr; $count:expr) => {
|
||||
$crate::model::Array::from_vec(vec![$value.into(); $count])
|
||||
$crate::model::Array::from_vec($crate::model::eco_vec![$value.into(); $count])
|
||||
};
|
||||
|
||||
($($value:expr),* $(,)?) => {
|
||||
$crate::model::Array::from_vec(vec![$($value.into()),*])
|
||||
$crate::model::Array::from_vec($crate::model::eco_vec![$($value.into()),*])
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(inline)]
|
||||
pub use crate::__array as array;
|
||||
#[doc(hidden)]
|
||||
pub use ecow::eco_vec;
|
||||
|
||||
/// A reference counted array with value semantics.
|
||||
#[derive(Default, Clone, PartialEq, Hash)]
|
||||
pub struct Array(Arc<Vec<Value>>);
|
||||
pub struct Array(EcoVec<Value>);
|
||||
|
||||
impl Array {
|
||||
/// Create a new, empty array.
|
||||
@ -33,9 +35,9 @@ impl Array {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Create a new array from a vector of values.
|
||||
pub fn from_vec(vec: Vec<Value>) -> Self {
|
||||
Self(Arc::new(vec))
|
||||
/// Create a new array from an eco vector of values.
|
||||
pub fn from_vec(vec: EcoVec<Value>) -> Self {
|
||||
Self(vec)
|
||||
}
|
||||
|
||||
/// The length of the array.
|
||||
@ -50,7 +52,7 @@ impl Array {
|
||||
|
||||
/// Mutably borrow the first value in the array.
|
||||
pub fn first_mut(&mut self) -> StrResult<&mut Value> {
|
||||
Arc::make_mut(&mut self.0).first_mut().ok_or_else(array_is_empty)
|
||||
self.0.make_mut().first_mut().ok_or_else(array_is_empty)
|
||||
}
|
||||
|
||||
/// The last value in the array.
|
||||
@ -60,7 +62,7 @@ impl Array {
|
||||
|
||||
/// Mutably borrow the last value in the array.
|
||||
pub fn last_mut(&mut self) -> StrResult<&mut Value> {
|
||||
Arc::make_mut(&mut self.0).last_mut().ok_or_else(array_is_empty)
|
||||
self.0.make_mut().last_mut().ok_or_else(array_is_empty)
|
||||
}
|
||||
|
||||
/// Borrow the value at the given index.
|
||||
@ -74,18 +76,18 @@ impl Array {
|
||||
pub fn at_mut(&mut self, index: i64) -> StrResult<&mut Value> {
|
||||
let len = self.len();
|
||||
self.locate(index)
|
||||
.and_then(move |i| Arc::make_mut(&mut self.0).get_mut(i))
|
||||
.and_then(move |i| self.0.make_mut().get_mut(i))
|
||||
.ok_or_else(|| out_of_bounds(index, len))
|
||||
}
|
||||
|
||||
/// Push a value to the end of the array.
|
||||
pub fn push(&mut self, value: Value) {
|
||||
Arc::make_mut(&mut self.0).push(value);
|
||||
self.0.push(value);
|
||||
}
|
||||
|
||||
/// Remove the last value in the array.
|
||||
pub fn pop(&mut self) -> StrResult<Value> {
|
||||
Arc::make_mut(&mut self.0).pop().ok_or_else(array_is_empty)
|
||||
self.0.pop().ok_or_else(array_is_empty)
|
||||
}
|
||||
|
||||
/// Insert a value at the specified index.
|
||||
@ -96,7 +98,7 @@ impl Array {
|
||||
.filter(|&i| i <= self.0.len())
|
||||
.ok_or_else(|| out_of_bounds(index, len))?;
|
||||
|
||||
Arc::make_mut(&mut self.0).insert(i, value);
|
||||
self.0.insert(i, value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -108,7 +110,7 @@ impl Array {
|
||||
.filter(|&i| i < self.0.len())
|
||||
.ok_or_else(|| out_of_bounds(index, len))?;
|
||||
|
||||
Ok(Arc::make_mut(&mut self.0).remove(i))
|
||||
Ok(self.0.remove(i))
|
||||
}
|
||||
|
||||
/// Extract a contigous subregion of the array.
|
||||
@ -126,7 +128,7 @@ impl Array {
|
||||
.ok_or_else(|| out_of_bounds(end, len))?
|
||||
.max(start);
|
||||
|
||||
Ok(Self::from_vec(self.0[start..end].to_vec()))
|
||||
Ok(Self::from_vec(self.0[start..end].into()))
|
||||
}
|
||||
|
||||
/// Whether the array contains a specific value.
|
||||
@ -170,7 +172,7 @@ impl Array {
|
||||
if func.argc().map_or(false, |count| count != 1) {
|
||||
bail!(func.span(), "function must have exactly one parameter");
|
||||
}
|
||||
let mut kept = vec![];
|
||||
let mut kept = EcoVec::new();
|
||||
for item in self.iter() {
|
||||
let args = Args::new(func.span(), [item.clone()]);
|
||||
if func.call(vm, args)?.cast::<bool>().at(func.span())? {
|
||||
@ -244,7 +246,7 @@ impl Array {
|
||||
|
||||
/// Return a new array with all items from this and nested arrays.
|
||||
pub fn flatten(&self) -> Self {
|
||||
let mut flat = Vec::with_capacity(self.0.len());
|
||||
let mut flat = EcoVec::with_capacity(self.0.len());
|
||||
for item in self.iter() {
|
||||
if let Value::Array(nested) = item {
|
||||
flat.extend(nested.flatten().into_iter());
|
||||
@ -287,8 +289,8 @@ impl Array {
|
||||
/// Returns an error if two values could not be compared.
|
||||
pub fn sorted(&self) -> StrResult<Self> {
|
||||
let mut result = Ok(());
|
||||
let mut vec = (*self.0).clone();
|
||||
vec.sort_by(|a, b| {
|
||||
let mut vec = self.0.clone();
|
||||
vec.make_mut().sort_by(|a, b| {
|
||||
a.partial_cmp(b).unwrap_or_else(|| {
|
||||
if result.is_ok() {
|
||||
result = Err(format_eco!(
|
||||
@ -369,31 +371,28 @@ impl Add for Array {
|
||||
|
||||
impl AddAssign for Array {
|
||||
fn add_assign(&mut self, rhs: Array) {
|
||||
match Arc::try_unwrap(rhs.0) {
|
||||
Ok(vec) => self.extend(vec),
|
||||
Err(rc) => self.extend(rc.iter().cloned()),
|
||||
}
|
||||
self.0.extend(rhs.0);
|
||||
}
|
||||
}
|
||||
|
||||
impl Extend<Value> for Array {
|
||||
fn extend<T: IntoIterator<Item = Value>>(&mut self, iter: T) {
|
||||
Arc::make_mut(&mut self.0).extend(iter);
|
||||
self.0.extend(iter);
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<Value> for Array {
|
||||
fn from_iter<T: IntoIterator<Item = Value>>(iter: T) -> Self {
|
||||
Self(Arc::new(iter.into_iter().collect()))
|
||||
Self(iter.into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Array {
|
||||
type Item = Value;
|
||||
type IntoIter = std::vec::IntoIter<Value>;
|
||||
type IntoIter = ecow::IntoIter<Value>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
Arc::take(self.0).into_iter()
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,8 @@ use std::num::NonZeroUsize;
|
||||
use std::ops::Add;
|
||||
use std::str::FromStr;
|
||||
|
||||
use ecow::EcoString;
|
||||
|
||||
use super::{
|
||||
castable, Array, Content, Dict, Func, Label, Regex, Selector, Str, Transform, Value,
|
||||
};
|
||||
@ -13,7 +15,6 @@ use crate::geom::{
|
||||
Rel, Sides, Smart,
|
||||
};
|
||||
use crate::syntax::Spanned;
|
||||
use crate::util::EcoString;
|
||||
|
||||
/// Cast from a value to a specific type.
|
||||
pub trait Cast<V = Value>: Sized {
|
||||
|
@ -6,6 +6,7 @@ use std::ops::{Add, AddAssign};
|
||||
use std::sync::Arc;
|
||||
|
||||
use comemo::Tracked;
|
||||
use ecow::EcoString;
|
||||
use siphasher::sip128::{Hasher128, SipHasher};
|
||||
use thin_vec::ThinVec;
|
||||
use typst_macros::node;
|
||||
@ -16,7 +17,7 @@ use super::{
|
||||
};
|
||||
use crate::diag::{SourceResult, StrResult};
|
||||
use crate::syntax::Span;
|
||||
use crate::util::{EcoString, ReadableTypeId};
|
||||
use crate::util::ReadableTypeId;
|
||||
use crate::World;
|
||||
|
||||
/// Composable representation of styled content.
|
||||
|
@ -3,10 +3,12 @@ use std::fmt::{self, Debug, Formatter, Write};
|
||||
use std::ops::{Add, AddAssign};
|
||||
use std::sync::Arc;
|
||||
|
||||
use ecow::{format_eco, EcoString};
|
||||
|
||||
use super::{array, Array, Str, Value};
|
||||
use crate::diag::StrResult;
|
||||
use crate::syntax::is_ident;
|
||||
use crate::util::{format_eco, ArcExt, EcoString};
|
||||
use crate::util::ArcExt;
|
||||
|
||||
/// Create a new [`Dict`] from key-value pairs.
|
||||
#[macro_export]
|
||||
|
@ -5,6 +5,7 @@ use std::mem;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use comemo::{Track, Tracked, TrackedMut};
|
||||
use ecow::EcoVec;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
use super::{
|
||||
@ -797,7 +798,7 @@ impl Eval for ast::Array {
|
||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||
let items = self.items();
|
||||
|
||||
let mut vec = Vec::with_capacity(items.size_hint().0);
|
||||
let mut vec = EcoVec::with_capacity(items.size_hint().0);
|
||||
for item in items {
|
||||
match item {
|
||||
ast::ArrayItem::Pos(expr) => vec.push(expr.eval(vm)?),
|
||||
|
@ -3,6 +3,7 @@ use std::hash::{Hash, Hasher};
|
||||
use std::sync::Arc;
|
||||
|
||||
use comemo::{Track, Tracked, TrackedMut};
|
||||
use ecow::EcoString;
|
||||
|
||||
use super::{
|
||||
Args, CastInfo, Dict, Eval, Flow, Node, NodeId, Route, Scope, Scopes, Selector,
|
||||
@ -11,7 +12,6 @@ use super::{
|
||||
use crate::diag::{bail, SourceResult, StrResult};
|
||||
use crate::syntax::ast::{self, AstNode, Expr};
|
||||
use crate::syntax::{SourceId, Span, SyntaxNode};
|
||||
use crate::util::EcoString;
|
||||
use crate::World;
|
||||
|
||||
/// An evaluatable function.
|
||||
|
@ -2,13 +2,14 @@ use std::fmt::{self, Debug, Formatter};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::num::NonZeroUsize;
|
||||
|
||||
use ecow::EcoString;
|
||||
use once_cell::sync::OnceCell;
|
||||
|
||||
use super::{Content, Module, NodeId, StyleChain, StyleMap, Vt};
|
||||
use crate::diag::SourceResult;
|
||||
use crate::doc::Document;
|
||||
use crate::geom::{Abs, Dir};
|
||||
use crate::util::{hash128, EcoString};
|
||||
use crate::util::hash128;
|
||||
|
||||
/// Definition of Typst's standard library.
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
|
@ -1,9 +1,10 @@
|
||||
//! Methods on values.
|
||||
|
||||
use ecow::EcoString;
|
||||
|
||||
use super::{Args, Str, Value, Vm};
|
||||
use crate::diag::{At, SourceResult};
|
||||
use crate::syntax::Span;
|
||||
use crate::util::EcoString;
|
||||
|
||||
/// Call a method on a value.
|
||||
pub fn call(
|
||||
|
@ -1,9 +1,10 @@
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
use std::sync::Arc;
|
||||
|
||||
use ecow::{format_eco, EcoString};
|
||||
|
||||
use super::{Content, Scope, Value};
|
||||
use crate::diag::StrResult;
|
||||
use crate::util::{format_eco, EcoString};
|
||||
|
||||
/// An evaluated module, ready for importing or typesetting.
|
||||
#[derive(Clone, Hash)]
|
||||
|
@ -2,10 +2,11 @@
|
||||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use ecow::format_eco;
|
||||
|
||||
use super::{format_str, Regex, Value};
|
||||
use crate::diag::StrResult;
|
||||
use crate::geom::{Axes, Axis, GenAlign, Length, Numeric, PartialStroke, Rel, Smart};
|
||||
use crate::util::format_eco;
|
||||
use Value::*;
|
||||
|
||||
/// Bail with a type mismatch error.
|
||||
|
@ -2,9 +2,10 @@ use std::collections::BTreeMap;
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
use std::hash::Hash;
|
||||
|
||||
use ecow::EcoString;
|
||||
|
||||
use super::{Func, FuncType, Library, Value};
|
||||
use crate::diag::StrResult;
|
||||
use crate::util::EcoString;
|
||||
|
||||
/// A stack of scopes.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
|
@ -3,24 +3,26 @@ use std::fmt::{self, Debug, Display, Formatter, Write};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::{Add, AddAssign, Deref};
|
||||
|
||||
use ecow::EcoString;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
use super::{castable, dict, Array, Dict, Value};
|
||||
use crate::diag::StrResult;
|
||||
use crate::geom::GenAlign;
|
||||
use crate::util::{format_eco, EcoString};
|
||||
|
||||
/// Create a new [`Str`] from a format string.
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __format_str {
|
||||
($($tts:tt)*) => {{
|
||||
$crate::model::Str::from(format_eco!($($tts)*))
|
||||
$crate::model::Str::from($crate::model::format_eco!($($tts)*))
|
||||
}};
|
||||
}
|
||||
|
||||
#[doc(inline)]
|
||||
pub use crate::__format_str as format_str;
|
||||
#[doc(hidden)]
|
||||
pub use ecow::format_eco;
|
||||
|
||||
/// An immutable reference counted string.
|
||||
#[derive(Default, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
@ -407,11 +409,13 @@ impl From<String> for Str {
|
||||
Self(s.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Cow<'_, str>> for Str {
|
||||
fn from(s: Cow<str>) -> Self {
|
||||
Self(s.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<char> for Str {
|
||||
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
|
||||
Self(iter.into_iter().collect())
|
||||
|
@ -3,8 +3,9 @@ use std::collections::BTreeSet;
|
||||
use std::fmt::{self, Debug, Display, Formatter, Write};
|
||||
use std::sync::Arc;
|
||||
|
||||
use ecow::EcoString;
|
||||
|
||||
use crate::diag::StrResult;
|
||||
use crate::util::EcoString;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use typst_macros::symbols;
|
||||
|
@ -4,6 +4,7 @@ use std::fmt::{self, Debug, Formatter};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::sync::Arc;
|
||||
|
||||
use ecow::{format_eco, EcoString};
|
||||
use siphasher::sip128::{Hasher128, SipHasher};
|
||||
|
||||
use super::{
|
||||
@ -13,7 +14,6 @@ use super::{
|
||||
use crate::diag::StrResult;
|
||||
use crate::geom::{Abs, Angle, Color, Em, Fr, Length, Ratio, Rel, RgbaColor};
|
||||
use crate::syntax::{ast, Span};
|
||||
use crate::util::{format_eco, EcoString};
|
||||
|
||||
/// A computational value.
|
||||
#[derive(Clone)]
|
||||
|
@ -5,13 +5,13 @@
|
||||
use std::num::NonZeroUsize;
|
||||
use std::ops::Deref;
|
||||
|
||||
use ecow::EcoString;
|
||||
use unscanny::Scanner;
|
||||
|
||||
use super::{
|
||||
is_id_continue, is_id_start, is_newline, split_newlines, Span, SyntaxKind, SyntaxNode,
|
||||
};
|
||||
use crate::geom::{AbsUnit, AngleUnit};
|
||||
use crate::util::EcoString;
|
||||
|
||||
/// A typed AST node.
|
||||
pub trait AstNode: Sized {
|
||||
|
@ -1,9 +1,9 @@
|
||||
use ecow::{format_eco, EcoString};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
use unicode_xid::UnicodeXID;
|
||||
use unscanny::Scanner;
|
||||
|
||||
use super::{ErrorPos, SyntaxKind};
|
||||
use crate::util::{format_eco, EcoString};
|
||||
|
||||
/// Splits up a string of source code into tokens.
|
||||
#[derive(Clone)]
|
||||
|
@ -3,10 +3,11 @@ use std::ops::{Deref, Range};
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use ecow::EcoString;
|
||||
|
||||
use super::ast::AstNode;
|
||||
use super::{SourceId, Span, SyntaxKind};
|
||||
use crate::diag::SourceError;
|
||||
use crate::util::EcoString;
|
||||
|
||||
/// A node in the untyped syntax tree.
|
||||
#[derive(Clone, PartialEq, Hash)]
|
||||
|
@ -1,10 +1,10 @@
|
||||
use std::collections::HashSet;
|
||||
use std::ops::Range;
|
||||
|
||||
use ecow::{format_eco, EcoString};
|
||||
use unicode_math_class::MathClass;
|
||||
|
||||
use super::{ast, is_newline, ErrorPos, LexMode, Lexer, SyntaxKind, SyntaxNode};
|
||||
use crate::util::{format_eco, EcoString};
|
||||
|
||||
/// Parse a source file.
|
||||
pub fn parse(text: &str) -> SyntaxNode {
|
||||
|
@ -6,7 +6,7 @@ use comemo::Prehashed;
|
||||
|
||||
/// A shared buffer that is cheap to clone and hash.
|
||||
#[derive(Clone, Hash, Eq, PartialEq)]
|
||||
pub struct Buffer(Prehashed<Arc<Vec<u8>>>);
|
||||
pub struct Buffer(Arc<Prehashed<Vec<u8>>>);
|
||||
|
||||
impl Buffer {
|
||||
/// Return a view into the buffer.
|
||||
@ -22,19 +22,13 @@ impl Buffer {
|
||||
|
||||
impl From<&[u8]> for Buffer {
|
||||
fn from(slice: &[u8]) -> Self {
|
||||
Self(Prehashed::new(Arc::new(slice.to_vec())))
|
||||
Self(Arc::new(Prehashed::new(slice.to_vec())))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for Buffer {
|
||||
fn from(vec: Vec<u8>) -> Self {
|
||||
Self(Prehashed::new(Arc::new(vec)))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Arc<Vec<u8>>> for Buffer {
|
||||
fn from(arc: Arc<Vec<u8>>) -> Self {
|
||||
Self(Prehashed::new(arc))
|
||||
Self(Arc::new(Prehashed::new(vec)))
|
||||
}
|
||||
}
|
||||
|
||||
|
515
src/util/eco.rs
515
src/util/eco.rs
@ -1,515 +0,0 @@
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::{self, Debug, Display, Formatter, Write};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::{Add, AddAssign, Deref};
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::ArcExt;
|
||||
|
||||
/// Create a new [`EcoString`] from a format string.
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __format_eco {
|
||||
($($tts:tt)*) => {{
|
||||
use std::fmt::Write;
|
||||
let mut s = $crate::util::EcoString::new();
|
||||
write!(s, $($tts)*).unwrap();
|
||||
s
|
||||
}};
|
||||
}
|
||||
|
||||
#[doc(inline)]
|
||||
pub use crate::__format_eco as format_eco;
|
||||
|
||||
/// An economical string with inline storage and clone-on-write semantics.
|
||||
#[derive(Clone)]
|
||||
pub struct EcoString(Repr);
|
||||
|
||||
/// The internal representation. Either:
|
||||
/// - inline when below a certain number of bytes, or
|
||||
/// - reference-counted on the heap with clone-on-write semantics.
|
||||
#[derive(Clone)]
|
||||
enum Repr {
|
||||
Small { buf: [u8; LIMIT], len: u8 },
|
||||
Large(Arc<String>),
|
||||
}
|
||||
|
||||
/// The maximum number of bytes that can be stored inline.
|
||||
///
|
||||
/// The value is chosen such that an `EcoString` fits exactly into 16 bytes
|
||||
/// (which are needed anyway due to the `Arc`s alignment, at least on 64-bit
|
||||
/// platforms).
|
||||
///
|
||||
/// Must be at least 4 to hold any char.
|
||||
const LIMIT: usize = 14;
|
||||
|
||||
impl EcoString {
|
||||
/// Create a new, empty string.
|
||||
pub const fn new() -> Self {
|
||||
Self(Repr::Small { buf: [0; LIMIT], len: 0 })
|
||||
}
|
||||
|
||||
/// Create a new, empty string with the given `capacity`.
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
if capacity <= LIMIT {
|
||||
Self::new()
|
||||
} else {
|
||||
Self(Repr::Large(Arc::new(String::with_capacity(capacity))))
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an instance from an existing string-like type.
|
||||
pub fn from_str_like<S>(s: S) -> Self
|
||||
where
|
||||
S: AsRef<str> + Into<String>,
|
||||
{
|
||||
let slice = s.as_ref();
|
||||
let len = slice.len();
|
||||
Self(if len <= LIMIT {
|
||||
let mut buf = [0; LIMIT];
|
||||
buf[..len].copy_from_slice(slice.as_bytes());
|
||||
Repr::Small { buf, len: len as u8 }
|
||||
} else {
|
||||
Repr::Large(Arc::new(s.into()))
|
||||
})
|
||||
}
|
||||
|
||||
/// Whether the string is empty.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// The length of the string in bytes.
|
||||
pub fn len(&self) -> usize {
|
||||
match &self.0 {
|
||||
Repr::Small { len, .. } => usize::from(*len),
|
||||
Repr::Large(string) => string.len(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A string slice containing the entire string.
|
||||
pub fn as_str(&self) -> &str {
|
||||
self
|
||||
}
|
||||
|
||||
/// Append the given character at the end.
|
||||
pub fn push(&mut self, c: char) {
|
||||
match &mut self.0 {
|
||||
Repr::Small { buf, len } => {
|
||||
let prev = usize::from(*len);
|
||||
if c.len_utf8() == 1 && prev < LIMIT {
|
||||
buf[prev] = c as u8;
|
||||
*len += 1;
|
||||
} else {
|
||||
self.push_str(c.encode_utf8(&mut [0; 4]));
|
||||
}
|
||||
}
|
||||
Repr::Large(rc) => Arc::make_mut(rc).push(c),
|
||||
}
|
||||
}
|
||||
|
||||
/// Append the given string slice at the end.
|
||||
pub fn push_str(&mut self, string: &str) {
|
||||
match &mut self.0 {
|
||||
Repr::Small { buf, len } => {
|
||||
let prev = usize::from(*len);
|
||||
let new = prev + string.len();
|
||||
if new <= LIMIT {
|
||||
buf[prev..new].copy_from_slice(string.as_bytes());
|
||||
*len = new as u8;
|
||||
} else {
|
||||
let mut spilled = String::with_capacity(new);
|
||||
spilled.push_str(self);
|
||||
spilled.push_str(string);
|
||||
self.0 = Repr::Large(Arc::new(spilled));
|
||||
}
|
||||
}
|
||||
Repr::Large(rc) => Arc::make_mut(rc).push_str(string),
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove the last character from the string.
|
||||
pub fn pop(&mut self) -> Option<char> {
|
||||
let c = self.as_str().chars().rev().next()?;
|
||||
match &mut self.0 {
|
||||
Repr::Small { len, .. } => {
|
||||
*len -= c.len_utf8() as u8;
|
||||
}
|
||||
Repr::Large(rc) => {
|
||||
Arc::make_mut(rc).pop();
|
||||
}
|
||||
}
|
||||
Some(c)
|
||||
}
|
||||
|
||||
/// Clear the string.
|
||||
pub fn clear(&mut self) {
|
||||
match &mut self.0 {
|
||||
Repr::Small { len, .. } => *len = 0,
|
||||
Repr::Large(rc) => {
|
||||
if Arc::strong_count(rc) == 1 {
|
||||
Arc::make_mut(rc).clear();
|
||||
} else {
|
||||
*self = Self::new();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the string to lowercase.
|
||||
pub fn to_lowercase(&self) -> Self {
|
||||
if let Repr::Small { mut buf, len } = self.0 {
|
||||
if self.is_ascii() {
|
||||
buf[..usize::from(len)].make_ascii_lowercase();
|
||||
return Self(Repr::Small { buf, len });
|
||||
}
|
||||
}
|
||||
|
||||
self.as_str().to_lowercase().into()
|
||||
}
|
||||
|
||||
/// Convert the string to uppercase.
|
||||
pub fn to_uppercase(&self) -> Self {
|
||||
if let Repr::Small { mut buf, len } = self.0 {
|
||||
if self.is_ascii() {
|
||||
buf[..usize::from(len)].make_ascii_uppercase();
|
||||
return Self(Repr::Small { buf, len });
|
||||
}
|
||||
}
|
||||
|
||||
self.as_str().to_uppercase().into()
|
||||
}
|
||||
|
||||
/// Repeat this string `n` times.
|
||||
pub fn repeat(&self, n: usize) -> Self {
|
||||
if n == 0 {
|
||||
return Self::new();
|
||||
}
|
||||
|
||||
if let Repr::Small { buf, len } = self.0 {
|
||||
let prev = usize::from(len);
|
||||
let new = prev.saturating_mul(n);
|
||||
if new <= LIMIT {
|
||||
let src = &buf[..prev];
|
||||
let mut buf = [0; LIMIT];
|
||||
for i in 0..n {
|
||||
buf[prev * i..prev * (i + 1)].copy_from_slice(src);
|
||||
}
|
||||
return Self(Repr::Small { buf, len: new as u8 });
|
||||
}
|
||||
}
|
||||
|
||||
self.as_str().repeat(n).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for EcoString {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &str {
|
||||
match &self.0 {
|
||||
// Safety:
|
||||
// The buffer contents stem from correct UTF-8 sources:
|
||||
// - Valid ASCII characters
|
||||
// - Other string slices
|
||||
// - Chars that were encoded with char::encode_utf8
|
||||
// Furthermore, we still do the bounds-check on the len in case
|
||||
// it gets corrupted somehow.
|
||||
Repr::Small { buf, len } => unsafe {
|
||||
std::str::from_utf8_unchecked(&buf[..usize::from(*len)])
|
||||
},
|
||||
Repr::Large(string) => string.as_str(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for EcoString {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for EcoString {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
Debug::fmt(self.as_str(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for EcoString {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
Display::fmt(self.as_str(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for EcoString {}
|
||||
|
||||
impl PartialEq for EcoString {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.as_str().eq(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<str> for EcoString {
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
self.as_str().eq(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<&str> for EcoString {
|
||||
fn eq(&self, other: &&str) -> bool {
|
||||
self.as_str().eq(*other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<EcoString> for str {
|
||||
fn eq(&self, other: &EcoString) -> bool {
|
||||
self.eq(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<EcoString> for &str {
|
||||
fn eq(&self, other: &EcoString) -> bool {
|
||||
(*self).eq(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for EcoString {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.as_str().cmp(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for EcoString {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.as_str().partial_cmp(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for EcoString {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.as_str().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for EcoString {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
self.push_str(s);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_char(&mut self, c: char) -> fmt::Result {
|
||||
self.push(c);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for EcoString {
|
||||
type Output = Self;
|
||||
|
||||
fn add(mut self, rhs: Self) -> Self::Output {
|
||||
self += rhs;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for EcoString {
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
self.push_str(rhs.as_str());
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for EcoString {
|
||||
fn as_ref(&self) -> &str {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for EcoString {
|
||||
fn borrow(&self) -> &str {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<char> for EcoString {
|
||||
fn from(c: char) -> Self {
|
||||
let mut buf = [0; LIMIT];
|
||||
let len = c.encode_utf8(&mut buf).len();
|
||||
Self(Repr::Small { buf, len: len as u8 })
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for EcoString {
|
||||
fn from(s: &str) -> Self {
|
||||
Self::from_str_like(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for EcoString {
|
||||
fn from(s: String) -> Self {
|
||||
Self::from_str_like(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Cow<'_, str>> for EcoString {
|
||||
fn from(s: Cow<str>) -> Self {
|
||||
match s {
|
||||
Cow::Borrowed(s) => s.into(),
|
||||
Cow::Owned(s) => s.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<char> for EcoString {
|
||||
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
|
||||
let mut s = Self::new();
|
||||
for c in iter {
|
||||
s.push(c);
|
||||
}
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<Self> for EcoString {
|
||||
fn from_iter<T: IntoIterator<Item = Self>>(iter: T) -> Self {
|
||||
let mut s = Self::new();
|
||||
for piece in iter {
|
||||
s.push_str(&piece);
|
||||
}
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
impl Extend<char> for EcoString {
|
||||
fn extend<T: IntoIterator<Item = char>>(&mut self, iter: T) {
|
||||
for c in iter {
|
||||
self.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EcoString> for String {
|
||||
fn from(s: EcoString) -> Self {
|
||||
match s.0 {
|
||||
Repr::Small { .. } => s.as_str().to_owned(),
|
||||
Repr::Large(rc) => Arc::take(rc),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&EcoString> for String {
|
||||
fn from(s: &EcoString) -> Self {
|
||||
s.as_str().to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const ALPH: &str = "abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
#[test]
|
||||
fn test_str_new() {
|
||||
// Test inline strings.
|
||||
assert_eq!(EcoString::new(), "");
|
||||
assert_eq!(EcoString::from('a'), "a");
|
||||
assert_eq!(EcoString::from('😀'), "😀");
|
||||
assert_eq!(EcoString::from("abc"), "abc");
|
||||
|
||||
// Test around the inline limit.
|
||||
assert_eq!(EcoString::from(&ALPH[..LIMIT - 1]), ALPH[..LIMIT - 1]);
|
||||
assert_eq!(EcoString::from(&ALPH[..LIMIT]), ALPH[..LIMIT]);
|
||||
assert_eq!(EcoString::from(&ALPH[..LIMIT + 1]), ALPH[..LIMIT + 1]);
|
||||
|
||||
// Test heap string.
|
||||
assert_eq!(EcoString::from(ALPH), ALPH);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_str_push() {
|
||||
let mut v = EcoString::new();
|
||||
v.push('a');
|
||||
v.push('b');
|
||||
v.push_str("cd😀");
|
||||
assert_eq!(v, "abcd😀");
|
||||
assert_eq!(v.len(), 8);
|
||||
|
||||
// Test fully filling the inline storage.
|
||||
v.push_str("efghij");
|
||||
assert_eq!(v.len(), LIMIT);
|
||||
|
||||
// Test spilling with `push`.
|
||||
let mut a = v.clone();
|
||||
a.push('k');
|
||||
assert_eq!(a, "abcd😀efghijk");
|
||||
assert_eq!(a.len(), 15);
|
||||
|
||||
// Test spilling with `push_str`.
|
||||
let mut b = v.clone();
|
||||
b.push_str("klmn");
|
||||
assert_eq!(b, "abcd😀efghijklmn");
|
||||
assert_eq!(b.len(), 18);
|
||||
|
||||
// v should be unchanged.
|
||||
assert_eq!(v.len(), LIMIT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_str_pop() {
|
||||
// Test with inline string.
|
||||
let mut v = EcoString::from("Hello World!");
|
||||
assert_eq!(v.pop(), Some('!'));
|
||||
assert_eq!(v, "Hello World");
|
||||
|
||||
// Remove one-by-one.
|
||||
for _ in 0..10 {
|
||||
v.pop();
|
||||
}
|
||||
|
||||
assert_eq!(v, "H");
|
||||
assert_eq!(v.pop(), Some('H'));
|
||||
assert_eq!(v, "");
|
||||
assert!(v.is_empty());
|
||||
|
||||
// Test with large string.
|
||||
let mut v = EcoString::from(ALPH);
|
||||
assert_eq!(v.pop(), Some('z'));
|
||||
assert_eq!(v.len(), 25);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_str_index() {
|
||||
// Test that we can use the index syntax.
|
||||
let v = EcoString::from("abc");
|
||||
assert_eq!(&v[..2], "ab");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_str_case() {
|
||||
assert_eq!(EcoString::new().to_uppercase(), "");
|
||||
assert_eq!(EcoString::from("abc").to_uppercase(), "ABC");
|
||||
assert_eq!(EcoString::from("AΣ").to_lowercase(), "aς");
|
||||
assert_eq!(
|
||||
EcoString::from("a").repeat(100).to_uppercase(),
|
||||
EcoString::from("A").repeat(100)
|
||||
);
|
||||
assert_eq!(
|
||||
EcoString::from("Ö").repeat(20).to_lowercase(),
|
||||
EcoString::from("ö").repeat(20)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_str_repeat() {
|
||||
// Test with empty string.
|
||||
assert_eq!(EcoString::new().repeat(0), "");
|
||||
assert_eq!(EcoString::new().repeat(100), "");
|
||||
|
||||
// Test non-spilling and spilling case.
|
||||
let v = EcoString::from("abc");
|
||||
assert_eq!(v.repeat(0), "");
|
||||
assert_eq!(v.repeat(3), "abcabcabc");
|
||||
assert_eq!(v.repeat(5), "abcabcabcabcabc");
|
||||
}
|
||||
}
|
@ -2,12 +2,9 @@
|
||||
|
||||
pub mod fat;
|
||||
|
||||
#[macro_use]
|
||||
mod eco;
|
||||
mod buffer;
|
||||
|
||||
pub use buffer::Buffer;
|
||||
pub use eco::{format_eco, EcoString};
|
||||
|
||||
use std::any::TypeId;
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
|
Loading…
Reference in New Issue
Block a user