qubit-value

Type-safe value container framework with unified abstractions for single values, multi-values, and named values with complete serde support

Rust CI Coverage Crates.io Rust License

基于 qubit_datatype::DataType 的类型安全值容器框架,提供单值、多值与命名值 的统一抽象,支持泛型构造/获取/设置与类型转换,并完整支持 serde 序列化。

概述

Qubit Value 提供了以类型安全方式处理动态类型值的综合解决方案。它在静态类型和运行时 灵活性之间架起桥梁,为值的存储、检索和转换提供强大的抽象,同时保持 Rust 的安全保证。

配置对象支持: 如果您需要基于不同类型多值设计的配置对象,建议使用
qubit-config crate,它提供了完整的
配置管理功能。您可以在 GitHub
crates.io 上找到更多信息。

特性

🎯 核心设计

📦 核心类型

安装

Cargo.toml 中添加:

[dependencies]
qubit-value = "0.7"

使用示例

单值操作

use qubit_value::{Value, ValueError};
use qubit_datatype::DataType;
use num_bigint::BigInt;
use bigdecimal::BigDecimal;
use std::str::FromStr;

// 泛型构造与类型推断获取
let v = Value::new(8080i32);
let port: i32 = v.get()?;  // 从变量类型推断
assert_eq!(port, 8080);

// 具名获取(返回 Copy 或引用)
assert_eq!(v.get_int32()?, 8080);

// 函数参数中的类型推断
fn check_port(p: i32) -> bool { p > 1024 }
assert!(check_port(v.get()?));  // 从函数签名推断为 i32

// 通过 to<T>() 进行跨类型转换
assert_eq!(v.to::<i64>()?, 8080i64);
assert_eq!(v.to::<String>()?, "8080".to_string());

// 大数类型与类型推断
let big_int = Value::new(BigInt::from(12345678901234567890i64));
let num: BigInt = big_int.get()?;  // 类型推断

// 空值与类型管理
let mut any = Value::Int32(42);
any.clear();
assert!(any.is_empty());
assert_eq!(any.data_type(), DataType::Int32);
any.set_type(DataType::String);
any.set("hello")?;
assert_eq!(any.get_string()?, "hello");

扩展类型

use qubit_value::Value;
use std::time::Duration;
use url::Url;
use std::collections::HashMap;

// Duration(时长)
let v = Value::new(Duration::from_secs(30));
let d: Duration = v.get()?;
assert_eq!(d, Duration::from_secs(30));
// 默认字符串转换使用毫秒单位
let s: String = v.to()?;
assert_eq!(s, "30000ms");
let v2 = Value::String("30s".to_string());
let d2: Duration = v2.to()?;
assert_eq!(d2, Duration::from_secs(30));

// Url
let url = Url::parse("https://example.com").unwrap();
let v = Value::new(url.clone());
let got: Url = v.get()?;
assert_eq!(got, url);
// 从字符串解析
let v2 = Value::String("https://example.com".to_string());
let got2: Url = v2.to()?;
assert_eq!(got2, url);

// HashMap<String, String>(字符串映射)
let mut map = HashMap::new();
map.insert("host".to_string(), "localhost".to_string());
let v = Value::new(map.clone());
let got: HashMap<String, String> = v.get()?;
assert_eq!(got, map);

// JSON 逃生舱
let j = serde_json::json!({"key": "value"});
let v = Value::from_json_value(j.clone());
let got: serde_json::Value = v.get()?;
assert_eq!(got, j);

// 将任意可序列化类型存为 JSON
#[derive(serde::Serialize, serde::Deserialize)]
struct Config { host: String, port: u16 }
let cfg = Config { host: "localhost".to_string(), port: 8080 };
let v = Value::from_serializable(&cfg)?;
let restored: Config = v.deserialize_json()?;

多值操作

use qubit_value::{MultiValues, ValueError};
use qubit_datatype::DataType;

// 从 Vec<T> 泛型构造
let mut ports = MultiValues::new(vec![8080i32, 8081, 8082]);
assert_eq!(ports.count(), 3);
assert_eq!(ports.get_int32s()?, &[8080, 8081, 8082]);

// 可以直接传数组、切片、vector 和借用的 vector
let array_ports = MultiValues::new([8080i32, 8081, 8082]);
let more_ports = [9000i32, 9001];
let borrowed = MultiValues::new(more_ports.as_slice());
let owned = vec![7000i32, 7001];
let borrowed_vec = MultiValues::new(&owned);

// 字符串列表可以直接从 &str 集合构造
let servers = MultiValues::new(["api", "worker", "cache"]);
assert_eq!(servers.get_strings()?, &["api", "worker", "cache"]);

// 泛型获取与类型推断(克隆 Vec)
let nums: Vec<i32> = ports.get()?;

// 获取首元素
let first: i32 = ports.get_first()?;
assert_eq!(first, 8080);

// 泛型添加:单个 / Vec / 切片
ports.add(8083)?;
ports.add(vec![8084, 8085])?;
ports.add(&[8086, 8087][..])?;
ports.add([8088, 8089])?;

// 泛型设置:替换整个列表
ports.set(vec![9001, 9002])?;
ports.set([9100, 9101])?;
ports.set(&owned)?;
assert_eq!(ports.get_int32s()?, &[7000, 7001]);

// 合并(类型需一致)
let mut a = MultiValues::Int32(vec![1, 2]);
let b = MultiValues::Int32(vec![3, 4]);
a.merge(&b)?;
assert_eq!(a.get_int32s()?, &[1, 2, 3, 4]);

// 转为单值(取首元素)
let single = a.to_value();
let first_val: i32 = single.get()?;
assert_eq!(first_val, 1);

带默认值的读取与转换

带默认值的 API 只会在值为空或列表没有元素时使用 fallback。类型不匹配和转换失败 仍会正常返回错误,不会被默认值掩盖。

use qubit_datatype::DataType;
use qubit_value::{MultiValues, Value};

// 严格类型读取,并在空值时使用默认值
let value = Value::Empty(DataType::String);
let host: String = value.get_or("localhost")?;
assert_eq!(host, "localhost");

let value = Value::String("8080".to_string());
let port: u16 = value.to_or(9000u16)?;
assert_eq!(port, 8080);

// 多值严格读取,并使用集合默认值
let values = MultiValues::Empty(DataType::String);
let paths: Vec<String> = values.get_or(["cache", "tmp"])?;
assert_eq!(paths, vec!["cache".to_string(), "tmp".to_string()]);

// 首元素转换,并使用标量默认值
let values = MultiValues::Empty(DataType::UInt16);
let port: u16 = values.to_or(8080u16)?;
assert_eq!(port, 8080);

// 列表转换,并使用数组或切片默认值
let values = MultiValues::Empty(DataType::String);
let tags: Vec<String> = values.to_list_or(["blue", "green"])?;
assert_eq!(tags, vec!["blue".to_string(), "green".to_string()]);

集合参数写法

集合类 API 接受调用处最常见的便捷写法。这适用于 MultiValues::newMultiValues::setMultiValues::add,也适用于 get_orto_list_or 等带列表默认值的读取接口。

use qubit_datatype::DataType;
use qubit_value::MultiValues;

let array_values = MultiValues::new([1i32, 2, 3]);
let slice_source = [4i32, 5, 6];
let slice_values = MultiValues::new(slice_source.as_slice());
let vec_source = vec![7i32, 8, 9];
let vec_values = MultiValues::new(vec_source.clone());
let borrowed_vec_values = MultiValues::new(&vec_source);

let mut values = MultiValues::Empty(DataType::Int32);
values.set([10, 11, 12])?;
values.add(slice_source.as_slice())?;
values.add(&vec_source)?;

let strings = MultiValues::new(["api", "worker"]);
let fallback: Vec<String> = MultiValues::Empty(DataType::String)
    .get_or(["cache", "tmp"])?;

命名值操作

use qubit_value::{NamedValue, NamedMultiValues, Value, MultiValues};

// 命名单值
let mut nv = NamedValue::new("timeout", Value::new(30i32));
assert_eq!(nv.name(), "timeout");
let timeout: i32 = nv.get()?;
assert_eq!(timeout, 30);

nv.set_name("read_timeout");
nv.set(45i32)?;
assert_eq!(nv.get_int32()?, 45);

// 命名多值
let mut nmv = NamedMultiValues::new("ports", MultiValues::new(vec![8080i32, 8081]));
nmv.add(8082)?;
let first_port: i32 = nmv.get_first()?;
assert_eq!(first_port, 8080);

// 命名多值 → 命名单值(取首元素)
let first_named = nmv.to_named_value();
assert_eq!(first_named.name(), "ports");
let val: i32 = first_named.get()?;
assert_eq!(val, 8080);

API 参考

泛型 API

构造

MultiValues::new 支持 Vec<T>&Vec<T>&[T][T; N]&[T; N]。字符串值还支持 Vec<&str>&Vec<&str>&[&str][&str; N]&[&str; N],内部会生成 Vec<String>

new 支持的 Tboolchari8i16i32i64i128u8u16u32u64u128f32f64String&strNaiveDateNaiveTimeNaiveDateTimeDateTime<Utc>BigIntBigDecimalisizeusizeDurationUrlHashMap<String, String>serde_json::Value

获取

get<T>() 执行严格类型匹配——存储的变体必须与 T 完全一致。 跨类型转换请使用 to<T>()

设置

类型转换

各目标类型支持的源变体:

目标 T支持的源变体
boolBool;整数变体(0=false,非零=true);String10truefalsetrue/false 忽略大小写)
i8Int8BoolChar;所有整数变体;Float32/64StringBigInteger/BigDecimal
i16Int16BoolChar;所有整数变体;Float32/64StringBigInteger/BigDecimal
i32Int32BoolChar;所有整数变体;Float32/64StringBigInteger/BigDecimal
i64Int64BoolChar;所有整数变体;Float32/64StringBigInteger/BigDecimal
i128Int128BoolChar;所有整数变体;Float32/64StringBigInteger/BigDecimal
isizeIntSizeBoolChar;所有整数变体;Float32/64StringBigInteger/BigDecimal
u8UInt8BoolChar;所有整数变体(范围检查);String
u16UInt8/16/32/64/128BoolChar;有符号整数变体(范围检查);String
u32UInt8/16/32/64/128BoolChar;有符号整数变体(范围检查);String
u64UInt8/16/32/64/128BoolChar;有符号整数变体(范围检查);String
u128UInt8/16/32/64/128BoolChar;有符号整数变体(范围检查);String
usizeUIntSizeBoolChar;所有整数变体(范围检查);String
f32Float32/64BoolChar;所有整数变体;StringBigInteger/BigDecimal
f64Float64BoolChar;所有数值变体;StringBigInteger/BigDecimal
charChar
String所有变体(整数/浮点/bool/char/日期时间/Duration/Url/StringMap/Json
NaiveDateDate
NaiveTimeTime
NaiveDateTimeDateTime
DateTime<Utc>Instant
BigIntBigInteger
BigDecimalBigDecimal
DurationDuration;整数变体和 BigInteger(使用配置的时长单位);String(可带 ns/us/ms/s/m/h/d 后缀;无后缀时使用配置的时长单位)
UrlUrlString
HashMap<String, String>StringMap
serde_json::ValueJsonString(解析为 JSON);StringMap

具名 API

单值

多值

JSON 工具方法(Value 上)

工具方法

单值

多值

错误类型

use qubit_value::{ValueError, ValueResult};
use qubit_datatype::DataType;

// 主要错误变体
ValueError::NoValue                           // 访问了空值
ValueError::TypeMismatch { expected, actual } // get<T>() 类型不匹配
ValueError::ConversionFailed { from, to }     // to<T>() 不支持的转换方向
ValueError::ConversionError(String)           // to<T>() 范围检查或解析失败
ValueError::IndexOutOfBounds { index, len }   // 多值索引越界
ValueError::JsonSerializationError(String)    // JSON 序列化失败
ValueError::JsonDeserializationError(String)  // JSON 反序列化失败

所有可能失败的操作均返回 ValueResult<T> = Result<T, ValueError>

支持的数据类型

基本标量类型

字符串

日期/时间类型

大数类型

扩展类型

序列化支持

所有类型均实现 Serialize/Deserialize

序列化时保持完整的类型信息,反序列化时进行类型验证。

性能说明

依赖项

[dependencies]
qubit-datatype = "0.2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "2.0"
chrono = { version = "0.4", features = ["serde"] }
url = { version = "2.5", features = ["serde"] }
num-bigint = { version = "0.4", features = ["serde"] }
bigdecimal = { version = "0.4", features = ["serde"] }

测试

本项目保持全面的测试覆盖,对所有功能进行详细验证。发布或提交前请运行 ./ci-check.sh

许可证

Copyright (c) 2025 - 2026 Haixing Hu, Qubit Co. Ltd. All rights reserved.

根据 Apache 许可证 2.0 版("许可证")授权; 除非遵守许可证,否则您不得使用此文件。 您可以在以下位置获取许可证副本:

http://www.apache.org/licenses/LICENSE-2.0

除非适用法律要求或书面同意,否则根据许可证分发的软件 按"原样"分发,不附带任何明示或暗示的担保或条件。 有关许可证下的特定语言管理权限和限制,请参阅许可证。

完整的许可证文本请参阅 LICENSE

贡献

欢迎贡献!请随时提交 Pull Request。

作者

胡海星 - Qubit Co. Ltd.

---

有关 Qubit 开源项目的更多信息,请访问我们的 GitHub 组织