Repository: DioxusLabs/dioxus
File: packages/fullstack-protocol/src/lib.rs (Rust)

Code before:
        ciborium::into_writer(&self.data, &mut serialized).unwrap();

        let data = base64::engine::general_purpose::STANDARD.encode(serialized);

        let format_js_list_of_strings = |list: &[Option<String>]| {
            let body = list
                .iter()
                .map(|s| match s {
                    Some(s) => format!(r#""{s}""#),
                    None => r#""unknown""#.to_string(),
                })
                .collect::<Vec<_>>()
                .join(",");
            format!("[{}]", body)
        };

        SerializedHydrationData {
            data,
            #[cfg(debug_assertions)]
            debug_types: format_js_list_of_strings(&self.debug_types),
            #[cfg(debug_assertions)]
            debug_locations: format_js_list_of_strings(
                &self
                    .debug_locations
                    .iter()
                    .map(|s| Some(s.as_ref().unwrap().replace("\\", "\\\\").to_string()))
                    .collect::<Vec<_>>(),
            ),
        }
    }
}

/// Data that was serialized on the server for hydration on the client. This includes
/// extra information about the types and sources of the serialized data in debug mode
pub struct SerializedHydrationData {
    /// The base64 encoded serialized data
    pub data: String,
    /// A list of the types of each serialized data
    #[cfg(debug_assertions)]
    pub debug_types: String,
    /// A list of the locations of each serialized data
    #[cfg(debug_assertions)]
    pub debug_locations: String,
}

/// An error that can occur when trying to take data from the server
#[derive(Debug)]
pub enum TakeDataError {
    /// Deserializing the data failed
    DeserializationError(ciborium::de::Error<std::io::Error>),
    /// No data was available

Review comment (bug):
I think this is close, but there are a few small things we could change to make this more robust:
- Can you change this to `s.as_ref().map(|s| s.replace(...))` instead so it handles the None case?
- This should be implemented in `format_js_list_of_strings` in case the debug types ever contain a character we need to escape
- There are a few other cases we need to handle when formatting strings: `\n` should map to `\\n` and `\"` should map to `\\"`

Code after:
        ciborium::into_writer(&self.data, &mut serialized).unwrap();

        let data = base64::engine::general_purpose::STANDARD.encode(serialized);

        let format_js_list_of_strings = |list: &[Option<String>]| {
            let body = list
                .iter()
                .map(|s| match s {
                    Some(s) => {
                        // Escape backslashes, quotes, and newlines
                        let escaped = s
                            .replace(r#"\"#, r#"\\"#)
                            .replace("\n", r#"\n"#)
                            .replace(r#"""#, r#"\""#);

                        format!(r#""{escaped}""#)
                    }
                    None => r#""unknown""#.to_string(),
                })
                .collect::<Vec<_>>()
                .join(",");
            format!("[{}]", body)
        };

        SerializedHydrationData {
            data,
            #[cfg(debug_assertions)]
            debug_types: format_js_list_of_strings(&self.debug_types),
            #[cfg(debug_assertions)]
            debug_locations: format_js_list_of_strings(&self.debug_locations),
        }
    }
}

/// Data that was serialized on the server for hydration on the client. This includes
/// extra information about the types and sources of the serialized data in debug mode
pub struct SerializedHydrationData {
    /// The base64 encoded serialized data
    pub data: String,
    /// A list of the types of each serialized data
    #[cfg(debug_assertions)]
    pub debug_types: String,
    /// A list of the locations of each serialized data
    #[cfg(debug_assertions)]
    pub debug_locations: String,
}

/// An error that can occur when trying to take data from the server
#[derive(Debug)]
pub enum TakeDataError {
    /// Deserializing the data failed