mod mtl;
mod obj;
#[cfg(test)]
mod test;
use std::borrow::Cow;
use std::iter::Enumerate;
use std::ops::Index;
use std::ops::IndexMut;
pub use mtl::parse_mtl;
use nom::{
branch::alt,
bytes::complete::tag,
character::complete::digit1,
combinator::{map, opt},
error::Error,
multi::{fold_many0, fold_many1},
Input, Parser,
};
pub use obj::parse_obj;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum TokenizeError {
#[error("Parse Error: `{0}`")]
Parse(String),
}
#[derive(Clone, Debug, PartialEq)]
pub enum Token<'a> {
Ignore,
String(Cow<'a, str>),
Float(f32),
Int(i32),
Slash,
Vertex,
VertexNormal,
VertexTexture,
VertexParam,
Face,
Point,
Line,
MaterialLib,
UseMaterial,
Object,
Group,
Smoothing,
Bevel,
CInterp,
DInterp,
Lod,
ShadowObj,
TraceObj,
TextureMapLib,
UseTextureMap,
Spectral,
Xyz,
NewMaterial,
AmbientColor,
DiffuseColor,
SpecularColor,
EmissiveCoefficient,
SpecularExponent,
Disolved,
Halo,
Transparancy,
TransmissionFactor,
Sharpness,
IndexOfRefraction,
IlluminationModel,
TextureMapAmbient,
TextureMapDiffuse,
TextureMapSpecular,
TextureMapShininess,
TextureMapDisolved,
AntiAliasMap,
DisplacementMap,
Decal,
BumpMap,
ReflectionMap,
ReflectionType,
OptionBlendU,
OptionBlendV,
OptionBumpMultiplier,
OptionBoost,
OptionColorCorrect,
OptionClamp,
OptionIMFChan,
OptionRange,
OptionOffset,
OptionScale,
OptionTurbulence,
OptionTextureResolution,
}
#[derive(Debug, Clone)]
pub struct TokenSet<'a> {
tokens: Vec<Token<'a>>,
}
impl TokenSet<'_> {
pub fn is_empty(&self) -> bool {
self.tokens.is_empty()
}
pub fn split_at(&self, index: usize) -> (Self, Self) {
let (a, b) = self.tokens.split_at(index);
(Self { tokens: a.to_vec() }, Self { tokens: b.to_vec() })
}
pub fn len(&self) -> usize {
self.tokens.len()
}
}
impl<'a> Index<usize> for TokenSet<'a> {
type Output = Token<'a>;
fn index(&self, index: usize) -> &Self::Output {
&self.tokens[index]
}
}
impl IndexMut<usize> for TokenSet<'_> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.tokens[index]
}
}
impl<'a> From<Vec<Token<'a>>> for TokenSet<'a> {
fn from(tokens: Vec<Token<'a>>) -> Self {
Self { tokens }
}
}
impl<'a> AsRef<Vec<Token<'a>>> for TokenSet<'a> {
fn as_ref(&self) -> &Vec<Token<'a>> {
&self.tokens
}
}
impl<'a> Iterator for TokenSet<'a> {
type Item = Token<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.tokens.first().cloned()
}
}
impl<'a> Input for TokenSet<'a> {
type Item = Token<'a>;
type Iter = TokenSet<'a>;
type IterIndices = Enumerate<Self::Iter>;
fn input_len(&self) -> usize {
self.len()
}
fn take(&self, index: usize) -> Self {
Self {
tokens: self.tokens.iter().take(index).cloned().collect(),
}
}
fn take_from(&self, index: usize) -> Self {
Self {
tokens: self.tokens[index..].to_vec(),
}
}
fn take_split(&self, index: usize) -> (Self, Self) {
self.split_at(index)
}
fn position<P>(&self, predicate: P) -> Option<usize>
where
P: Fn(Self::Item) -> bool,
{
self.tokens.iter().position(|t| predicate(t.clone()))
}
fn iter_elements(&self) -> Self::Iter {
self.clone()
}
fn iter_indices(&self) -> Self::IterIndices {
self.iter_elements().enumerate()
}
fn slice_index(&self, count: usize) -> Result<usize, nom::Needed> {
if self.len() >= count {
Ok(count)
} else {
Err(nom::Needed::new(count - self.len()))
}
}
}
fn parse_digit<'a>() -> impl Parser<&'a str, Output = Token<'a>, Error = Error<&'a str>> {
map(
(
opt(alt((tag("+"), tag("-")))),
fold_many1(digit1, Vec::new, |mut acc: Vec<_>, item| {
acc.push(item);
acc
}),
),
|(sign, s): (Option<&str>, Vec<&str>)| {
let mut val = s.join("").parse::<i32>().unwrap_or_default();
if sign == Some("-") {
val *= -1;
}
Token::Int(val)
},
)
}
#[allow(clippy::type_complexity)]
fn parse_float<'a>() -> impl Parser<&'a str, Output = Token<'a>, Error = Error<&'a str>> {
map(
(
opt(alt((tag("+"), tag("-")))),
alt((
map(
(
fold_many0(digit1, Vec::new, |mut acc: Vec<_>, item| {
acc.push(item);
acc
}),
tag("."),
opt(fold_many1(digit1, Vec::new, |mut acc: Vec<_>, item| {
acc.push(item);
acc
})),
opt(map(
(
alt((tag("e"), tag("E"))),
opt(alt((tag("+"), tag("-")))),
digit1,
),
|(e, sign, digits)| {
let mut acc = String::new();
acc.push_str(e);
if let Some(sign) = sign {
acc.push_str(sign);
}
acc.push_str(digits);
acc
},
)),
),
|(f, _, s, e)| (f, s.unwrap_or_default(), e.unwrap_or_default()),
),
map(
(
opt(fold_many1(digit1, Vec::new, |mut acc: Vec<_>, item| {
acc.push(item);
acc
})),
tag("."),
fold_many1(digit1, Vec::new, |mut acc: Vec<_>, item| {
acc.push(item);
acc
}),
opt(map(
(
alt((tag("e"), tag("E"))),
opt(alt((tag("+"), tag("-")))),
digit1,
),
|(e, sign, digits)| {
let mut acc = String::new();
acc.push_str(e);
if let Some(sign) = sign {
acc.push_str(sign);
}
acc.push_str(digits);
acc
},
)),
),
|(f, _, s, e)| (f.unwrap_or_default(), s, e.unwrap_or_default()),
),
)),
),
|(sign, (f, s, e)): (Option<&str>, (Vec<&str>, Vec<&str>, String))| {
let mut acc = Vec::new();
if !f.is_empty() {
acc.extend(f);
}
acc.push(".");
if !s.is_empty() {
acc.extend(s);
}
if !e.is_empty() {
acc.push(e.as_str());
}
let mut val = acc.join("").parse::<f32>().unwrap_or_default();
if sign == Some("-") {
val *= -1.0;
}
Token::Float(val)
},
)
}