Crate descriptor[][src]

Expand description

Easy pretty print your Rust struct into single element or list.

Simple Example

use descriptor::{object_describe_to_string, table_describe_to_string, Descriptor};

#[derive(Descriptor)]
struct User {
    name: String,
    age: i32,
    address: Address,
}

#[derive(Descriptor)]
struct Address {
    street: String,
    town: String,
}

let user1 = User{
    name: "Adrien".to_string(),
    age: 32,
    address: Address{
        street: "Main street".to_string(),
        town: "NY".to_string()
    }
};
let user2 = User{
    name: "Corentin".to_string(),
    age: 40,
    address: Address{
        street: "10 rue de la paix".to_string(),
        town: "Paris".to_string()
    }
};
let description = object_describe_to_string(&user1).unwrap();

assert_eq!(r#"
Name:    Adrien
Age:     32
Address:
  Street: Main street
  Town:   NY
"#,  description);

let table = table_describe_to_string(&vec![user1, user2]).unwrap();

assert_eq!(r#"
NAME     AGE ADDRESS.STREET    ADDRESS.TOWN
Adrien   32  Main street       NY
Corentin 40  10 rue de la paix Paris
"#, format!("\n{}", table));

Macro attributes

Struct attributes

#[descriptor(into = AnotherStruct)]

The into parameter convert the struct into another before describe.

The From or Into Trait should be implemented and AnotherStruct should implement the Describe trait.

use descriptor::{Descriptor, object_describe_to_string};

#[derive(Descriptor)]
pub struct ProgressDescribe {
    pub transfer: String,
}

impl From<&Progress> for ProgressDescribe {
    fn from(progress: &Progress) -> Self {
        let bar_length = 20;
        let pad_l = ((progress.processed * bar_length) / progress.total.max(1)) as usize;
        let bar = format!(
            "[{:=>pad_l$}>{:>pad_r$}] {}/{}",
            "",
            "",
            progress.processed,
            progress.total,
            pad_l = pad_l,
            pad_r = bar_length as usize - pad_l
        );

        Self {
            transfer: bar,
        }
    }
}

#[derive(Descriptor)]
#[descriptor(into = ProgressDescribe)]
struct Progress {
    pub processed: u64,
    pub total: u64,
}

let progress = Progress{
   processed: 20,
   total: 40,
};
let description = object_describe_to_string(&progress).unwrap();

assert_eq!(r#"
Transfer: [==========>          ] 20/40
"#,  description);

#[descriptor(extra_fields = ExtraStruct)]

Add fields from another struct into the description, useful for computed values without overriding real values.

use descriptor::{Descriptor, object_describe_to_string};

#[derive(Descriptor)]
#[descriptor(extra_fields = AgeEntity)]
struct User {
    name: String,
    created_at: i32,
}

#[derive(Descriptor)]
pub struct AgeEntity {
    pub age: String,
}

impl From<&User> for AgeEntity {
    fn from(u: &User) -> Self {
        Self {
            age: format!("{} days ago", u.created_at),
        }
    }
}

let progress = User{
   name: "Adrien".to_string(),
   created_at: 40,
};
let description = object_describe_to_string(&progress).unwrap();
assert_eq!(r#"
Name:       Adrien
Created At: 40
Age:        40 days ago
"#,  description);

#[descriptor(default_headers = [""])]

Overrides default headers when using the table output.

use descriptor::{Descriptor, table_describe_to_string};
#[derive(Descriptor, Clone)]
#[descriptor(default_headers = ["brand", "seat"])]
struct Car {
    brand: String,
    seat: i16,
    model: String,
}
let cars = vec![
    Car{brand: "Audi".to_string(), seat:4, model: "A3".to_string()},
    Car{brand: "Mercedes".to_string(), seat: 2, model: "GLC".to_string()}
];

let description = table_describe_to_string(&cars).unwrap();
assert_eq!(r#"
BRAND    SEAT
Audi     4
Mercedes 2
"#,  format!("\n{}", description));

Field attributes

#[descriptor(flatten)]

Flatten a struct into another.

use descriptor::{Descriptor, object_describe_to_string};

#[derive(Descriptor)]
struct User {
    name: String,
    age: i32,
    #[descriptor(flatten)]
    address: Address
}

#[derive(Descriptor)]
struct Address {
    street: String,
    town: String,
}

let foo = User{
    name: "Adrien".to_string(),
    age: 32,
    address: Address{
        street: "Main street".to_string(),
        town: "NY".to_string()
    }
};
let description = object_describe_to_string(&foo).unwrap();

assert_eq!(r#"
Name:    Adrien
Age:     32
Street:  Main street
Town:    NY
"#,  description);

#[descriptor(map = func)]

Takes a transformation function as parameter, called before generating the field.

Return value of the functions should not be a struct, use into parameter for this use case.

use descriptor::{Descriptor, object_describe_to_string};

fn age_to_string(val: &i32) -> String {
  format!("{} years", val)
}

#[derive(Descriptor)]
struct User {
    name: String,
    #[descriptor(map = age_to_string)]
    age: i32,
}
let foo = User{
    name: "Adrien".to_string(),
    age: 32,
};
let description = object_describe_to_string(&foo).unwrap();
assert_eq!(r#"
Name: Adrien
Age:  32 years
"#,  description);

map parameter can be used with resolve_option parameter.

If the field is an Option, it extract it before calling the transformation function.

use descriptor::{Descriptor, object_describe_to_string};

fn age_to_string(val: &i32) -> String {
  format!("{} years", val)
}

#[derive(Descriptor)]
struct User {
    name: String,
    #[descriptor(map = age_to_string, resolve_option)]
    age: Option<i32>,
}
let foo = User{
    name: "Adrien".to_string(),
    age: Option::Some(32),
};
let description = object_describe_to_string(&foo).unwrap();
assert_eq!(r#"
Name: Adrien
Age:  32 years
"#,  description);

#[descriptor(into)]

Act like into parameter in struct level,

into parameter can be used with map parameter in field level.

Sometimes, it’s impossible to implement Into and From for some struct, you can use a public method from the struct

use descriptor::{Descriptor, object_describe_to_string};
#[derive(Descriptor)]
struct Download {
    pub filename: String,
    #[descriptor(into = String, map = Progress::to_string)]
    pub progress: Progress,
}

#[derive(Descriptor)]
struct Progress {
    pub processed: u64,
    pub total: u64,
}
impl Progress {
    fn to_string(&self) -> String {
        format!("{}/{}", self.processed, self.total)
    }
}
let download = Download{
    filename: "debian-11.iso".to_string(),
    progress: Progress{ processed: 2, total: 4},
};

let description = object_describe_to_string(&download).unwrap();
assert_eq!(r#"
Filename: debian-11.iso
Progress: 2/4
"#,  description);

#[descriptor(output_table)]

Output a table-like output inside the description.

use descriptor::{Descriptor, object_describe_to_string};
#[derive(Descriptor, Clone)]
struct Car {
    name: String,
    seat: i16,
}

#[derive(Descriptor)]
struct User {
    name: String,
    cars: Vec<Car>,
    #[descriptor(output_table)]
    cars_list: Vec<Car>,
}

let cars = vec![Car{name: "Audi".to_string(), seat:4}, Car{name: "Mercedes".to_string(), seat: 2}];

let user = User{
    name: "Adrien".to_string(),
    cars: cars.clone(),
    cars_list: cars.clone(),
};
let description = object_describe_to_string(&user).unwrap();
assert_eq!(r#"
Name:      Adrien
Cars:
- Name: Audi
  Seat: 4
- Name: Mercedes
  Seat: 2
Cars List:
  NAME       SEAT
  Audi       4
  Mercedes   2
"#,  description);

#[descriptor(skip)]

  • #[descriptor(skip)]: Skip this field from description and default headers in table
  • #[descriptor(skip_description)]: Skip this field only from description
  • #[descriptor(skip_header)]: Skip this field from default headers
use descriptor::{Descriptor, object_describe_to_string, table_describe_to_string, table_describe_with_header_to_string, object_describe};
#[derive(Descriptor, Clone)]
struct Car {
    brand: String,
    #[descriptor(skip_header)]
    seat: i16,
    #[descriptor(skip_description)]
    model: String,
    #[descriptor(skip)]
    serial: String,
}
let  car = Car{brand: "Audi".to_string(), seat:4, model: "A3".to_string(), serial: "SN".to_string()};

let cars = vec![
    car.clone(),
    Car{brand: "Mercedes".to_string(), seat: 2, model: "GLC".to_string(), serial: "WD".to_string()}
];

let description = object_describe_to_string(&car).unwrap();
assert_eq!(r#"
Brand: Audi
Seat:  4
"#,  format!("{}", description));

let table = table_describe_to_string(&cars).unwrap();
assert_eq!(r#"
BRAND    MODEL
Audi     A3
Mercedes GLC
"#,  format!("\n{}", table));

let table = table_describe_with_header_to_string(&cars,&vec!["seat".to_string()] ).unwrap();
assert_eq!(r#"
SEAT
4
2
"#,  format!("\n{}", table));

#[descriptor(rename_header)]

Rename the auto-generated name for table header

use descriptor::{Descriptor, object_describe_to_string, table_describe_to_string, table_describe_with_header_to_string, object_describe};
#[derive(Descriptor)]
struct Car {
    #[descriptor(rename_header="Marque")]
    brand: String,
    #[descriptor(rename_header="Siege")]
    seat: i16,
}

let cars = vec![
    Car{brand: "Audi".to_string(), seat:4},
    Car{brand: "Mercedes".to_string(), seat: 2}
];

let table = table_describe_to_string(&cars).unwrap();
assert_eq!(r#"
Marque   Siege
Audi     4
Mercedes 2
"#,  format!("\n{}", table));

Enum parameters

#[descriptor(rename_description = "Renamed")]

Rename the value for enums.

use descriptor::{object_describe_to_string, Descriptor};
#[derive(Descriptor)]
struct User {
    name: String,
    role: Role,
}
#[derive(Descriptor)]
enum Role {
    Admin,
    #[descriptor(rename_description = "User role")]
    User,
}

let description = object_describe_to_string(&User {
   name: "Adrien".to_string(),
   role: Role::User,
}).unwrap();
assert_eq!(r#"
Name: Adrien
Role: User role
"#, description);

Structs

Traits

Functions