use super::{display::BufferDisplayPath, error::BufferWriteError};
use crate::{
fs_utils::{self, FilesystemConfig},
text_stream::{TextByteStream, TextRevision},
};
use bevy::prelude::Component;
use std::path::PathBuf;
#[derive(Clone, Component, Debug, Default, Eq, PartialEq)]
pub struct BufferFile {
path: Option<PathBuf>,
saved_revision: TextRevision,
}
impl BufferFile {
#[must_use]
pub fn scratch(saved_revision: impl Into<TextRevision>) -> Self {
Self {
path: None,
saved_revision: saved_revision.into(),
}
}
#[must_use]
pub fn backed_by(path: PathBuf, saved_revision: impl Into<TextRevision>) -> Self {
Self {
path: Some(path),
saved_revision: saved_revision.into(),
}
}
#[must_use]
pub const fn path(&self) -> Option<&PathBuf> {
self.path.as_ref()
}
#[must_use]
pub fn is_modified(&self, stream: &TextByteStream) -> bool {
self.saved_revision != stream.revision()
}
pub fn write(
&mut self,
stream: &TextByteStream,
config: &FilesystemConfig,
) -> Result<BufferWriteReport, BufferWriteError> {
let Some(path) = &self.path else {
return Err(BufferWriteError::NoFileName);
};
let written_path = fs_utils::atomic_write(path, stream.as_bytes(), config)
.map_err(BufferWriteError::File)?;
self.saved_revision = stream.revision();
Ok(BufferWriteReport::new(
BufferDisplayPath::from_resolved_path(&written_path, config),
stream.as_str(),
stream.as_bytes().len(),
))
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct BufferWriteReport {
display_path: BufferDisplayPath,
line_count: usize,
byte_count: usize,
}
impl BufferWriteReport {
pub(super) fn new(display_path: BufferDisplayPath, text: &str, byte_count: usize) -> Self {
Self {
display_path,
line_count: line_count(text),
byte_count,
}
}
#[must_use]
pub fn status_text(&self) -> String {
format!(
"\"{}\" {}L, {}B written",
self.display_path.as_str(),
self.line_count,
self.byte_count
)
}
}
fn line_count(text: &str) -> usize {
if text.is_empty() {
0
} else {
text.lines().count()
}
}