#![allow(unused_imports)]
#![allow(dead_code)]
#[macro_use]
extern crate nom;
#[macro_use]
extern crate log;
extern crate cgmath;
use nom::{
bytes::complete::{tag, take_until, take_while1, take_while_m_n},
character::{
complete::{alphanumeric1 as alphanumeric, digit1, line_ending as eol},
is_alphabetic, is_alphanumeric, is_digit,
},
combinator::map_res,
error::ErrorKind,
multi::many0,
number::complete::float,
sequence::{terminated, tuple},
IResult, InputTakeAtPosition,
};
use std::{
collections::{HashMap, HashSet},
num::ParseIntError,
str::{self, from_utf8, FromStr},
};
pub type Vec3 = cgmath::Vector3<f32>;
pub type Vec4 = cgmath::Vector4<f32>;
pub type Mat4 = cgmath::Matrix4<f32>;
#[cfg(feature = "cgmath")]
pub type Vec4 = cgmath::Vector4<f32>;
#[cfg(feature = "cgmath")]
pub type Mat4 = cgmath::Matrix4<f32>;
pub mod error;
pub use error::{Error, ParseError, ResolveError};
fn is_space(chr: u8) -> bool {
chr == b'\t' || chr == b' '
}
named!(take_spaces, take_while!(is_space));
fn end_of_line(input: &[u8]) -> IResult<&[u8], &[u8]> {
if input.is_empty() {
Ok((input, input))
} else {
eol(input)
}
}
#[inline]
fn is_cr_or_lf(chr: u8) -> bool {
chr == b'\n' || chr == b'\r'
}
fn take_not_cr_or_lf(input: &[u8]) -> IResult<&[u8], &[u8]> {
input.split_at_position_complete(|item| is_cr_or_lf(item))
}
fn single_comma(input: &[u8]) -> IResult<&[u8], &[u8]> {
if !input.is_empty() && (input[0] == b',') {
Ok((&input[1..], &input[..1]))
} else {
Err(nom::Err::Error((input, nom::error::ErrorKind::Tag)))
}
}
fn take_not_comma_or_eol(input: &[u8]) -> IResult<&[u8], &[u8]> {
input.split_at_position_complete(|item| item == b',' || is_cr_or_lf(item))
}
fn take_not_space(input: &[u8]) -> IResult<&[u8], &[u8]> {
input.split_at_position_complete(|item| is_space(item))
}
fn read_cmd_id_str(input: &[u8]) -> IResult<&[u8], &[u8]> {
let (ii, o) = input.split_at_position1_complete(|item| !is_digit(item), ErrorKind::Digit)?;
let (i, _) = space0(ii)?;
Ok((i, o))
}
named!(
category<Command>,
do_parse!(
tag!(b"!CATEGORY")
>> sp
>> content: take_not_cr_or_lf
>> (Command::Category(CategoryCmd {
category: std::str::from_utf8(content).unwrap().to_string()
}))
)
);
named!(
keywords_list<Vec<&[u8]>>,
separated_nonempty_list!(single_comma, take_not_comma_or_eol)
);
named!(
keywords<Command>,
do_parse!(
tag!(b"!KEYWORDS")
>> sp
>> keywords: keywords_list
>> (Command::Keywords(KeywordsCmd {
keywords: keywords
.iter()
.map(|&kw| std::str::from_utf8(kw).unwrap().trim().to_string())
.collect()
}))
)
);
#[derive(Debug, PartialEq)]
pub struct Color {
pub red: u8,
pub green: u8,
pub blue: u8,
}
impl Color {
pub fn new(red: u8, green: u8, blue: u8) -> Color {
Color { red, green, blue }
}
}
fn from_hex(input: &[u8]) -> Result<u8, nom::error::ErrorKind> {
match std::str::from_utf8(input) {
Ok(s) => match u8::from_str_radix(s, 16) {
Ok(val) => Ok(val),
Err(_) => Err(ErrorKind::AlphaNumeric),
},
Err(_) => Err(ErrorKind::AlphaNumeric),
}
}
fn is_hex_digit(c: u8) -> bool {
(c as char).is_digit(16)
}
fn hex_primary(input: &[u8]) -> IResult<&[u8], u8> {
map_res(take_while_m_n(2, 2, is_hex_digit), from_hex)(input)
}
fn hex_color(input: &[u8]) -> IResult<&[u8], Color> {
let (input, _) = tag(b"#")(input)?;
let (input, (red, green, blue)) = tuple((hex_primary, hex_primary, hex_primary))(input)?;
Ok((input, Color { red, green, blue }))
}
named!(
digit1_as_u8<u8>,
map_res!(map_res!(digit1, str::from_utf8), str::parse::<u8>)
);
named!(
colour_alpha<Option<u8>>,
opt!(complete!(do_parse!(
sp >> tag!(b"ALPHA") >> sp >> alpha: digit1_as_u8 >> (alpha)
)))
);
named!(
colour_luminance<Option<u8>>,
opt!(complete!(do_parse!(
sp >> tag!(b"LUMINANCE") >> sp >> luminance: digit1_as_u8 >> (luminance)
)))
);
named!(
material_grain_size<GrainSize>,
alt!(
do_parse!(tag!(b"SIZE") >> sp >> size: float >> (GrainSize::Size(size)))
| do_parse!(
tag!(b"MINSIZE")
>> sp
>> min_size: float
>> sp
>> tag!(b"MAXSIZE")
>> sp
>> max_size: float
>> (GrainSize::MinMaxSize((min_size, max_size)))
)
)
);
named!(
glitter_material<ColorFinish>,
do_parse!(
tag_no_case!(b"GLITTER")
>> sp
>> tag_no_case!(b"VALUE")
>> sp
>> value: hex_color
>> alpha: colour_alpha
>> luminance: colour_luminance
>> sp
>> tag_no_case!(b"FRACTION")
>> sp
>> surface_fraction: float
>> sp
>> tag_no_case!(b"VFRACTION")
>> sp
>> volume_fraction: float
>> sp
>> grain_size: material_grain_size
>> (ColorFinish::Material(MaterialFinish::Glitter(GlitterMaterial {
value,
alpha,
luminance,
surface_fraction,
volume_fraction,
size: grain_size
})))
)
);
named!(
speckle_material<ColorFinish>,
do_parse!(
tag_no_case!(b"SPECKLE")
>> sp
>> tag_no_case!(b"VALUE")
>> sp
>> value: hex_color
>> alpha: colour_alpha
>> luminance: colour_luminance
>> sp
>> tag_no_case!(b"FRACTION")
>> sp
>> surface_fraction: float
>> sp
>> grain_size: material_grain_size
>> (ColorFinish::Material(MaterialFinish::Speckle(SpeckleMaterial {
value,
alpha,
luminance,
surface_fraction,
size: grain_size
})))
)
);
named!(
other_material<ColorFinish>,
do_parse!(
raw_content: take_not_cr_or_lf
>> (ColorFinish::Material(MaterialFinish::Other(
str::from_utf8(raw_content).unwrap().trim().to_string()
)))
)
);
named!(
material_finish<ColorFinish>,
do_parse!(
tag_no_case!(b"MATERIAL")
>> material_finish: alt!(glitter_material | speckle_material | other_material)
>> (material_finish)
)
);
named!(
color_finish<Option<ColorFinish>>,
opt!(complete!(do_parse!(
sp >> color_finish:
alt!(
tag_no_case!(b"CHROME") => { |_| ColorFinish::Chrome } |
tag_no_case!(b"PEARLESCENT") => { |_| ColorFinish::Pearlescent } |
tag_no_case!(b"RUBBER") => { |_| ColorFinish::Rubber } |
tag_no_case!(b"MATTE_METALLIC") => { |_| ColorFinish::MatteMetallic } |
tag_no_case!(b"METAL") => { |_| ColorFinish::Metal } |
material_finish
)
>> (color_finish)
)))
);
named!(
meta_colour<Command>,
do_parse!(
tag!(b"!COLOUR")
>> sp
>> name: take_not_space
>> sp
>> tag!(b"CODE")
>> sp
>> code: color_id
>> sp
>> tag!(b"VALUE")
>> sp
>> value: hex_color
>> sp
>> tag!(b"EDGE")
>> sp
>> edge: hex_color
>> alpha: colour_alpha
>> luminance: colour_luminance
>> finish: color_finish
>> (Command::Colour(ColourCmd {
name: std::str::from_utf8(name).unwrap().to_string(),
code,
value,
edge,
alpha,
luminance,
finish: finish
}))
)
);
named!(
comment<Command>,
do_parse!(
content: take_not_cr_or_lf
>> (Command::Comment(CommentCmd::new(std::str::from_utf8(content).unwrap())))
)
);
named!(
meta_cmd<Command>,
alt!(complete!(category) | complete!(keywords) | complete!(meta_colour) | comment)
);
named!(
read_vec3<Vec3>,
do_parse!(x: float >> sp >> y: float >> sp >> z: float >> (Vec3 { x: x, y: y, z: z }))
);
named!(
color_id<u32>,
map_res!(map_res!(digit1, str::from_utf8), str::parse::<u32>)
);
#[inline]
fn is_filename_char(chr: u8) -> bool {
is_alphanumeric(chr) || chr == b'/' || chr == b'\\' || chr == b'.' || chr == b'-'
}
fn filename_char(input: &[u8]) -> IResult<&[u8], &[u8]> {
input.split_at_position1_complete(|item| !is_filename_char(item), ErrorKind::AlphaNumeric)
}
named!(filename<&str>, map_res!(filename_char, str::from_utf8));
named!(
file_ref_cmd<Command>,
do_parse!(
color: color_id
>> sp
>> pos: read_vec3
>> sp
>> row0: read_vec3
>> sp
>> row1: read_vec3
>> sp
>> row2: read_vec3
>> sp
>> file: filename
>> (Command::SubFileRef(SubFileRefCmd {
color: color,
pos: pos,
row0: row0,
row1: row1,
row2: row2,
file: SubFileRef::UnresolvedRef(file.to_string())
}))
)
);
named!(
line_cmd<Command>,
do_parse!(
color: color_id
>> sp
>> v1: read_vec3
>> sp
>> v2: read_vec3
>> space0
>> (Command::Line(LineCmd {
color: color,
vertices: [v1, v2]
}))
)
);
named!(
tri_cmd<Command>,
do_parse!(
color: color_id
>> sp
>> v1: read_vec3
>> sp
>> v2: read_vec3
>> sp
>> v3: read_vec3
>> space0
>> (Command::Triangle(TriangleCmd {
color: color,
vertices: [v1, v2, v3]
}))
)
);
named!(
quad_cmd<Command>,
do_parse!(
color: color_id
>> sp
>> v1: read_vec3
>> sp
>> v2: read_vec3
>> sp
>> v3: read_vec3
>> sp
>> v4: read_vec3
>> space0
>> (Command::Quad(QuadCmd {
color: color,
vertices: [v1, v2, v3, v4]
}))
)
);
named!(
opt_line_cmd<Command>,
do_parse!(
color: color_id
>> sp
>> v1: read_vec3
>> sp
>> v2: read_vec3
>> sp
>> v3: read_vec3
>> sp
>> v4: read_vec3
>> space0
>> (Command::OptLine(OptLineCmd {
color: color,
vertices: [v1, v2],
control_points: [v3, v4]
}))
)
);
fn space0(input: &[u8]) -> IResult<&[u8], &[u8]> {
input.split_at_position_complete(|item| !is_space(item))
}
fn sp(input: &[u8]) -> IResult<&[u8], &[u8]> {
input.split_at_position1_complete(|item| !is_space(item), ErrorKind::Space)
}
fn space_or_eol0(input: &[u8]) -> IResult<&[u8], &[u8]> {
input.split_at_position_complete(|item| !is_space(item) && !is_cr_or_lf(item))
}
named!(empty_line, terminated!(space0, end_of_line));
named!(
read_line<Command>,
do_parse!(
space_or_eol0
>> cmd: switch!(read_cmd_id_str,
b"0" => call!(meta_cmd) |
b"1" => call!(file_ref_cmd) |
b"2" => call!(line_cmd) |
b"3" => call!(tri_cmd) |
b"4" => call!(quad_cmd) |
b"5" => call!(opt_line_cmd)
)
>> end_of_line
>> (cmd)
)
);
pub fn parse_raw(ldr_content: &[u8]) -> Result<Vec<Command>, Error> {
parse_raw_with_filename("", ldr_content)
}
fn parse_raw_with_filename(filename: &str, ldr_content: &[u8]) -> Result<Vec<Command>, Error> {
many0(read_line)(ldr_content).map_or_else(
|e| Err(Error::Parse(ParseError::new_from_nom(filename, &e))),
|(_, cmds)| Ok(cmds),
)
}
struct QueuedFileRef {
filename: String,
referer: SourceFileRef,
}
struct ResolveQueue {
queue: Vec<QueuedFileRef>,
pending_count: HashMap<String, u32>,
}
#[derive(Debug, Copy, Clone)]
pub struct DrawContext {
pub transform: Mat4,
pub color: u32,
}
pub struct CommandIterator<'a> {
stack: Vec<(&'a SourceFile, usize, DrawContext)>,
source_map: &'a SourceMap,
}
pub struct LocalCommandIterator<'a> {
stack: Vec<&'a SourceFile>,
index: usize,
source_map: &'a SourceMap,
}
impl SourceFile {
pub fn iter<'a>(&'a self, source_map: &'a SourceMap) -> CommandIterator<'a> {
let draw_ctx = DrawContext {
transform: Mat4::from_scale(1.0),
color: 16,
};
CommandIterator {
stack: vec![(&self, 0, draw_ctx)],
source_map,
}
}
pub fn local_iter<'a>(&'a self, source_map: &'a SourceMap) -> LocalCommandIterator<'a> {
LocalCommandIterator {
stack: vec![&self],
index: 0,
source_map,
}
}
}
impl<'a> Iterator for CommandIterator<'a> {
type Item = (DrawContext, &'a Command);
fn next(&mut self) -> Option<(DrawContext, &'a Command)> {
while let Some(entry) = self.stack.last_mut() {
let cmds = &entry.0.cmds;
let index = &mut entry.1;
let draw_ctx = &entry.2;
if *index < cmds.len() {
let cmd = &cmds[*index];
*index += 1;
if let Command::SubFileRef(sfr_cmd) = &cmd {
if let SubFileRef::ResolvedRef(resolved_ref) = &sfr_cmd.file {
let source_file_2 = resolved_ref.get(self.source_map);
let local_transform = Mat4::from_cols(
Vec4::new(sfr_cmd.row0.x, sfr_cmd.row1.x, sfr_cmd.row2.x, 0.0),
Vec4::new(sfr_cmd.row0.y, sfr_cmd.row1.y, sfr_cmd.row2.y, 0.0),
Vec4::new(sfr_cmd.row0.z, sfr_cmd.row1.z, sfr_cmd.row2.z, 0.0),
Vec4::new(sfr_cmd.pos.x, sfr_cmd.pos.y, sfr_cmd.pos.z, 1.0),
);
let draw_ctx = DrawContext {
transform: draw_ctx.transform * local_transform,
color: 16,
};
self.stack.push((source_file_2, 0, draw_ctx));
continue;
}
} else if let Command::Comment(_) = &cmd {
continue;
}
return Some((*draw_ctx, cmd));
}
self.stack.pop();
}
None
}
}
impl<'a> Iterator for LocalCommandIterator<'a> {
type Item = &'a Command;
fn next(&mut self) -> Option<&'a Command> {
while let Some(source_file) = self.stack.last() {
let cmds = &source_file.cmds;
if self.index < cmds.len() {
let index = self.index;
self.index += 1;
return Some(&cmds[index]);
}
self.index = 0;
self.stack.pop();
}
None
}
}
impl ResolveQueue {
fn new() -> ResolveQueue {
return ResolveQueue {
queue: vec![],
pending_count: HashMap::new(),
};
}
fn push(&mut self, filename: &str, referer_filename: &str, referer: SourceFileRef) {
if let Some(num_pending) = self.pending_count.get_mut(referer_filename) {
assert!(*num_pending > 0);
*num_pending += 1;
} else {
self.pending_count.insert(referer_filename.to_string(), 1);
}
self.queue.push(QueuedFileRef {
filename: filename.to_string(),
referer,
});
}
fn pop(&mut self, source_map: &SourceMap) -> Option<(QueuedFileRef, u32)> {
match self.queue.pop() {
Some(qfr) => {
let num_pending = self
.pending_count
.get_mut(&qfr.referer.get(source_map).filename)
.unwrap();
*num_pending -= 1;
Some((qfr, *num_pending))
}
None => None,
}
}
fn reset(&mut self) {
self.queue.clear();
self.pending_count.clear();
}
}
fn load_and_parse_single_file(
filename: &str,
resolver: &dyn FileRefResolver,
) -> Result<SourceFile, Error> {
let raw_content = resolver.resolve(filename)?;
let mut source_file = SourceFile {
filename: filename.to_string(),
raw_content,
cmds: Vec::new(),
};
let cmds = parse_raw_with_filename(&source_file.filename[..], &source_file.raw_content[..])?;
source_file.cmds = cmds;
Ok(source_file)
}
pub fn parse(
filename: &str,
resolver: &dyn FileRefResolver,
source_map: &mut SourceMap,
) -> Result<SourceFileRef, Error> {
if let Some(existing_file) = source_map.find_filename(filename) {
return Ok(existing_file);
}
debug!("Processing root file '{}'", filename);
let root_file = load_and_parse_single_file(filename, resolver)?;
trace!(
"Post-loading resolving subfile refs of root file: {}",
filename
);
let root_file_ref = source_map.insert(root_file);
let mut queue = ResolveQueue::new();
source_map.resolve_file_refs(root_file_ref, &mut queue);
while let Some(queued_file) = queue.pop(source_map) {
let num_pending_left = queued_file.1;
let filename = &queued_file.0.filename;
debug!("Processing sub-file: '{}'", filename);
match source_map.find_filename(filename) {
Some(_) => trace!("Already parsed; reusing sub-file: {}", filename),
None => {
trace!("Not yet parsed; parsing sub-file: {}", filename);
let source_file = load_and_parse_single_file(&filename[..], resolver)?;
let source_file_ref = source_map.insert(source_file);
trace!(
"Post-loading resolving subfile refs of sub-file: {}",
filename
);
source_map.resolve_file_refs(source_file_ref, &mut queue);
}
}
if num_pending_left == 0 {
trace!(
"Re-resolving referer file on last resolved ref: {}",
queued_file.0.referer.get(source_map).filename
);
source_map.resolve_file_refs(queued_file.0.referer, &mut queue);
}
}
Ok(root_file_ref)
}
#[derive(Debug, PartialEq)]
pub struct CategoryCmd {
pub category: String,
}
#[derive(Debug, PartialEq)]
pub struct KeywordsCmd {
pub keywords: Vec<String>,
}
#[derive(Debug, PartialEq)]
pub enum ColorFinish {
Chrome,
Pearlescent,
Rubber,
MatteMetallic,
Metal,
Material(MaterialFinish),
}
#[derive(Debug, PartialEq)]
pub enum MaterialFinish {
Glitter(GlitterMaterial),
Speckle(SpeckleMaterial),
Other(String),
}
#[derive(Debug, PartialEq)]
pub enum GrainSize {
Size(f32),
MinMaxSize((f32, f32)),
}
#[derive(Debug, PartialEq)]
pub struct GlitterMaterial {
pub value: Color,
pub alpha: Option<u8>,
pub luminance: Option<u8>,
pub surface_fraction: f32,
pub volume_fraction: f32,
pub size: GrainSize,
}
#[derive(Debug, PartialEq)]
pub struct SpeckleMaterial {
pub value: Color,
pub alpha: Option<u8>,
pub luminance: Option<u8>,
pub surface_fraction: f32,
pub size: GrainSize,
}
#[derive(Debug, PartialEq)]
pub struct ColourCmd {
pub name: String,
pub code: u32,
pub value: Color,
pub edge: Color,
pub alpha: Option<u8>,
pub luminance: Option<u8>,
pub finish: Option<ColorFinish>,
}
#[derive(Debug, PartialEq)]
pub struct CommentCmd {
pub text: String,
}
#[derive(Debug, PartialEq)]
pub struct SourceFile {
pub filename: String,
pub raw_content: Vec<u8>,
pub cmds: Vec<Command>,
}
#[derive(Debug)]
pub struct SourceMap {
source_files: Vec<SourceFile>,
filename_map: HashMap<String, usize>,
}
#[derive(Debug, Copy, Clone, Default, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct SourceFileRef {
index: usize,
}
impl CommentCmd {
pub fn new(text: &str) -> CommentCmd {
CommentCmd {
text: text.to_string(),
}
}
}
impl SourceFileRef {
pub fn get<'a>(&'a self, source_map: &'a SourceMap) -> &'a SourceFile {
source_map.get(*self)
}
}
impl SourceMap {
pub fn new() -> SourceMap {
SourceMap {
source_files: vec![],
filename_map: HashMap::new(),
}
}
fn get(&self, source_file_ref: SourceFileRef) -> &SourceFile {
&self.source_files[source_file_ref.index]
}
fn get_mut(&mut self, source_file_ref: SourceFileRef) -> &mut SourceFile {
&mut self.source_files[source_file_ref.index]
}
fn find_filename(&self, filename: &str) -> Option<SourceFileRef> {
match self.filename_map.get(filename) {
Some(&index) => Some(SourceFileRef { index }),
None => None,
}
}
fn insert(&mut self, source_file: SourceFile) -> SourceFileRef {
if let Some(&index) = self.filename_map.get(&source_file.filename) {
return SourceFileRef { index };
} else {
let index = self.source_files.len();
self.filename_map
.insert(source_file.filename.clone(), index);
self.source_files.push(source_file);
return SourceFileRef { index };
}
}
fn resolve_file_refs(&mut self, source_file_ref: SourceFileRef, queue: &mut ResolveQueue) {
let mut filename_map = HashMap::new();
std::mem::swap(&mut filename_map, &mut self.filename_map);
let source_file = self.get_mut(source_file_ref);
let referer_filename = source_file.filename.clone();
let mut subfile_set = HashSet::new();
for cmd in &mut source_file.cmds {
if let Command::SubFileRef(sfr_cmd) = cmd {
if let SubFileRef::UnresolvedRef(subfilename) = &sfr_cmd.file {
let subfilename = subfilename.clone();
if let Some(existing_source_file) = filename_map
.get(&subfilename)
.map(|&index| SourceFileRef { index })
{
trace!(
"Updating resolved subfile ref in {} -> {}",
referer_filename,
subfilename
);
sfr_cmd.file = SubFileRef::ResolvedRef(existing_source_file);
} else if subfile_set.contains(&subfilename) {
trace!(
"Ignoring already-queued unresolved subfile ref in {} -> {}",
referer_filename,
subfilename
);
} else {
trace!(
"Queuing unresolved subfile ref in {} -> {}",
referer_filename,
subfilename
);
queue.push(&subfilename[..], &referer_filename[..], source_file_ref);
subfile_set.insert(subfilename);
}
}
}
}
std::mem::swap(&mut filename_map, &mut self.filename_map);
}
}
#[derive(Debug, PartialEq)]
pub enum SubFileRef {
ResolvedRef(SourceFileRef),
UnresolvedRef(String),
}
#[derive(Debug, PartialEq)]
pub struct SubFileRefCmd {
pub color: u32,
pub pos: Vec3,
pub row0: Vec3,
pub row1: Vec3,
pub row2: Vec3,
pub file: SubFileRef,
}
#[derive(Debug, PartialEq)]
pub struct LineCmd {
pub color: u32,
pub vertices: [Vec3; 2],
}
#[derive(Debug, PartialEq)]
pub struct TriangleCmd {
pub color: u32,
pub vertices: [Vec3; 3],
}
#[derive(Debug, PartialEq)]
pub struct QuadCmd {
pub color: u32,
pub vertices: [Vec3; 4],
}
#[derive(Debug, PartialEq)]
pub struct OptLineCmd {
pub color: u32,
pub vertices: [Vec3; 2],
pub control_points: [Vec3; 2],
}
#[derive(Debug, PartialEq)]
pub enum Command {
Category(CategoryCmd),
Keywords(KeywordsCmd),
Colour(ColourCmd),
Comment(CommentCmd),
SubFileRef(SubFileRefCmd),
Line(LineCmd),
Triangle(TriangleCmd),
Quad(QuadCmd),
OptLine(OptLineCmd),
}
pub trait FileRefResolver {
fn resolve(&self, filename: &str) -> Result<Vec<u8>, ResolveError>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_color_id() {
assert_eq!(color_id(b""), Err(Err::Error((&b""[..], ErrorKind::Digit))));
assert_eq!(color_id(b"1"), Ok((&b""[..], 1)));
assert_eq!(color_id(b"16 "), Ok((&b" "[..], 16)));
}
#[test]
fn test_from_hex() {
assert_eq!(from_hex(b"0"), Ok(0));
assert_eq!(from_hex(b"1"), Ok(1));
assert_eq!(from_hex(b"a"), Ok(10));
assert_eq!(from_hex(b"F"), Ok(15));
assert_eq!(from_hex(b"G"), Err(ErrorKind::AlphaNumeric));
assert_eq!(from_hex(b"10"), Ok(16));
assert_eq!(from_hex(b"FF"), Ok(255));
assert_eq!(from_hex(b"1G"), Err(ErrorKind::AlphaNumeric));
assert_eq!(from_hex(b"100"), Err(ErrorKind::AlphaNumeric));
assert_eq!(from_hex(b"\xFF"), Err(ErrorKind::AlphaNumeric));
}
#[test]
fn test_hex_color() {
assert_eq!(hex_color(b""), Err(Err::Error((&b""[..], ErrorKind::Tag))));
assert_eq!(
hex_color(b"#"),
Err(Err::Error((&b""[..], ErrorKind::TakeWhileMN)))
);
assert_eq!(
hex_color(b"#1"),
Err(Err::Error((&b"1"[..], ErrorKind::TakeWhileMN)))
);
assert_eq!(
hex_color(b"#12345Z"),
Err(Err::Error((&b"5Z"[..], ErrorKind::TakeWhileMN)))
);
assert_eq!(
hex_color(b"#123456"),
Ok((&b""[..], Color::new(0x12, 0x34, 0x56)))
);
assert_eq!(
hex_color(b"#ABCDEF"),
Ok((&b""[..], Color::new(0xAB, 0xCD, 0xEF)))
);
assert_eq!(
hex_color(b"#8E5cAf"),
Ok((&b""[..], Color::new(0x8E, 0x5C, 0xAF)))
);
assert_eq!(
hex_color(b"#123456e"),
Ok((&b"e"[..], Color::new(0x12, 0x34, 0x56)))
);
}
#[test]
fn test_colour_alpha() {
assert_eq!(colour_alpha(b""), Ok((&b""[..], None)));
assert_eq!(colour_alpha(b" ALPHA 0"), Ok((&b""[..], Some(0))));
assert_eq!(colour_alpha(b" ALPHA 1"), Ok((&b""[..], Some(1))));
assert_eq!(colour_alpha(b" ALPHA 128"), Ok((&b""[..], Some(128))));
assert_eq!(colour_alpha(b" ALPHA 255"), Ok((&b""[..], Some(255))));
assert_eq!(colour_alpha(b" ALPHA 34 "), Ok((&b" "[..], Some(34))));
assert_eq!(colour_alpha(b" ALPHA"), Ok((&b" ALPHA"[..], None)));
assert_eq!(colour_alpha(b" ALPHA 256"), Ok((&b" ALPHA 256"[..], None)));
}
#[test]
fn test_colour_luminance() {
assert_eq!(colour_luminance(b""), Ok((&b""[..], None)));
assert_eq!(colour_luminance(b" LUMINANCE 0"), Ok((&b""[..], Some(0))));
assert_eq!(colour_luminance(b" LUMINANCE 1"), Ok((&b""[..], Some(1))));
assert_eq!(
colour_luminance(b" LUMINANCE 128"),
Ok((&b""[..], Some(128)))
);
assert_eq!(
colour_luminance(b" LUMINANCE 255"),
Ok((&b""[..], Some(255)))
);
assert_eq!(
colour_luminance(b" LUMINANCE 34 "),
Ok((&b" "[..], Some(34)))
);
assert_eq!(
colour_luminance(b" LUMINANCE"),
Ok((&b" LUMINANCE"[..], None))
);
assert_eq!(
colour_luminance(b" LUMINANCE 256"),
Ok((&b" LUMINANCE 256"[..], None))
);
}
#[test]
fn test_material_grain_size() {
assert_eq!(
material_grain_size(b""),
Err(Err::Incomplete(Needed::Size(4)))
);
assert_eq!(
material_grain_size(b"SIZE"),
Err(Err::Error((&b"SIZE"[..], ErrorKind::Alt)))
);
assert_eq!(
material_grain_size(b"SIZE 1"),
Ok((&b""[..], GrainSize::Size(1.0)))
);
assert_eq!(
material_grain_size(b"SIZE 0.02"),
Ok((&b""[..], GrainSize::Size(0.02)))
);
assert_eq!(
material_grain_size(b"MINSIZE"),
Err(Err::Error((&b"MINSIZE"[..], ErrorKind::Alt)))
);
assert_eq!(
material_grain_size(b"MINSIZE 0.02"),
Err(Err::Error((&b"MINSIZE 0.02"[..], ErrorKind::Alt)))
);
assert_eq!(
material_grain_size(b"MINSIZE 0.02 MAXSIZE 0.04"),
Ok((&b""[..], GrainSize::MinMaxSize((0.02, 0.04))))
);
}
#[test]
fn test_glitter_material() {
assert_eq!(glitter_material(b""), Err(Err::Incomplete(Needed::Size(7))));
assert_eq!(
glitter_material(b"GLITTER"),
Err(Err::Error((&b""[..], ErrorKind::Space)))
);
assert_eq!(
glitter_material(b"GLITTER VALUE #123456 FRACTION 1.0 VFRACTION 0.3 SIZE 1"),
Ok((
&b""[..],
ColorFinish::Material(MaterialFinish::Glitter(GlitterMaterial {
value: Color::new(0x12, 0x34, 0x56),
alpha: None,
luminance: None,
surface_fraction: 1.0,
volume_fraction: 0.3,
size: GrainSize::Size(1.0)
}))
))
);
assert_eq!(
glitter_material(b"GLITTER VALUE #123456 ALPHA 128 FRACTION 1.0 VFRACTION 0.3 SIZE 1"),
Ok((
&b""[..],
ColorFinish::Material(MaterialFinish::Glitter(GlitterMaterial {
value: Color::new(0x12, 0x34, 0x56),
alpha: Some(128),
luminance: None,
surface_fraction: 1.0,
volume_fraction: 0.3,
size: GrainSize::Size(1.0)
}))
))
);
assert_eq!(
glitter_material(
b"GLITTER VALUE #123456 LUMINANCE 32 FRACTION 1.0 VFRACTION 0.3 SIZE 1"
),
Ok((
&b""[..],
ColorFinish::Material(MaterialFinish::Glitter(GlitterMaterial {
value: Color::new(0x12, 0x34, 0x56),
alpha: None,
luminance: Some(32),
surface_fraction: 1.0,
volume_fraction: 0.3,
size: GrainSize::Size(1.0)
}))
))
);
assert_eq!(
glitter_material(
b"GLITTER VALUE #123456 FRACTION 1.0 VFRACTION 0.3 MINSIZE 0.02 MAXSIZE 0.04"
),
Ok((
&b""[..],
ColorFinish::Material(MaterialFinish::Glitter(GlitterMaterial {
value: Color::new(0x12, 0x34, 0x56),
alpha: None,
luminance: None,
surface_fraction: 1.0,
volume_fraction: 0.3,
size: GrainSize::MinMaxSize((0.02, 0.04))
}))
))
);
}
#[test]
fn test_speckle_material() {
assert_eq!(speckle_material(b""), Err(Err::Incomplete(Needed::Size(7))));
assert_eq!(
speckle_material(b"SPECKLE"),
Err(Err::Error((&b""[..], ErrorKind::Space)))
);
assert_eq!(
speckle_material(b"SPECKLE VALUE #123456 FRACTION 1.0 SIZE 1"),
Ok((
&b""[..],
ColorFinish::Material(MaterialFinish::Speckle(SpeckleMaterial {
value: Color::new(0x12, 0x34, 0x56),
alpha: None,
luminance: None,
surface_fraction: 1.0,
size: GrainSize::Size(1.0)
}))
))
);
assert_eq!(
speckle_material(b"SPECKLE VALUE #123456 ALPHA 128 FRACTION 1.0 SIZE 1"),
Ok((
&b""[..],
ColorFinish::Material(MaterialFinish::Speckle(SpeckleMaterial {
value: Color::new(0x12, 0x34, 0x56),
alpha: Some(128),
luminance: None,
surface_fraction: 1.0,
size: GrainSize::Size(1.0)
}))
))
);
assert_eq!(
speckle_material(b"SPECKLE VALUE #123456 LUMINANCE 32 FRACTION 1.0 SIZE 1"),
Ok((
&b""[..],
ColorFinish::Material(MaterialFinish::Speckle(SpeckleMaterial {
value: Color::new(0x12, 0x34, 0x56),
alpha: None,
luminance: Some(32),
surface_fraction: 1.0,
size: GrainSize::Size(1.0)
}))
))
);
assert_eq!(
speckle_material(b"SPECKLE VALUE #123456 FRACTION 1.0 MINSIZE 0.02 MAXSIZE 0.04"),
Ok((
&b""[..],
ColorFinish::Material(MaterialFinish::Speckle(SpeckleMaterial {
value: Color::new(0x12, 0x34, 0x56),
alpha: None,
luminance: None,
surface_fraction: 1.0,
size: GrainSize::MinMaxSize((0.02, 0.04))
}))
))
);
}
#[test]
fn test_color_finish() {
assert_eq!(color_finish(b""), Ok((&b""[..], None)));
assert_eq!(color_finish(b"CHROME"), Ok((&b"CHROME"[..], None)));
assert_eq!(
color_finish(b" CHROME"),
Ok((&b""[..], Some(ColorFinish::Chrome)))
);
assert_eq!(
color_finish(b" PEARLESCENT"),
Ok((&b""[..], Some(ColorFinish::Pearlescent)))
);
assert_eq!(
color_finish(b" RUBBER"),
Ok((&b""[..], Some(ColorFinish::Rubber)))
);
assert_eq!(
color_finish(b" MATTE_METALLIC"),
Ok((&b""[..], Some(ColorFinish::MatteMetallic)))
);
assert_eq!(
color_finish(b" METAL"),
Ok((&b""[..], Some(ColorFinish::Metal)))
);
assert_eq!(
color_finish(b" CHROMEas"),
Ok((&b"as"[..], Some(ColorFinish::Chrome)))
);
assert_eq!(
color_finish(b" MATERIAL custom values"),
Ok((
&b""[..],
Some(ColorFinish::Material(MaterialFinish::Other(
"custom values".to_string()
)))
))
);
}
#[test]
fn test_digit1_as_u8() {
assert_eq!(
digit1_as_u8(b""),
Err(Err::Error((&b""[..], ErrorKind::Digit)))
);
assert_eq!(digit1_as_u8(b"0"), Ok((&b""[..], 0u8)));
assert_eq!(digit1_as_u8(b"1"), Ok((&b""[..], 1u8)));
assert_eq!(digit1_as_u8(b"255"), Ok((&b""[..], 255u8)));
assert_eq!(
digit1_as_u8(b"256"),
Err(Err::Error((&b"256"[..], ErrorKind::MapRes)))
);
assert_eq!(digit1_as_u8(b"32 "), Ok((&b" "[..], 32u8)));
}
#[test]
fn test_meta_colour() {
assert_eq!(meta_colour(b""), Err(Err::Incomplete(Needed::Size(7))));
assert_eq!(
meta_colour(b"!COLOUR test_col CODE 20 VALUE #123456"),
Err(Err::Error((&b""[..], ErrorKind::Space)))
);
assert_eq!(
meta_colour(b"!COLOUR test_col CODE 20 VALUE #123456 EDGE #abcdef"),
Ok((
&b""[..],
Command::Colour(ColourCmd {
name: "test_col".to_string(),
code: 20,
value: Color::new(0x12, 0x34, 0x56),
edge: Color::new(0xAB, 0xCD, 0xEF),
alpha: None,
luminance: None,
finish: None
})
))
);
assert_eq!(
meta_colour(b"!COLOUR test_col CODE 20 VALUE #123456 EDGE #abcdef ALPHA 128"),
Ok((
&b""[..],
Command::Colour(ColourCmd {
name: "test_col".to_string(),
code: 20,
value: Color::new(0x12, 0x34, 0x56),
edge: Color::new(0xAB, 0xCD, 0xEF),
alpha: Some(128),
luminance: None,
finish: None
})
))
);
assert_eq!(
meta_colour(b"!COLOUR test_col CODE 20 VALUE #123456 EDGE #abcdef LUMINANCE 32"),
Ok((
&b""[..],
Command::Colour(ColourCmd {
name: "test_col".to_string(),
code: 20,
value: Color::new(0x12, 0x34, 0x56),
edge: Color::new(0xAB, 0xCD, 0xEF),
alpha: None,
luminance: Some(32),
finish: None
})
))
);
assert_eq!(
meta_colour(
b"!COLOUR test_col CODE 20 VALUE #123456 EDGE #abcdef ALPHA 64 LUMINANCE 32"
),
Ok((
&b""[..],
Command::Colour(ColourCmd {
name: "test_col".to_string(),
code: 20,
value: Color::new(0x12, 0x34, 0x56),
edge: Color::new(0xAB, 0xCD, 0xEF),
alpha: Some(64),
luminance: Some(32),
finish: None
})
))
);
assert_eq!(
meta_colour(b"!COLOUR test_col CODE 20 VALUE #123456 EDGE #abcdef CHROME"),
Ok((
&b""[..],
Command::Colour(ColourCmd {
name: "test_col".to_string(),
code: 20,
value: Color::new(0x12, 0x34, 0x56),
edge: Color::new(0xAB, 0xCD, 0xEF),
alpha: None,
luminance: None,
finish: Some(ColorFinish::Chrome)
})
))
);
assert_eq!(
meta_colour(b"!COLOUR test_col CODE 20 VALUE #123456 EDGE #abcdef ALPHA 128 RUBBER"),
Ok((
&b""[..],
Command::Colour(ColourCmd {
name: "test_col".to_string(),
code: 20,
value: Color::new(0x12, 0x34, 0x56),
edge: Color::new(0xAB, 0xCD, 0xEF),
alpha: Some(128),
luminance: None,
finish: Some(ColorFinish::Rubber)
})
))
);
}
#[test]
fn test_vec3() {
assert_eq!(
read_vec3(b"0 0 0"),
Ok((&b""[..], Vec3::new(0.0, 0.0, 0.0)))
);
assert_eq!(
read_vec3(b"0 0 0 1"),
Ok((&b" 1"[..], Vec3::new(0.0, 0.0, 0.0)))
);
assert_eq!(
read_vec3(b"2 5 -7"),
Ok((&b""[..], Vec3::new(2.0, 5.0, -7.0)))
);
assert_eq!(
read_vec3(b"2.3 5 -7.4"),
Ok((&b""[..], Vec3::new(2.3, 5.0, -7.4)))
);
}
#[test]
fn test_read_cmd_id_str() {
assert_eq!(read_cmd_id_str(b"0"), Ok((&b""[..], &b"0"[..])));
assert_eq!(read_cmd_id_str(b"0 "), Ok((&b""[..], &b"0"[..])));
assert_eq!(read_cmd_id_str(b"0 "), Ok((&b""[..], &b"0"[..])));
assert_eq!(read_cmd_id_str(b"0 e"), Ok((&b"e"[..], &b"0"[..])));
assert_eq!(
read_cmd_id_str(b"4547 ssd"),
Ok((&b"ssd"[..], &b"4547"[..]))
);
}
#[test]
fn test_end_of_line() {
assert_eq!(end_of_line(b""), Ok((&b""[..], &b""[..])));
assert_eq!(end_of_line(b"\n"), Ok((&b""[..], &b"\n"[..])));
assert_eq!(end_of_line(b"\r\n"), Ok((&b""[..], &b"\r\n"[..])));
}
#[test]
fn test_take_not_cr_or_lf() {
assert_eq!(take_not_cr_or_lf(b""), Ok((&b""[..], &b""[..])));
assert_eq!(take_not_cr_or_lf(b"\n"), Ok((&b"\n"[..], &b""[..])));
assert_eq!(take_not_cr_or_lf(b"\r\n"), Ok((&b"\r\n"[..], &b""[..])));
assert_eq!(take_not_cr_or_lf(b"\n\n\n"), Ok((&b"\n\n\n"[..], &b""[..])));
assert_eq!(
take_not_cr_or_lf(b"\r\n\r\n\r\n"),
Ok((&b"\r\n\r\n\r\n"[..], &b""[..]))
);
assert_eq!(take_not_cr_or_lf(b" a \n"), Ok((&b"\n"[..], &b" a "[..])));
assert_eq!(take_not_cr_or_lf(b"test"), Ok((&b""[..], &b"test"[..])));
}
use nom::error::ErrorKind;
use nom::{Err, Needed};
#[test]
fn test_single_comma() {
assert_eq!(
single_comma(b""),
Err(Err::Error((&b""[..], ErrorKind::Tag)))
);
assert_eq!(single_comma(b","), Ok((&b""[..], &b","[..])));
assert_eq!(single_comma(b",s"), Ok((&b"s"[..], &b","[..])));
assert_eq!(
single_comma(b"w,s"),
Err(Err::Error((&b"w,s"[..], ErrorKind::Tag)))
);
}
#[test]
fn test_keywords_list() {
assert_eq!(
keywords_list(b""),
Err(Err::Error((&b""[..], ErrorKind::SeparatedList)))
);
assert_eq!(keywords_list(b"a"), Ok((&b""[..], vec![&b"a"[..]])));
assert_eq!(
keywords_list(b"a,b,c"),
Ok((&b""[..], vec![&b"a"[..], &b"b"[..], &b"c"[..]]))
);
}
#[test]
fn test_filename_char() {
assert_eq!(
filename_char(b""),
Err(Err::Error((&b""[..], ErrorKind::AlphaNumeric)))
);
assert_eq!(filename_char(b"a"), Ok((&b""[..], &b"a"[..])));
assert_eq!(filename_char(b"a-"), Ok((&b""[..], &b"a-"[..])));
assert_eq!(
filename_char(b"a/sad.bak\\ww.dat"),
Ok((&b""[..], &b"a/sad.bak\\ww.dat"[..]))
);
}
#[test]
fn test_filename() {
assert_eq!(filename(b"asd\\kw/l.ldr"), Ok((&b""[..], "asd\\kw/l.ldr")));
assert_eq!(filename(b"asdkwl.ldr"), Ok((&b""[..], "asdkwl.ldr")));
assert_eq!(
filename(b"asd\\kw/l.ldr\n"),
Ok((&b"\n"[..], "asd\\kw/l.ldr"))
);
assert_eq!(filename(b"asdkwl.ldr\n"), Ok((&b"\n"[..], "asdkwl.ldr")));
assert_eq!(
filename(b"asd\\kw/l.ldr\r\n"),
Ok((&b"\r\n"[..], "asd\\kw/l.ldr"))
);
assert_eq!(
filename(b"asdkwl.ldr\r\n"),
Ok((&b"\r\n"[..], "asdkwl.ldr"))
);
}
#[test]
fn test_category_cmd() {
let res = Command::Category(CategoryCmd {
category: "Figure Accessory".to_string(),
});
assert_eq!(category(b"!CATEGORY Figure Accessory"), Ok((&b""[..], res)));
}
#[test]
fn test_keywords_cmd() {
let res = Command::Keywords(KeywordsCmd {
keywords: vec![
"western".to_string(),
"wild west".to_string(),
"spaghetti western".to_string(),
"horse opera".to_string(),
"cowboy".to_string(),
],
});
assert_eq!(
keywords(b"!KEYWORDS western, wild west, spaghetti western, horse opera, cowboy"),
Ok((&b""[..], res))
);
}
#[test]
fn test_comment_cmd() {
let comment = b"test of comment, with \"weird\" characters";
let res = Command::Comment(CommentCmd::new(std::str::from_utf8(comment).unwrap()));
assert_eq!(meta_cmd(comment), Ok((&b""[..], res)));
assert_eq!(
meta_cmd(b""),
Ok((&b""[..], Command::Comment(CommentCmd::new(""))))
);
}
#[test]
fn test_file_ref_cmd() {
let res = Command::SubFileRef(SubFileRefCmd {
color: 16,
pos: Vec3::new(0.0, 0.0, 0.0),
row0: Vec3::new(1.0, 0.0, 0.0),
row1: Vec3::new(0.0, 1.0, 0.0),
row2: Vec3::new(0.0, 0.0, 1.0),
file: SubFileRef::UnresolvedRef("aaaaaaddd".to_string()),
});
assert_eq!(
file_ref_cmd(b"16 0 0 0 1 0 0 0 1 0 0 0 1 aaaaaaddd"),
Ok((&b""[..], res))
);
}
#[test]
fn test_space0() {
assert_eq!(space0(b""), Ok((&b""[..], &b""[..])));
assert_eq!(space0(b" "), Ok((&b""[..], &b" "[..])));
assert_eq!(space0(b" "), Ok((&b""[..], &b" "[..])));
assert_eq!(space0(b" a"), Ok((&b"a"[..], &b" "[..])));
assert_eq!(space0(b"a "), Ok((&b"a "[..], &b""[..])));
}
#[test]
fn test_space_or_eol0() {
assert_eq!(space_or_eol0(b""), Ok((&b""[..], &b""[..])));
assert_eq!(space_or_eol0(b" "), Ok((&b""[..], &b" "[..])));
assert_eq!(space_or_eol0(b" "), Ok((&b""[..], &b" "[..])));
assert_eq!(space_or_eol0(b" a"), Ok((&b"a"[..], &b" "[..])));
assert_eq!(space_or_eol0(b"a "), Ok((&b"a "[..], &b""[..])));
assert_eq!(space_or_eol0(b"\n"), Ok((&b""[..], &b"\n"[..])));
assert_eq!(space_or_eol0(b"\n\n\n"), Ok((&b""[..], &b"\n\n\n"[..])));
assert_eq!(space_or_eol0(b"\n\r\n"), Ok((&b""[..], &b"\n\r\n"[..])));
assert_eq!(
space_or_eol0(b"\n\r\r\r\n"),
Ok((&b""[..], &b"\n\r\r\r\n"[..]))
);
assert_eq!(space_or_eol0(b" \n"), Ok((&b""[..], &b" \n"[..])));
assert_eq!(space_or_eol0(b" \n "), Ok((&b""[..], &b" \n "[..])));
assert_eq!(
space_or_eol0(b" \n \r\n"),
Ok((&b""[..], &b" \n \r\n"[..]))
);
assert_eq!(
space_or_eol0(b" \n \r\n "),
Ok((&b""[..], &b" \n \r\n "[..]))
);
assert_eq!(space_or_eol0(b" \nsa"), Ok((&b"sa"[..], &b" \n"[..])));
assert_eq!(
space_or_eol0(b" \n \r\nsa"),
Ok((&b"sa"[..], &b" \n \r\n"[..]))
);
}
#[test]
fn test_empty_line() {
assert_eq!(empty_line(b""), Ok((&b""[..], &b""[..])));
assert_eq!(empty_line(b" "), Ok((&b""[..], &b" "[..])));
assert_eq!(empty_line(b" "), Ok((&b""[..], &b" "[..])));
assert_eq!(
empty_line(b" a"),
Err(Err::Error((&b"a"[..], ErrorKind::CrLf)))
);
assert_eq!(
empty_line(b"a "),
Err(Err::Error((&b"a "[..], ErrorKind::CrLf)))
);
}
#[test]
fn test_read_cmd() {
let res = Command::Comment(CommentCmd::new("this doesn't matter"));
assert_eq!(read_line(b"0 this doesn't matter"), Ok((&b""[..], res)));
}
#[test]
fn test_read_line_cmd() {
let res = Command::Line(LineCmd {
color: 16,
vertices: [Vec3::new(1.0, 1.0, 0.0), Vec3::new(0.9239, 1.0, 0.3827)],
});
assert_eq!(
read_line(b"2 16 1 1 0 0.9239 1 0.3827"),
Ok((&b""[..], res))
);
}
#[test]
fn test_read_tri_cmd() {
let res = Command::Triangle(TriangleCmd {
color: 16,
vertices: [
Vec3::new(1.0, 1.0, 0.0),
Vec3::new(0.9239, 1.0, 0.3827),
Vec3::new(0.9239, 0.0, 0.3827),
],
});
assert_eq!(
read_line(b"3 16 1 1 0 0.9239 1 0.3827 0.9239 0 0.3827"),
Ok((&b""[..], res))
);
let res = Command::Triangle(TriangleCmd {
color: 16,
vertices: [
Vec3::new(1.0, 1.0, 0.0),
Vec3::new(0.9239, 1.0, 0.3827),
Vec3::new(0.9239, 0.0, 0.3827),
],
});
assert_eq!(
read_line(b"3 16 1 1 0 0.9239 1 0.3827 0.9239 0 0.3827 "),
Ok((&b""[..], res))
);
}
#[test]
fn test_read_quad_cmd() {
let res = Command::Quad(QuadCmd {
color: 16,
vertices: [
Vec3::new(1.0, 1.0, 0.0),
Vec3::new(0.9239, 1.0, 0.3827),
Vec3::new(0.9239, 0.0, 0.3827),
Vec3::new(1.0, 0.0, 0.0),
],
});
assert_eq!(
read_line(b"4 16 1 1 0 0.9239 1 0.3827 0.9239 0 0.3827 1 0 0"),
Ok((&b""[..], res))
);
}
#[test]
fn test_read_opt_line_cmd() {
let res = Command::OptLine(OptLineCmd {
color: 16,
vertices: [Vec3::new(1.0, 1.0, 0.0), Vec3::new(0.9239, 1.0, 0.3827)],
control_points: [Vec3::new(0.9239, 0.0, 0.3827), Vec3::new(1.0, 0.0, 0.0)],
});
assert_eq!(
read_line(b"5 16 1 1 0 0.9239 1 0.3827 0.9239 0 0.3827 1 0 0"),
Ok((&b""[..], res))
);
}
#[test]
fn test_read_line_subfileref() {
let res = Command::SubFileRef(SubFileRefCmd {
color: 16,
pos: Vec3::new(0.0, 0.0, 0.0),
row0: Vec3::new(1.0, 0.0, 0.0),
row1: Vec3::new(0.0, 1.0, 0.0),
row2: Vec3::new(0.0, 0.0, 1.0),
file: SubFileRef::UnresolvedRef("aa/aaaaddd".to_string()),
});
assert_eq!(
read_line(b"1 16 0 0 0 1 0 0 0 1 0 0 0 1 aa/aaaaddd"),
Ok((&b""[..], res))
);
}
#[test]
fn test_parse_raw() {
let cmd0 = Command::Comment(CommentCmd::new("this is a comment"));
let cmd1 = Command::Line(LineCmd {
color: 16,
vertices: [Vec3::new(0.0, 0.0, 0.0), Vec3::new(1.0, 1.0, 1.0)],
});
assert_eq!(
parse_raw(b"0 this is a comment\n2 16 0 0 0 1 1 1").unwrap(),
vec![cmd0, cmd1]
);
let cmd0 = Command::Comment(CommentCmd::new("this doesn't matter"));
let cmd1 = Command::SubFileRef(SubFileRefCmd {
color: 16,
pos: Vec3::new(0.0, 0.0, 0.0),
row0: Vec3::new(1.0, 0.0, 0.0),
row1: Vec3::new(0.0, 1.0, 0.0),
row2: Vec3::new(0.0, 0.0, 1.0),
file: SubFileRef::UnresolvedRef("aa/aaaaddd".to_string()),
});
assert_eq!(
parse_raw(b"\n0 this doesn't matter\n\n1 16 0 0 0 1 0 0 0 1 0 0 0 1 aa/aaaaddd")
.unwrap(),
vec![cmd0, cmd1]
);
let cmd0 = Command::Comment(CommentCmd::new("this doesn't \"matter\""));
let cmd1 = Command::SubFileRef(SubFileRefCmd {
color: 16,
pos: Vec3::new(0.0, 0.0, 0.0),
row0: Vec3::new(1.0, 0.0, 0.0),
row1: Vec3::new(0.0, 1.0, 0.0),
row2: Vec3::new(0.0, 0.0, 1.0),
file: SubFileRef::UnresolvedRef("aa/aaaaddd".to_string()),
});
assert_eq!(
parse_raw(
b"\r\n0 this doesn't \"matter\"\r\n\r\n1 16 0 0 0 1 0 0 0 1 0 0 0 1 aa/aaaaddd\n"
)
.unwrap(),
vec![cmd0, cmd1]
);
let cmd0 = Command::SubFileRef(SubFileRefCmd {
color: 16,
pos: Vec3::new(0.0, 0.0, 0.0),
row0: Vec3::new(1.0, 0.0, 0.0),
row1: Vec3::new(0.0, 1.0, 0.0),
row2: Vec3::new(0.0, 0.0, 1.0),
file: SubFileRef::UnresolvedRef("aa/aaaaddd".to_string()),
});
let cmd1 = Command::SubFileRef(SubFileRefCmd {
color: 16,
pos: Vec3::new(0.0, 0.0, 0.0),
row0: Vec3::new(1.0, 0.0, 0.0),
row1: Vec3::new(0.0, 1.0, 0.0),
row2: Vec3::new(0.0, 0.0, 1.0),
file: SubFileRef::UnresolvedRef("aa/aaaaddd".to_string()),
});
assert_eq!(
parse_raw(
b"1 16 0 0 0 1 0 0 0 1 0 0 0 1 aa/aaaaddd\n1 16 0 0 0 1 0 0 0 1 0 0 0 1 aa/aaaaddd"
)
.unwrap(),
vec![cmd0, cmd1]
);
}
#[test]
fn test_source_file_iter() {
let mut source_map = SourceMap::new();
let source_file = SourceFile {
filename: "tata".to_string(),
raw_content: vec![],
cmds: vec![Command::Triangle(TriangleCmd {
color: 2,
vertices: [
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(1.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
],
})],
};
let source_file_ref = source_map.insert(source_file);
let s = SourceFile {
filename: "toto".to_string(),
raw_content: vec![],
cmds: vec![
Command::Triangle(TriangleCmd {
color: 16,
vertices: [
Vec3::new(0.0, 0.0, 1.0),
Vec3::new(1.0, 0.0, 1.0),
Vec3::new(0.0, 1.0, 1.0),
],
}),
Command::Comment(CommentCmd::new("my comment")),
Command::SubFileRef(SubFileRefCmd {
color: 24,
pos: Vec3::new(0.0, 0.0, 0.0),
row0: Vec3::new(0.0, 0.0, 0.0),
row1: Vec3::new(0.0, 0.0, 0.0),
row2: Vec3::new(0.0, 0.0, 0.0),
file: SubFileRef::ResolvedRef(source_file_ref),
}),
Command::Quad(QuadCmd {
color: 1,
vertices: [
Vec3::new(0.0, 1.0, 0.0),
Vec3::new(0.0, 1.0, 1.0),
Vec3::new(1.0, 1.0, 1.0),
Vec3::new(1.0, 1.0, 0.0),
],
}),
],
};
let source_file_ref = source_map.insert(s);
let source_file = source_file_ref.get(&source_map);
for c in source_file.iter(&source_map) {
trace!("cmd: {:?}", c);
}
let cmds: Vec<_> = source_file.iter(&source_map).map(|(_, cmd)| cmd).collect();
assert_eq!(3, cmds.len());
assert!(matches!(&cmds[0], Command::Triangle(_)));
assert!(matches!(&cmds[1], Command::Triangle(_)));
assert!(matches!(&cmds[2], Command::Quad(_)));
if let Command::Triangle(tri_cmd) = &cmds[0] {
assert_eq!(16, tri_cmd.color);
}
if let Command::Triangle(tri_cmd) = &cmds[1] {
assert_eq!(2, tri_cmd.color);
}
if let Command::Quad(quad_cmd) = &cmds[2] {
assert_eq!(1, quad_cmd.color);
}
let cmds: Vec<_> = source_file.local_iter(&source_map).collect();
assert_eq!(4, cmds.len());
assert!(matches!(&cmds[0], Command::Triangle(_)));
assert!(matches!(&cmds[1], Command::Comment(_)));
assert!(matches!(&cmds[2], Command::SubFileRef(_)));
assert!(matches!(&cmds[3], Command::Quad(_)));
if let Command::Triangle(tri_cmd) = &cmds[0] {
assert_eq!(16, tri_cmd.color);
}
if let Command::Comment(comment_cmd) = &cmds[1] {
assert_eq!("my comment", comment_cmd.text);
}
if let Command::SubFileRef(sfr_cmd) = &cmds[2] {
assert_eq!(24, sfr_cmd.color);
}
if let Command::Quad(quad_cmd) = &cmds[3] {
assert_eq!(1, quad_cmd.color);
}
}
}