use super::{
directory_manager::DirectoryManager,
folder_graph::*,
pipelines::PipelineManager,
resources::{CreatedResource, RemovedResource},
utils, FolderGraph, PathStrExt, ViewPathLocationExt, YyResource, YyResourceHandler,
YypSerialization,
};
use crate::Resource;
use anyhow::{format_err, Context, Result as AnyResult};
use log::*;
use std::{collections::HashMap, fs, path::Path};
use yy_typings::{sprite_yy::*, utils::TrailingCommaUtility, Yyp};
#[derive(Debug)]
pub struct YypBoss {
pub directory_manager: DirectoryManager,
pub pipeline_manager: PipelineManager,
pub sprites: YyResourceHandler<Sprite>,
yyp: Yyp,
folder_graph: FolderGraph,
resource_names: HashMap<String, Resource>,
tcu: TrailingCommaUtility,
dirty: bool,
}
impl YypBoss {
pub fn new(path_to_yyp: &Path) -> AnyResult<YypBoss> {
let tcu = TrailingCommaUtility::new();
let yyp = utils::deserialize(path_to_yyp, Some(&tcu)).with_context(|| "on the yyp")?;
let directory_manager = DirectoryManager::new(path_to_yyp)?;
let mut yyp_boss = Self {
yyp,
dirty: false,
folder_graph: FolderGraph::root(),
resource_names: HashMap::new(),
tcu,
sprites: YyResourceHandler::new(),
pipeline_manager: PipelineManager::new(&directory_manager)?,
directory_manager: DirectoryManager::new(path_to_yyp)?,
};
for new_folder in yyp_boss.yyp.folders.iter() {
let mut folder_graph = &mut yyp_boss.folder_graph;
for section in new_folder.folder_path.component_paths() {
let parent_path = folder_graph.view_path();
let section = section.trim_yy().to_owned();
let entry = folder_graph.folders.entry(section.clone());
let new_member = entry.or_insert(SubfolderMember {
child: FolderGraph::new(section, parent_path),
order: new_folder.order,
});
folder_graph = &mut new_member.child;
}
}
for sprite_resource in yyp_boss
.yyp
.resources
.iter()
.filter(|value| value.id.path.starts_with("sprites"))
{
let sprite_path = yyp_boss
.directory_manager
.root_directory()
.join(&sprite_resource.id.path);
let sprite_yy: Sprite = utils::deserialize(&sprite_path, Some(&yyp_boss.tcu))
.with_context(|| format!("on sprite {:?}", sprite_path))?;
yyp_boss
.folder_graph
.find_subfolder_mut(&sprite_yy.parent)?
.files
.insert(
sprite_yy.name.clone(),
FileMember {
child: FilesystemPath::new(Sprite::SUBPATH_NAME, &sprite_yy.name),
order: sprite_resource.order,
},
);
yyp_boss
.resource_names
.insert(sprite_yy.name.clone(), Resource::Sprite);
yyp_boss.sprites.load_on_startup(sprite_yy);
}
Ok(yyp_boss)
}
pub fn default_texture_path(&self) -> Option<TexturePath> {
self.yyp
.texture_groups
.iter()
.find(|tex| tex.name == "Default")
.map(|texture_group| texture_group.into())
}
pub fn current_resource_names(&self) -> Vec<(String, Resource)> {
self.resource_names.clone().into_iter().collect()
}
pub fn new_folder_end(
&mut self,
parent_path: &ViewPath,
name: String,
) -> Result<ViewPath, FolderGraphError> {
let subfolder = self.folder_graph.find_subfolder_mut(parent_path)?;
let order = subfolder.max_suborder().map(|v| v + 1).unwrap_or_default();
if subfolder.folders.contains_key(&name) {
return Err(FolderGraphError::FolderAlreadyPresent);
}
let path = parent_path.path.join(&name);
subfolder.folders.insert(
name.clone(),
SubfolderMember {
child: FolderGraph::new(name.clone(), parent_path.clone()),
order,
},
);
self.yyp.folders.push(YypFolder {
folder_path: path.clone(),
order,
name: name.clone(),
..YypFolder::default()
});
self.dirty = true;
Ok(ViewPath { path, name })
}
pub fn new_folder_order(
&mut self,
parent_path: ViewPath,
name: String,
order: usize,
) -> Result<ViewPath, FolderGraphError> {
let subfolder = self.folder_graph.find_subfolder_mut(&parent_path)?;
if subfolder.folders.contains_key(&name) {
return Err(FolderGraphError::FolderAlreadyPresent);
}
subfolder.folders.insert(
name.clone(),
SubfolderMember {
child: FolderGraph::new(name.clone(), parent_path.clone()),
order,
},
);
let path = parent_path.path.join(&name);
self.yyp.folders.push(YypFolder {
folder_path: path.clone(),
order,
name: name.clone(),
..YypFolder::default()
});
self.dirty = true;
for (folder_name, folder) in subfolder.folders.iter_mut() {
if folder.order <= order {
folder.order += 1;
if let Err(e) = folder.update_yyp(&mut self.yyp.folders) {
error!(
"We couldn't find {0} in the Yyp, even though we had {0} in the FolderGraph.\
This may become a hard error in the future. E: {1}",
folder_name, e
)
}
}
}
for (file_name, file) in subfolder.files.iter_mut() {
if file.order <= order {
file.order += 1;
if let Err(e) = file.update_yyp(&mut self.yyp.resources) {
error!(
"We couldn't find {0} in the Yyp, even though we had {0} in the FolderGraph.\
This may become a hard error in the future. E: {1}",
file_name, e
)
}
}
}
Ok(ViewPath { path, name })
}
pub fn new_resource_end(
&mut self,
parent_path: ViewPath,
resource_name: &str,
resource_kind: Resource,
) -> Result<CreatedResource, FolderGraphError> {
let subfolder = self.folder_graph.find_subfolder_mut(&parent_path)?;
let order = subfolder.max_suborder().map(|v| v + 1).unwrap_or_default();
if subfolder.files.contains_key(resource_name) {
return Err(FolderGraphError::FileAlreadyPresent);
}
let child = FilesystemPath::new(resource_kind.base_name(), resource_name);
subfolder.files.insert(
resource_name.to_owned(),
FileMember {
child: child.clone(),
order,
},
);
self.add_new_yyp_resource(child, order, resource_kind);
Ok(CreatedResource(resource_kind))
}
pub fn remove_resource(
&mut self,
resource_name: &str,
resource_kind: Resource,
) -> Result<RemovedResource, FolderGraphError> {
let fp = FilesystemPath::new(resource_kind.base_name(), resource_name);
self.remove_yyp_resource(&fp);
if let Some(subfolder) = self.folder_graph.find_subfolder_by_file(resource_name) {
subfolder.files.remove(resource_name);
} else {
return Err(FolderGraphError::FolderGraphOutofSyncWithYyp);
}
Ok(RemovedResource(resource_kind))
}
pub fn get_resource(&self, resource_name: &str) -> Option<CreatedResource> {
self.resource_names
.get(resource_name)
.map(|resource_kind| CreatedResource(*resource_kind))
}
fn add_new_yyp_resource(&mut self, id: FilesystemPath, order: usize, resource: Resource) {
self.resource_names.insert(id.name.clone(), resource);
let new_yyp_resource = YypResource { id, order };
self.yyp.resources.push(new_yyp_resource);
self.dirty = true;
}
fn remove_yyp_resource(&mut self, resource: &FilesystemPath) {
self.resource_names.remove(&resource.name);
if let Some(pos) = self.yyp.resources.iter().position(|p| &p.id == resource) {
self.yyp.resources.remove(pos);
}
self.dirty = true;
}
pub fn serialize(&mut self) -> AnyResult<()> {
self.sprites.serialize(&self.directory_manager)?;
self.pipeline_manager
.serialize(&self.directory_manager)
.context("serializing pipelines")?;
if self.dirty {
let string = self.yyp.yyp_serialization(0);
fs::write(&self.directory_manager.yyp(), &string)?;
self.dirty = false;
}
Ok(())
}
}
impl YypBoss {
pub fn root_path(&self) -> ViewPath {
ViewPath {
name: "folders".to_string(),
path: ViewPathLocation("folders".to_string()),
}
}
pub fn yyp(&self) -> &Yyp {
&self.yyp
}
pub fn root_folder(&self) -> &FolderGraph {
&self.folder_graph
}
pub fn folder(&self, view_path: &ViewPath) -> Option<FolderGraph> {
if view_path.name != self.folder_graph.name {
let mut folder = &self.folder_graph;
for path in view_path.path.component_paths() {
folder = &folder
.folders
.get(path)
.ok_or_else(|| format_err!("Couldn't find subfolder {}", path))
.ok()?
.child;
}
Some(folder.clone())
} else {
Some(self.folder_graph.clone())
}
}
}
impl Into<Yyp> for YypBoss {
fn into(self) -> Yyp {
self.yyp
}
}
impl PartialEq for YypBoss {
fn eq(&self, other: &Self) -> bool {
self.yyp == other.yyp
&& self.folder_graph == other.folder_graph
&& self.resource_names == other.resource_names
}
}