Attribute Macro gsettings_macro::gen_settings

source ·
#[gen_settings]
Expand description

Macro for typesafe gio::Settings key access.

The macro’s main purpose is to reduce the risk of mistyping a key, using the wrong method to access values, inputting incorrect values, and to reduce boilerplate. Additionally, the summary, description, and default value are included in the documentation of each generated method. This would be beneficial if you use tools like rust-analyzer.

⚠️ IMPORTANT ⚠️

Both gio and glib need to be in scope, so unless they are direct crate dependencies, you need to import them because gen_settings is using them internally. For example:

use gtk::{gio, glib};

Example

use gsettings_macro::gen_settings;

#[gen_settings(file = "./tests/io.github.seadve.test.gschema.xml")]
pub struct ApplicationSettings;

let settings = ApplicationSettings::new("io.github.seadve.test");

// `i` D-Bus type
settings.set_window_width(100);
assert_eq!(settings.window_width(), 100);

// enums
settings.set_alert_sound(AlertSound::Glass);
assert_eq!(settings.alert_sound(), AlertSound::Glass);

// bitflags
settings.set_space_style(SpaceStyle::BEFORE_COLON | SpaceStyle::BEFORE_COMMA);
assert_eq!(
    settings.space_style(),
    SpaceStyle::BEFORE_COLON | SpaceStyle::BEFORE_COMMA
);

Note: The file path is relative to the project root or where the Cargo.toml file is located.

Generated methods

The procedural macro generates the following gio::Settings methods for each key in the schema:

  • set -> set_${key}, which panics when writing in a readonly key, and try_set_${key}, which behaves the same as the original method.
  • get -> ${key}
  • connect_changed -> connect_${key}_changed
  • bind -> bind_${key}
  • create_action -> create_${key}_action
  • default_value -> ${key}_default_value
  • reset -> reset_${key}

Known D-Bus type signatures

The setter and getter methods has the following parameter and return type, depending on the key’s type signature.

Type SignatureParameter TypeReturn Type
bboolbool
ii32i32
uu32u32
xi64i64
tu64u64
df64f64
(ii)(i32, i32)(i32, i32)
as&[&str]Vec<String>
s *&strString

* If the key of type signature s has no choice attribute specified in the GSchema, the parameter and return types stated in the table would be applied. Otherwise, it will generate an enum, like described in the next section, and use it as the parameter and return types, instead of &str and String respectively.

It will not compile if the type signature is not defined above. However, it is possible to explicitly skip generating methods for a specific key or type signature using the attribute #[gen_settings_skip], or define a custom parameter and return types using #[gen_settings_define] attribute. The usage of the latter will be further explained in the following sections.

Enums and Flags

The macro will also automatically generate enums or flags. If it is an enum, it would generated a normal Rust enum with each nick specified in the GSchema converted to pascal case as an enum variant. The enum would implement both ToVariant and FromVariant, Clone, Hash, PartialEq, Eq, PartialOrd, and Ord. On the other hand, if it is a flag, it would generate bitflags same as the bitflags generated by the bitflags macro with each nick specified in the GSchema converted to screaming snake case as a const flag.

The generated types, enum or bitflags, would have the same visibility and scope with the generated struct.

Skipping methods generation

This would be helpful if you want to have full control with the key without the macro intervening. For example:

use gsettings_macro::gen_settings;

#[gen_settings(
    file = "./tests/io.github.seadve.test.gschema.xml",
    id = "io.github.seadve.test"
)]
// Skip generating methods for keys with type signature `(ss)`
#[gen_settings_skip(signature = "(ss)")]
// Skip generating methods for the key of name `some-key-name`
#[gen_settings_skip(key_name = "some-key-name")]
pub struct Settings;

impl Settings {
    pub fn set_some_key_name(value: &std::path::Path) {
        ...
    }
}

Defining custom types

use gsettings_macro::gen_settings;

use std::path::{Path, PathBuf};

#[gen_settings(file = "./tests/io.github.seadve.test.gschema.xml")]
// Define custom parameter and return types for keys with type `(ss)`
#[gen_settings_define(
    signature = "(ss)",
    arg_type = "(&str, &str)",
    ret_type = "(String, String)"
)]
// Define custom parameter and return types for key with name `cache-dir`
#[gen_settings_define(key_name = "cache-dir", arg_type = "&Path", ret_type = "PathBuf")]
pub struct SomeAppSettings;

let settings = SomeAppSettings::new("io.github.seadve.test");

settings.set_cache_dir(Path::new("/some_dir"));
assert_eq!(settings.cache_dir(), PathBuf::from("/some_dir"));

settings.set_string_tuple(("hi", "hi2"));
assert_eq!(settings.string_tuple(), ("hi".into(), "hi2".into()));

The type specified in arg_type and ret_type has to be on scope or you can specify the full path.

If you somehow do not want an enum parameter and return types for s type signature with choices. You can also use this to override that behavior.

Note: The type has to implement both ToVariant and FromVariant or it would fail to compile.

Default trait

The schema id can be specified as an attribute, making it implement Default and create a new constructor without parameters. Otherwise, it will not implement Default and would require the schema id as an parameter in the the constructor or the new method.

The following is an example of defining the id attribute in the macro:

use gsettings_macro::gen_settings;

#[gen_settings(
    file = "./tests/io.github.seadve.test.gschema.xml",
    id = "io.github.seadve.test"
)]
pub struct ApplicationSettings;

// The id is specified above so it is not needed
// to specify it in the constructor.
let settings = ApplicationSettings::new();
let another_instance = ApplicationSettings::default();