1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use std::{
    io::Seek,
    io::{SeekFrom, Write},
    marker::PhantomData,
    mem::replace,
    num::TryFromIntError,
};

use byteorder::{ByteOrder, WriteBytesExt};

pub enum UnresolvedStatus {
    Offset(u64),
    Resolved,
}

pub struct Unresolved<T: Resolvable, E: ByteOrder> {
    inner: UnresolvedStatus,
    _value_ty: PhantomData<T>,
    _byte_order_ty: PhantomData<E>,
}

pub trait Resolvable: Sized + Copy {
    fn write_to<W: Write, O: ByteOrder>(self, output: W) -> std::io::Result<()>;
}

impl Resolvable for u32 {
    fn write_to<W: Write, O: ByteOrder>(self, mut output: W) -> std::io::Result<()> {
        output.write_u32::<O>(self)
    }
}

impl<T: Resolvable, E: ByteOrder> Unresolved<T, E> {
    pub fn resolve<W: Write + Seek>(
        &mut self,
        mut writer: W,
        value: T,
    ) -> Result<T, std::io::Error> {
        let saved_pos = match replace(&mut self.inner, UnresolvedStatus::Resolved) {
            UnresolvedStatus::Offset(pos) => pos,
            UnresolvedStatus::Resolved => panic!("already resolved"),
        };

        let pos = writer.stream_position()?;
        writer.seek(SeekFrom::Start(saved_pos))?;
        value.write_to::<_, E>(&mut writer)?;
        writer.seek(SeekFrom::Start(pos))?;

        Ok(value)
    }
}

impl<T, E> Unresolved<T, E>
where
    T: Resolvable + TryFrom<u64, Error = TryFromIntError>,
    E: ByteOrder,
{
    pub fn resolve_with_position<W: Write + Seek>(
        &mut self,
        mut writer: W,
    ) -> Result<T, std::io::Error> {
        let value = T::try_from(writer.stream_position()?).unwrap();

        self.resolve(writer, value)
    }

    pub fn resolve_with_relative_offset<W: Write + Seek>(
        &mut self,
        mut writer: W,
        pos: u64,
    ) -> Result<T, std::io::Error> {
        let offset = writer.stream_position()? - pos;
        let value = T::try_from(offset).unwrap();

        self.resolve(writer, value)
    }
}

impl<T, E> Drop for Unresolved<T, E>
where
    T: Resolvable,
    E: ByteOrder,
{
    fn drop(&mut self) {
        if let UnresolvedStatus::Offset(pos) = self.inner {
            panic!(
                "unresolved {} at 0x{:x} dropped before resolving",
                std::any::type_name::<T>(),
                pos
            );
        }
    }
}

pub trait WriteFormatsExt {
    fn write_unresolved<T: Resolvable, E: ByteOrder>(
        &mut self,
    ) -> Result<Unresolved<T, E>, std::io::Error>;
    fn write_unresolved_u32<E: ByteOrder>(&mut self) -> Result<Unresolved<u32, E>, std::io::Error>;
}

impl<W: Write + Seek> WriteFormatsExt for W {
    fn write_unresolved<T: Resolvable, E: ByteOrder>(
        &mut self,
    ) -> Result<Unresolved<T, E>, std::io::Error> {
        let offset = self.stream_position()?;
        let unresolved_value_size = std::mem::size_of::<T>();

        self.seek(SeekFrom::Current(unresolved_value_size as i64))?;

        Ok(Unresolved {
            _value_ty: PhantomData::default(),
            _byte_order_ty: PhantomData::default(),
            inner: UnresolvedStatus::Offset(offset),
        })
    }

    fn write_unresolved_u32<E: ByteOrder>(&mut self) -> Result<Unresolved<u32, E>, std::io::Error> {
        self.write_unresolved::<u32, E>()
    }
}