1     type Directory<T, U> where T : Discard =
2         let directory_path : String
3         let f : String * Option<T> -> T
4         let ids : mut obs/Multiset<String>
5         val id_assets : obs/Map<String, U>
6         let paths : obs/Multiset<String>
7         let path_files : mut obs/Map<String, T>
8         let id_atoms = Map<String, mut Atom<Option<U>>>.new
9         let path_modified = Map<String, Time>.new
10        let tokens = List<Token>.new
11    
12        fun destruct =
13            for i = tokens.size - 1 downto 0 do
14                tokens[i].discard
15    
16    let get_path (id : String) =
17        if not id.contains ' '
18        then id
19        else id.split_first ' '
20    
21    let update<T, U> (directory : Directory<T, U>) (path : String) where T : Discard =
22        let modified =
23            let full_path = "${directory.directory_path}/$path"
24            if fs/exists full_path
25            then fs/modified_time full_path
26            else Time.new
27    
28        let stored_modified = directory.path_modified[path]
29        if modified <> stored_modified then
30            directory.path_modified.insert path modified
31            let maybe_prev_file = directory.path_files.try_get path
32            let maybe_file = if modified == Time@zero then
33                None
34            else
35                let full_path = "${directory.directory_path}/$path"
36                let file = directory.f full_path maybe_prev_file
37                Some file
38    
39            case maybe_file of
40                None ->
41                    if directory.path_files.contains path then
42                        directory.path_files.remove path
43    
44                Some file ->
45                    directory.path_files.add path file
46    
47            if maybe_prev_file ? Some prev_file then
48                prev_file.discard
49    
50    object Directory<T, U> where T : Discard =
51        def create (directory_path : String
52                    f : String * Option<T> -> T
53                    g : obs/Map<String, T> -> obs/Map<String, U>) =
54            let path_files = obs/Map<String, T>.new
55            let ids = obs/Multiset<String>.new
56            let path_ids = Obs.to_multimap ids { id -> (get_path id, id) }
57            let id_files = path_ids.join path_files
58            let paths = Obs.map ids { get_path _ }
59            let directory = Directory<T, U>
60                id_assets = g id_files
61                =ids =paths =path_files =f =directory_path
62    
63            paths.bind
64                { path -> directory.path_modified.add path Time.new
65                          update directory path }
66    
67                { path -> directory.path_modified.remove path
68                          let file = path_files[path]
69                          path_files.remove path
70                          file.discard }
71            |> directory.tokens.add
72    
73            directory.id_assets.bind
74                { id x -> if directory.id_atoms.contains id then
75                              directory.id_atoms[id].set x }
76    
77                { id _ x -> if directory.id_atoms.contains id then
78                                directory.id_atoms[id].set x }
79    
80                { id _ -> if directory.id_atoms.contains id then
81                              directory.id_atoms[id].set None }
82            |> directory.tokens.add
83    
84            directory
85    
86    type Directory<T, U>
87        def get (id : String) =
88            if not ids.contains id then
89                ids.add id
90    
91            id_assets[id]
92    
93        def atom (id : String) =
94            if not ids.contains id then
95                ids.add id
96    
97            if not id_atoms.contains id then
98                let atom = id_assets.try_get id |> Atom<Option<U>>
99                id_atoms.add id atom
100   
101           id_atoms[id].as_readonly
102   
103       def update_all =
104           for path in paths do
105               update self path
106   
107       def try_update (path : String) =
108           if paths.contains path then
109               update self path
110