// Copyright 2025 RisingWave Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fmt::Display;
use std::ops::{Deref, DerefMut};
use std::str::FromStr;

use serde::{Deserialize, Serialize};

/// A session config wrapper that represents unset (`None`) as empty string.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
#[serde(transparent)]
pub struct OptionConfig<T>(pub Option<T>);

impl<T: FromStr> FromStr for OptionConfig<T> {
    type Err = T::Err;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.is_empty() {
            Ok(Self(None))
        } else {
            Ok(Self(Some(s.parse()?)))
        }
    }
}

impl<T: Display> Display for OptionConfig<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if let Some(inner) = &self.0 {
            inner.fmt(f)
        } else {
            write!(f, "")
        }
    }
}

impl<T> From<Option<T>> for OptionConfig<T> {
    fn from(v: Option<T>) -> Self {
        Self(v)
    }
}

impl<T> From<OptionConfig<T>> for Option<T> {
    fn from(v: OptionConfig<T>) -> Self {
        v.0
    }
}

impl<T> Deref for OptionConfig<T> {
    type Target = Option<T>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<T> DerefMut for OptionConfig<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}
