Skip to content

Instantly share code, notes, and snippets.

@mexx
Last active January 20, 2016 21:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mexx/aef547bdc4f07663fe1b to your computer and use it in GitHub Desktop.
Save mexx/aef547bdc4f07663fe1b to your computer and use it in GitHub Desktop.
OWIN file system for `BuildAction = Resource` typed resouces
namespace Mexx.Owin
open System
open System.Collections.Generic
open System.IO
open System.Reflection
open System.Resources
open Microsoft.Owin.FileSystems
/// <summary>
/// Looks up files using global resources in the specified assembly.
/// This file system is case insensitive.
/// </summary>
type GlobalResourceFileSystem(assembly: Assembly, resourceName: string, baseNamespace: string) =
let lastModified = lazy (FileInfo(assembly.Location).LastWriteTimeUtc)
let createResourceSet resourceName =
new ResourceSet(assembly.GetManifestResourceStream resourceName)
let createStreamFor resourceName fileName () =
use resourceSet = createResourceSet resourceName
resourceSet.GetObject(fileName, true) :?> UnmanagedMemoryStream :> Stream
let resourceName = sprintf "%s.g.resources" resourceName
let createFileInfo fileName =
let createReadStream = createStreamFor resourceName (baseNamespace + fileName)
let mutable length = None
{ new IFileInfo with
member __.CreateReadStream(): Stream =
let result = createReadStream()
length <- Some result.Length
result
member __.IsDirectory: bool = false
member __.LastModified: DateTime = lastModified.Force()
member __.Length: int64 =
match length with
| None ->
use stream = createReadStream()
length <- Some stream.Length
length
| x -> x
|> Option.get
member __.Name: string = fileName
member __.PhysicalPath: string = null }
let resourceAvailable = lazy(
resourceName
|> assembly.GetManifestResourceInfo
|> Option.ofObj)
let enumerateContents = lazy(
use resourceSet = createResourceSet resourceName
resourceSet.GetEnumerator()
|> Seq.unfold (fun e -> if e.MoveNext() then Some(e.Key :?> string, e) else None)
|> Seq.where (fun x -> x.StartsWith(baseNamespace))
|> Seq.map (fun x -> x.Substring(baseNamespace.Length))
|> Seq.toList)
interface IFileSystem with
/// <summary>
/// Enumerate a directory at the given path, if any.
/// This file system uses a flat directory structure. Everything under the base namespace is considered to be one directory.
/// </summary>
/// <param name="subpath">The path that identifies the directory</param>
/// <param name="contents">The contents if any</param>
/// <returns>True if a directory was located at the given path</returns>
member __.TryGetDirectoryContents(subpath: string, contents: byref<IEnumerable<IFileInfo>>): bool =
let result =
resourceAvailable.Force()
|> Option.bind (fun _ -> if String.IsNullOrEmpty(subpath) || subpath <> "/" then None else subpath |> Some)
|> Option.map (fun _ -> enumerateContents.Force() |> List.map createFileInfo)
match result with
| None ->
contents <- null
false
| Some list ->
contents <- list
true
/// <summary>
/// Locate a file at the given path
/// </summary>
/// <param name="subpath">The path that identifies the file</param>
/// <param name="fileInfo">The discovered file if any</param>
/// <returns>True if a file was located at the given path</returns>
member __.TryGetFileInfo(subpath: string, fileInfo: byref<IFileInfo>): bool =
let result =
resourceAvailable.Force()
|> Option.bind (fun _ -> if String.IsNullOrEmpty(subpath) || subpath.[0] <> '/' then None else subpath |> Some)
|> Option.map createFileInfo
match result with
| None ->
fileInfo <- null
false
| Some file ->
fileInfo <- file
true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment