空白そらしろfix: merge parser changes
ce687fe2创建于 2025年5月30日历史提交
// Copyright (c) 2025, Huawei Technologies Co., Ltd.
// All rights reserved.
//
// 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::Write;

use smartstring::alias::String;

use crate::StdString;

/// This trait is designed to simplify code and enhance readability.
pub trait SmartStringExt {
    fn null() -> String {
        String::from("NULL")
    }
    fn from_slice(origin: &[StdString]) -> Vec<String>;

    fn from_bool(value: bool) -> String {
        match value {
            true => "true",
            false => "false",
        }
        .into()
    }

    fn from_f32(value: f32) -> String;

    fn from_f64(value: f64) -> String;

    fn from_i32(value: i32) -> String;

    fn from_i64(value: i64) -> String;

    fn from_vecu8(value: &[u8]) -> String;

    fn from_bools(value: &[bool]) -> String {
        let mut ret = String::new();
        let mut iter = value.iter().peekable();

        while let Some(&v) = iter.next() {
            ret.push_str(&String::from_bool(v));
            iter.peek().is_some().then(|| ret.push(','));
        }

        ret
    }

    fn from_f32s(value: &[f32]) -> String;

    fn from_f64s(value: &[f64]) -> String;

    fn from_i64s(value: &[i64]) -> String;

    fn from_2dvecu8(value: &Vec<Vec<u8>>) -> String;

    fn vecu8s2strings(value: &[Vec<u8>]) -> Vec<String> {
        value.iter().map(|x| String::from_vecu8(x)).collect()
    }
}

/// This macro is a boilerplate code, because slice to
/// string conversion is actually an iterative call to the conversion method.
/// The following is a sample code of macro expansion.
///
/// ## Expanded
/// ```
/// use parser::SmartStringExt;
/// use smartstring::alias::String;
///
/// fn from_i64s(value: &[i64]) -> String {
///     let mut ret = String::new();
///
///     let mut iter = value.iter().peekable();
///     while let Some(&v) = iter.next() {
///         iter.peek().is_some().then(|| ret.push_str(", "));
///         let _ = write!(&mut ret, "{}", String::from_i64(v));
///     }
///
///     ret
/// }
/// ```
/// ## Note
/// In order to avoid calling the join method, a for loop is used here instead
/// of slice iteration, but this problem has not been avoided:
/// [`String::from_i64`] returns a String type. Besides,
/// we can see that this String is converted from &str, while the parameter of
/// [`String::push_str`] is of &str type, that is,
/// there is a waste of left-hand-right-hand operation here.
macro_rules! impl_from_slice {
    ($name:ident, $ty:ty, $method:ident) => {
        fn $name(value: &[$ty]) -> String {
            let mut ret = String::new();

            let mut iter = value.iter().peekable();
            while let Some(&v) = iter.next() {
                if !ret.is_empty() {
                    ret.push_str(", ");
                }
                let _ = write!(&mut ret, "{}", String::$method(v));
            }

            ret
        }
    };
}

/// ## Performance
///
/// This implementation uses the [`ryu`] and [`itoa`] crates for converting numbers to strings.
/// These crates generally offer better performance compared to the standard library implementations.
///
/// ## Note
/// The code duplication here is unavoidable because [`rustc`] needs to know the size of a type at compile time.
/// Therefore, a unified `Float` type cannot be used, as `f32` (4 bytes) and `f64` (8 bytes) have different sizes.
impl SmartStringExt for String {
    impl_from_slice!(from_f32s, f32, from_f32);

    impl_from_slice!(from_f64s, f64, from_f64);

    impl_from_slice!(from_i64s, i64, from_i64);

    fn from_slice(origin: &[StdString]) -> Vec<String> {
        origin
            .iter()
            .filter_map(|i| if i.is_empty() { None } else { Some(i.into()) })
            .collect()
    }

    fn from_f32(value: f32) -> String {
        let mut buf = ryu::Buffer::new();
        let s = buf.format(value);
        String::from(s)
    }

    fn from_f64(value: f64) -> String {
        let mut buf = ryu::Buffer::new();
        let s = buf.format(value);
        String::from(s)
    }

    fn from_i32(value: i32) -> String {
        let mut buf = itoa::Buffer::new();
        let s = buf.format(value);
        String::from(s)
    }

    fn from_i64(value: i64) -> String {
        let mut buf = itoa::Buffer::new();
        let s = buf.format(value);
        String::from(s)
    }

    /// Performance
    /// Using [`from_utf8_unchecked`] can provide higher performance,
    /// unless the file is modified or an error occurs when writing
    /// the file, there will be no illegal strings.
    fn from_vecu8(value: &[u8]) -> String {
        match std::str::from_utf8(value) {
            Ok(s) => String::from(s),
            Err(_) => String::from("InvalidUTF8Str"),
        }
    }

    fn from_2dvecu8(value: &Vec<Vec<u8>>) -> String {
        let mut ret = String::new();
        let mut iter = value.iter().peekable();

        while let Some(v) = iter.next() {
            ret.push_str(&String::from_vecu8(v));
            iter.peek().is_some().then(|| ret.push(';'));
        }

        ret
    }
}