libbtrfs 0.0.20

Rust library for working with the btrfs filesystem
Documentation
use bindgen::callbacks::{self, IntKind};
use std::{env, path::PathBuf};

const HELPER_PREFIX: &str = "RUST_CONST_HELPER";
const VERSION_PREFIX: &str = "__LINUX_VERSION";

#[derive(Debug)]
struct Callbacks;

impl callbacks::ParseCallbacks for Callbacks {
    fn generated_name_override(&self, item: callbacks::ItemInfo<'_>) -> Option<String> {
        item.name
            .starts_with(HELPER_PREFIX)
            .then(|| item.name[HELPER_PREFIX.len() + 1..].into())
    }

    fn item_name(&self, item: callbacks::ItemInfo) -> Option<String> {
        match item.name {
            name if name.starts_with("btrfs_ioctl_vol_args_v2") => match name {
                "btrfs_ioctl_vol_args_v2__bindgen_ty_2" => Some("vol_args_v2_volume"),
                "btrfs_ioctl_vol_args_v2__bindgen_ty_1" => Some("vol_args_v2_qgroup"),
                "btrfs_ioctl_vol_args_v2__bindgen_ty_1__bindgen_ty_1" => {
                    Some("vol_args_v2_qgroup_opts")
                }
                _ => None,
            },
            _ => None,
        }
        .map(Into::into)
    }

    fn int_macro(&self, name: &str, _value: i64) -> Option<IntKind> {
        match name {
            "BTRFS_DEVICE_PATH_NAME_MAX"
            | "BTRFS_FSID_SIZE"
            | "BTRFS_LABEL_SIZE"
            | "BTRFS_PATH_NAME_MAX"
            | "BTRFS_UUID_SIZE"
            | "BTRFS_UUID_UNPARSED_SIZE"
            | "BTRFS_VOL_NAME_MAX" => Some(IntKind::Custom {
                name: "usize",
                is_signed: false,
            }),

            "BTRFS_CSUM_SIZE"
            | "BTRFS_IOCTL_MAGIC"
            | "BTRFS_MAX_METADATA_BLOCKSIZE"
            | "BTRFS_SAME_DATA_DIFFERS" => None,

            _ if name.ends_with("KEY")
                || name.starts_with("BTRFS_SUBVOL_SYNC")
                || name.starts_with("BTRFS_ENCODED_IO") =>
            {
                None
            }
            _ if name.starts_with("BTRFS_FT") => Some(IntKind::U8),
            _ => Some(IntKind::U64),
        }
    }
}

#[derive(Debug)]
struct VersionCallbacks;

impl callbacks::ParseCallbacks for VersionCallbacks {
    fn will_parse_macro(&self, name: &str) -> callbacks::MacroParsingBehavior {
        if let Some(v) = name.strip_prefix(VERSION_PREFIX) {
            println!("cargo::rustc-cfg=VERSION{v}");
        }
        callbacks::MacroParsingBehavior::Ignore
    }
}

fn main() {
    println!("cargo::rustc-check-cfg=cfg(VERSION_6_15)");
    println!("cargo::rustc-check-cfg=cfg(VERSION_6_13)");
    println!("cargo::rustc-check-cfg=cfg(VERSION_6_3)");
    println!("cargo::rustc-check-cfg=cfg(VERSION_6_2)");
    println!("cargo::rustc-check-cfg=cfg(VERSION_3_12)");

    println!("cargo::rerun-if-changed=bindings/wrapper.h");
    println!("cargo::rerun-if-changed=bindings/version.h");

    bindgen::Builder::default()
        .header("bindings/version.h")
        .parse_callbacks(Box::new(VersionCallbacks))
        .generate()
        .expect("Unable to determine version");

    let bindings = bindgen::Builder::default()
        .header("bindings/wrapper.h")
        // Note: unicode-case feature is not enabled for bindgen regex dependency
        // so unicode must be disabled for a case insensitve patten to work
        // https://docs.rs/regex/latest/regex/index.html#crate-features
        .allowlist_item(r"(?i-u:btrfs).*")
        .anon_fields_prefix("inner")
        .derive_default(false)
        .derive_debug(false)
        .disable_nested_struct_naming()
        .parse_callbacks(Box::new(Callbacks))
        .prepend_enum_name(false)
        .generate()
        .expect("Unable to generate bindings");

    let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_dir.join("generated.rs"))
        .expect("Unable to write bindings");
}