// indymilter – asynchronous milter library
// Copyright © 2021–2023 David Bürgin <dbuergin@gluet.ch>
//
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation, either version 3 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program. If not, see <https://www.gnu.org/licenses/>.

use std::ffi::{CStr, CString};

/// Infallible conversion to a C string.
pub trait IntoCString {
    /// Converts this value to a C string.
    fn into_c_string(self) -> CString;
}

impl IntoCString for CString {
    fn into_c_string(self) -> Self {
        self
    }
}

impl IntoCString for &CStr {
    fn into_c_string(self) -> CString {
        self.to_owned()
    }
}

impl IntoCString for String {
    fn into_c_string(self) -> CString {
        let s = if self.contains('\0') {
            sanitize_nul(&self)
        } else {
            self
        };
        CString::new(s).unwrap()
    }
}

impl IntoCString for &str {
    fn into_c_string(self) -> CString {
        if self.contains('\0') {
            let s = sanitize_nul(self);
            CString::new(s).unwrap()
        } else {
            CString::new(self).unwrap()
        }
    }
}

fn sanitize_nul(s: &str) -> String {
    s.replace('\0', "\u{fffd}")
}

#[cfg(test)]
mod tests {
    use super::*;
    use byte_strings::c_str;

    #[test]
    fn into_c_string_ok() {
        assert_eq!("bonjour".into_c_string().as_ref(), c_str!("bonjour"));
        assert_eq!("al\0ias\0".into_c_string().as_ref(), c_str!("al�ias�"));
        assert_eq!(c_str!(b"ab\xefe").into_c_string().as_ref(), c_str!(b"ab\xefe"));
    }
}
