Toy implementation of the gemini protocol
#[repr(u8)]
#[derive(Debug, PartialEq, Eq)]
pub enum Header {
    Input(header::Input) = 1,
    Success(header::Success) = 2,
    Redirect(header::Redirect) = 3,
    FailTemp(header::FailTemp) = 4,
    FailPerm(header::FailPerm) = 5,
    CertRequired(header::CertRequired) = 6,
}
pub mod raw {
    pub use super::Meta;
    pub type Status = [u8; 2];
    pub struct Header {
        pub meta: Meta,
        pub status: Status,
    }
}

pub mod header {
    use url::Url;

    use crate::response::Mime;

    use super::Meta;

    #[derive(Debug, PartialEq, Eq)]
    pub struct Input {
        pub prompt: String,
        pub sensitive: Option<bool>,
    }
    impl From<(u8, Meta)> for Input {
        fn from((code, meta): (u8, Meta)) -> Self {
            let prompt = super::Meta::try_from(meta).unwrap().0;
            let sensitive = match code {
                0 => Some(false),
                1 => Some(true),
                _ => None,
            };
            Self { prompt, sensitive }
        }
    }
    #[derive(Debug, PartialEq, Eq)]
    pub struct Success {
        pub mime: Mime,
    }
    impl From<(u8, Meta)> for Success {
        fn from((_, meta): (u8, Meta)) -> Self {
            let mime = Mime::try_from(Meta::try_from(meta).unwrap()).expect("failed to read mime");
            Self { mime }
        }
    }
    #[derive(Debug, PartialEq, Eq)]
    pub struct Redirect {
        pub url: Url,
        pub temporary: Option<bool>,
    }
    impl From<(u8, Meta)> for Redirect {
        fn from((code, meta): (u8, Meta)) -> Self {
            let temporary = match code {
                0 => Some(true),
                1 => Some(false),
                _ => None,
            };
            Self {
                url: meta.0.parse().expect("not a valid url"),
                temporary,
            }
        }
    }
    #[derive(Debug, PartialEq, Eq)]
    pub struct FailTemp {
        pub message: String,
        pub typ: Option<sub::FailTemp>,
    }
    impl From<(u8, Meta)> for FailTemp {
        fn from((code, meta): (u8, Meta)) -> Self {
            let typ = sub::FailTemp::try_from(code).ok();
            Self {
                message: meta.0,
                typ,
            }
        }
    }
    #[derive(Debug, PartialEq, Eq)]
    pub struct FailPerm {
        pub message: String,
        pub typ: Option<sub::FailPerm>,
    }
    impl From<(u8, Meta)> for FailPerm {
        fn from((code, meta): (u8, Meta)) -> Self {
            let typ = sub::FailPerm::try_from(code).ok();
            Self {
                message: meta.0,
                typ,
            }
        }
    }
    #[derive(Debug, PartialEq, Eq)]
    pub struct CertRequired {
        pub message: String,
        pub typ: Option<sub::CertRequired>,
    }
    impl From<(u8, Meta)> for CertRequired {
        fn from(value: (u8, Meta)) -> Self {
            let (code, meta) = value;
            let typ = sub::CertRequired::try_from(code).ok();
            Self {
                message: meta.0,
                typ,
            }
        }
    }
    mod sub {
        use num_enum::TryFromPrimitive;
        #[derive(Debug, PartialEq, Eq, TryFromPrimitive)]
        #[repr(u8)]
        pub enum FailTemp {
            TemporaryFailure = 0,
            ServerUnavailable = 1,
            CgiError = 2,
            ProxyError = 3,
            SlowDown = 4,
        }
        #[derive(Debug, PartialEq, Eq, TryFromPrimitive)]
        #[repr(u8)]
        pub enum FailPerm {
            PermanentFailure = 0,
            NotFound = 1,
            Gone = 2,
            ProxyRequestRefused = 3,
            BadRequest = 9,
        }
        #[derive(Debug, PartialEq, Eq, TryFromPrimitive)]
        #[repr(u8)]
        pub enum CertRequired {
            ClientCertificateRequired = 0,
            CertificateNotAuthorised = 1,
            CertificateNotValid = 2,
        }
    }
}
impl TryFrom<raw::Header> for Header {
    type Error = String;

    fn try_from(value: raw::Header) -> Result<Self, Self::Error> {
        let raw::Header { status, meta } = value;
        let [status, sub] = status;
        match match status {
            1 => Some(Header::Input((sub, meta).into())),
            2 => Some(Header::Success((sub, meta).into())),
            3 => Some(Header::Redirect((sub, meta).into())),
            4 => Some(Header::FailTemp((sub, meta).into())),
            5 => Some(Header::FailPerm((sub, meta).into())),
            6 => Some(Header::CertRequired((sub, meta).into())),
            _ => None,
        } {
            Some(s) => Ok(s),
            None => Err(format!("bad status code: [{:?},{:?}]", status, sub)),
        }
    }
}
pub use mime::Mime;

#[derive(Debug, PartialEq, Eq)]
pub struct Meta(pub String);
impl From<Meta> for Mime {
    fn from(value: Meta) -> Self {
        value.0.parse().unwrap()
    }
}