pub struct BitRange {
    start: usize,
    length: usize
}

impl BitRange {
    pub const fn from_to(start_bit: usize, end_bit: usize) -> Self {
        Self{start: start_bit, length: (end_bit - start_bit) + 1}
    }

    pub const fn get_mask(&self) -> usize {
        self.max_value()
    }

    pub const fn max_value(&self) -> usize {
        (1 << self.length) - 1
    }

    pub const fn as_value(&self, value: usize) -> usize {
        (value & self.get_mask()) << self.start
    }

    pub const fn or_value(&self, dst_value: usize, value: usize) -> usize {
        dst_value | self.as_value(value)
    }
}

pub struct Bit {
    pos: usize
}

impl Bit {
    pub const fn at(pos: usize) -> Self {
        Bit{pos}
    }

    pub const fn as_value(&self, is_set: bool) -> usize {
        if is_set {
            1 << self.pos
        }

        else {
            0
        }
    }

    pub const fn or_value(&self, dst_value: usize, is_set: bool) -> usize {
        if is_set {
            dst_value | self.as_value(is_set)
        }

        else {
            dst_value
        }
    }
}

macro_rules! create_bit_functions {
    ($type_val:ty) => {
        paste::item! {
            const fn [< get_mask_ $type_val >](range: &BitRange) -> $type_val {
                range.get_mask() as $type_val
            }

            pub const fn [< clear_value_ $type_val >](dst: $type_val, range: &BitRange) -> $type_val {
                dst & !([< get_mask_ $type_val >](range) << (range.start as $type_val))
            }

            pub const fn [< set_value_ $type_val >](dst: $type_val, value: $type_val, range: &BitRange) -> $type_val {
                [< clear_value_ $type_val >](dst, range) | ((value & [< get_mask_ $type_val >](range)) << range.start)
            }

            pub const fn [< get_value_ $type_val >](src: $type_val, range: &BitRange) -> $type_val {
                (src & ([< get_mask_ $type_val >](range) << (range.start as $type_val))) >> range.start
            }

            pub const fn [< bit_set_ $type_val >](src: $type_val, bit: usize) -> bool {
                src & (1 << bit) != 0
            }
        }
    };
}

create_bit_functions!(u8);
create_bit_functions!(u16);
create_bit_functions!(u32);