Rust双面人插件库two-face的使用,实现高效双向数据转换与处理

Rust双面人插件库two-face的使用,实现高效双向数据转换与处理

two-face是一个为syntect提供额外语法和主题定义的Rust库,包含了许多默认集中缺少的常见语法定义,如TOML、TypeScript和Dockerfile等。

示例

以下是使用two-face库的基本示例:

use two_face::re_exports::syntect;

const TOML_TEXT: &str = "\
[section]
key = 123
";

fn main() {
    let syn_set = two_face::syntax::extra_newlines();
    let theme_set = two_face::theme::extra();

    let syn_ref = syn_set.find_syntax_by_extension("toml").unwrap();
    let theme = theme_set.get(two_face::theme::EmbeddedThemeName::Nord);
    let htmlified = syntect::html::highlighted_html_for_string(
        TOML_TEXT,
        &syn_set,
        syn_ref,
        theme
    ).unwrap();

    println!("{htmlified}");
}

这段代码会输出以下HTML格式的TOML语法高亮结果:

<pre style="background-color:#2e3440;">
<span style="color:#d8dee9;">[section]
</span><span style="color:#81a1c1;">key </span><span style="color:#d8dee9;">= </span><span style="color:#b48ead;">123
</span></pre>

完整示例

下面是一个更完整的示例,展示如何使用two-face进行代码高亮处理:

use two_face::{syntax, theme};
use two_face::re_exports::syntect;

fn highlight_code(code: &str, extension: &str) -> String {
    // 获取语法集和主题集
    let syn_set = syntax::extra_newlines();
    let theme_set = theme::extra();
    
    // 查找对应的语法和主题
    let syntax_ref = syn_set.find_syntax_by_extension(extension)
        .unwrap_or_else(|| syn_set.find_syntax_plain_text());
    
    let theme = theme_set.get(theme::EmbeddedThemeName::Nord);
    
    // 生成高亮HTML
    syntect::html::highlighted_html_for_string(
        code,
        &syn_set,
        syntax_ref,
        theme
    ).unwrap()
}

fn main() {
    let rust_code = r#"
fn main() {
    println!("Hello, world!");
}
"#;
    
    let toml_code = r#"
[package]
name = "my_project"
version = "0.1.0"
"#;
    
    // 高亮Rust代码
    let rust_html = highlight_code(rust_code, "rs");
    println!("Rust code highlighted:\n{}", rust_html);
    
    // 高亮TOML代码
    let toml_html = highlight_code(toml_code, "toml");
    println!("TOML code highlighted:\n{}", toml_html);
}

特性标志

two-face根据syntect的底层正则表达式实现分为不同的特性标志:

  • 默认: syntect-onig
  • 替代实现: syntect-fancy
特性 描述
syntect-onig / syntect-fancy 启用我们从syntect所需的最小功能集
syntect-default-onig / syntect-default-fancy 最小功能集加上syntect的默认功能集

语法支持

two-face支持大量语法定义,包括但不限于:

  • C, C++, C#, Java, JavaScript, TypeScript
  • Rust, Go, Python, Ruby
  • TOML, YAML, JSON
  • Dockerfile, Makefile
  • Markdown, HTML, CSS

主题支持

two-face提供多种主题,包括:

  • Nord
  • Dracula
  • GitHub
  • Solarized (light/dark)
  • One Half (light/dark)
  • Visual Studio Dark+

安装

要使用two-face,可以将以下内容添加到您的Cargo.toml中:

[dependencies]
two-face = "0.4.3"

或者运行以下命令:

cargo add two-face

对于特定功能,可以添加特性标志:

[dependencies]
two-face = { version = "0.4.3", features = ["syntect-default-onig"] }

two-face是一个强大的语法高亮库,特别适合需要支持多种编程语言和配置文件的应用程序。它通过提供额外的语法定义和主题,扩展了syntect的功能,使得在Rust应用中实现高质量的代码高亮变得更加容易。


1 回复

Rust双面人插件库two-face的使用指南

介绍

two-face是一个Rust库,专注于提供高效的双向数据转换与处理能力。它允许开发者在不同类型之间建立双向映射关系,简化了数据转换的复杂性,特别适合需要频繁在不同数据表示形式之间转换的场景。

主要特性

  • 双向数据转换:支持类型A到类型B以及类型B到类型A的双向转换
  • 高效处理:使用Rust的零成本抽象,转换过程几乎无额外开销
  • 可扩展性:支持自定义转换逻辑
  • 错误处理:提供完善的错误处理机制

安装

在Cargo.toml中添加依赖:

[dependencies]
two-face = "0.3"

基本使用方法

1. 简单类型转换

use two_face::TwoFace;

// 定义两个结构体
#[derive(Debug, PartialEq)]
struct Celsius(f32);

#[derive(Debug, PartialEq)]
struct Fahrenheit(f32);

// 实现双向转换
impl TwoFace for Celsius {
    type Other = Fahrenheit;
    
    fn into_other(self) -> Self::Other {
        Fahrenheit((self.0 * 9.0/5.0) + 32.0)
    }
    
    fn from_other(other: Self::Other) -> Self {
        Celsius((other.0 - 32.0) * 5.0/9.0)
    }
}

fn main() {
    let temp_c = Celsius(25.0);
    let temp_f: Fahrenheit = temp_c.into_other();
    println!("25°C = {:?}", temp_f); // 25°C = Fahrenheit(77.0)
    
    let converted_back = Celsius::from_other(temp_f);
    println!("77°F = {:?}", converted_back); // 77°F = Celsius(25.0)
}

2. 复杂结构转换

use two_face::TwoFace;
use serde::{Serialize, Deserialize};

#[derive(Debug, Serialize, Deserialize)]
struct UserJson {
    name: String,
    age: u8,
    email: String,
}

#[derive(Debug)]
struct UserDb {
    id: i32,
    full_name: String,
    years_old: u8,
    contact_email: String,
}

impl TwoFace for UserJson {
    type Other = UserDb;
    
    fn into_other(self) -> Self::Other {
        UserDb {
            id: 0, // 假设数据库会自动生成ID
            full_name: self.name,
            years_old: self.age,
            contact_email: self.email,
        }
    }
    
    fn from_other(other: Self::Other) -> Self {
        UserJson {
            name: other.full_name,
            age: other.years_old,
            email: other.contact_email,
        }
    }
}

fn main() {
    let json_user = UserJson {
        name: "Alice".to_string(),
        age: 30,
        email: "alice@example.com".to_string(),
    };
    
    let db_user: UserDb = json_user.into_other();
    println!("Database user: {:?}", db_user);
    
    let json_user_again = UserJson::from_other(db_user);
    println!("JSON user: {:?}", json_user_again);
}

3. 使用派生宏简化实现

two-face提供了派生宏来简化实现:

use two_face::{TwoFace, two_face};

#[two_face]
#[derive(Debug)]
struct Point2D {
    x: f64,
    y: f64,
}

#[two_face]
#[derive(Debug)]
struct PolarPoint {
    r: f64,
    theta: f64,
}

impl TwoFace for Point2D {
    type Other = PolarPoint;
    
    fn into_other(self) -> Self::Other {
        PolarPoint {
            r: (self.x.powi(2) + self.y.powi(2)).sqrt(),
            theta: self.y.atan2(self.x),
        }
    }
    
    fn from_other(other: Self::Other) -> Self {
        Point2D {
            x: other.r * other.theta.cos(),
            y: other.r * other.theta.sin(),
        }
    }
}

fn main() {
    let cartesian = Point2D { x: 3.0, y: 4.0 };
    let polar: PolarPoint = cartesian.into_other();
    println!("Polar coordinates: {:?}", polar);
    
    let cartesian_again = Point2D::from_other(polar);
    println!("Cartesian coordinates: {:?}", cartesian_again);
}

高级用法

1. 链式转换

use two_face::TwoFace;

#[derive(Debug)]
struct A(i32);

#[derive(Debug)]
struct B(i32);

#[derive(Debug)]
struct C(i32);

impl TwoFace for A {
    type Other = B;
    
    fn into_other(self) -> Self::Other {
        B(self.0 * 2)
    }
    
    fn from_other(other: Self::Other) -> Self {
        A(other.0 / 2)
    }
}

impl TwoFace for B {
    type Other = C;
    
    fn into_other(self) -> Self::Other {
        C(self.0 + 10)
    }
    
    fn from_other(other: Self::Other) -> Self {
        B(other.0 - 10)
    }
}

fn main() {
    let a = A(5);
    let c: C = a.into_other().into_other();
    println!("A -> B -> C: {:?}", c); // C(20)
    
    let a_again = A::from_other(B::from_other(c));
    println!("C -> B -> A: {:?}", a_again); // A(5)
}

2. 错误处理

use two_face::TwoFace;
use thiserror::Error;

#[derive(Debug, Error)]
enum ConversionError {
    #[error("Value out of range")]
    OutOfRange,
}

#[derive(Debug)]
struct PositiveNumber(u32);

#[derive(Debug)]
struct NegativeNumber(i32);

impl TwoFace for PositiveNumber {
    type Other = NegativeNumber;
    type Error = ConversionError;
    
    fn into_other(self) -> Result<Self::Other, Self::Error> {
        if self.0 > i32::MAX as u32 {
            Err(ConversionError::OutOfRange)
        } else {
            Ok(NegativeNumber(-(self.0 as i32)))
        }
    }
    
    fn from_other(other: Self::Other) -> Result<Self, Self::Error> {
        if -other.0 < 0 {
            Err(ConversionError::OutOfRange)
        } else {
            Ok(PositiveNumber((-other.0) as u32))
        }
    }
}

fn main() {
    let pos = PositiveNumber(42);
    match pos.into_other() {
        Ok(neg) => println!("Converted to negative: {:?}", neg),
        Err(e) => println!("Conversion failed: {}", e),
    }
}

性能建议

  1. 对于频繁转换的场景,考虑实现IntoFrom trait以获得更好的性能
  2. 对于大型数据结构,考虑使用引用或智能指针避免数据复制
  3. 在性能关键路径上,避免不必要的中间转换

完整示例

下面是一个结合了多种特性的完整示例,展示了two-face库在实际应用中的使用:

use two_face::{TwoFace, two_face};
use serde::{Serialize, Deserialize};
use thiserror::Error;

// 错误类型定义
#[derive(Debug, Error)]
enum AppError {
    #[error("Temperature out of valid range")]
    TempOutOfRange,
    #[error("User data conversion error")]
    UserConversionError,
}

// 温度转换
#[two_face]
#[derive(Debug, Clone)]
struct Celsius(f32);

#[two_face]
#[derive(Debug, Clone)]
struct Fahrenheit(f32);

impl TwoFace for Celsius {
    type Other = Fahrenheit;
    type Error = AppError;
    
    fn into_other(self) -> Result<Self::Other, Self::Error> {
        if self.0 < -273.15 {
            Err(AppError::TempOutOfRange)
        } else {
            Ok(Fahrenheit((self.0 * 9.0/5.0) + 32.0))
        }
    }
    
    fn from_other(other: Self::Other) -> Result<Self, Self::Error> {
        let celsius = (other.0 - 32.0) * 5.0/9.0;
        if celsius < -273.15 {
            Err(AppError::TempOutOfRange)
        } else {
            Ok(Celsius(celsius))
        }
    }
}

// 用户数据转换
#[derive(Debug, Serialize, Deserialize, Clone)]
struct UserJson {
    name: String,
    age: u8,
    email: String,
    preferred_temp: Celsius,  // 使用摄氏度
}

#[derive(Debug, Clone)]
struct UserDb {
    id: i32,
    full_name: String,
    years_old: u8,
    contact_email: String,
    temp_preference: Fahrenheit,  // 数据库存储华氏度
}

impl TwoFace for UserJson {
    type Other = UserDb;
    type Error = AppError;
    
    fn into_other(self) -> Result<Self::Other, Self::Error> {
        Ok(UserDb {
            id: 0, // 假设ID由数据库生成
            full_name: self.name,
            years_old: self.age,
            contact_email: self.email,
            temp_preference: self.preferred_temp.into_other()?,
        })
    }
    
    fn from_other(other: Self::Other) -> Result<Self, Self::Error> {
        Ok(UserJson {
            name: other.full_name,
            age: other.years_old,
            email: other.contact_email,
            preferred_temp: Celsius::from_other(other.temp_preference)?,
        })
    }
}

fn main() -> Result<(), AppError> {
    // 创建用户JSON数据
    let user_json = UserJson {
        name: "Bob".to_string(),
        age: 35,
        email: "bob@example.com".to_string(),
        preferred_temp: Celsius(22.0),
    };
    
    // 转换为数据库格式
    let user_db = user_json.clone().into_other()?;
    println!("Database user: {:?}", user_db);
    
    // 转换回JSON格式
    let user_json_again = UserJson::from_other(user_db)?;
    println!("JSON user: {:?}", user_json_again);
    
    // 测试错误处理 - 无效温度
    let invalid_temp = Celsius(-300.0);
    match invalid_temp.into_other() {
        Ok(f) => println!("Converted temp: {:?}", f),
        Err(e) => println!("Error: {}", e),  // 会打印温度超出范围的错误
    }
    
    Ok(())
}

这个完整示例展示了:

  1. 简单类型(温度)和复杂类型(用户数据)的双向转换
  2. 自定义错误处理
  3. 链式转换(用户数据中包含温度转换)
  4. 使用派生宏简化实现
  5. 实际应用场景中的数据格式转换

two-face库通过其双向转换能力,为Rust开发者提供了一种灵活且高效的方式来处理不同类型之间的数据转换问题,特别适合需要双向映射的复杂应用场景。

回到顶部