Repository: bevyengine/bevy
File: crates/bevy_animation/src/graph.rs (Rust)

Code before:
        // Deserialize a `SerializedAnimationGraph` directly, so that we can
        // get the list of the animation clips it refers to and load them.
        let mut deserializer = ron::de::Deserializer::from_bytes(&bytes)?;
        let serialized_animation_graph = SerializedAnimationGraph::deserialize(&mut deserializer)
            .map_err(|err| deserializer.span_error(err))?;

        // Load all `AssetPath`s to convert from a `SerializedAnimationGraph` to a real
        // `AnimationGraph`. This is effectively a `DiGraph::map`, but this allows us to return
        // errors.
        let mut animation_graph = DiGraph::with_capacity(
            serialized_animation_graph.graph.node_count(),
            serialized_animation_graph.graph.edge_count(),
        );

        let mut already_warned = false;
        for serialized_node in serialized_animation_graph.graph.node_weights() {
            animation_graph.add_node(AnimationGraphNode {
                node_type: match serialized_node.node_type {
                    SerializedAnimationNodeType::Clip(ref clip) => match clip {
                        MigrationSerializedAnimationClip::Modern(path) => {
                            AnimationNodeType::Clip(load_context.load(path.clone()))
                        }
                        MigrationSerializedAnimationClip::Legacy(
                            SerializedAnimationClip::AssetPath(path),
                        ) => {
                            if !already_warned {
                                warn!(
                                    "Loaded an AnimationGraph asset which contains a legacy-style \
                                    SerializedAnimationClip. Please re-save the asset using \
                                    AnimationGraph::save to automatically migrate to the new format"
                                );
                                already_warned = true;
                            }
                            AnimationNodeType::Clip(load_context.load(path.clone()))
                        }
                        MigrationSerializedAnimationClip::Legacy(
                            SerializedAnimationClip::AssetId(_),
                        ) => {
                            return Err(AnimationGraphLoadError::GraphContainsLegacyAssetId);
                        }
                    },
                    SerializedAnimationNodeType::Blend => AnimationNodeType::Blend,
                    SerializedAnimationNodeType::Add => AnimationNodeType::Add,
                },
                mask: serialized_node.mask,
                weight: serialized_node.weight,
            });
        }
        for edge in serialized_animation_graph.graph.raw_edges() {
            animation_graph.add_edge(edge.source(), edge.target(), ());
        }

Review comment (suggestion):
I suppose `once!()` would warn only once globally and not once per load ? I.e. it can't be used here if we want to warn for each asset that needs to be migrated.

Code after:
        // Deserialize a `SerializedAnimationGraph` directly, so that we can
        // get the list of the animation clips it refers to and load them.
        let mut deserializer = ron::de::Deserializer::from_bytes(&bytes)?;
        let serialized_animation_graph = SerializedAnimationGraph::deserialize(&mut deserializer)
            .map_err(|err| deserializer.span_error(err))?;

        // Load all `AssetPath`s to convert from a `SerializedAnimationGraph` to a real
        // `AnimationGraph`. This is effectively a `DiGraph::map`, but this allows us to return
        // errors.
        let mut animation_graph = DiGraph::with_capacity(
            serialized_animation_graph.graph.node_count(),
            serialized_animation_graph.graph.edge_count(),
        );

        let mut already_warned = false;
        for serialized_node in serialized_animation_graph.graph.node_weights() {
            animation_graph.add_node(AnimationGraphNode {
                node_type: match serialized_node.node_type {
                    SerializedAnimationNodeType::Clip(ref clip) => match clip {
                        MigrationSerializedAnimationClip::Modern(path) => {
                            AnimationNodeType::Clip(load_context.load(path.clone()))
                        }
                        MigrationSerializedAnimationClip::Legacy(
                            SerializedAnimationClip::AssetPath(path),
                        ) => {
                            if !already_warned {
                                let path = load_context.asset_path();
                                warn!(
                                    "Loaded an AnimationGraph asset at \"{path}\" which contains a \
                                    legacy-style SerializedAnimationClip. Please re-save the asset \
                                    using AnimationGraph::save to automatically migrate to the new \
                                    format"
                                );
                                already_warned = true;
                            }
                            AnimationNodeType::Clip(load_context.load(path.clone()))
                        }
                        MigrationSerializedAnimationClip::Legacy(
                            SerializedAnimationClip::AssetId(_),
                        ) => {
                            return Err(AnimationGraphLoadError::GraphContainsLegacyAssetId);
                        }
                    },
                    SerializedAnimationNodeType::Blend => AnimationNodeType::Blend,
                    SerializedAnimationNodeType::Add => AnimationNodeType::Add,
                },
                mask: serialized_node.mask,
                weight: serialized_node.weight,
            });
        }
        for edge in serialized_animation_graph.graph.raw_edges() {