CSV reading

This commit is contained in:
Laurenz 2022-07-27 00:09:15 +02:00
parent fc574b3945
commit 9362c279de
11 changed files with 113 additions and 3 deletions

51
Cargo.lock generated
View File

@ -83,6 +83,18 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bstr"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
dependencies = [
"lazy_static",
"memchr",
"regex-automata",
"serde",
]
[[package]]
name = "bytemuck"
version = "1.9.1"
@ -126,6 +138,28 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "csv"
version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1"
dependencies = [
"bstr",
"csv-core",
"itoa 0.4.8",
"ryu",
"serde",
]
[[package]]
name = "csv-core"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
dependencies = [
"memchr",
]
[[package]]
name = "data-url"
version = "0.1.1"
@ -272,6 +306,12 @@ dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]]
name = "itoa"
version = "1.0.2"
@ -431,7 +471,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "249f9b33a3192626f2cd9f4b0cd66c1ec32d65968d58cf4d8239977feddddead"
dependencies = [
"bitflags",
"itoa",
"itoa 1.0.2",
"ryu",
]
@ -547,6 +587,12 @@ dependencies = [
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
[[package]]
name = "regex-syntax"
version = "0.6.26"
@ -665,7 +711,7 @@ version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
dependencies = [
"itoa",
"itoa 1.0.2",
"ryu",
"serde",
]
@ -816,6 +862,7 @@ version = "0.1.0"
dependencies = [
"bytemuck",
"codespan-reporting",
"csv",
"dirs",
"flate2",
"fxhash",

View File

@ -41,6 +41,7 @@ usvg = { version = "0.22", default-features = false }
# External implementation of user-facing features
syntect = { version = "5", default-features = false, features = ["default-syntaxes", "regex-fancy"] }
rex = { git = "https://github.com/laurmaedje/ReX" }
csv = "1"
# PDF export
miniz_oxide = "0.5"

View File

@ -79,6 +79,8 @@ pub fn typeset(ctx: &mut Context, id: SourceId) -> TypResult<Vec<Frame>> {
/// The core context which holds the configuration and stores.
pub struct Context {
/// The loader for fonts and files.
pub loader: Arc<dyn Loader>,
/// Stores loaded source files.
pub sources: SourceStore,
/// Stores parsed font faces.
@ -97,6 +99,7 @@ impl Context {
/// Create a new context.
pub fn new(loader: Arc<dyn Loader>, config: Config) -> Self {
Self {
loader: Arc::clone(&loader),
sources: SourceStore::new(Arc::clone(&loader)),
fonts: FontStore::new(Arc::clone(&loader)),
images: ImageStore::new(loader),

View File

@ -97,6 +97,7 @@ pub fn new() -> Scope {
std.def_fn("roman", utility::roman);
std.def_fn("symbol", utility::symbol);
std.def_fn("lorem", utility::lorem);
std.def_fn("csv", utility::csv);
// Predefined colors.
std.define("black", Color::BLACK);

View File

@ -2,12 +2,15 @@
pub use std::fmt::{self, Debug, Formatter};
pub use std::hash::Hash;
pub use std::io;
pub use std::num::NonZeroUsize;
pub use std::sync::Arc;
pub use typst_macros::node;
pub use crate::diag::{with_alternative, At, Error, StrResult, TypError, TypResult};
pub use crate::diag::{
failed_to_load, with_alternative, At, Error, StrResult, TypError, TypResult,
};
pub use crate::eval::{
Arg, Args, Array, Cast, Dict, Dynamic, Func, Machine, Node, RawAlign, RawLength,
RawStroke, Scope, Smart, Value,

View File

@ -0,0 +1,30 @@
use crate::library::prelude::*;
/// Read structured data from a CSV file.
pub fn csv(vm: &mut Machine, args: &mut Args) -> TypResult<Value> {
let Spanned { v: path, span } =
args.expect::<Spanned<EcoString>>("path to csv file")?;
let path = vm.locate(&path).at(span)?;
let try_load = || -> io::Result<Value> {
let data = vm.ctx.loader.load(&path)?;
let mut builder = csv::ReaderBuilder::new();
builder.has_headers(false);
let mut reader = builder.from_reader(data.as_slice());
let mut vec = vec![];
for result in reader.records() {
vec.push(Value::Array(
result?.iter().map(|field| Value::Str(field.into())).collect(),
))
}
Ok(Value::Array(Array::from_vec(vec)))
};
try_load()
.map_err(|err| failed_to_load("csv file", &path, err))
.at(span)
}

View File

@ -1,10 +1,12 @@
//! Computational utility functions.
mod color;
mod data;
mod math;
mod string;
pub use color::*;
pub use data::*;
pub use math::*;
pub use string::*;

BIN
tests/ref/utility/csv.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

4
tests/res/bad.csv Normal file
View File

@ -0,0 +1,4 @@
A,B
1,2
3,4,5
6,7
1 A,B
2 1,2
3 3,4,5
4 6,7

4
tests/res/zoo.csv Normal file
View File

@ -0,0 +1,4 @@
Name,Species,Weight,Length
Debby,Rhinoceros,1900kg,390cm
Fluffy,Tiger,115kg,310cm
Sleepy,Dolphin,150kg,180cm
1 Name Species Weight Length
2 Debby Rhinoceros 1900kg 390cm
3 Fluffy Tiger 115kg 310cm
4 Sleepy Dolphin 150kg 180cm

15
tests/typ/utility/csv.typ Normal file
View File

@ -0,0 +1,15 @@
// Test reading structured CSV data.
---
#set page(width: auto)
#let data = csv("/res/zoo.csv")
#let cells = data(0).map(strong) + data.slice(1).flatten()
#table(columns: data(0).len(), ..cells)
---
// Error: 6-16 file not found (searched at typ/utility/nope.csv)
#csv("nope.csv")
---
// Error: 6-20 failed to load csv file (CSV error: record 2 (line: 3, byte: 8): found record with 3 fields, but the previous record has 2 fields)
#csv("/res/bad.csv")