Repository: launchbadge/sqlx
File: sqlx-macros-core/src/query/data.rs (Rust)

Code before:
        assert!(!dyn_data.db_name.is_empty());
        assert!(!dyn_data.hash.is_empty());

        if DB::NAME == dyn_data.db_name {
            let describe: Describe<DB> = serde_json::from_value(dyn_data.describe)?;
            Ok(QueryData {
                db_name: SerializeDbName::default(),
                query: dyn_data.query,
                describe,
                hash: dyn_data.hash,
            })
        } else {
            Err(format!(
                "expected query data for {}, got data for {}",
                DB::NAME,
                dyn_data.db_name
            )
            .into())
        }
    }

    pub(super) fn save_in(&self, dir: impl AsRef<Path>) -> crate::Result<()> {
        let path = dir.as_ref().join(format!("query-{}.json", self.hash));
        match std::fs::remove_file(&path) {
            Ok(()) => {}
            Err(err) if err.kind() == std::io::ErrorKind::NotFound => {}
            Err(err) => return Err(format!("failed to delete {path:?}: {err:?}").into()),
        }
        let mut file = match std::fs::OpenOptions::new()
            .write(true)
            .create_new(true)
            .open(&path)
        {
            Ok(file) => file,
            // We overlapped with a concurrent invocation and the other one succeeded.
            Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => return Ok(()),
            Err(err) => {
                return Err(format!("failed to exclusively create {path:?}: {err:?}").into())
            }
        };

        let data = serde_json::to_string_pretty(self)
            .map_err(|err| format!("failed to serialize query data: {err:?}"))?;
        file.write_all(data.as_bytes())
            .map_err(|err| format!("failed to write query data to file: {err:?}"))?;

        // Ensure there is a newline at the end of the JSON file to avoid
        // accidental modification by IDE and make github diff tool happier.
        file.write_all(b"\n")
            .map_err(|err| format!("failed to append a newline to file: {err:?}"))?;


Review comment (bug):
I thought this might fail on Windows if another process has the file open for reading, but Rust sets the `FILE_SHARE_DELETE` flag by default: https://doc.rust-lang.org/stable/std/os/windows/fs/trait.OpenOptionsExt.html#tymethod.share_mode

Code after:
        assert!(!dyn_data.db_name.is_empty());
        assert!(!dyn_data.hash.is_empty());

        if DB::NAME == dyn_data.db_name {
            let describe: Describe<DB> = serde_json::from_value(dyn_data.describe)?;
            Ok(QueryData {
                db_name: SerializeDbName::default(),
                query: dyn_data.query,
                describe,
                hash: dyn_data.hash,
            })
        } else {
            Err(format!(
                "expected query data for {}, got data for {}",
                DB::NAME,
                dyn_data.db_name
            )
            .into())
        }
    }

    pub(super) fn save_in(&self, dir: impl AsRef<Path>) -> crate::Result<()> {
        use std::io::ErrorKind;

        let path = dir.as_ref().join(format!("query-{}.json", self.hash));
        match std::fs::remove_file(&path) {
            Ok(()) => {}
            Err(err)
                if matches!(
                    err.kind(),
                    ErrorKind::NotFound | ErrorKind::PermissionDenied,
                ) => {}
            Err(err) => return Err(format!("failed to delete {path:?}: {err:?}").into()),
        }
        let mut file = match std::fs::OpenOptions::new()
            .write(true)
            .create_new(true)
            .open(&path)
        {
            Ok(file) => file,
            // We overlapped with a concurrent invocation and the other one succeeded.
            Err(err) if matches!(err.kind(), ErrorKind::AlreadyExists) => return Ok(()),
            Err(err) => {
                return Err(format!("failed to exclusively create {path:?}: {err:?}").into())
            }
        };

        let data = serde_json::to_string_pretty(self)
            .map_err(|err| format!("failed to serialize query data: {err:?}"))?;
        file.write_all(data.as_bytes())
            .map_err(|err| format!("failed to write query data to file: {err:?}"))?;