data.rs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. use std::collections::HashMap;
  2. use ariadne::Cache;
  3. use chumsky::span::Span as _;
  4. pub use rust_decimal::Decimal;
  5. use crate::{
  6. check::CheckLevel,
  7. io::{FilesystemData, SourceFile},
  8. };
  9. pub mod ledger;
  10. pub mod spec;
  11. pub struct UnitTag;
  12. impl stringstore::NamespaceTag for UnitTag {
  13. const PREFIX: &'static str = "unit";
  14. }
  15. pub type UnitName = stringstore::StoredString<UnitTag>;
  16. pub struct AccountTag;
  17. impl stringstore::NamespaceTag for AccountTag {
  18. const PREFIX: &'static str = "acc";
  19. }
  20. pub type AccountName = stringstore::StoredString<AccountTag>;
  21. #[derive(Debug)]
  22. pub enum DataError {
  23. IOError(std::io::Error),
  24. Report(Box<ariadne::Report<'static, Span>>),
  25. Validation(String),
  26. }
  27. impl std::fmt::Display for DataError {
  28. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  29. <Self as std::fmt::Debug>::fmt(self, f)
  30. }
  31. }
  32. impl From<std::io::Error> for DataError {
  33. fn from(value: std::io::Error) -> Self {
  34. Self::IOError(value)
  35. }
  36. }
  37. impl From<ariadne::Report<'static, Span>> for DataError {
  38. fn from(value: ariadne::Report<'static, Span>) -> Self {
  39. Self::Report(value.into())
  40. }
  41. }
  42. impl std::error::Error for DataError {}
  43. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
  44. pub struct Span {
  45. range: (usize, usize),
  46. context: Option<SourceFile>,
  47. }
  48. impl Span {
  49. pub fn null_for_file(source: SourceFile) -> Self {
  50. Self {
  51. range: (usize::MAX, usize::MAX),
  52. context: Some(source),
  53. }
  54. }
  55. }
  56. impl Default for Span {
  57. fn default() -> Self {
  58. Self {
  59. range: (0, 0),
  60. context: None,
  61. }
  62. }
  63. }
  64. impl chumsky::span::Span for Span {
  65. type Offset = usize;
  66. type Context = SourceFile;
  67. fn new(context: Self::Context, range: std::ops::Range<Self::Offset>) -> Self {
  68. Self {
  69. context: Some(context),
  70. range: (range.start, range.end),
  71. }
  72. }
  73. fn start(&self) -> Self::Offset {
  74. self.range.0
  75. }
  76. fn end(&self) -> Self::Offset {
  77. self.range.1
  78. }
  79. fn context(&self) -> Self::Context {
  80. self.context.unwrap()
  81. }
  82. fn to_end(&self) -> Self {
  83. Self {
  84. context: self.context,
  85. range: (self.range.1, self.range.1),
  86. }
  87. }
  88. }
  89. impl ariadne::Span for Span {
  90. type SourceId = SourceFile;
  91. fn source(&self) -> &Self::SourceId {
  92. self.context.as_ref().unwrap()
  93. }
  94. fn start(&self) -> usize {
  95. self.range.0
  96. }
  97. fn end(&self) -> usize {
  98. self.range.1
  99. }
  100. }
  101. #[derive(Debug, Clone, Copy)]
  102. pub struct Spanned<T>(pub T, pub Span);
  103. impl<T> Spanned<T> {
  104. pub fn new(t: T, span: Span) -> Self {
  105. Self(t, span)
  106. }
  107. pub fn span(&self) -> Span {
  108. self.1
  109. }
  110. }
  111. impl<T> std::ops::Deref for Spanned<T> {
  112. type Target = T;
  113. fn deref(&self) -> &Self::Target {
  114. &self.0
  115. }
  116. }
  117. impl<T> std::ops::DerefMut for Spanned<T> {
  118. fn deref_mut(&mut self) -> &mut Self::Target {
  119. &mut self.0
  120. }
  121. }
  122. impl<T> AsRef<T> for Spanned<T> {
  123. fn as_ref(&self) -> &T {
  124. &self.0
  125. }
  126. }
  127. impl<T: PartialEq> PartialEq for Spanned<T> {
  128. fn eq(&self, other: &Self) -> bool {
  129. self.0.eq(&other.0)
  130. }
  131. }
  132. impl<T: Eq> Eq for Spanned<T> {}
  133. impl<T> From<T> for Spanned<T> {
  134. fn from(value: T) -> Self {
  135. Self(value, Span::default())
  136. }
  137. }
  138. impl<T: PartialOrd> PartialOrd for Spanned<T> {
  139. fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
  140. self.0.partial_cmp(&other.0)
  141. }
  142. }
  143. impl<T: Ord> Ord for Spanned<T> {
  144. fn cmp(&self, other: &Self) -> std::cmp::Ordering {
  145. self.0.cmp(&other.0)
  146. }
  147. }
  148. impl<T: std::fmt::Display> std::fmt::Display for Spanned<T> {
  149. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  150. self.0.fmt(f)
  151. }
  152. }
  153. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
  154. pub struct Datestamp {
  155. pub year: u16,
  156. pub month: u8,
  157. pub day: u8,
  158. }
  159. impl std::fmt::Display for Datestamp {
  160. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  161. write!(f, "{:04}-{:02}-{:02}", self.year, self.month, self.day)
  162. }
  163. }
  164. impl std::fmt::Debug for Datestamp {
  165. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  166. write!(f, "Datestamp ({self})")
  167. }
  168. }
  169. #[derive(Debug)]
  170. pub struct Root {
  171. path: std::path::PathBuf,
  172. spec_root: spec::SpecRoot,
  173. ledger_data: Vec<ledger::LedgerEntry>,
  174. account_ledger_data: HashMap<AccountName, Vec<Spanned<ledger::Transaction>>>,
  175. }
  176. impl Root {
  177. pub fn load(
  178. fsdata: &mut FilesystemData,
  179. path: &std::path::Path,
  180. check_level: CheckLevel,
  181. ) -> Result<Self, DataError> {
  182. let sf = SourceFile::new(path.as_os_str());
  183. let root_data = fsdata.fetch(&sf).unwrap();
  184. match toml::from_str::<spec::SpecRoot>(root_data.text()) {
  185. Ok(spec_root) => {
  186. let mut r = Self {
  187. path: path.into(),
  188. spec_root,
  189. ledger_data: vec![],
  190. account_ledger_data: Default::default(),
  191. };
  192. r.load_ledgers(fsdata)?;
  193. r.preprocess_ledger_data();
  194. crate::check::run_checks(&mut r, check_level)?;
  195. Ok(r)
  196. }
  197. Err(te) => {
  198. let Some(range) = te.span() else {
  199. panic!("TOML parse error with no range: {te}");
  200. };
  201. let report = ariadne::Report::build(
  202. ariadne::ReportKind::Error,
  203. Span::new(sf, range.clone()),
  204. )
  205. .with_label(ariadne::Label::new(Span::new(sf, range)).with_message(te.message()))
  206. .with_message("Failed to parse root TOML")
  207. .finish();
  208. Err(report.into())
  209. }
  210. }
  211. }
  212. fn load_ledger(
  213. &mut self,
  214. fsdata: &mut FilesystemData,
  215. path: &mut std::path::PathBuf,
  216. ) -> Result<(), DataError> {
  217. log::debug!("Loading ledger data from {}", path.display());
  218. let md = std::fs::metadata(path.as_path()).map_err(DataError::IOError)?;
  219. if md.is_dir() {
  220. // recurse
  221. for de in std::fs::read_dir(path.as_path()).map_err(DataError::IOError)? {
  222. let de = de.map_err(DataError::IOError)?;
  223. path.push(de.file_name());
  224. self.load_ledger(fsdata, path)?;
  225. path.pop();
  226. }
  227. } else {
  228. let path = std::fs::canonicalize(path)?;
  229. let Some(filename) = path.file_name() else {
  230. return Ok(());
  231. };
  232. // skip filenames beginning with a dot
  233. if filename.as_encoded_bytes()[0] == b'.' {
  234. log::info!("Skipping file {}", path.display());
  235. return Ok(());
  236. }
  237. let sf = SourceFile::new_from_string(path.into_os_string());
  238. if let Ok(data) = fsdata.fetch(&sf) {
  239. self.ledger_data
  240. .extend(ledger::parse_ledger(sf, &self.spec_root, data.text())?);
  241. } else {
  242. log::error!("Failed to load data from {}", std::path::Path::new(sf.as_str()).display());
  243. }
  244. }
  245. Ok(())
  246. }
  247. fn load_ledgers(&mut self, fsdata: &mut FilesystemData) -> Result<(), DataError> {
  248. let mut ledger_path = std::fs::canonicalize(self.path.as_path())?;
  249. ledger_path.pop();
  250. ledger_path.push(&self.spec_root.ledger_path);
  251. let mut ledger_path = std::fs::canonicalize(ledger_path)?;
  252. self.load_ledger(fsdata, &mut ledger_path)?;
  253. Ok(())
  254. }
  255. fn preprocess_ledger_data(&mut self) {
  256. for entry in &self.ledger_data {
  257. let ledger::LedgerEntry::Transaction(tx) = &entry else {
  258. continue;
  259. };
  260. for bal in &tx.changes {
  261. self.account_ledger_data
  262. .entry(*bal.account)
  263. .or_default()
  264. .push(tx.clone());
  265. }
  266. }
  267. /*for txns in self.account_ledger_data.values_mut() {
  268. txns.sort_by_key(|txn| txn.datestamp);
  269. }*/
  270. }
  271. pub fn all_ledger_data(&self) -> &[ledger::LedgerEntry] {
  272. self.ledger_data.as_slice()
  273. }
  274. pub fn ledger_data_for(&self, aname: AccountName) -> Option<&[Spanned<ledger::Transaction>]> {
  275. self.account_ledger_data.get(&aname).map(Vec::as_slice)
  276. }
  277. pub fn ledger_data_from(
  278. &self,
  279. source: SourceFile,
  280. ) -> impl Iterator<Item = &ledger::LedgerEntry> {
  281. self.all_ledger_data()
  282. .iter()
  283. .filter(move |le| le.span().context == Some(source))
  284. }
  285. pub fn account_names(&self) -> impl Iterator<Item = AccountName> {
  286. self.spec_root.accounts.keys().cloned()
  287. }
  288. pub fn account_spec(&self, aname: AccountName) -> Option<&spec::AccountSpec> {
  289. self.spec_root.accounts.get(&aname)
  290. }
  291. pub fn unit_spec(&self, unit: UnitName) -> Option<&spec::UnitSpec> {
  292. self.spec_root.units.get(&unit)
  293. }
  294. pub fn spec_root(&self) -> &spec::SpecRoot {
  295. &self.spec_root
  296. }
  297. pub fn balance(&self, aname: AccountName) -> Option<HashMap<UnitName, Decimal>> {
  298. let mut running = HashMap::<UnitName, Decimal>::new();
  299. for le in self.ledger_data_for(aname)? {
  300. for b in &le.changes {
  301. if *b.account != aname {
  302. continue;
  303. }
  304. let v = running.entry(*b.unit).or_default();
  305. *v = v.checked_add(*b.amount)?;
  306. }
  307. }
  308. Some(running)
  309. }
  310. }