fieldwork-borrow 0.1.0

Field-level borrow splitting for Rust
Documentation
//! e8: multiple method calls (cur_model + cur_mat_anim) while mutating uv_mats

use fieldwork_borrow::with_;

pub struct Database {
    pub models: Vec<Model>,
    pub mat_anims: Vec<MaterialAnimation>,
}

pub struct Connection {
    pub models: Vec<ModelConnection>,
}

pub struct MatAnimConnection {
    pub mat_anim_id: usize,
}

pub struct ModelConnection {
    pub mat_anims: Vec<MatAnimConnection>,
}

pub struct Model {
    pub materials: Vec<Material>,
}

pub struct Material;

pub struct MaterialAnimation {
    pub tracks: Vec<MaterialTrack>,
}

pub struct MaterialTrack {
    value: f64,
}

impl MaterialTrack {
    pub fn eval_uv_mat(&self, _frame: u16) -> f64 {
        self.value
    }
}

pub struct Viewer {
    db: Database,
    conn: Connection,
    model_id: usize,
    uv_mats: Vec<f64>,
    mat_anim_state: AnimState,
}

struct AnimState {
    connection_idx: Option<usize>,
    frame: u16,
}

impl Viewer {
    // Original problem code (won't compile):
    // fn problem(&mut self) {
    //     if let Some(mat_anim) = self.cur_mat_anim() {
    //         for track in &mat_anim.tracks {
    //             for (i, _) in self.cur_model().materials.iter().enumerate() {
    //                 self.uv_mats[i] = track.eval_uv_mat(self.mat_anim_state.frame);
    //             }
    //         }
    //     }
    // }

    fn problem(&mut self) {
        with_!(self, {
            methods {
                cur_model(&db, copy model_id) -> &Model {
                    &db.models[model_id]
                }
                cur_mat_anim(&db, &conn, copy model_id, &mat_anim_state) -> Option<&MaterialAnimation> {
                    let idx = mat_anim_state.connection_idx;
                    idx.map(|i| {
                        let anim_id = conn.models[model_id].mat_anims[i].mat_anim_id;
                        &db.mat_anims[anim_id]
                    })
                }
            }
            fields {
                mut uv_mats
                mat_anim_state
            }
            body {
                if let Some(mat_anim) = self.cur_mat_anim() {
                    for track in &mat_anim.tracks {
                        for (i, _) in self.cur_model().materials.iter().enumerate() {
                            uv_mats[i] = track.eval_uv_mat(mat_anim_state.frame);
                        }
                    }
                }
            }
        });
    }
}

#[test]
fn test_e8() {
    let mut viewer = Viewer {
        db: Database {
            models: vec![Model {
                materials: vec![Material, Material, Material],
            }],
            mat_anims: vec![MaterialAnimation {
                tracks: vec![MaterialTrack { value: 42.0 }],
            }],
        },
        conn: Connection {
            models: vec![ModelConnection {
                mat_anims: vec![MatAnimConnection { mat_anim_id: 0 }],
            }],
        },
        model_id: 0,
        uv_mats: vec![0.0, 0.0, 0.0],
        mat_anim_state: AnimState {
            connection_idx: Some(0),
            frame: 0,
        },
    };

    viewer.problem();

    assert_eq!(viewer.uv_mats, vec![42.0, 42.0, 42.0]);
}