Skip to content

Instantly share code, notes, and snippets.

@agrif
Created December 9, 2013 20:21
Show Gist options
  • Select an option

  • Save agrif/7880109 to your computer and use it in GitHub Desktop.

Select an option

Save agrif/7880109 to your computer and use it in GitHub Desktop.
#[feature(macro_rules)];
use std::str;
use std::io;
use std::vec;
// shim because Trait::<for T>::foo isn't implemented yet
fn hintfunc<T>(x: T) -> T { x }
macro_rules! hint(
($e:expr :: $t:ty) => (hintfunc::<$t>($e))
)
#[deriving(Eq,FromPrimitive,ToStr)]
enum TagType {
End = 0,
Byte = 1,
Short = 2,
Int = 3,
Long = 4,
Float = 5,
Double = 6,
ByteArray = 7,
String = 8,
List = 9,
Compound = 10,
IntArray = 11
}
impl TagType {
fn to_pretty(&self) -> ~str {
match *self {
End => ~"TAG_End",
Byte => ~"TAG_Byte",
Short => ~"TAG_Short",
Int => ~"TAG_Int",
Long => ~"TAG_Long",
Float => ~"TAG_Float",
Double => ~"TAG_Double",
ByteArray => ~"TAG_Byte_Array",
String => ~"TAG_String",
List => ~"TAG_List",
Compound => ~"TAG_Compound",
IntArray => ~"TAG_Int_Array"
}
}
}
trait Tag<N: TagName> {
fn get_type(&self) -> TagType;
fn get_name_ptr<'a>(&'a self) -> &'a N;
fn pretty_print_part(&self, writer: &mut Writer, indent: |&mut Writer|);
fn read(reader: &mut Reader) -> ~Self;
}
trait NamedTag {
fn get_name<'a>(&'a self) -> &'a str;
}
impl NamedTag for ~Tag<~str> {
fn get_name<'a>(&'a self) -> &'a str {
(*self.get_name_ptr()).as_slice()
}
}
trait PrettyPrintTag {
fn pretty_print(&self, writer: &mut Writer, indent: |&mut Writer|);
}
impl PrettyPrintTag for ~Tag<~str> {
fn pretty_print(&self, writer: &mut Writer, indent: |&mut Writer|) {
indent(writer);
write!(writer, "{}(\"{}\"): ", self.get_type().to_pretty(), self.get_name());
self.pretty_print_part(writer, indent);
write!(writer, "\n");
}
}
impl<T: Tag<()>> PrettyPrintTag for ~T {
fn pretty_print(&self, writer: &mut Writer, indent: |&mut Writer|) {
indent(writer);
write!(writer, "{}: ", self.get_type().to_pretty());
self.pretty_print_part(writer, indent);
write!(writer, "\n");
}
}
trait TagName : Send {
fn read(reader: &mut Reader) -> Self;
}
impl TagName for () {
fn read(_: &mut Reader) {}
}
impl TagName for ~str {
fn read(reader: &mut Reader) -> ~str {
let size = reader.read_be_u16();
let name = reader.read_bytes(size as uint);
return str::from_utf8_owned(name);
}
}
struct EndTag {
name: ()
}
impl Tag<()> for EndTag {
fn get_type(&self) -> TagType { End }
fn get_name_ptr<'a>(&'a self) -> &'a () { &(self.name) }
fn pretty_print_part(&self, _: &mut Writer, _: |&mut Writer|) {}
fn read(_: &mut Reader) -> ~EndTag { ~EndTag { name: () } }
}
macro_rules! tag(
($name:ident(id=$tagtype:ident, value=$t:ty) {
fn pretty_print_part(&self, $printervar:ident) $print:expr
fn read($readervar:ident) $read:expr
}) => (
struct $name<N> {
name: N,
value: $t
}
impl<N: TagName> Tag<N> for $name<N> {
fn get_type(&self) -> TagType { $tagtype }
fn get_name_ptr<'a>(&'a self) -> &'a N { &(self.name) }
fn pretty_print_part(&self, $printervar: &mut Writer, _: |&mut Writer|) {
$print
}
fn read(reader: &mut Reader) -> ~$name<N> {
return ~$name {
name: TagName::read(reader),
value: $read
};
}
}
);
($name:ident(id=$tagtype:ident, value=$t:ty) = {$reader:ident}) => (
tag!($name(id=$tagtype, value=$t) {
fn pretty_print_part(&self, writer) {
write!(writer, "{}", self.value);
}
fn read(reader) {
reader.$reader()
}
})
);
)
tag!(ByteTag(id=Byte, value=i8) = {read_i8})
tag!(ShortTag(id=Short, value=i16) = {read_be_i16})
tag!(IntTag(id=Int, value=i32) = {read_be_i32})
tag!(LongTag(id=Long, value=i64) = {read_be_i64})
tag!(FloatTag(id=Float, value=f32) = {read_be_f32})
tag!(DoubleTag(id=Double, value=f64) = {read_be_f64})
tag!(ByteArrayTag(id=ByteArray, value=~[u8]) {
fn pretty_print_part(&self, writer) {
write!(writer, "[{0, plural, one{# byte} other{# bytes}}]", self.value.len());
}
fn read(reader) {
let size = reader.read_be_u32();
reader.read_bytes(size as uint)
}
})
tag!(StringTag(id=String, value=~str) {
fn pretty_print_part(&self, writer) {
write!(writer, "{}", self.value);
}
fn read(reader) {
let size = reader.read_be_u16();
let bytes = reader.read_bytes(size as uint);
str::from_utf8_owned(bytes)
}
})
struct ListTag<N, T> {
name: N,
value: ~[~T]
}
impl<N: TagName, T: Tag<()>> Tag<N> for ListTag<N, T> {
fn get_type(&self) -> TagType { List }
fn get_name_ptr<'a>(&'a self) -> &'a N { &(self.name) }
fn pretty_print_part(&self, writer: &mut Writer, indent: |&mut Writer|) {
let mut subtype = End;
if (self.value.len() > 0) {
subtype = self.value[0].get_type();
}
write!(writer, "{0, plural, one{# entry} other{# entries}} of type {1}\n", self.value.len(), subtype.to_pretty());
indent(writer);
write!(writer, "{}\n", "{");
for t in self.value.iter() {
t.pretty_print(writer, |w| { write!(w, " "); indent(w); });
}
indent(writer);
write!(writer, "{}", "}");
}
fn read(reader: &mut Reader) -> ~ListTag<N, T> {
let name = TagName::read(reader);
let size = reader.read_be_u32();
return ~ListTag {
name: name,
value: vec::from_fn(size as uint, |_| Tag::read(reader))
};
}
}
struct CompoundTag<N> {
name: N,
value: ~[~Tag<~str>]
}
impl<N: TagName> Tag<N> for CompoundTag<N> {
fn get_type(&self) -> TagType { Compound }
fn get_name_ptr<'a>(&'a self) -> &'a N { &self.name }
fn pretty_print_part(&self, writer: &mut Writer, indent: |&mut Writer|) {
write!(writer, "{0, plural, one{# entry} other{# entries}}\n", self.value.len());
indent(writer);
write!(writer, "{}\n", "{");
for t in self.value.iter() {
t.pretty_print(writer, |w| { write!(w, " "); indent(w); });
}
indent(writer);
write!(writer, "{}", "}");
}
fn read(reader: &mut Reader) -> ~CompoundTag<N> {
let name = TagName::read(reader);
let mut gather = ~[];
loop {
match read(reader) {
Some(tag) => gather.push(tag),
None => break
}
}
return ~CompoundTag {
name: name,
value: gather
};
}
}
tag!(IntArrayTag(id=IntArray, value=~[i32]) {
fn pretty_print_part(&self, writer) {
write!(writer, "[{0, plural, one{# int} other{# ints}}]", self.value.len());
}
fn read(reader) {
let size = reader.read_be_u32();
vec::from_fn(size as uint, |_| reader.read_be_i32())
}
})
fn read<N: TagName>(reader: &mut Reader) -> Option<~Tag<N>> {
let t: Option<TagType> = FromPrimitive::from_u8(reader.read_u8());
match t {
Some(End) => None,
Some(Byte) =>
Some(hint!(Tag::read(reader) :: ~ByteTag<N>) as ~Tag<N>),
Some(Short) =>
Some(hint!(Tag::read(reader) :: ~ShortTag<N>) as ~Tag<N>),
Some(Int) =>
Some(hint!(Tag::read(reader) :: ~IntTag<N>) as ~Tag<N>),
Some(Long) =>
Some(hint!(Tag::read(reader) :: ~LongTag<N>) as ~Tag<N>),
Some(Float) =>
Some(hint!(Tag::read(reader) :: ~FloatTag<N>) as ~Tag<N>),
Some(Double) =>
Some(hint!(Tag::read(reader) :: ~DoubleTag<N>) as ~Tag<N>),
Some(ByteArray) =>
Some(hint!(Tag::read(reader) :: ~ByteArrayTag<N>) as ~Tag<N>),
Some(String) =>
Some(hint!(Tag::read(reader) :: ~StringTag<N>) as ~Tag<N>),
Some(List) => {
let name: N = TagName::read(reader);
let st: Option<TagType> = FromPrimitive::from_u8(reader.read_u8());
match st {
Some(End) =>
Some(~ListTag{name: name, value: hint!(Tag::read(reader) :: ~ListTag<(), EndTag>).value} as ~Tag<N>),
Some(Byte) =>
Some(~ListTag{name: name, value: hint!(Tag::read(reader) :: ~ListTag<(), ByteTag<()>>).value} as ~Tag<N>),
Some(Short) =>
Some(~ListTag{name: name, value: hint!(Tag::read(reader) :: ~ListTag<(), ShortTag<()>>).value} as ~Tag<N>),
Some(Int) =>
Some(~ListTag{name: name, value: hint!(Tag::read(reader) :: ~ListTag<(), IntTag<()>>).value} as ~Tag<N>),
Some(Long) =>
Some(~ListTag{name: name, value: hint!(Tag::read(reader) :: ~ListTag<(), LongTag<()>>).value} as ~Tag<N>),
Some(Float) =>
Some(~ListTag{name: name, value: hint!(Tag::read(reader) :: ~ListTag<(), FloatTag<()>>).value} as ~Tag<N>),
Some(Double) =>
Some(~ListTag{name: name, value: hint!(Tag::read(reader) :: ~ListTag<(), DoubleTag<()>>).value} as ~Tag<N>),
Some(ByteArray) =>
Some(~ListTag{name: name, value: hint!(Tag::read(reader) :: ~ListTag<(), ByteArrayTag<()>>).value} as ~Tag<N>),
Some(String) =>
Some(~ListTag{name: name, value: hint!(Tag::read(reader) :: ~ListTag<(), StringTag<()>>).value} as ~Tag<N>),
Some(List) => None, // FIXME impossible with the current system
Some(Compound) =>
Some(~ListTag{name: name, value: hint!(Tag::read(reader) :: ~ListTag<(), CompoundTag<()>>).value} as ~Tag<N>),
Some(IntArray) =>
Some(~ListTag{name: name, value: hint!(Tag::read(reader) :: ~ListTag<(), IntArrayTag<()>>).value} as ~Tag<N>),
None => None // FIXME
}
},
Some(Compound) =>
Some(hint!(Tag::read(reader) :: ~CompoundTag<N>) as ~Tag<N>),
Some(IntArray) =>
Some(hint!(Tag::read(reader) :: ~IntArrayTag<N>) as ~Tag<N>),
None => None // FIXME
}
}
fn main() {
let mut reader = io::stdin();
let mut writer = io::stdout();
let tag: ~Tag<~str> = read(&mut reader as &mut Reader).unwrap();
tag.pretty_print(&mut writer as &mut Writer, |_| ());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment