# BookStorageService

- File ID: file:14qnurl:bookstorageservice-cs
- Path: NexusReader.Infrastructure/Services/BookStorageService.cs
- Role: service
- Layer: infrastructure

## Purpose

Provides an infrastructure service that persists ebook and cover files to the local web root filesystem and returns web-relative paths for stored assets.

## Key Details

Stores files under web root uploads/ and covers/ with GUID-prefixed filenames, ensures directories exist before write, and returns forward-slash web-relative paths to avoid Windows backslash issues; SaveCoverAsync(byte[]) returns null for empty input.

## Symbols

- BookStorageService: Provides an infrastructure service that persists ebook and cover files to the local web root filesystem and returns web-relative paths for stored assets.
- BookStorageService(): Ctor; captures IWebHostEnvironment for resolving web root paths.
- SaveEbookAsync(): Wraps a byte-array payload into a MemoryStream and delegates to the Stream overload to persist an ebook.
- SaveEbookAsync(): Writes the provided stream to the uploads folder under web root with a GUID-prefixed unique filename and returns a web-relative path (forward-slash).
- SaveCoverAsync(): Validates byte-array cover data and delegates to the Stream overload; returns null for empty input.
- SaveCoverAsync(): Writes the provided cover stream to the covers folder under web root with a GUID-prefixed unique filename and returns a web-relative path (nullable).
- EnsureDirectoryExists(): Checks for the existence of a directory and creates it if missing.
- BookStorageService: Infrastructure implementation for persisting book assets to local filesystem exposed via IBookStorageService.

## Dependencies

- No resolved dependencies.

## Calls

- No resolved calls.

## Unresolved References

- dependency-target: NexusReader.Application.Abstractions.Services (IBookStorageService)
- dependency-target: Microsoft.AspNetCore.Hosting (IWebHostEnvironment)
- call-target: self
- call-target: self
- call-target: self
- call-target: self

## Coverage

- Symbols: 8
- Executable symbols: 6
- Executable symbols with control flow: 6
- Grounded edges: 8
- Unresolved references: 6
- Control-flow fragments: 6
- Unresolved jumps: 0
- Unsupported constructs: 0
- Control-flow coverage: 100%
- Required diagrams: 2
- Generated diagrams: 4

## Control Flow Fragments

- BookStorageService: 3 nodes, 2 edges, 0 jumps, 0 unsupported constructs
- SaveEbookAsync: 7 nodes, 6 edges, 1 jumps, 0 unsupported constructs
- SaveEbookAsync: 12 nodes, 12 edges, 1 jumps, 0 unsupported constructs
- SaveCoverAsync: 14 nodes, 15 edges, 1 jumps, 0 unsupported constructs
- SaveCoverAsync: 12 nodes, 12 edges, 1 jumps, 0 unsupported constructs
- EnsureDirectoryExists: 10 nodes, 11 edges, 0 jumps, 0 unsupported constructs

## Diagrams

### BookStorageService Neighborhood

```mermaid
flowchart LR
    class_gamvik_bookstorageservice("BookStorageService")
    file_14qnurl_bookstorageservice_cs("BookStorageService")
    method_1iw78py_saveebookasync[["SaveEbookAsync()"]]
    method_1o0t990_savecoverasync[["SaveCoverAsync()"]]
    method_1ouv7tw_savecoverasync[["SaveCoverAsync()"]]
    method_axmshz_bookstorageservice[["BookStorageService()"]]
    method_je6l0s_ensuredirectoryexists[["EnsureDirectoryExists()"]]
    method_mpmpcx_saveebookasync[["SaveEbookAsync()"]]
    type_9todq3_bookstorageservice[/"BookStorageService"/]
```

### BookStorageService Execution Overview

```mermaid
flowchart TD
    class_gamvik_bookstorageservice("BookStorageService")
    method_1iw78py_saveebookasync[["SaveEbookAsync()"]]
    method_1o0t990_savecoverasync[["SaveCoverAsync()"]]
    method_1ouv7tw_savecoverasync[["SaveCoverAsync()"]]
    method_axmshz_bookstorageservice[["BookStorageService()"]]
    method_je6l0s_ensuredirectoryexists[["EnsureDirectoryExists()"]]
    method_mpmpcx_saveebookasync[["SaveEbookAsync()"]]
    unresolved_unresolved_1a9m2p_call_target{"Unresolved: EnsureDirectoryExists"}
    unresolved_unresolved_1p1xq63_call_target{"Unresolved: SaveEbookAsync"}
    unresolved_unresolved_wakoep_call_target{"Unresolved: EnsureDirectoryExists"}
    unresolved_unresolved_zjmxe8_call_target{"Unresolved: SaveCoverAsync"}
    class_gamvik_bookstorageservice -->|contains| method_1iw78py_saveebookasync
    class_gamvik_bookstorageservice -->|contains| method_1o0t990_savecoverasync
    class_gamvik_bookstorageservice -->|contains| method_1ouv7tw_savecoverasync
    class_gamvik_bookstorageservice -->|contains| method_axmshz_bookstorageservice
    class_gamvik_bookstorageservice -->|contains| method_je6l0s_ensuredirectoryexists
    class_gamvik_bookstorageservice -->|contains| method_mpmpcx_saveebookasync
    method_1o0t990_savecoverasync -->|unresolved call| unresolved_unresolved_wakoep_call_target
    method_1o0t990_savecoverasync -->|unresolved call| unresolved_unresolved_zjmxe8_call_target
    method_mpmpcx_saveebookasync -->|unresolved call| unresolved_unresolved_1a9m2p_call_target
    method_mpmpcx_saveebookasync -->|unresolved call| unresolved_unresolved_1p1xq63_call_target
```

### SaveCoverAsync() Control Flow

```mermaid
flowchart TD
    method_1o0t990_savecoverasync__call_47_SaveCoverAsync[["SaveCoverAsync"]]
    method_1o0t990_savecoverasync__action_45_guard_clause_45__condition{"data == null || data.Length == 0"}
    method_1o0t990_savecoverasync__action_45_guard_clause_45__false("continue")
    method_1o0t990_savecoverasync__action_45_guard_clause_45__merge("merge")
    method_1o0t990_savecoverasync__action_45_guard_clause_45__true("return-null")
    method_1o0t990_savecoverasync__action_45_savecoverasync_guard_clause_45_0__condition{"data == null || data.Length == 0"}
    method_1o0t990_savecoverasync__action_45_savecoverasync_guard_clause_45_0__false("continue")
    method_1o0t990_savecoverasync__action_45_savecoverasync_guard_clause_45_0__merge("merge")
    method_1o0t990_savecoverasync__action_45_savecoverasync_guard_clause_45_0__true("exit")
    method_1o0t990_savecoverasync__action_46_resource_create_46__linear("Create MemoryStream for byte[]")
    method_1o0t990_savecoverasync__action_47_savecoverasync_await_47_1__await[["Waits for async work"]]
    method_1o0t990_savecoverasync__entry(["SaveCoverAsync entry"])
    method_1o0t990_savecoverasync__action_47_savecoverasync_return_47_2__return(["Returns result"])
    method_1o0t990_savecoverasync__implicit_return(["return"])
    method_1o0t990_savecoverasync__action_45_guard_clause_45__condition -->|continue| method_1o0t990_savecoverasync__action_45_guard_clause_45__false
    method_1o0t990_savecoverasync__action_45_guard_clause_45__condition -->|return-null| method_1o0t990_savecoverasync__action_45_guard_clause_45__true
    method_1o0t990_savecoverasync__action_45_guard_clause_45__false -->|next| method_1o0t990_savecoverasync__action_45_guard_clause_45__merge
    method_1o0t990_savecoverasync__action_45_guard_clause_45__merge -->|next| method_1o0t990_savecoverasync__action_45_savecoverasync_guard_clause_45_0__condition
    method_1o0t990_savecoverasync__action_45_guard_clause_45__true -->|next| method_1o0t990_savecoverasync__action_45_guard_clause_45__merge
    method_1o0t990_savecoverasync__action_45_savecoverasync_guard_clause_45_0__condition -->|continue| method_1o0t990_savecoverasync__action_45_savecoverasync_guard_clause_45_0__false
    method_1o0t990_savecoverasync__action_45_savecoverasync_guard_clause_45_0__condition -->|exit| method_1o0t990_savecoverasync__action_45_savecoverasync_guard_clause_45_0__true
    method_1o0t990_savecoverasync__action_45_savecoverasync_guard_clause_45_0__false -->|next| method_1o0t990_savecoverasync__action_45_savecoverasync_guard_clause_45_0__merge
    method_1o0t990_savecoverasync__action_45_savecoverasync_guard_clause_45_0__merge -->|next| method_1o0t990_savecoverasync__action_46_resource_create_46__linear
    method_1o0t990_savecoverasync__action_45_savecoverasync_guard_clause_45_0__true -->|next| method_1o0t990_savecoverasync__action_45_savecoverasync_guard_clause_45_0__merge
    method_1o0t990_savecoverasync__action_46_resource_create_46__linear -->|next| method_1o0t990_savecoverasync__action_47_savecoverasync_await_47_1__await
    method_1o0t990_savecoverasync__action_47_savecoverasync_await_47_1__await -->|return| method_1o0t990_savecoverasync__action_47_savecoverasync_return_47_2__return
    method_1o0t990_savecoverasync__action_47_savecoverasync_return_47_2__return -->|MemoryStream stream from data, fileName| method_1o0t990_savecoverasync__call_47_SaveCoverAsync
    method_1o0t990_savecoverasync__call_47_SaveCoverAsync -->|return| method_1o0t990_savecoverasync__implicit_return
    method_1o0t990_savecoverasync__entry -->|next| method_1o0t990_savecoverasync__action_45_guard_clause_45__condition
```

### EnsureDirectoryExists() Control Flow

```mermaid
flowchart TD
    method_je6l0s_ensuredirectoryexists__action_68_ensuredirectoryexists_branch_68_0__condition{"!Directory.Exists(path)"}
    method_je6l0s_ensuredirectoryexists__action_68_ensuredirectoryexists_branch_68_0__false("false")
    method_je6l0s_ensuredirectoryexists__action_68_ensuredirectoryexists_branch_68_0__merge("merge")
    method_je6l0s_ensuredirectoryexists__action_68_ensuredirectoryexists_branch_68_0__true("true")
    method_je6l0s_ensuredirectoryexists__action_68_guard_clause_68__condition{"!Directory.Exists(path)"}
    method_je6l0s_ensuredirectoryexists__action_68_guard_clause_68__false("continue")
    method_je6l0s_ensuredirectoryexists__action_68_guard_clause_68__merge("merge")
    method_je6l0s_ensuredirectoryexists__action_68_guard_clause_68__true("create-directory")
    method_je6l0s_ensuredirectoryexists__entry(["EnsureDirectoryExists entry"])
    method_je6l0s_ensuredirectoryexists__implicit_return(["return"])
    method_je6l0s_ensuredirectoryexists__action_68_ensuredirectoryexists_branch_68_0__condition -->|false| method_je6l0s_ensuredirectoryexists__action_68_ensuredirectoryexists_branch_68_0__false
    method_je6l0s_ensuredirectoryexists__action_68_ensuredirectoryexists_branch_68_0__condition -->|true| method_je6l0s_ensuredirectoryexists__action_68_ensuredirectoryexists_branch_68_0__true
    method_je6l0s_ensuredirectoryexists__action_68_ensuredirectoryexists_branch_68_0__false -->|next| method_je6l0s_ensuredirectoryexists__action_68_ensuredirectoryexists_branch_68_0__merge
    method_je6l0s_ensuredirectoryexists__action_68_ensuredirectoryexists_branch_68_0__merge -->|next| method_je6l0s_ensuredirectoryexists__action_68_guard_clause_68__condition
    method_je6l0s_ensuredirectoryexists__action_68_ensuredirectoryexists_branch_68_0__true -->|next| method_je6l0s_ensuredirectoryexists__action_68_ensuredirectoryexists_branch_68_0__merge
    method_je6l0s_ensuredirectoryexists__action_68_guard_clause_68__condition -->|continue| method_je6l0s_ensuredirectoryexists__action_68_guard_clause_68__false
    method_je6l0s_ensuredirectoryexists__action_68_guard_clause_68__condition -->|create-directory| method_je6l0s_ensuredirectoryexists__action_68_guard_clause_68__true
    method_je6l0s_ensuredirectoryexists__action_68_guard_clause_68__false -->|next| method_je6l0s_ensuredirectoryexists__action_68_guard_clause_68__merge
    method_je6l0s_ensuredirectoryexists__action_68_guard_clause_68__merge -->|return| method_je6l0s_ensuredirectoryexists__implicit_return
    method_je6l0s_ensuredirectoryexists__action_68_guard_clause_68__true -->|next| method_je6l0s_ensuredirectoryexists__action_68_guard_clause_68__merge
    method_je6l0s_ensuredirectoryexists__entry -->|next| method_je6l0s_ensuredirectoryexists__action_68_ensuredirectoryexists_branch_68_0__condition
```

## Diagram Validation

- Status: pass
