You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: shape_inference.rs
    path of file: ./repos/wonnx/wonnx-preprocessing/src
    
    the code of the file until where you have to start completion: use std::{borrow::Cow, collections::HashMap};

use protobuf::ProtobufEnum;
use thiserror::Error;
use wonnx::{
    onnx::{
        GraphProto, NodeProto, TensorProto, TensorShapeProto, TensorShapeProto_Dimension,
        TypeProto, TypeProto_Tensor, TypeProto_oneof_value, ValueInfoProto,
    },
    utils::{
        AttributeNotFoundError, DataTypeError, InputTensor, NodeAttributes, ScalarType, Shape,
    },
};

use crate::constant_folding::{calculate_constant_node_outputs, ConstantFoldingError};

pub fn apply_dynamic_dimensions(graph: &mut GraphProto, dynamic_dims: &HashMap<String, i64>) {
    // Apply to values
    for value_info in graph.mut_value_info() {
        apply_dynamic_dimensions_value(value_info, dynamic_dims);
    }

    for value_info in graph.mut_input() {
        apply_dynamic_dimensions_value(value_info, dynamic_dims);
    }

    for value_info in graph.mut_output() {
        apply_dynamic_dimensions_value(value_info, dynamic_dims);
    }
}

/// Divide a number by the indicated dividend,
    
    async fn test_shape_inference_for_model(path: &str, should_fold_constants: bool) {
        let mut model =
            ModelProto::parse_from_bytes(&std::fs::read(path).expect("ONNX Model path not found."))
                .unwrap();

        let graph = model.mut_graph();
        let infos = dimensions_infos(graph).unwrap();
        graph.value_info.clear();
        infer_shapes(graph, should_fold_constants, 13)
            .await
            .unwrap();
        let new_infos = dimensions_infos(graph).unwrap();

        let keys_in_old: HashSet<String> = infos.keys().cloned().collect();
        let keys_in_new: HashSet<String> = new_infos.keys().cloned().collect();
        let all_keys: HashSet<String> = keys_in_old.union(&keys_in_new).cloned().collect();

        for key in all_keys {
            if !keys_in_old.contains(&key) || !keys_in_new.contains(&key) || infos[&key].is_empty()
            {
                // Key is new after shape inference (the original model was apparently missing shapes)
                // Key missing after shape inference (this may be the result of constant folding)
                // Empty dims in source means missing... ignore
            } else {
                assert_eq!(
                    infos[&key], new_infos[&key],
                    "different shape inferred for {key}"
                )
            }
        }
    }