using System.Text.Json;
namespace Sandbox.Resources;
///
/// A collection of helper methods for making your own resource compiler.
///
public abstract partial class ResourceCompiler
{
///
/// Writes resource to a JSON file, using the ResourceGenerator to create the resource.
///
///
///
protected async Task WriteToJson() where T : Resource
{
if ( !TryParseEmbeddedResource( out var serialized ) || !serialized.HasValue )
return false;
var generator = ResourceGenerator.Create( serialized.Value );
if ( generator is null || !generator.CacheToDisk ) return false;
var resource = await generator.CreateAsync( new ResourceGenerator.Options { ForDisk = true, Compiler = this }, default );
if ( resource is null ) return false;
Context.Data.Write( JsonSerializer.Serialize( resource ) );
return true;
}
///
/// Try to parse the source as an EmbeddedResource
/// Returns false if the source is not valid JSON or doesn't contain a ResourceGenerator.
///
protected bool TryParseEmbeddedResource( out EmbeddedResource? resource )
{
resource = null;
var json = Context.ReadSourceAsString();
if ( json is null ) return false;
// It's keyvalues
if ( json.StartsWith( "<" ) ) return false;
try
{
var parsed = JsonSerializer.Deserialize( json );
if ( string.IsNullOrEmpty( parsed.ResourceGenerator ) ) return false;
resource = parsed;
return true;
}
catch
{
// invalid json probably means it isn't json!
return false;
}
}
///
/// Create a deterministic path for a generated resource based on the embedded resource data.
///
protected string CreateGeneratedResourcePath( EmbeddedResource embed, string subfolder, string extension ) where T : Resource
{
var generator = ResourceGenerator.Create( embed );
if ( generator is null ) return null;
var di = DisplayInfo.For( generator );
var crc = Sandbox.Utility.Crc64.FromString( embed.Data.ToJsonString() );
var generatorName = (di.ClassName ?? di.Name).ToLower();
generatorName = generatorName.GetFilenameSafe();
return $"/{subfolder}/generated/{generatorName}/{crc:x}.{extension}";
}
///
/// Generic method to compile an embedded resource by creating a child context.
/// This handles the common pattern of creating a generator, generating a path,
/// creating a child context, and setting the compiled path.
///
protected bool CompileEmbeddedResource( ref EmbeddedResource embed, string subfolder, string extension, BaseFileSystem fs ) where T : Resource
{
var generator = ResourceGenerator.Create( embed );
if ( generator is null ) return false;
if ( !generator.CacheToDisk )
return false;
var transientPath = CreateGeneratedResourcePath( embed, subfolder, extension );
if ( transientPath is null ) return false;
var absPath = fs.GetFullPath( transientPath );
//
// generate a filename for the resource
//
var child = Context.CreateChild( absPath );
child.SetInputData( JsonSerializer.Serialize( embed ) );
child.Compile();
//
// store it in the json, so the compiled json will load the resource
//
embed.CompiledPath = transientPath.Trim( '/' );
return true;
}
}