├── .github ├── dependabot.yml └── workflows │ └── rust.yml ├── .gitignore ├── CHANGES.md ├── Cargo.toml ├── LICENSE.md ├── Makefile ├── README.md ├── build.rs ├── docs └── irmin.h ├── examples ├── json_merge.rs └── tezos.rs └── src ├── bindings.rs ├── commit.rs ├── config.rs ├── hash.rs ├── info.rs ├── irmin_string.rs ├── key.rs ├── lib.rs ├── metadata.rs ├── path.rs ├── remote.rs ├── repo.rs ├── store.rs ├── tree.rs ├── ty.rs ├── util.rs └── value.rs /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "13:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: libirmin 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | push: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | rust: 13 | strategy: 14 | fail-fast: true 15 | matrix: 16 | os: 17 | #- macos-latest 18 | - ubuntu-latest 19 | ocaml-compiler: 20 | - 4.13.1 21 | runs-on: ${{ matrix.os }} 22 | steps: 23 | - name: Checkout code 24 | uses: actions/checkout@v2 25 | 26 | - name: Use OCaml ${{ matrix.ocaml-version }} 27 | uses: avsm/setup-ocaml@v2 28 | with: 29 | ocaml-compiler: ${{ matrix.ocaml-compiler}} 30 | 31 | - run: curl https://sh.rustup.rs -sSf | sh -s -- --profile minimal -y 32 | - run: opam install libirmin 33 | - run: opam exec -- make test 34 | 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | target 3 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | ## 0.3.3 2 | 3 | - New release for Irmin 3.3 4 | 5 | ## 0.3.1-beta.0 6 | 7 | - Initial release, compatible with libirmin 3.1 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "irmin" 3 | version = "0.3.3" 4 | authors = ["zach "] 5 | edition = "2021" 6 | repository = "https://github.com/mirage/irmin" 7 | description = "Irmin bindings for rust" 8 | keywords = ["irmin", "database", "git"] 9 | license = "ISC" 10 | documentation = "https://docs.rs/irmin" 11 | 12 | [build-dependencies] 13 | bindgen = "0.63" 14 | 15 | [dependencies] 16 | serde_json = "1" 17 | serde = {version = "1", features = ["derive"]} 18 | 19 | [features] 20 | docs = [] 21 | 22 | [package.metadata.docs.rs] 23 | features = [ "docs" ] 24 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 Zach Shipko 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test 2 | test: 3 | cargo test -- --test-threads=1 4 | 5 | header: 6 | cp $(OPAM_SWITCH_PREFIX)/lib/libirmin/include/irmin.h ./docs/irmin.h 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # irmin-rs 2 | 3 | 4 | 5 | 6 | 7 | [irmin](https://irmin.org) bindings for Rust 8 | 9 | [Documentation](https://docs.rs/irmin) 10 | 11 | This crate enables you to call directly into irmin from your Rust application and 12 | can be used to open an existing irmin store from Rust that may have been created 13 | by an application written in OCaml. 14 | 15 | ## Building 16 | 17 | After installing [libirmin](https://github.com/mirage/irmin) using opam, you can run: 18 | 19 | ``` 20 | $ cargo build 21 | ``` 22 | 23 | And the build script should be able to find the location of the `libirmin` library and header files. 24 | 25 | If `libirmin.so` and `irmin.h` were not installed using opam and they're not in `~/.local` or 26 | `/usr/local`, then you can specify where to look for them using the `LIBIRMIN_PREFIX` env 27 | variable. 28 | 29 | ## Testing 30 | 31 | Tests must be executed using a single thread: 32 | 33 | ``` 34 | $ cargo test -- --test-threads=1 35 | ``` 36 | 37 | or 38 | 39 | ``` 40 | $ make test 41 | ``` 42 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | fn find_path(paths: Vec>) -> (PathBuf, PathBuf) { 4 | if cfg!(feature = "docs") { 5 | return (PathBuf::new(), PathBuf::from("docs/irmin.h")); 6 | } 7 | 8 | for path in paths.into_iter().flatten() { 9 | let lib = path.join("lib").join("libirmin.so"); 10 | let header = path.join("include").join("irmin.h"); 11 | if lib.exists() && header.exists() { 12 | return (lib, header); 13 | } 14 | } 15 | 16 | panic!("Unable to locate libirmin installation, try `opam install libirmin` or setting LIBIRMIN_PREFIX") 17 | } 18 | 19 | fn main() { 20 | let path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); 21 | let opam_prefix = 22 | std::env::var("OPAM_SWITCH_PREFIX").map(|x| PathBuf::from(x).join("lib").join("libirmin")); 23 | let libirmin_prefix = std::env::var("LIBIRMIN_PREFIX").map(PathBuf::from); 24 | let local_opam = PathBuf::from("_opam").join("lib").join("libirmin"); 25 | let home_local = std::env::var("HOME").map(|x| PathBuf::from(x).join(".local")); 26 | 27 | let (lib, header) = find_path(vec![ 28 | Ok(path.join("..")), 29 | libirmin_prefix, 30 | opam_prefix, 31 | Ok(local_opam), 32 | home_local, 33 | Ok(PathBuf::from("/usr/local")), 34 | ]); 35 | 36 | println!("cargo:rerun-if-changed={}", header.display()); 37 | 38 | if cfg!(not(feature = "docs")) { 39 | println!( 40 | "cargo:rustc-link-arg=-Wl,-rpath,{}", 41 | lib.parent().unwrap().display() 42 | ); 43 | println!( 44 | "cargo:rustc-link-search={}", 45 | lib.parent().unwrap().display() 46 | ); 47 | println!("cargo:rustc-link-lib=irmin"); 48 | println!("cargo:rerun-if-changed={}", header.display()); 49 | } 50 | 51 | let bindings = bindgen::builder() 52 | .header(header.to_str().unwrap()) 53 | .allowlist_type("Irmin.*") 54 | .allowlist_function("irmin.*") 55 | .allowlist_function("caml.*") 56 | .generate() 57 | .unwrap(); 58 | 59 | let out_path = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap()); 60 | bindings.write_to_file(out_path.join("c.rs")).unwrap(); 61 | } 62 | -------------------------------------------------------------------------------- /docs/irmin.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | typedef struct IrminType IrminType; 5 | typedef struct IrminValue IrminValue; 6 | typedef struct IrminMetadata IrminMetadata; 7 | typedef struct IrminContents IrminContents; 8 | typedef struct IrminConfig IrminConfig; 9 | typedef struct IrminRepo IrminRepo; 10 | typedef struct Irmin Irmin; 11 | typedef struct IrminPath IrminPath; 12 | typedef struct IrminCommitKey IrminCommitKey; 13 | typedef struct IrminKindedKey IrminKindedKey; 14 | typedef struct IrminTree IrminTree; 15 | typedef struct IrminCommit IrminCommit; 16 | typedef struct IrminInfo IrminInfo; 17 | typedef struct IrminHash IrminHash; 18 | typedef struct IrminString IrminString; 19 | typedef struct IrminPathArray IrminPathArray; 20 | typedef struct IrminCommitArray IrminCommitArray; 21 | typedef struct IrminBranchArray IrminBranchArray; 22 | typedef struct IrminRemote IrminRemote; 23 | void caml_startup(char *argv[]); 24 | void caml_shutdown(); 25 | IrminType* irmin_type_unit(void); 26 | IrminType* irmin_type_bool(void); 27 | IrminType* irmin_type_int(void); 28 | IrminType* irmin_type_float(void); 29 | IrminType* irmin_type_string(void); 30 | IrminType* irmin_type_bytes(void); 31 | IrminType* irmin_type_list(IrminType* x1575); 32 | IrminType* irmin_type_array(IrminType* x1576); 33 | IrminType* irmin_type_option(IrminType* x1577); 34 | IrminType* irmin_type_json(void); 35 | IrminType* irmin_type_json_value(void); 36 | IrminType* irmin_type_path(IrminRepo* x1580); 37 | IrminType* irmin_type_commit(IrminRepo* x1581); 38 | IrminType* irmin_type_metadata(IrminRepo* x1582); 39 | IrminType* irmin_type_tree(IrminRepo* x1583); 40 | IrminType* irmin_type_hash(IrminRepo* x1584); 41 | IrminType* irmin_type_commit_key(IrminRepo* x1585); 42 | IrminType* irmin_type_contents_key(IrminRepo* x1586); 43 | IrminType* irmin_type_node_key(IrminRepo* x1587); 44 | IrminType* irmin_type_kinded_key(IrminRepo* x1588); 45 | IrminType* irmin_type_contents(IrminRepo* x1589); 46 | IrminType* irmin_type_pair(IrminType* x1591, IrminType* x1590); 47 | IrminType* irmin_type_triple(IrminType* x1594, IrminType* x1593, 48 | IrminType* x1592); 49 | IrminString* irmin_type_name(IrminType* x1595); 50 | IrminType* irmin_type_diff(IrminType* x1596); 51 | void irmin_type_free(IrminType* x1597); 52 | IrminValue* irmin_value_unit(void); 53 | IrminValue* irmin_value_int(int64_t x1599); 54 | IrminValue* irmin_value_float(double x1600); 55 | IrminValue* irmin_value_bool(_Bool x1601); 56 | IrminValue* irmin_value_clone(IrminValue* x1602); 57 | void* irmin_realloc(void* x1604, void* x1603); 58 | IrminString* irmin_value_get_string(IrminValue* x1605); 59 | int64_t irmin_value_get_int(IrminValue* x1606); 60 | _Bool irmin_value_get_bool(IrminValue* x1607); 61 | double irmin_value_get_float(IrminValue* x1608); 62 | IrminValue* irmin_value_bytes(char* x1610, int64_t x1609); 63 | IrminValue* irmin_value_string(char* x1612, int64_t x1611); 64 | IrminValue* irmin_value_array(IrminValue** x1614, uint64_t x1613); 65 | IrminValue* irmin_value_list(IrminValue** x1616, uint64_t x1615); 66 | IrminValue* irmin_value_option(IrminValue* x1617); 67 | IrminValue* irmin_value_pair(IrminValue* x1619, IrminValue* x1618); 68 | IrminValue* irmin_value_triple(IrminValue* x1622, IrminValue* x1621, 69 | IrminValue* x1620); 70 | IrminString* irmin_value_to_string(IrminType* x1624, IrminValue* x1623); 71 | IrminValue* irmin_value_of_string(IrminType* x1627, char* x1626, 72 | int64_t x1625); 73 | IrminString* irmin_value_to_bin(IrminType* x1629, IrminValue* x1628); 74 | IrminValue* irmin_value_of_bin(IrminType* x1632, char* x1631, int64_t x1630); 75 | IrminString* irmin_value_to_json(IrminType* x1634, IrminValue* x1633); 76 | IrminValue* irmin_value_of_json(IrminType* x1637, char* x1636, int64_t x1635); 77 | _Bool irmin_value_equal(IrminType* x1640, IrminValue* x1639, 78 | IrminValue* x1638); 79 | int irmin_value_compare(IrminType* x1643, IrminValue* x1642, 80 | IrminValue* x1641); 81 | void irmin_value_free(IrminValue* x1644); 82 | IrminString* irmin_string_new(char* x1646, int64_t x1645); 83 | char* irmin_string_data(IrminString* x1647); 84 | uint64_t irmin_string_length(IrminString* x1648); 85 | void irmin_string_free(IrminString* x1649); 86 | IrminInfo* irmin_info_new(IrminRepo* x1652, char* x1651, char* x1650); 87 | void irmin_info_update(IrminRepo* x1656, IrminInfo* x1655, char* x1654, 88 | char* x1653); 89 | IrminString* irmin_info_message(IrminRepo* x1658, IrminInfo* x1657); 90 | IrminString* irmin_info_author(IrminRepo* x1660, IrminInfo* x1659); 91 | int64_t irmin_info_date(IrminRepo* x1662, IrminInfo* x1661); 92 | void irmin_info_free(IrminInfo* x1663); 93 | _Bool irmin_log_level(char* x1664); 94 | IrminConfig* irmin_config_pack(char* x1666, char* x1665); 95 | IrminConfig* irmin_config_tezos(void); 96 | IrminConfig* irmin_config_git(char* x1668); 97 | IrminConfig* irmin_config_git_mem(char* x1669); 98 | IrminConfig* irmin_config_fs(char* x1671, char* x1670); 99 | IrminConfig* irmin_config_mem(char* x1673, char* x1672); 100 | void irmin_config_free(IrminConfig* x1674); 101 | _Bool irmin_config_set(IrminConfig* x1678, char* x1677, IrminType* x1676, 102 | IrminValue* x1675); 103 | _Bool irmin_config_set_root(IrminConfig* x1680, char* x1679); 104 | Irmin* irmin_main(IrminRepo* x1681); 105 | Irmin* irmin_of_branch(IrminRepo* x1683, char* x1682); 106 | Irmin* irmin_of_commit(IrminRepo* x1685, IrminCommit* x1684); 107 | IrminCommit* irmin_get_head(Irmin* x1686); 108 | void irmin_set_head(Irmin* x1688, IrminCommit* x1687); 109 | _Bool irmin_fast_forward(Irmin* x1690, IrminCommit* x1689); 110 | _Bool irmin_merge_with_branch(Irmin* x1693, char* x1692, IrminInfo* x1691); 111 | _Bool irmin_merge_with_commit(Irmin* x1696, IrminCommit* x1695, 112 | IrminInfo* x1694); 113 | _Bool irmin_merge_into(Irmin* x1699, Irmin* x1698, IrminInfo* x1697); 114 | _Bool irmin_set(Irmin* x1703, IrminPath* x1702, IrminContents* x1701, 115 | IrminInfo* x1700); 116 | _Bool irmin_test_and_set(Irmin* x1708, IrminPath* x1707, 117 | IrminContents* x1706, IrminContents* x1705, 118 | IrminInfo* x1704); 119 | _Bool irmin_test_and_set_tree(Irmin* x1713, IrminPath* x1712, 120 | IrminTree* x1711, IrminTree* x1710, 121 | IrminInfo* x1709); 122 | _Bool irmin_set_tree(Irmin* x1717, IrminPath* x1716, IrminTree* x1715, 123 | IrminInfo* x1714); 124 | IrminContents* irmin_find(Irmin* x1719, IrminPath* x1718); 125 | IrminMetadata* irmin_find_metadata(Irmin* x1721, IrminPath* x1720); 126 | IrminTree* irmin_find_tree(Irmin* x1723, IrminPath* x1722); 127 | _Bool irmin_remove(Irmin* x1726, IrminPath* x1725, IrminInfo* x1724); 128 | _Bool irmin_mem(Irmin* x1728, IrminPath* x1727); 129 | _Bool irmin_mem_tree(Irmin* x1730, IrminPath* x1729); 130 | IrminPathArray* irmin_list(Irmin* x1732, IrminPath* x1731); 131 | uint64_t irmin_path_array_length(IrminRepo* x1734, IrminPathArray* x1733); 132 | IrminPath* irmin_path_array_get(IrminRepo* x1737, IrminPathArray* x1736, 133 | uint64_t x1735); 134 | IrminRemote* irmin_remote_store(Irmin* x1738); 135 | IrminRemote* irmin_remote(IrminRepo* x1740, char* x1739); 136 | IrminRemote* irmin_remote_with_auth(IrminRepo* x1744, char* x1743, 137 | char* x1742, char* x1741); 138 | IrminCommit* irmin_fetch(Irmin* x1747, int x1746, IrminRemote* x1745); 139 | IrminCommit* irmin_pull(Irmin* x1751, int x1750, IrminRemote* x1749, 140 | IrminInfo* x1748); 141 | IrminCommit* irmin_push(Irmin* x1754, int x1753, IrminRemote* x1752); 142 | void irmin_remote_free(IrminRemote* x1755); 143 | void irmin_path_array_free(IrminPathArray* x1756); 144 | void irmin_free(Irmin* x1757); 145 | IrminTree* irmin_tree_new(IrminRepo* x1758); 146 | IrminTree* irmin_tree_of_contents(IrminRepo* x1761, IrminContents* x1760, 147 | IrminMetadata* x1759); 148 | IrminTree* irmin_tree_clone(IrminRepo* x1763, IrminTree* x1762); 149 | IrminHash* irmin_tree_hash(IrminRepo* x1765, IrminTree* x1764); 150 | IrminTree* irmin_tree_of_hash(IrminRepo* x1767, IrminHash* x1766); 151 | IrminKindedKey* irmin_tree_key(IrminRepo* x1769, IrminTree* x1768); 152 | IrminTree* irmin_tree_of_key(IrminRepo* x1771, IrminKindedKey* x1770); 153 | _Bool irmin_tree_mem(IrminRepo* x1774, IrminTree* x1773, IrminPath* x1772); 154 | _Bool irmin_tree_mem_tree(IrminRepo* x1777, IrminTree* x1776, 155 | IrminPath* x1775); 156 | IrminContents* irmin_tree_find(IrminRepo* x1780, IrminTree* x1779, 157 | IrminPath* x1778); 158 | IrminMetadata* irmin_tree_find_metadata(IrminRepo* x1783, IrminTree* x1782, 159 | IrminPath* x1781); 160 | IrminTree* irmin_tree_find_tree(IrminRepo* x1786, IrminTree* x1785, 161 | IrminPath* x1784); 162 | _Bool irmin_tree_add(IrminRepo* x1791, IrminTree* x1790, IrminPath* x1789, 163 | IrminContents* x1788, IrminMetadata* x1787); 164 | _Bool irmin_tree_add_tree(IrminRepo* x1795, IrminTree* x1794, 165 | IrminPath* x1793, IrminTree* x1792); 166 | _Bool irmin_tree_remove(IrminRepo* x1798, IrminTree* x1797, IrminPath* x1796); 167 | _Bool irmin_tree_equal(IrminRepo* x1801, IrminTree* x1800, IrminTree* x1799); 168 | IrminPathArray* irmin_tree_list(IrminRepo* x1804, IrminTree* x1803, 169 | IrminPath* x1802); 170 | _Bool irmin_kinded_key_is_contents(IrminRepo* x1806, IrminKindedKey* x1805); 171 | _Bool irmin_kinded_key_is_node(IrminRepo* x1808, IrminKindedKey* x1807); 172 | void irmin_tree_free(IrminTree* x1809); 173 | void irmin_kinded_key_free(IrminKindedKey* x1810); 174 | IrminRepo* irmin_repo_new(IrminConfig* x1811); 175 | IrminBranchArray* irmin_repo_branches(IrminRepo* x1812); 176 | uint64_t irmin_branch_array_length(IrminRepo* x1814, IrminBranchArray* x1813); 177 | IrminString* irmin_branch_array_get(IrminRepo* x1817, 178 | IrminBranchArray* x1816, uint64_t x1815); 179 | _Bool irmin_hash_equal(IrminRepo* x1820, IrminHash* x1819, IrminHash* x1818); 180 | IrminHash* irmin_contents_hash(IrminRepo* x1822, IrminContents* x1821); 181 | IrminContents* irmin_contents_of_hash(IrminRepo* x1824, IrminHash* x1823); 182 | IrminContents* irmin_contents_of_key(IrminRepo* x1826, IrminKindedKey* x1825); 183 | IrminString* irmin_contents_to_string(IrminRepo* x1828, IrminContents* x1827); 184 | IrminContents* irmin_contents_of_string(IrminRepo* x1831, char* x1830, 185 | int64_t x1829); 186 | IrminString* irmin_hash_to_string(IrminRepo* x1833, IrminHash* x1832); 187 | IrminHash* irmin_hash_of_string(IrminRepo* x1836, char* x1835, int64_t x1834); 188 | IrminMetadata* irmin_metadata_default(IrminRepo* x1837); 189 | _Bool irmin_repo_has_error(IrminRepo* x1838); 190 | IrminString* irmin_repo_get_error(IrminRepo* x1839); 191 | void irmin_hash_free(IrminHash* x1840); 192 | void irmin_branch_array_free(IrminBranchArray* x1841); 193 | void irmin_repo_free(IrminRepo* x1842); 194 | void irmin_metadata_free(IrminMetadata* x1843); 195 | void irmin_contents_free(IrminContents* x1844); 196 | IrminInfo* irmin_commit_info(IrminRepo* x1846, IrminCommit* x1845); 197 | IrminHash* irmin_commit_hash(IrminRepo* x1848, IrminCommit* x1847); 198 | IrminCommitKey* irmin_commit_key(IrminRepo* x1850, IrminCommit* x1849); 199 | IrminCommit* irmin_commit_of_hash(IrminRepo* x1852, IrminHash* x1851); 200 | IrminCommit* irmin_commit_of_key(IrminRepo* x1854, IrminCommitKey* x1853); 201 | IrminCommit* irmin_commit_new(IrminRepo* x1859, IrminCommit** x1858, 202 | uint64_t x1857, IrminTree* x1856, 203 | IrminInfo* x1855); 204 | IrminCommitArray* irmin_commit_parents(IrminRepo* x1861, IrminCommit* x1860); 205 | _Bool irmin_commit_equal(IrminRepo* x1864, IrminCommit* x1863, 206 | IrminCommit* x1862); 207 | IrminTree* irmin_commit_tree(IrminRepo* x1866, IrminCommit* x1865); 208 | uint64_t irmin_commit_array_length(IrminRepo* x1868, IrminCommitArray* x1867); 209 | IrminCommit* irmin_commit_array_get(IrminRepo* x1871, 210 | IrminCommitArray* x1870, uint64_t x1869); 211 | void irmin_commit_array_free(IrminCommitArray* x1872); 212 | void irmin_commit_free(IrminCommit* x1873); 213 | void irmin_commit_key_free(IrminCommitKey* x1874); 214 | IrminPath* irmin_path(IrminRepo* x1876, char** x1875); 215 | IrminPath* irmin_path_of_string(IrminRepo* x1879, char* x1878, int64_t x1877); 216 | IrminPath* irmin_path_empty(IrminRepo* x1880); 217 | IrminString* irmin_path_to_string(IrminRepo* x1882, IrminPath* x1881); 218 | IrminPath* irmin_path_parent(IrminRepo* x1884, IrminPath* x1883); 219 | IrminPath* irmin_path_append(IrminRepo* x1888, IrminPath* x1887, char* x1886, 220 | int64_t x1885); 221 | IrminPath* irmin_path_append_path(IrminRepo* x1891, IrminPath* x1890, 222 | IrminPath* x1889); 223 | _Bool irmin_path_equal(IrminRepo* x1894, IrminPath* x1893, IrminPath* x1892); 224 | void irmin_path_free(IrminPath* x1895); 225 | 226 | 227 | #ifndef IRMIN_NO_AUTO 228 | static void _irmin_cleanup(void *p) { if (p) { irmin_free(*(Irmin**)p); p = (void*)0;} }; 229 | #define AUTO __attribute__((cleanup(_irmin_cleanup))) 230 | #endif 231 | 232 | -------------------------------------------------------------------------------- /examples/json_merge.rs: -------------------------------------------------------------------------------- 1 | use irmin::*; 2 | 3 | fn main() -> Result<(), Error> { 4 | // Configure an in-memory store with `Json` contents 5 | let config = Config::::mem(None)?; 6 | 7 | // Initialize the repo 8 | let repo = Repo::new(config)?; 9 | 10 | // Open the main branch 11 | let mut store = Store::new(&repo)?; 12 | 13 | // Create the path to store values at 14 | let path = repo.path(&["foo", "bar"])?; 15 | 16 | // Store a value in the main branch 17 | let a = json!({ 18 | "x": 1i32, 19 | "y": 2i32, 20 | "z": 3i32, 21 | }); 22 | store.set( 23 | &path, 24 | a.as_object().unwrap(), 25 | Info::new(&repo, "example", "initial commit")?, 26 | )?; 27 | let head = store.head()?.unwrap(); 28 | 29 | // Crate `branch1` from the latest commit on `main` 30 | let mut branch1 = Store::of_branch(&repo, "branch1")?; 31 | branch1.set_head(&head); 32 | 33 | // Crate `branch2` from the latest commit on `main` 34 | let mut branch2 = Store::of_branch(&repo, "branch2")?; 35 | branch2.set_head(&head); 36 | 37 | // Set `x` to 0 and store in `branch1` 38 | let b = json!({ 39 | "x": 0i32, 40 | "y": 2i32, 41 | "z": 3i32, 42 | }); 43 | branch1.set( 44 | &path, 45 | b.as_object().unwrap(), 46 | Info::new(&repo, "example", "initial commit")?, 47 | )?; 48 | 49 | // Set `y` to 0 and store in `branch2` 50 | let c = json!({ 51 | "x": 1i32, 52 | "y": 0i32, 53 | "z": 3i32, 54 | }); 55 | branch2.set( 56 | &path, 57 | c.as_object().unwrap(), 58 | Info::new(&repo, "example", "initial commit")?, 59 | )?; 60 | 61 | // Merge `branch1` into `main` 62 | assert!(store.merge(&branch1, repo.info("example", "merge branch1")?)?); 63 | 64 | // Merge `branch2` into `main` 65 | assert!(store.merge(&branch2, repo.info("example", "merge branch2")?)?); 66 | 67 | // Check that the contents have been merged correctly 68 | let v = store.find(&path)?.unwrap(); 69 | assert!( 70 | &v == json!({ 71 | "x": 0, 72 | "y": 0, 73 | "z": 3, 74 | }) 75 | .as_object() 76 | .unwrap() 77 | ); 78 | 79 | Ok(()) 80 | } 81 | -------------------------------------------------------------------------------- /examples/tezos.rs: -------------------------------------------------------------------------------- 1 | use irmin::*; 2 | 3 | fn list_path(store: &Store, path: Path) -> Result<(), Error> { 4 | for k in store.list(&path)? { 5 | let p = path.append_path(&k)?; 6 | 7 | // If the store has contents at `p` then print the path 8 | if store.mem(&p) { 9 | println!("{}", p.to_string()?); 10 | } else { 11 | list_path(store, p)?; 12 | } 13 | } 14 | 15 | Ok(()) 16 | } 17 | 18 | fn main() -> Result<(), Error> { 19 | let args: Vec<_> = std::env::args().collect(); 20 | 21 | if args.len() < 3 { 22 | println!("usage: {} /path/to/tezos/context ", &args[0]); 23 | return Ok(()); 24 | } 25 | 26 | // Configure an in-memory store with `Json` contents 27 | let mut config = Config::::tezos()?; 28 | assert!(config.set_root(&args[1])); 29 | 30 | // Initialize the repo 31 | let repo = Repo::new(config)?; 32 | 33 | // Resolve commit 34 | let hash = Hash::of_string(&repo, &args[2])?; 35 | let commit = Commit::of_hash(&repo, &hash)?.expect("Commit not found"); 36 | 37 | // Open the store 38 | let store = Store::of_commit(&repo, &commit)?; 39 | 40 | // List contract paths 41 | let path = repo.path(&["data", "contracts"])?; 42 | list_path(&store, path)?; 43 | 44 | Ok(()) 45 | } 46 | -------------------------------------------------------------------------------- /src/bindings.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_upper_case_globals)] 2 | #![allow(non_camel_case_types)] 3 | #![allow(non_snake_case)] 4 | 5 | #[macro_export] 6 | macro_rules! check { 7 | ($r:expr, $x:expr) => { 8 | if $x.is_null() { 9 | match crate::error_msg($r) { 10 | Some(e) => return Err(Error::Exc(e)), 11 | None => return Err(Error::NullPtr), 12 | } 13 | } 14 | }; 15 | ($r:expr, $x:expr, $y:expr) => { 16 | if $x == $y { 17 | match crate::error_msg($r) { 18 | Some(e) => return Err(Error::Exc(e)), 19 | None => (), 20 | } 21 | } 22 | }; 23 | } 24 | 25 | #[macro_export] 26 | macro_rules! check_opt { 27 | ($r:expr, $x:expr) => { 28 | if $x.is_null() { 29 | match crate::error_msg($r) { 30 | Some(e) => return Err(Error::Exc(e)), 31 | None => return Ok(None), 32 | } 33 | } 34 | }; 35 | } 36 | 37 | include!(concat!(env!("OUT_DIR"), "/c.rs")); 38 | -------------------------------------------------------------------------------- /src/commit.rs: -------------------------------------------------------------------------------- 1 | use crate::internal::*; 2 | 3 | /// Wrapper around Irmin commits 4 | pub struct Commit<'a> { 5 | pub ptr: *mut IrminCommit, 6 | pub(crate) repo: UntypedRepo<'a>, 7 | } 8 | 9 | impl<'a> Drop for Commit<'a> { 10 | fn drop(&mut self) { 11 | unsafe { irmin_commit_free(self.ptr) } 12 | } 13 | } 14 | 15 | impl<'a> PartialEq for Commit<'a> { 16 | fn eq(&self, other: &Commit<'a>) -> bool { 17 | unsafe { irmin_commit_equal(self.repo.ptr, self.ptr, other.ptr) } 18 | } 19 | } 20 | 21 | impl<'a> Commit<'a> { 22 | /// Create a new commit 23 | pub fn new( 24 | repo: &'a Repo, 25 | parents: impl AsRef<[&'a Commit<'a>]>, 26 | tree: &Tree, 27 | info: Info, 28 | ) -> Result, Error> { 29 | let parents: Vec<_> = parents.as_ref().iter().map(|x| x.ptr).collect(); 30 | let ptr = unsafe { 31 | irmin_commit_new( 32 | repo.ptr, 33 | parents.as_ptr() as *mut _, 34 | parents.len() as u64, 35 | tree.ptr, 36 | info.ptr, 37 | ) 38 | }; 39 | check!(repo.ptr, ptr); 40 | Ok(Commit { 41 | ptr, 42 | repo: UntypedRepo::new(repo), 43 | }) 44 | } 45 | 46 | /// Find the commit associated with the given hash 47 | pub fn of_hash( 48 | repo: &'a Repo, 49 | hash: &Hash, 50 | ) -> Result>, Error> { 51 | let ptr = unsafe { irmin_commit_of_hash(repo.ptr, hash.ptr) }; 52 | check_opt!(repo.ptr, ptr); 53 | Ok(Some(Commit { 54 | ptr, 55 | repo: UntypedRepo::new(repo), 56 | })) 57 | } 58 | 59 | /// Get the hash associated with a commit 60 | pub fn hash(&self) -> Result { 61 | let ptr = unsafe { irmin_commit_hash(self.repo.ptr, self.ptr) }; 62 | check!(self.repo.ptr, ptr); 63 | Ok(Hash { 64 | ptr, 65 | repo: self.repo.clone(), 66 | }) 67 | } 68 | 69 | /// Find the commit associated with the given key 70 | pub fn of_key( 71 | repo: &'a Repo, 72 | key: &CommitKey, 73 | ) -> Result>, Error> { 74 | let ptr = unsafe { irmin_commit_of_key(repo.ptr, key.ptr) }; 75 | check_opt!(repo.ptr, ptr); 76 | Ok(Some(Commit { 77 | ptr, 78 | repo: UntypedRepo::new(repo), 79 | })) 80 | } 81 | 82 | /// Get the key associated with a commit 83 | pub fn key(&self) -> Result { 84 | let ptr = unsafe { irmin_commit_key(self.repo.ptr, self.ptr) }; 85 | check!(self.repo.ptr, ptr); 86 | Ok(CommitKey { 87 | ptr, 88 | repo: self.repo.clone(), 89 | }) 90 | } 91 | 92 | /// Get commit info 93 | pub fn info(&self) -> Result { 94 | let ptr = unsafe { irmin_commit_info(self.repo.ptr, self.ptr) }; 95 | check!(self.repo.ptr, ptr); 96 | Ok(Info { 97 | ptr, 98 | repo: self.repo.clone(), 99 | }) 100 | } 101 | 102 | pub fn tree(&self) -> Result, Error> { 103 | let ptr = unsafe { irmin_commit_tree(self.repo.ptr, self.ptr) }; 104 | check!(self.repo.ptr, ptr); 105 | Ok(Tree { 106 | ptr, 107 | repo: self.repo.clone(), 108 | _t: std::marker::PhantomData, 109 | }) 110 | } 111 | 112 | /// Get commit parents 113 | pub fn parents(&self) -> Result, Error> { 114 | let p = unsafe { irmin_commit_parents(self.repo.ptr, self.ptr) }; 115 | check!(self.repo.ptr, p); 116 | let len = unsafe { irmin_commit_array_length(self.repo.ptr, p) }; 117 | let mut dest = Vec::new(); 118 | for i in 0..len { 119 | let c = unsafe { irmin_commit_array_get(self.repo.ptr, p, i) }; 120 | if c.is_null() { 121 | continue; 122 | } 123 | dest.push(Commit { 124 | ptr: c, 125 | repo: self.repo.clone(), 126 | }) 127 | } 128 | 129 | unsafe { irmin_commit_array_free(p) } 130 | 131 | Ok(dest) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | use crate::internal::*; 2 | 3 | /// Wrapper around Irmin.config 4 | pub struct Config { 5 | pub ptr: *mut IrminConfig, 6 | _t: std::marker::PhantomData, 7 | } 8 | 9 | impl Drop for Config { 10 | fn drop(&mut self) { 11 | unsafe { irmin_config_free(self.ptr) } 12 | } 13 | } 14 | 15 | /// Builtin content types 16 | pub enum ContentType { 17 | String, 18 | Json, 19 | JsonValue, 20 | } 21 | 22 | const CONTENTS_STRING: &str = "string\0"; 23 | 24 | const CONTENTS_JSON: &str = "json\0"; 25 | 26 | const CONTENTS_JSON_VALUE: &str = "json-value\0"; 27 | 28 | /// Used to specify the content type of a store 29 | pub trait Contents 30 | where 31 | Self: Sized, 32 | { 33 | fn content_type() -> ContentType; 34 | fn to_value(&self) -> Result; 35 | fn from_value(v: &Value) -> Result; 36 | 37 | fn of_hash<'a>(repo: &'a Repo, hash: &Hash) -> Option { 38 | let ptr = unsafe { irmin_contents_of_hash(repo.ptr, hash.ptr) }; 39 | if ptr.is_null() { 40 | return None; 41 | } 42 | let ty = match Type::contents(repo) { 43 | Ok(t) => t, 44 | Err(_) => return None, 45 | }; 46 | let v = Value { 47 | ptr: ptr as *mut _, 48 | ty, 49 | }; 50 | match Self::from_value(&v) { 51 | Ok(x) => Some(x), 52 | Err(_) => None, 53 | } 54 | } 55 | 56 | fn hash<'a>(&self, repo: &'a Repo) -> Result, Error> { 57 | let v = self.to_value()?; 58 | 59 | let ptr = unsafe { irmin_contents_hash(repo.ptr, v.ptr as *mut _) }; 60 | check!(repo.ptr, ptr); 61 | Ok(Hash { 62 | ptr, 63 | repo: UntypedRepo::new(repo), 64 | }) 65 | } 66 | 67 | fn ty() -> Result { 68 | match Self::content_type() { 69 | ContentType::String => Type::string(), 70 | ContentType::Json => Type::json(), 71 | ContentType::JsonValue => Type::json_value(), 72 | } 73 | } 74 | } 75 | 76 | impl ContentType { 77 | fn ptr(c: Option) -> *const u8 { 78 | match c { 79 | Some(ContentType::String) => CONTENTS_STRING.as_ptr(), 80 | Some(ContentType::Json) => CONTENTS_JSON.as_ptr(), 81 | Some(ContentType::JsonValue) => CONTENTS_JSON_VALUE.as_ptr(), 82 | None => std::ptr::null(), 83 | } 84 | } 85 | } 86 | 87 | /// Available hash types 88 | pub enum HashType { 89 | Blake2b, 90 | Blake2s, 91 | Rmd160, 92 | Sha1, 93 | Sha224, 94 | Sha256, 95 | Sha384, 96 | Sha512, 97 | } 98 | 99 | const HASH_SHA1: &str = "sha1\0"; 100 | const HASH_SHA224: &str = "sha224\0"; 101 | const HASH_SHA256: &str = "sha256\0"; 102 | const HASH_SHA384: &str = "sha384\0"; 103 | const HASH_SHA512: &str = "sha512\0"; 104 | const HASH_RMD160: &str = "rmd160\0"; 105 | 106 | const HASH_BLAKE2B: &str = "blake2b\0"; 107 | 108 | const HASH_BLAKE2S: &str = "blake2s\0"; 109 | 110 | impl HashType { 111 | fn ptr(h: Option) -> *const u8 { 112 | match h { 113 | Some(HashType::Sha1) => HASH_SHA1.as_ptr(), 114 | Some(HashType::Sha224) => HASH_SHA224.as_ptr(), 115 | Some(HashType::Sha256) => HASH_SHA256.as_ptr(), 116 | Some(HashType::Sha384) => HASH_SHA384.as_ptr(), 117 | Some(HashType::Sha512) => HASH_SHA512.as_ptr(), 118 | Some(HashType::Blake2b) => HASH_BLAKE2B.as_ptr(), 119 | Some(HashType::Blake2s) => HASH_BLAKE2S.as_ptr(), 120 | Some(HashType::Rmd160) => HASH_RMD160.as_ptr(), 121 | None => std::ptr::null(), 122 | } 123 | } 124 | } 125 | 126 | impl Contents for IrminString { 127 | fn content_type() -> ContentType { 128 | ContentType::String 129 | } 130 | 131 | fn to_value(&self) -> Result { 132 | Value::string(self) 133 | } 134 | 135 | fn from_value(v: &Value) -> Result { 136 | v.get_string() 137 | } 138 | } 139 | 140 | impl Contents for String { 141 | fn content_type() -> ContentType { 142 | ContentType::String 143 | } 144 | 145 | fn to_value(&self) -> Result { 146 | Value::string(self) 147 | } 148 | 149 | fn from_value(v: &Value) -> Result { 150 | v.get_string().map(|x| x.into()) 151 | } 152 | } 153 | 154 | impl Contents for Vec { 155 | fn content_type() -> ContentType { 156 | ContentType::String 157 | } 158 | 159 | fn to_value(&self) -> Result { 160 | Value::bytes(self) 161 | } 162 | 163 | fn from_value(v: &Value) -> Result { 164 | v.get_string().map(|x| x.into()) 165 | } 166 | } 167 | 168 | impl Contents for serde_json::Value { 169 | fn content_type() -> ContentType { 170 | ContentType::JsonValue 171 | } 172 | 173 | fn to_value(&self) -> Result { 174 | let ty = Type::json_value()?; 175 | let s = serde_json::to_string(self)?; 176 | Value::of_string(ty, s) 177 | } 178 | 179 | fn from_value(v: &Value) -> Result { 180 | let s = v.to_string()?; 181 | serde_json::from_str(s.as_ref()).map_err(Error::from) 182 | } 183 | } 184 | 185 | impl Contents for serde_json::Map { 186 | fn content_type() -> ContentType { 187 | ContentType::Json 188 | } 189 | 190 | fn to_value(&self) -> Result { 191 | let ty = Type::json()?; 192 | let s = serde_json::to_string(self)?; 193 | Value::of_string(ty, s) 194 | } 195 | 196 | fn from_value(v: &Value) -> Result { 197 | let s = v.to_string()?; 198 | 199 | serde_json::from_str(s.as_ref()).map_err(Error::from) 200 | } 201 | } 202 | 203 | impl Config { 204 | /// Create configuration for Tezos context store 205 | pub fn tezos() -> Result, Error> { 206 | unsafe { 207 | let ptr = irmin_config_tezos(); 208 | if ptr.is_null() { 209 | return Err(Error::NullPtr); 210 | } 211 | Ok(Config { 212 | ptr, 213 | _t: std::marker::PhantomData, 214 | }) 215 | } 216 | } 217 | } 218 | 219 | impl Config { 220 | /// Create configuration for Irmin_pack store 221 | pub fn pack(hash: Option) -> Result, Error> { 222 | unsafe { 223 | let hash = HashType::ptr(hash); 224 | let contents = ContentType::ptr(Some(T::content_type())); 225 | let ptr = irmin_config_pack(hash as *mut _, contents as *mut _); 226 | if ptr.is_null() { 227 | return Err(Error::NullPtr); 228 | } 229 | Ok(Config { 230 | ptr, 231 | _t: std::marker::PhantomData, 232 | }) 233 | } 234 | } 235 | 236 | /// Create configuration for Irmin_mem store 237 | pub fn mem(hash: Option) -> Result, Error> { 238 | unsafe { 239 | let hash = HashType::ptr(hash); 240 | let contents = ContentType::ptr(Some(T::content_type())); 241 | let ptr = irmin_config_mem(hash as *mut _, contents as *mut _); 242 | if ptr.is_null() { 243 | return Err(Error::NullPtr); 244 | } 245 | Ok(Config { 246 | ptr, 247 | _t: std::marker::PhantomData, 248 | }) 249 | } 250 | } 251 | 252 | /// Create configuration for Irmin_fs store 253 | pub fn fs(hash: Option) -> Result, Error> { 254 | unsafe { 255 | let hash = HashType::ptr(hash); 256 | let contents = ContentType::ptr(Some(T::content_type())); 257 | let ptr = irmin_config_fs(hash as *mut _, contents as *mut _); 258 | if ptr.is_null() { 259 | return Err(Error::NullPtr); 260 | } 261 | Ok(Config { 262 | ptr, 263 | _t: std::marker::PhantomData, 264 | }) 265 | } 266 | } 267 | 268 | /// Create configuration for Irmin_git on-disk store 269 | pub fn git() -> Result, Error> { 270 | unsafe { 271 | let contents = ContentType::ptr(Some(T::content_type())); 272 | let ptr = irmin_config_git(contents as *mut _); 273 | if ptr.is_null() { 274 | return Err(Error::NullPtr); 275 | } 276 | Ok(Config { 277 | ptr, 278 | _t: std::marker::PhantomData, 279 | }) 280 | } 281 | } 282 | 283 | /// Create configuration for Irmin_git in-memory store 284 | pub fn git_mem() -> Result, Error> { 285 | unsafe { 286 | let contents = ContentType::ptr(Some(T::content_type())); 287 | let ptr = irmin_config_git_mem(contents as *mut _); 288 | if ptr.is_null() { 289 | return Err(Error::NullPtr); 290 | } 291 | Ok(Config { 292 | ptr, 293 | _t: std::marker::PhantomData, 294 | }) 295 | } 296 | } 297 | 298 | /// Set configuration key 299 | pub fn set(&mut self, key: impl AsRef, ty: &Type, v: &Value) -> bool { 300 | let key = cstring(key); 301 | unsafe { irmin_config_set(self.ptr, key.as_ptr() as *mut _, ty.ptr, v.ptr) } 302 | } 303 | 304 | /// Set root key 305 | pub fn set_root(&mut self, root: impl AsRef) -> bool { 306 | let v = cstring(root.as_ref().to_str().expect("Invalid path")); 307 | unsafe { irmin_config_set_root(self.ptr, v.as_ptr() as *mut _) } 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /src/hash.rs: -------------------------------------------------------------------------------- 1 | use crate::internal::*; 2 | 3 | /// Wrapper around Irmin hash type 4 | pub struct Hash<'a> { 5 | pub ptr: *mut IrminHash, 6 | pub(crate) repo: UntypedRepo<'a>, 7 | } 8 | 9 | impl<'a> PartialEq for Hash<'a> { 10 | fn eq(&self, other: &Hash<'a>) -> bool { 11 | unsafe { irmin_hash_equal(self.repo.ptr, self.ptr, other.ptr) } 12 | } 13 | } 14 | 15 | impl<'a> Hash<'a> { 16 | /// Convert from string to Hash 17 | pub fn of_string( 18 | repo: &'a Repo, 19 | s: impl AsRef, 20 | ) -> Result, Error> { 21 | let s = s.as_ref(); 22 | let ptr = unsafe { irmin_hash_of_string(repo.ptr, s.as_ptr() as *mut _, s.len() as i64) }; 23 | check!(repo.ptr, ptr); 24 | Ok(Hash { 25 | ptr, 26 | repo: UntypedRepo::new(repo), 27 | }) 28 | } 29 | 30 | /// Convert from Hash to String 31 | pub fn to_string(&self) -> Result { 32 | let s = unsafe { irmin_hash_to_string(self.repo.ptr, self.ptr) }; 33 | IrminString::wrap(s).map(|x| x.into()) 34 | } 35 | } 36 | 37 | impl<'a> Drop for Hash<'a> { 38 | fn drop(&mut self) { 39 | unsafe { irmin_hash_free(self.ptr) } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/info.rs: -------------------------------------------------------------------------------- 1 | use crate::internal::*; 2 | 3 | /// Wrapper around irmin commit info 4 | pub struct Info<'a> { 5 | pub ptr: *mut IrminInfo, 6 | pub(crate) repo: UntypedRepo<'a>, 7 | } 8 | 9 | impl<'a> Info<'a> { 10 | /// Create new commit info 11 | pub fn new( 12 | repo: &Repo, 13 | author: impl AsRef, 14 | message: impl AsRef, 15 | ) -> Result { 16 | let message = cstring(message); 17 | let author = cstring(author); 18 | let ptr = unsafe { 19 | irmin_info_new( 20 | repo.ptr, 21 | author.as_ptr() as *mut _, 22 | message.as_ptr() as *mut _, 23 | ) 24 | }; 25 | check!(repo.ptr, ptr); 26 | Ok(Info { 27 | ptr, 28 | repo: UntypedRepo::new(repo), 29 | }) 30 | } 31 | 32 | /// Get date 33 | pub fn date(&self) -> i64 { 34 | unsafe { irmin_info_date(self.repo.ptr, self.ptr) } 35 | } 36 | 37 | /// Get author 38 | pub fn author(&self) -> Result { 39 | let ptr = unsafe { irmin_info_author(self.repo.ptr, self.ptr) }; 40 | IrminString::wrap(ptr) 41 | } 42 | 43 | /// Get message 44 | pub fn message(&self) -> Result { 45 | let ptr = unsafe { irmin_info_message(self.repo.ptr, self.ptr) }; 46 | IrminString::wrap(ptr) 47 | } 48 | } 49 | 50 | impl<'a> Drop for Info<'a> { 51 | fn drop(&mut self) { 52 | unsafe { irmin_info_free(self.ptr) } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/irmin_string.rs: -------------------------------------------------------------------------------- 1 | use crate::internal::*; 2 | 3 | /// IrminString is a wrapper around strings returned by libirmin 4 | pub struct IrminString(pub *mut crate::bindings::IrminString, pub usize); 5 | 6 | impl std::fmt::Debug for IrminString { 7 | fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { 8 | self.as_str().fmt(fmt) 9 | } 10 | } 11 | 12 | impl Drop for IrminString { 13 | fn drop(&mut self) { 14 | unsafe { irmin_string_free(self.0 as *mut _) } 15 | } 16 | } 17 | 18 | impl IrminString { 19 | pub(crate) fn wrap(ptr: *mut crate::bindings::IrminString) -> Result { 20 | if ptr.is_null() { 21 | return Err(Error::NullPtr); 22 | } 23 | let len = unsafe { irmin_string_length(ptr) }; 24 | Ok(IrminString(ptr, len as usize)) 25 | } 26 | 27 | /// Create a new IrminString from bytes 28 | pub fn new(s: impl AsRef<[u8]>) -> Result { 29 | let len = s.as_ref().len(); 30 | let s = unsafe { irmin_string_new(s.as_ref().as_ptr() as *mut _, len as i64) }; 31 | if s.is_null() { 32 | return Err(Error::NullPtr); 33 | } 34 | Ok(IrminString(s, len)) 35 | } 36 | 37 | /// Access IrminString as str 38 | pub fn as_str(&self) -> &str { 39 | self.as_ref() 40 | } 41 | 42 | /// Access bytes of IrminString 43 | pub fn as_slice(&self) -> &[u8] { 44 | self.as_ref() 45 | } 46 | } 47 | 48 | impl PartialEq for IrminString { 49 | fn eq(&self, other: &IrminString) -> bool { 50 | self.as_slice() == other.as_slice() 51 | } 52 | } 53 | 54 | impl AsRef<[u8]> for IrminString { 55 | fn as_ref(&self) -> &[u8] { 56 | unsafe { 57 | let data = irmin_string_data(self.0); 58 | std::slice::from_raw_parts_mut(data as *mut u8, self.1) 59 | } 60 | } 61 | } 62 | 63 | impl AsRef for IrminString { 64 | fn as_ref(&self) -> &str { 65 | unsafe { 66 | let data = irmin_string_data(self.0); 67 | let s = std::slice::from_raw_parts_mut(data as *mut u8, self.1); 68 | std::str::from_utf8_unchecked(s) 69 | } 70 | } 71 | } 72 | 73 | impl AsRef for IrminString { 74 | fn as_ref(&self) -> &std::ffi::CStr { 75 | unsafe { 76 | let data = irmin_string_data(self.0); 77 | let b = std::slice::from_raw_parts_mut(data as *mut u8, self.1 + 1); 78 | std::ffi::CStr::from_bytes_with_nul_unchecked(b) 79 | } 80 | } 81 | } 82 | 83 | impl From for String { 84 | fn from(x: IrminString) -> String { 85 | x.as_str().to_string() 86 | } 87 | } 88 | 89 | impl From for Vec { 90 | fn from(x: IrminString) -> Vec { 91 | x.as_slice().into() 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/key.rs: -------------------------------------------------------------------------------- 1 | use crate::internal::*; 2 | 3 | pub struct CommitKey<'a> { 4 | pub ptr: *mut IrminCommitKey, 5 | 6 | pub(crate) repo: UntypedRepo<'a>, 7 | } 8 | 9 | impl<'a> Drop for CommitKey<'a> { 10 | fn drop(&mut self) { 11 | unsafe { irmin_commit_key_free(self.ptr) } 12 | } 13 | } 14 | 15 | impl<'a> CommitKey<'a> { 16 | /// Convert CommitKey to string representation using `Irmin.Type.to_string` 17 | pub fn to_string(&self) -> Result { 18 | let t = unsafe { irmin_type_commit_key(self.repo.ptr) }; 19 | let s = unsafe { irmin_value_to_string(t, self.ptr as *mut _) }; 20 | unsafe { irmin_type_free(t) } 21 | IrminString::wrap(s) 22 | } 23 | } 24 | 25 | pub struct KindedKey<'a> { 26 | pub ptr: *mut IrminKindedKey, 27 | pub(crate) repo: UntypedRepo<'a>, 28 | } 29 | 30 | impl<'a> Drop for KindedKey<'a> { 31 | fn drop(&mut self) { 32 | unsafe { irmin_kinded_key_free(self.ptr) } 33 | } 34 | } 35 | 36 | impl<'a> KindedKey<'a> { 37 | /// Convert KindedKey to string representation using `Irmin.Type.to_string` 38 | pub fn to_string(&self) -> Result { 39 | let t = unsafe { irmin_type_commit_key(self.repo.ptr) }; 40 | let s = unsafe { irmin_value_to_string(t, self.ptr as *mut _) }; 41 | unsafe { irmin_type_free(t) } 42 | IrminString::wrap(s) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /// Set internal log level 2 | pub fn set_log_level(s: Option<&str>) -> bool { 3 | let s = s.map(internal::cstring); 4 | unsafe { 5 | bindings::irmin_log_level( 6 | s.map(|x| x.as_ptr() as *mut _) 7 | .unwrap_or_else(std::ptr::null_mut), 8 | ) 9 | } 10 | } 11 | 12 | fn error_msg(repo: *mut bindings::IrminRepo) -> Option { 13 | let s = unsafe { bindings::irmin_repo_get_error(repo) }; 14 | if s.is_null() { 15 | return None; 16 | } 17 | match IrminString::wrap(s) { 18 | Ok(s) => Some(s), 19 | Err(_) => None, 20 | } 21 | } 22 | 23 | #[macro_use] 24 | pub mod bindings; 25 | 26 | mod commit; 27 | mod config; 28 | mod hash; 29 | mod info; 30 | mod irmin_string; 31 | mod key; 32 | mod metadata; 33 | mod path; 34 | mod remote; 35 | mod repo; 36 | mod store; 37 | mod tree; 38 | mod ty; 39 | mod util; 40 | mod value; 41 | 42 | pub(crate) mod prelude { 43 | pub use crate::commit::Commit; 44 | pub use crate::config::{Config, ContentType, Contents, HashType}; 45 | pub use crate::hash::Hash; 46 | pub use crate::info::Info; 47 | pub use crate::irmin_string::IrminString; 48 | pub use crate::key::{CommitKey, KindedKey}; 49 | pub use crate::metadata::Metadata; 50 | pub use crate::path::Path; 51 | pub use crate::remote::Remote; 52 | pub use crate::repo::Repo; 53 | pub use crate::store::Store; 54 | pub use crate::tree::Tree; 55 | pub use crate::ty::Type; 56 | pub use crate::value::Value; 57 | pub use crate::Error; 58 | 59 | pub type Json = serde_json::Map; 60 | pub type JsonValue = serde_json::Value; 61 | pub use serde_json::json; 62 | } 63 | 64 | pub(crate) mod internal { 65 | pub use crate::bindings::*; 66 | pub use crate::prelude::*; 67 | pub use crate::util::*; 68 | } 69 | 70 | pub use crate::prelude::*; 71 | 72 | #[derive(Debug)] 73 | pub enum Error { 74 | NullPtr, 75 | Exc(IrminString), 76 | Json(serde_json::Error), 77 | } 78 | 79 | impl From for Error { 80 | fn from(e: serde_json::Error) -> Error { 81 | Error::Json(e) 82 | } 83 | } 84 | 85 | #[cfg(test)] 86 | mod tests { 87 | use crate::*; 88 | #[test] 89 | fn test_store() -> Result<(), Error> { 90 | let config = Config::::git_mem()?; 91 | let repo = Repo::new(config)?; 92 | let mut store = Store::new(&repo)?; 93 | 94 | let info = repo.info("irmin", "set")?; 95 | let path = Path::from_str(&repo, "foo/bar")?; 96 | let value = serde_json::json!({ 97 | "a": 1i64, 98 | "b": 2i64, 99 | "c": 3i64, 100 | }); 101 | assert!(store.set(&path, &value, info)?); 102 | 103 | let head = store.head()?.unwrap(); 104 | assert!(head.parents()?.len() == 0); 105 | 106 | let s = store.find(&path)?; 107 | assert!(s.unwrap() == value); 108 | 109 | let path1 = path.parent()?.unwrap(); 110 | assert!(store.mem_tree(&path1)); 111 | 112 | let x = store.find_tree(&path1)?; 113 | assert!(x.is_some()); 114 | 115 | let path2 = repo.path(&["bar"])?; 116 | let y = x.unwrap().find(&path2)?; 117 | assert!(y.unwrap() == value); 118 | 119 | let value1 = serde_json::json!({ 120 | "a": 4i64, 121 | "b": 5i64, 122 | "c": 6i64, 123 | }); 124 | 125 | let info = Info::new(&repo, "irmin", "set")?; 126 | assert!(store.set(&path, &value1, info)?); 127 | 128 | let head1 = store.head()?.unwrap(); 129 | assert!(head1.parents()?.len() == 1); 130 | assert!(head1.parents()?[0] == head); 131 | 132 | let tree = head1.tree().unwrap(); 133 | assert!( 134 | tree == store 135 | .find_tree(&Path::empty(&repo).unwrap()) 136 | .unwrap() 137 | .unwrap() 138 | ); 139 | 140 | Ok(()) 141 | } 142 | 143 | #[test] 144 | fn test_tree() -> Result<(), Error> { 145 | let config = Config::::git_mem()?; 146 | let repo = Repo::new(config)?; 147 | 148 | let mut tree = repo.tree()?; 149 | let abc = repo.path(&["a", "b", "c"])?; 150 | let ab = repo.path(&["a", "b"])?; 151 | 152 | let v = String::from("123"); 153 | tree.add(&abc, &v, None)?; 154 | assert!(tree.mem(&abc)); 155 | assert!(tree.mem_tree(&ab)); 156 | 157 | Ok(()) 158 | } 159 | 160 | #[test] 161 | fn test_pull() -> Result<(), Error> { 162 | let _ = std::fs::remove_dir_all("/tmp/irmin-rs-test"); 163 | let mut config = Config::::git()?; 164 | config.set_root("/tmp/irmin-rs-test"); 165 | let repo = Repo::new(config)?; 166 | let mut store = Store::new(&repo)?; 167 | let remote = Remote::url(&repo, "https://github.com/mirage/irmin-py")?; 168 | store.pull(&remote, None, None)?; 169 | assert!(store.mem(&Path::from_str(&repo, "README.md")?)); 170 | Ok(()) 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/metadata.rs: -------------------------------------------------------------------------------- 1 | use crate::internal::*; 2 | 3 | /// Wrapper around irmin trees 4 | pub struct Metadata<'a> { 5 | pub ptr: *mut IrminMetadata, 6 | pub repo: UntypedRepo<'a>, 7 | } 8 | 9 | impl<'a> Drop for Metadata<'a> { 10 | fn drop(&mut self) { 11 | unsafe { irmin_metadata_free(self.ptr) } 12 | } 13 | } 14 | 15 | impl<'a> Metadata<'a> { 16 | /// Default metadata value for the given repo 17 | pub fn default(repo: &'a Repo) -> Result, Error> { 18 | let m = unsafe { irmin_metadata_default(repo.ptr) }; 19 | check!(repo.ptr, m); 20 | Ok(Metadata { 21 | ptr: m, 22 | repo: UntypedRepo::new(repo), 23 | }) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/path.rs: -------------------------------------------------------------------------------- 1 | use crate::internal::*; 2 | 3 | /// Wrapper around String_list Path type 4 | pub struct Path<'a> { 5 | pub ptr: *mut IrminPath, 6 | pub repo: UntypedRepo<'a>, 7 | } 8 | 9 | impl<'a> Drop for Path<'a> { 10 | fn drop(&mut self) { 11 | unsafe { irmin_path_free(self.ptr) } 12 | } 13 | } 14 | 15 | impl<'a> PartialEq for Path<'a> { 16 | fn eq(&self, other: &Path<'a>) -> bool { 17 | unsafe { irmin_path_equal(self.repo.ptr, self.ptr, other.ptr) } 18 | } 19 | } 20 | 21 | impl<'a> Path<'a> { 22 | /// Create a path from a string 23 | pub fn from_str(repo: &'a Repo, s: impl AsRef) -> Result { 24 | unsafe { 25 | let s = s.as_ref(); 26 | let ptr = irmin_path_of_string(repo.ptr, s.as_ptr() as *mut _, s.len() as i64); 27 | check!(repo.ptr, ptr); 28 | Ok(Path { 29 | ptr, 30 | repo: UntypedRepo::new(repo), 31 | }) 32 | } 33 | } 34 | 35 | /// Create a path from a slice 36 | pub fn new(repo: &'a Repo, s: &[impl AsRef]) -> Result, Error> { 37 | let s: Vec<_> = s.iter().map(cstring).collect(); 38 | let mut t: Vec<_> = s.iter().map(|x| x.as_ptr() as *mut u8).collect(); 39 | t.push(std::ptr::null_mut()); 40 | let ptr = unsafe { irmin_path(repo.ptr, t.as_ptr() as *mut _) }; 41 | check!(repo.ptr, ptr); 42 | Ok(Path { 43 | ptr, 44 | repo: UntypedRepo::new(repo), 45 | }) 46 | } 47 | 48 | /// Create an empty path 49 | pub fn empty(repo: &'a Repo) -> Result, Error> { 50 | let ptr = unsafe { irmin_path_empty(repo.ptr as *mut _) }; 51 | check!(repo.ptr, ptr); 52 | Ok(Path { 53 | ptr, 54 | repo: UntypedRepo::new(repo), 55 | }) 56 | } 57 | 58 | /// Get path's parent path 59 | pub fn parent(&self) -> Result>, Error> { 60 | let ptr = unsafe { irmin_path_parent(self.repo.ptr, self.ptr) }; 61 | check_opt!(self.repo.ptr, ptr); 62 | Ok(Some(Path { 63 | ptr, 64 | repo: self.repo.clone(), 65 | })) 66 | } 67 | 68 | /// Append to a path and return a new path 69 | pub fn append(&self, s: impl AsRef) -> Result, Error> { 70 | let s = s.as_ref(); 71 | let ptr = unsafe { 72 | irmin_path_append( 73 | self.repo.ptr, 74 | self.ptr, 75 | s.as_ptr() as *mut _, 76 | s.len() as i64, 77 | ) 78 | }; 79 | check!(self.repo.ptr, ptr); 80 | Ok(Path { 81 | ptr, 82 | repo: self.repo.clone(), 83 | }) 84 | } 85 | 86 | /// Append two paths 87 | pub fn append_path(&self, s: &Path) -> Result, Error> { 88 | let ptr = unsafe { irmin_path_append_path(self.repo.ptr, self.ptr, s.ptr) }; 89 | check!(self.repo.ptr, ptr); 90 | Ok(Path { 91 | ptr, 92 | repo: self.repo.clone(), 93 | }) 94 | } 95 | 96 | /// Convert a path to String 97 | pub fn to_string(&self) -> Result { 98 | let ptr = unsafe { irmin_path_to_string(self.repo.ptr, self.ptr) }; 99 | let s = IrminString::wrap(ptr); 100 | s.map(|x| x.into()) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/remote.rs: -------------------------------------------------------------------------------- 1 | use crate::internal::*; 2 | 3 | pub struct Remote<'a> { 4 | pub ptr: *mut IrminRemote, 5 | #[allow(dead_code)] 6 | repo: UntypedRepo<'a>, 7 | } 8 | 9 | impl<'a> Remote<'a> { 10 | /// Create `Remote` from an existing store 11 | pub fn store(store: &'a Store) -> Result, Error> { 12 | let ptr = unsafe { irmin_remote_store(store.ptr) }; 13 | check!(store.repo.ptr, ptr); 14 | Ok(Remote { 15 | ptr, 16 | repo: UntypedRepo::new(store.repo), 17 | }) 18 | } 19 | 20 | /// Remote from URL 21 | pub fn url(repo: &'a Repo, s: impl AsRef) -> Result, Error> { 22 | let mut s = cstring(s.as_ref()); 23 | let ptr = unsafe { irmin_remote(repo.ptr, s.as_mut_ptr() as *mut _) }; 24 | check!(repo.ptr, ptr); 25 | Ok(Remote { 26 | ptr, 27 | repo: UntypedRepo::new(repo), 28 | }) 29 | } 30 | 31 | /// Remote from URL with basic auth 32 | pub fn url_with_auth( 33 | repo: &'a Repo, 34 | s: impl AsRef, 35 | user: impl AsRef, 36 | token: impl AsRef, 37 | ) -> Result, Error> { 38 | let mut s = cstring(s.as_ref()); 39 | let mut user = cstring(user.as_ref()); 40 | let mut token = cstring(token.as_ref()); 41 | let ptr = unsafe { 42 | irmin_remote_with_auth( 43 | repo.ptr, 44 | s.as_mut_ptr() as *mut _, 45 | user.as_mut_ptr() as *mut _, 46 | token.as_mut_ptr() as *mut _, 47 | ) 48 | }; 49 | check!(repo.ptr, ptr); 50 | Ok(Remote { 51 | ptr, 52 | repo: UntypedRepo::new(repo), 53 | }) 54 | } 55 | } 56 | 57 | impl<'a> Drop for Remote<'a> { 58 | fn drop(&mut self) { 59 | unsafe { irmin_remote_free(self.ptr) } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/repo.rs: -------------------------------------------------------------------------------- 1 | use crate::internal::*; 2 | 3 | /// Wrapper around Irmin repo 4 | pub struct Repo { 5 | pub config: Config, 6 | pub ptr: *mut IrminRepo, 7 | } 8 | 9 | impl Repo { 10 | /// Create a new repo from the given config 11 | pub fn new(config: Config) -> Result, Error> { 12 | unsafe { 13 | let ptr = irmin_repo_new(config.ptr); 14 | if ptr.is_null() { 15 | return Err(Error::NullPtr); 16 | } 17 | Ok(Repo { config, ptr }) 18 | } 19 | } 20 | 21 | /// Get a list of all branches 22 | pub fn branches(&self) -> Result, Error> { 23 | let b = unsafe { irmin_repo_branches(self.ptr) }; 24 | check!(self.ptr, b); 25 | let mut dest = Vec::new(); 26 | let n = unsafe { irmin_branch_array_length(self.ptr, b) }; 27 | for i in 0..n { 28 | let p = unsafe { irmin_branch_array_get(self.ptr, b, i) }; 29 | if let Ok(s) = IrminString::wrap(p) { 30 | dest.push(s); 31 | } 32 | } 33 | unsafe { irmin_branch_array_free(b) }; 34 | Ok(dest) 35 | } 36 | 37 | /// Create a new path 38 | pub fn path(&self, s: &[impl AsRef]) -> Result { 39 | Path::new(self, s) 40 | } 41 | 42 | /// Create a new commit 43 | pub fn commit<'a>( 44 | &'a self, 45 | parents: impl AsRef<[&'a Commit<'a>]>, 46 | tree: &Tree, 47 | info: Info, 48 | ) -> Result, Error> { 49 | Commit::new(self, parents, tree, info) 50 | } 51 | 52 | /// Create an empty tree 53 | pub fn tree(&self) -> Result, Error> { 54 | Tree::new(self) 55 | } 56 | 57 | /// Create commit info 58 | pub fn info(&self, author: impl AsRef, message: impl AsRef) -> Result { 59 | Info::new(self, author, message) 60 | } 61 | } 62 | 63 | impl Drop for Repo { 64 | fn drop(&mut self) { 65 | unsafe { irmin_repo_free(self.ptr) } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/store.rs: -------------------------------------------------------------------------------- 1 | use crate::internal::*; 2 | 3 | /// Wrapper around Irmin.S 4 | pub struct Store<'a, T: Contents> { 5 | pub ptr: *mut Irmin, 6 | pub repo: &'a Repo, 7 | } 8 | 9 | impl<'a, T: Contents> Store<'a, T> { 10 | /// Open the main branch of a store 11 | pub fn new(repo: &'a Repo) -> Result, Error> { 12 | unsafe { 13 | let ptr = irmin_main(repo.ptr); 14 | check!(repo.ptr, ptr); 15 | Ok(Store { ptr, repo }) 16 | } 17 | } 18 | 19 | /// Specify the branch to open 20 | pub fn of_branch(repo: &'a Repo, branch: impl AsRef) -> Result, Error> { 21 | let branch = cstring(branch); 22 | unsafe { 23 | let ptr = irmin_of_branch(repo.ptr, branch.as_ptr() as *mut _); 24 | check!(repo.ptr, ptr); 25 | Ok(Store { ptr, repo }) 26 | } 27 | } 28 | 29 | /// Specify the commit to open 30 | pub fn of_commit(repo: &'a Repo, commit: &Commit) -> Result, Error> { 31 | unsafe { 32 | let ptr = irmin_of_commit(repo.ptr, commit.ptr); 33 | check!(repo.ptr, ptr); 34 | Ok(Store { ptr, repo }) 35 | } 36 | } 37 | 38 | /// Set a value, creating a new commit 39 | pub fn set(&mut self, path: &Path, value: &T, info: Info) -> Result { 40 | let value = value.to_value()?; 41 | unsafe { 42 | let r = irmin_set(self.ptr, path.ptr, value.ptr as *mut _, info.ptr); 43 | check!(self.repo.ptr, r, false); 44 | Ok(r) 45 | } 46 | } 47 | 48 | /// Set a value if `old` matches the current value 49 | pub fn test_and_set( 50 | &mut self, 51 | path: &Path, 52 | old: Option<&T>, 53 | value: Option<&T>, 54 | info: Info, 55 | ) -> Result { 56 | let old = match old { 57 | Some(value) => Some(value.to_value()?), 58 | None => None, 59 | }; 60 | let value = match value { 61 | Some(value) => Some(value.to_value()?), 62 | None => None, 63 | }; 64 | unsafe { 65 | let r = irmin_test_and_set( 66 | self.ptr, 67 | path.ptr, 68 | old.map(|x| x.ptr as *mut _) 69 | .unwrap_or_else(std::ptr::null_mut), 70 | value 71 | .map(|x| x.ptr as *mut _) 72 | .unwrap_or_else(std::ptr::null_mut), 73 | info.ptr, 74 | ); 75 | check!(self.repo.ptr, r, false); 76 | Ok(r) 77 | } 78 | } 79 | 80 | /// Set a tree, creating a new commit 81 | pub fn set_tree(&mut self, path: &Path, tree: &Tree, info: Info) -> Result { 82 | unsafe { 83 | let r = irmin_set_tree(self.ptr, path.ptr, tree.ptr, info.ptr); 84 | check!(self.repo.ptr, r, false); 85 | Ok(r) 86 | } 87 | } 88 | 89 | /// Set a tree if `old` matches the current tree 90 | pub fn test_and_set_tree( 91 | &mut self, 92 | path: &Path, 93 | old: Option<&Tree>, 94 | tree: Option<&Tree>, 95 | info: Info, 96 | ) -> Result { 97 | unsafe { 98 | let r = irmin_test_and_set_tree( 99 | self.ptr, 100 | path.ptr, 101 | old.map(|x| x.ptr).unwrap_or_else(std::ptr::null_mut), 102 | tree.map(|x| x.ptr).unwrap_or_else(std::ptr::null_mut), 103 | info.ptr, 104 | ); 105 | check!(self.repo.ptr, r, false); 106 | Ok(r) 107 | } 108 | } 109 | 110 | /// Find the value associated with the given path 111 | pub fn find(&self, path: &Path) -> Result, Error> { 112 | let r = unsafe { irmin_find(self.ptr, path.ptr) }; 113 | check_opt!(self.repo.ptr, r); 114 | let ty = T::ty()?; 115 | let v = Value { 116 | ptr: r as *mut _, 117 | ty, 118 | }; 119 | let v = T::from_value(&v)?; 120 | Ok(Some(v)) 121 | } 122 | 123 | /// Find the tree associated with the given path 124 | pub fn find_tree(&self, path: &Path) -> Result>, Error> { 125 | unsafe { 126 | let ptr = irmin_find_tree(self.ptr, path.ptr); 127 | check_opt!(self.repo.ptr, ptr); 128 | let x = Tree { 129 | ptr, 130 | repo: UntypedRepo::new(self.repo), 131 | _t: std::marker::PhantomData, 132 | }; 133 | Ok(Some(x)) 134 | } 135 | } 136 | 137 | /// Check for the existence of a value at the given path 138 | pub fn mem(&self, path: &Path) -> bool { 139 | unsafe { irmin_mem(self.ptr, path.ptr) } 140 | } 141 | 142 | /// Check for the existence of a tree at the given path 143 | pub fn mem_tree(&self, path: &Path) -> bool { 144 | unsafe { irmin_mem_tree(self.ptr, path.ptr) } 145 | } 146 | 147 | /// Remove the tree or value associated with the given path 148 | pub fn remove(&mut self, path: &Path, info: Info) -> bool { 149 | unsafe { irmin_remove(self.ptr, path.ptr, info.ptr) } 150 | } 151 | 152 | /// Get current head commit 153 | pub fn head(&self) -> Result>, Error> { 154 | let ptr = unsafe { irmin_get_head(self.ptr) }; 155 | check_opt!(self.repo.ptr, ptr); 156 | Ok(Some(Commit { 157 | ptr, 158 | repo: UntypedRepo::new(self.repo), 159 | })) 160 | } 161 | 162 | /// Set head commit 163 | pub fn set_head(&mut self, c: &Commit) { 164 | unsafe { irmin_set_head(self.ptr, c.ptr) } 165 | } 166 | 167 | /// Update current branch to the specified commit 168 | pub fn fast_forward(&mut self, c: &Commit) -> bool { 169 | unsafe { irmin_fast_forward(self.ptr, c.ptr) } 170 | } 171 | 172 | /// Merge with another branch 173 | pub fn merge_with_branch( 174 | &mut self, 175 | branch: impl AsRef, 176 | info: Info, 177 | ) -> Result { 178 | let branch = cstring(branch); 179 | let r = unsafe { irmin_merge_with_branch(self.ptr, branch.as_ptr() as *mut _, info.ptr) }; 180 | check!(self.repo.ptr, r, false); 181 | Ok(r) 182 | } 183 | 184 | /// Merge with another commit 185 | pub fn merge_with_commit(&mut self, commit: &Commit, info: Info) -> Result { 186 | let r = unsafe { irmin_merge_with_commit(self.ptr, commit.ptr, info.ptr) }; 187 | check!(self.repo.ptr, r, false); 188 | Ok(r) 189 | } 190 | 191 | /// Merge with another store 192 | pub fn merge(&mut self, store: &Store, info: Info) -> Result { 193 | let r = unsafe { irmin_merge_into(self.ptr, store.ptr, info.ptr) }; 194 | check!(self.repo.ptr, r, false); 195 | Ok(r) 196 | } 197 | 198 | /// List paths 199 | pub fn list(&self, path: &Path) -> Result, Error> { 200 | let p = unsafe { irmin_list(self.ptr, path.ptr) }; 201 | check!(self.repo.ptr, p); 202 | let len = unsafe { irmin_path_array_length(self.repo.ptr, p) }; 203 | let mut dest = Vec::new(); 204 | for i in 0..len { 205 | let path = unsafe { irmin_path_array_get(self.repo.ptr, p, i) }; 206 | if path.is_null() { 207 | continue; 208 | } 209 | dest.push(Path { 210 | ptr: path, 211 | repo: UntypedRepo::new(self.repo), 212 | }) 213 | } 214 | 215 | unsafe { irmin_path_array_free(p) } 216 | 217 | Ok(dest) 218 | } 219 | 220 | /// Pull from a remote respository, if the `info` parameter is set then 221 | /// a merge commit will be made 222 | pub fn pull( 223 | &mut self, 224 | remote: &Remote, 225 | depth: Option, 226 | info: Option<&Info>, 227 | ) -> Result { 228 | let info = match info { 229 | Some(i) => i.ptr as *mut _, 230 | None => std::ptr::null_mut(), 231 | }; 232 | let depth = depth.unwrap_or(-1); 233 | let c = unsafe { irmin_pull(self.ptr, depth, remote.ptr, info) }; 234 | check!(self.repo.ptr, c); 235 | Ok(Commit { 236 | ptr: c, 237 | repo: UntypedRepo::new(self.repo), 238 | }) 239 | } 240 | 241 | /// Fetch data from a remote repository 242 | pub fn fetch(&mut self, remote: &Remote, depth: Option) -> Result { 243 | let depth = depth.unwrap_or(-1); 244 | let c = unsafe { irmin_fetch(self.ptr, depth, remote.ptr) }; 245 | check!(self.repo.ptr, c); 246 | Ok(Commit { 247 | ptr: c, 248 | repo: UntypedRepo::new(self.repo), 249 | }) 250 | } 251 | 252 | /// Push to a remote repository 253 | pub fn push(&mut self, remote: &Remote, depth: Option) -> Result { 254 | let depth = depth.unwrap_or(-1); 255 | let c = unsafe { irmin_push(self.ptr, depth, remote.ptr) }; 256 | check!(self.repo.ptr, c); 257 | Ok(Commit { 258 | ptr: c, 259 | repo: UntypedRepo::new(self.repo), 260 | }) 261 | } 262 | } 263 | 264 | impl<'a, T: Contents> Drop for Store<'a, T> { 265 | fn drop(&mut self) { 266 | unsafe { irmin_free(self.ptr) } 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /src/tree.rs: -------------------------------------------------------------------------------- 1 | use crate::internal::*; 2 | 3 | /// Wrapper around irmin trees 4 | pub struct Tree<'a, T: Contents> { 5 | pub ptr: *mut IrminTree, 6 | pub repo: UntypedRepo<'a>, 7 | pub(crate) _t: std::marker::PhantomData, 8 | } 9 | 10 | impl<'a, T: Contents> Drop for Tree<'a, T> { 11 | fn drop(&mut self) { 12 | unsafe { irmin_tree_free(self.ptr) } 13 | } 14 | } 15 | 16 | impl<'a, T: Contents> PartialEq for Tree<'a, T> { 17 | fn eq(&self, other: &Tree<'a, T>) -> bool { 18 | unsafe { irmin_tree_equal(self.repo.ptr, self.ptr, other.ptr) } 19 | } 20 | } 21 | 22 | impl<'a, T: Contents> Tree<'a, T> { 23 | /// Create an empty tree 24 | pub fn new(repo: &'a Repo) -> Result, Error> { 25 | unsafe { 26 | let ptr = irmin_tree_new(repo.ptr); 27 | check!(repo.ptr, ptr); 28 | Ok(Tree { 29 | ptr, 30 | repo: UntypedRepo::new(repo), 31 | _t: std::marker::PhantomData, 32 | }) 33 | } 34 | } 35 | 36 | /// Compute the hash of a tree 37 | pub fn hash(&self) -> Result { 38 | let h = unsafe { irmin_tree_hash(self.repo.ptr, self.ptr) }; 39 | check!(self.repo.ptr, h); 40 | Ok(Hash { 41 | ptr: h, 42 | repo: self.repo.clone(), 43 | }) 44 | } 45 | 46 | /// 47 | pub fn key(&self) -> Result, Error> { 48 | let h = unsafe { irmin_tree_key(self.repo.ptr, self.ptr) }; 49 | check_opt!(self.repo.ptr, h); 50 | Ok(Some(KindedKey { 51 | ptr: h, 52 | repo: self.repo.clone(), 53 | })) 54 | } 55 | 56 | pub fn of_hash(repo: &'a Repo, h: &Hash) -> Result>, Error> { 57 | let ptr = unsafe { irmin_tree_of_hash(repo.ptr, h.ptr) }; 58 | check_opt!(repo.ptr, ptr); 59 | Ok(Some(Tree { 60 | ptr, 61 | repo: UntypedRepo::new(repo), 62 | _t: std::marker::PhantomData, 63 | })) 64 | } 65 | 66 | pub fn of_key(repo: &'a Repo, k: &KindedKey) -> Result>, Error> { 67 | let ptr = unsafe { irmin_tree_of_key(repo.ptr, k.ptr) }; 68 | check_opt!(repo.ptr, ptr); 69 | Ok(Some(Tree { 70 | ptr, 71 | repo: UntypedRepo::new(repo), 72 | _t: std::marker::PhantomData, 73 | })) 74 | } 75 | 76 | /// Update the tree with a value at the specified path 77 | pub fn add( 78 | &mut self, 79 | path: &Path, 80 | value: &T, 81 | metadata: Option<&Metadata>, 82 | ) -> Result<(), Error> { 83 | let x = unsafe { 84 | let value = value.to_value()?; 85 | let meta = match metadata { 86 | Some(m) => m.ptr, 87 | None => std::ptr::null_mut(), 88 | }; 89 | irmin_tree_add(self.repo.ptr, self.ptr, path.ptr, value.ptr as *mut _, meta) 90 | }; 91 | check!(self.repo.ptr, x, false); 92 | Ok(()) 93 | } 94 | 95 | /// Update the tree with a tree at the specified path 96 | pub fn add_tree(&mut self, path: &Path, tree: &Tree) -> Result<(), Error> { 97 | let x = unsafe { irmin_tree_add_tree(self.repo.ptr, self.ptr, path.ptr, tree.ptr) }; 98 | check!(self.repo.ptr, x, false); 99 | Ok(()) 100 | } 101 | 102 | /// Check for the existence of a value at the given path 103 | pub fn mem(&self, path: &Path) -> bool { 104 | unsafe { irmin_tree_mem(self.repo.ptr, self.ptr, path.ptr) } 105 | } 106 | 107 | /// Check for the existence of a tree at the given path 108 | pub fn mem_tree(&self, path: &Path) -> bool { 109 | unsafe { irmin_tree_mem_tree(self.repo.ptr, self.ptr, path.ptr) } 110 | } 111 | 112 | /// Remove any bindings for the given path 113 | pub fn remove(&mut self, path: &Path) -> Result<(), Error> { 114 | let x = unsafe { irmin_tree_remove(self.repo.ptr, self.ptr, path.ptr) }; 115 | check!(self.repo.ptr, x, false); 116 | Ok(()) 117 | } 118 | 119 | /// Find a value associated with a path 120 | pub fn find(&self, path: &Path) -> Result, Error> { 121 | unsafe { 122 | let ptr = irmin_tree_find(self.repo.ptr, self.ptr, path.ptr); 123 | check!(self.repo.ptr, ptr); 124 | if ptr.is_null() { 125 | return Ok(None); 126 | } 127 | let ty = T::ty()?; 128 | let x = Value { 129 | ptr: ptr as *mut _, 130 | ty, 131 | }; 132 | let value = T::from_value(&x)?; 133 | Ok(Some(value)) 134 | } 135 | } 136 | 137 | /// Find a tree associated with a path 138 | pub fn find_tree(&self, path: &Path) -> Result>, Error> { 139 | unsafe { 140 | let ptr = irmin_tree_find_tree(self.repo.ptr, self.ptr, path.ptr); 141 | check_opt!(self.repo.ptr, ptr); 142 | let x = Tree { 143 | ptr, 144 | repo: self.repo.clone(), 145 | _t: std::marker::PhantomData, 146 | }; 147 | Ok(Some(x)) 148 | } 149 | } 150 | 151 | /// List paths 152 | pub fn list(&self, path: &Path) -> Result, Error> { 153 | let p = unsafe { irmin_tree_list(self.repo.ptr, self.ptr, path.ptr) }; 154 | check!(self.repo.ptr, p); 155 | let len = unsafe { irmin_path_array_length(self.repo.ptr, p) }; 156 | let mut dest = Vec::new(); 157 | for i in 0..len { 158 | let path = unsafe { irmin_path_array_get(self.repo.ptr, p, i) }; 159 | if path.is_null() { 160 | continue; 161 | } 162 | dest.push(Path { 163 | ptr: path, 164 | repo: self.repo.clone(), 165 | }) 166 | } 167 | 168 | unsafe { irmin_path_array_free(p) } 169 | 170 | Ok(dest) 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/ty.rs: -------------------------------------------------------------------------------- 1 | use crate::internal::*; 2 | 3 | /// Wrapper around Irmin.Type 4 | pub struct Type { 5 | pub ptr: *mut IrminType, 6 | } 7 | 8 | impl Clone for Type { 9 | fn clone(&self) -> Type { 10 | let ptr = unsafe { irmin_value_clone(self.ptr as *mut _) as *mut IrminType }; 11 | Type { ptr } 12 | } 13 | } 14 | 15 | impl Drop for Type { 16 | fn drop(&mut self) { 17 | unsafe { irmin_type_free(self.ptr) } 18 | } 19 | } 20 | 21 | impl Type { 22 | /// Irmin.Type.string 23 | pub fn string() -> Result { 24 | let ptr = unsafe { irmin_type_string() }; 25 | if ptr.is_null() { 26 | return Err(Error::NullPtr); 27 | }; 28 | Ok(Type { ptr }) 29 | } 30 | 31 | /// Irmin.Type.int 32 | pub fn int() -> Result { 33 | let ptr = unsafe { irmin_type_int() }; 34 | if ptr.is_null() { 35 | return Err(Error::NullPtr); 36 | }; 37 | Ok(Type { ptr }) 38 | } 39 | 40 | /// Irmin.Type.float 41 | pub fn float() -> Result { 42 | let ptr = unsafe { irmin_type_float() }; 43 | if ptr.is_null() { 44 | return Err(Error::NullPtr); 45 | }; 46 | Ok(Type { ptr }) 47 | } 48 | 49 | /// Irmin.Type.bool 50 | pub fn bool() -> Result { 51 | let ptr = unsafe { irmin_type_bool() }; 52 | if ptr.is_null() { 53 | return Err(Error::NullPtr); 54 | }; 55 | Ok(Type { ptr }) 56 | } 57 | 58 | /// Irmin.Contents.Json.t 59 | pub fn json() -> Result { 60 | let ptr = unsafe { irmin_type_json() }; 61 | if ptr.is_null() { 62 | return Err(Error::NullPtr); 63 | }; 64 | Ok(Type { ptr }) 65 | } 66 | 67 | /// Irmin.Contents.Json_value.t 68 | pub fn json_value() -> Result { 69 | let ptr = unsafe { irmin_type_json_value() }; 70 | if ptr.is_null() { 71 | return Err(Error::NullPtr); 72 | }; 73 | Ok(Type { ptr }) 74 | } 75 | 76 | /// The path type for a Repo 77 | pub fn path(repo: &Repo) -> Result { 78 | let ptr = unsafe { irmin_type_path(repo.ptr) }; 79 | check!(repo.ptr, ptr); 80 | Ok(Type { ptr }) 81 | } 82 | 83 | /// The hash type for a Repo 84 | pub fn hash(repo: &Repo) -> Result { 85 | let ptr = unsafe { irmin_type_hash(repo.ptr) }; 86 | check!(repo.ptr, ptr); 87 | Ok(Type { ptr }) 88 | } 89 | 90 | /// The commit type for a Repo 91 | pub fn commit(repo: &Repo) -> Result { 92 | let ptr = unsafe { irmin_type_commit(repo.ptr) }; 93 | check!(repo.ptr, ptr); 94 | Ok(Type { ptr }) 95 | } 96 | 97 | pub fn commit_key(repo: &Repo) -> Result { 98 | let ptr = unsafe { irmin_type_commit_key(repo.ptr) }; 99 | check!(repo.ptr, ptr); 100 | Ok(Type { ptr }) 101 | } 102 | 103 | pub fn kinded_key(repo: &Repo) -> Result { 104 | let ptr = unsafe { irmin_type_kinded_key(repo.ptr) }; 105 | check!(repo.ptr, ptr); 106 | Ok(Type { ptr }) 107 | } 108 | 109 | pub fn metadata(repo: &Repo) -> Result { 110 | let ptr = unsafe { irmin_type_metadata(repo.ptr) }; 111 | check!(repo.ptr, ptr); 112 | Ok(Type { ptr }) 113 | } 114 | 115 | pub fn contents(repo: &Repo) -> Result { 116 | let ptr = unsafe { irmin_type_contents(repo.ptr) }; 117 | check!(repo.ptr, ptr); 118 | Ok(Type { ptr }) 119 | } 120 | 121 | /// The tree type for a Repo 122 | pub fn tree(repo: &Repo) -> Result { 123 | let ptr = unsafe { irmin_type_tree(repo.ptr) }; 124 | check!(repo.ptr, ptr); 125 | Ok(Type { ptr }) 126 | } 127 | 128 | /// Get the name of a type 129 | pub fn name(&self) -> Result { 130 | let name = unsafe { irmin_type_name(self.ptr) }; 131 | IrminString::wrap(name) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | use crate::internal::*; 2 | 3 | pub(crate) fn cstring(s: impl AsRef) -> String { 4 | let mut s = s.as_ref().to_string(); 5 | s.push('\0'); 6 | s 7 | } 8 | 9 | #[derive(Clone)] 10 | pub struct UntypedRepo<'a> { 11 | pub(crate) ptr: *mut IrminRepo, 12 | pub(crate) _t: std::marker::PhantomData<&'a ()>, 13 | } 14 | 15 | impl<'a> UntypedRepo<'a> { 16 | pub fn new(repo: &'a Repo) -> UntypedRepo<'a> { 17 | UntypedRepo { 18 | ptr: repo.ptr, 19 | _t: std::marker::PhantomData, 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/value.rs: -------------------------------------------------------------------------------- 1 | use crate::internal::*; 2 | 3 | /// Wrapper around OCaml values 4 | pub struct Value { 5 | pub ty: Type, 6 | pub ptr: *mut IrminValue, 7 | } 8 | 9 | impl Drop for Value { 10 | fn drop(&mut self) { 11 | unsafe { irmin_value_free(self.ptr) } 12 | } 13 | } 14 | 15 | impl Clone for Value { 16 | fn clone(&self) -> Value { 17 | let ptr = unsafe { irmin_value_clone(self.ptr as *mut _) }; 18 | Value { 19 | ty: self.ty.clone(), 20 | ptr, 21 | } 22 | } 23 | } 24 | 25 | impl PartialEq for Value { 26 | fn eq(&self, other: &Value) -> bool { 27 | let x = match (self.ty.name(), other.ty.name()) { 28 | (Ok(a), Ok(b)) => a == b, 29 | (_, _) => return false, 30 | }; 31 | x && unsafe { irmin_value_equal(self.ty.ptr, self.ptr, other.ptr) } 32 | } 33 | } 34 | 35 | impl Value { 36 | /// OCaml string 37 | pub fn string(s: impl AsRef) -> Result { 38 | let s = s.as_ref(); 39 | let ptr = 40 | unsafe { irmin_string_new(s.as_ptr() as *mut _, s.len() as i64) as *mut IrminValue }; 41 | if ptr.is_null() { 42 | return Err(Error::NullPtr); 43 | }; 44 | 45 | let ty = Type::string()?; 46 | 47 | Ok(Value { ptr, ty }) 48 | } 49 | 50 | /// OCaml string from Rust &[u8] 51 | pub fn bytes(s: impl AsRef<[u8]>) -> Result { 52 | let s = s.as_ref(); 53 | let ptr = 54 | unsafe { irmin_string_new(s.as_ptr() as *mut _, s.len() as i64) as *mut IrminValue }; 55 | if ptr.is_null() { 56 | return Err(Error::NullPtr); 57 | }; 58 | 59 | let ty = Type::string()?; 60 | 61 | Ok(Value { ptr, ty }) 62 | } 63 | 64 | /// OCaml int 65 | pub fn int(i: i64) -> Result { 66 | let ptr = unsafe { irmin_value_int(i) }; 67 | if ptr.is_null() { 68 | return Err(Error::NullPtr); 69 | }; 70 | 71 | let ty = Type::int()?; 72 | 73 | Ok(Value { ptr, ty }) 74 | } 75 | 76 | /// OCaml float 77 | pub fn float(i: f64) -> Result { 78 | let ptr = unsafe { irmin_value_float(i) }; 79 | if ptr.is_null() { 80 | return Err(Error::NullPtr); 81 | }; 82 | 83 | let ty = Type::float()?; 84 | 85 | Ok(Value { ptr, ty }) 86 | } 87 | 88 | /// OCaml bool 89 | pub fn bool(i: bool) -> Result { 90 | let ptr = unsafe { irmin_value_bool(i) }; 91 | if ptr.is_null() { 92 | return Err(Error::NullPtr); 93 | }; 94 | 95 | let ty = Type::bool()?; 96 | 97 | Ok(Value { ptr, ty }) 98 | } 99 | 100 | /// Parse a value of the specified type from Irmin's string encoding 101 | pub fn of_string(ty: Type, s: impl AsRef) -> Result { 102 | let s = s.as_ref(); 103 | 104 | let ptr = unsafe { irmin_value_of_string(ty.ptr, s.as_ptr() as *mut _, s.len() as i64) }; 105 | if ptr.is_null() { 106 | return Err(Error::NullPtr); 107 | } 108 | Ok(Value { ptr, ty }) 109 | } 110 | 111 | /// Encode a value using Irmin's string encoding 112 | pub fn to_string(&self) -> Result { 113 | let s = unsafe { irmin_value_to_string(self.ty.ptr, self.ptr) }; 114 | crate::IrminString::wrap(s) 115 | } 116 | 117 | /// Parse a value of the specified type from Irmin's JSON encoding 118 | pub fn of_json(ty: Type, s: impl AsRef) -> Result { 119 | let s = s.as_ref(); 120 | 121 | let ptr = unsafe { irmin_value_of_json(ty.ptr, s.as_ptr() as *mut _, s.len() as i64) }; 122 | if ptr.is_null() { 123 | return Err(Error::NullPtr); 124 | } 125 | Ok(Value { ptr, ty }) 126 | } 127 | 128 | /// Encode a value using Irmin's JSON encoding 129 | pub fn to_json(&self) -> Result { 130 | let s = unsafe { irmin_value_to_json(self.ty.ptr, self.ptr) }; 131 | crate::IrminString::wrap(s) 132 | } 133 | 134 | /// Parse a value of the specified type from Irmin's binary encoding 135 | pub fn of_bin(ty: Type, s: impl AsRef<[u8]>) -> Result { 136 | let s = s.as_ref(); 137 | let ptr = unsafe { irmin_value_of_bin(ty.ptr, s.as_ptr() as *mut _, s.len() as i64) }; 138 | if ptr.is_null() { 139 | return Err(Error::NullPtr); 140 | } 141 | Ok(Value { ptr, ty }) 142 | } 143 | 144 | /// Encode a value using Irmin's binary encoding 145 | pub fn to_bin(&self) -> Result { 146 | let s = unsafe { irmin_value_to_bin(self.ty.ptr, self.ptr) }; 147 | crate::IrminString::wrap(s) 148 | } 149 | 150 | /// Get IrminString from string value 151 | pub fn get_string(&self) -> Result { 152 | let s = unsafe { irmin_value_get_string(self.ptr) }; 153 | crate::IrminString::wrap(s) 154 | } 155 | } 156 | --------------------------------------------------------------------------------