1use crate::prelude::*;
7use alloc::borrow::Cow;
8use alloc::sync::Arc;
9use bacnet_types::enums::{
10 ErrorClass, ErrorCode, FileAccessMethod, ObjectType, PropertyIdentifier,
11};
12use bacnet_types::error::Error;
13use bacnet_types::primitives::{Date, ObjectIdentifier, PropertyValue, Time};
14
15use crate::clock::ClockReader;
16use crate::common::{self, read_identity_properties};
17use crate::traits::BACnetObject;
18
19mod metadata;
20mod resize;
21
22pub const DEFAULT_MAX_FILE_SIZE: u64 = 1_048_576;
35
36pub const DEFAULT_MAX_RECORD_COUNT: u64 = 10_000;
47
48const MAX_RECORD_CAP: u64 = DEFAULT_MAX_RECORD_COUNT;
51
52#[doc(hidden)]
60pub trait FileConfiguration: Send + Sync {
61 fn set_access_method(&mut self, method: FileAccessMethod);
63
64 fn set_stream_data(&mut self, data: Vec<u8>) -> Result<(), Error>;
66
67 fn stream_data(&self) -> Result<&[u8], Error>;
69
70 fn set_record_data(&mut self, records: Vec<Vec<u8>>) -> Result<(), Error>;
72
73 fn record_data(&self) -> Result<&[Vec<u8>], Error>;
75
76 fn set_max_file_size(&mut self, max_octets: u64) -> u64;
78
79 fn set_max_record_count(&mut self, max_records: u64) -> u64;
81}
82
83#[derive(Debug, Clone, PartialEq, Eq)]
85pub struct FileStreamRead {
86 pub data: Vec<u8>,
89 pub end_of_file: bool,
92}
93
94#[derive(Debug, Clone, PartialEq, Eq)]
96pub struct FileRecordRead {
97 pub records: Vec<Vec<u8>>,
100 pub end_of_file: bool,
103}
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111pub enum FileWriteStart {
112 At(u64),
115 Append,
117}
118
119pub trait FileStorage: Send + Sync {
153 fn read_stream(&self, _start: u64, _count: u64) -> Result<FileStreamRead, Error> {
155 Err(file_access_denied())
156 }
157
158 fn write_stream(&mut self, _start: FileWriteStart, _data: &[u8]) -> Result<u64, Error> {
163 Err(file_access_denied())
164 }
165
166 fn read_records(&self, _start: u64, _count: u64) -> Result<FileRecordRead, Error> {
168 Err(file_access_denied())
169 }
170
171 fn write_records(
175 &mut self,
176 _start: FileWriteStart,
177 _records: &[Vec<u8>],
178 ) -> Result<u64, Error> {
179 Err(file_access_denied())
180 }
181}
182
183fn invalid_file_access_method() -> Error {
184 common::protocol_error(ErrorClass::SERVICES, ErrorCode::INVALID_FILE_ACCESS_METHOD)
185}
186
187fn file_access_denied() -> Error {
188 common::protocol_error(ErrorClass::SERVICES, ErrorCode::FILE_ACCESS_DENIED)
189}
190
191fn invalid_file_start_position() -> Error {
192 common::protocol_error(ErrorClass::SERVICES, ErrorCode::INVALID_FILE_START_POSITION)
193}
194
195fn file_full() -> Error {
196 common::protocol_error(ErrorClass::OBJECT, ErrorCode::FILE_FULL)
197}
198
199const MAX_REPRESENTABLE: u64 = i32::MAX as u64;
202
203fn octet_total(records: &[Vec<u8>]) -> u64 {
204 records.iter().map(|r| r.len() as u64).sum()
205}
206
207pub struct FileObject {
218 oid: ObjectIdentifier,
219 name: String,
220 description: String,
221 file_type: String,
222 file_size: u64,
223 modification_date: (Date, Time),
224 archive: bool,
225 read_only: bool,
226 file_access_method: u32,
229 record_count: Option<u64>,
231 data: Vec<u8>,
233 records: Vec<Vec<u8>>,
235 max_file_size: u64,
237 max_record_count: u64,
239 clock: Option<Arc<dyn ClockReader>>,
241}
242
243impl FileObject {
244 pub fn new(
249 instance: u32,
250 name: impl Into<String>,
251 file_type: impl Into<String>,
252 ) -> Result<Self, Error> {
253 let oid = ObjectIdentifier::new(ObjectType::FILE, instance)?;
254 Ok(Self {
255 oid,
256 name: name.into(),
257 description: String::new(),
258 file_type: file_type.into(),
259 file_size: 0,
260 modification_date: crate::clock::UNSPECIFIED_DATETIME,
261 archive: false,
262 read_only: false,
263 file_access_method: FileAccessMethod::STREAM_ACCESS.to_raw(),
264 record_count: None,
265 data: Vec::new(),
266 records: Vec::new(),
267 max_file_size: DEFAULT_MAX_FILE_SIZE,
268 max_record_count: DEFAULT_MAX_RECORD_COUNT,
269 clock: None,
270 })
271 }
272
273 pub fn set_description(&mut self, desc: impl Into<String>) {
275 self.description = desc.into();
276 }
277
278 pub fn set_file_type(&mut self, ft: impl Into<String>) {
280 self.file_type = ft.into();
281 }
282
283 pub fn set_data(&mut self, data: Vec<u8>) {
288 self.data = data;
289 if self.file_access_method != FileAccessMethod::RECORD_ACCESS.to_raw() {
290 self.file_size = self.data.len() as u64;
291 }
292 self.mark_modified();
293 }
294
295 pub fn data(&self) -> &[u8] {
297 &self.data
298 }
299
300 pub fn set_file_access_method(&mut self, method: u32) {
308 let old_file_size = self.file_size;
309 self.file_access_method = method;
310 if method == FileAccessMethod::RECORD_ACCESS.to_raw() {
311 self.record_count = Some(self.records.len() as u64);
312 self.file_size = octet_total(&self.records);
313 } else {
314 self.record_count = None;
315 self.file_size = self.data.len() as u64;
316 }
317 if self.file_size != old_file_size {
318 self.mark_modified();
319 }
320 }
321
322 pub fn set_records(&mut self, records: Vec<Vec<u8>>) {
325 self.records = records;
326 if self.file_access_method == FileAccessMethod::RECORD_ACCESS.to_raw() {
327 self.record_count = Some(self.records.len() as u64);
328 self.file_size = octet_total(&self.records);
329 }
330 self.mark_modified();
331 }
332
333 pub fn records(&self) -> &[Vec<u8>] {
335 &self.records
336 }
337
338 pub fn set_modification_date(&mut self, date: Date, time: Time) {
340 let modification_date = (date, time);
341 if self.modification_date != modification_date {
342 self.modification_date = modification_date;
343 self.archive = false;
344 }
345 }
346
347 pub fn set_archive(&mut self, archive: bool) {
349 self.archive = archive;
350 }
351
352 pub fn set_read_only(&mut self, read_only: bool) {
354 self.read_only = read_only;
355 }
356
357 pub fn file_size(&self) -> u64 {
359 self.file_size
360 }
361
362 pub fn archive(&self) -> bool {
364 self.archive
365 }
366
367 pub fn read_only(&self) -> bool {
369 self.read_only
370 }
371
372 pub fn set_max_file_size(&mut self, max_octets: u64) {
379 self.max_file_size = max_octets.min(MAX_REPRESENTABLE);
380 }
381
382 pub fn max_file_size(&self) -> u64 {
384 self.max_file_size
385 }
386
387 pub fn set_max_record_count(&mut self, max_records: u64) {
397 self.max_record_count = max_records.min(MAX_RECORD_CAP).min(MAX_REPRESENTABLE);
398 }
399
400 pub fn max_record_count(&self) -> u64 {
402 self.max_record_count
403 }
404
405 fn require_access(&self, method: FileAccessMethod) -> Result<(), Error> {
408 if self.file_access_method == method.to_raw() {
409 Ok(())
410 } else {
411 Err(invalid_file_access_method())
412 }
413 }
414
415 fn mark_modified(&mut self) {
416 self.modification_date = crate::clock::stamp_datetime(self.clock.as_deref());
417 self.archive = false;
418 }
419}
420
421impl FileStorage for FileObject {
422 fn read_stream(&self, start: u64, count: u64) -> Result<FileStreamRead, Error> {
423 self.require_access(FileAccessMethod::STREAM_ACCESS)?;
424 let len = self.data.len() as u64;
425 if start > len {
426 return Err(invalid_file_start_position());
427 }
428 let end = start.saturating_add(count).min(len);
429 Ok(FileStreamRead {
430 data: self.data[start as usize..end as usize].to_vec(),
431 end_of_file: len == 0 || (end >= len && end > start),
432 })
433 }
434
435 fn write_stream(&mut self, start: FileWriteStart, data: &[u8]) -> Result<u64, Error> {
436 self.require_access(FileAccessMethod::STREAM_ACCESS)?;
437 let len = self.data.len() as u64;
438 let start = match start {
439 FileWriteStart::At(offset) => offset,
440 FileWriteStart::Append => len,
441 };
442 let end = start.checked_add(data.len() as u64).ok_or_else(file_full)?;
443 if end > self.max_file_size.max(len) {
444 return Err(file_full());
445 }
446 let (start_idx, end_idx) = (
447 usize::try_from(start).map_err(|_| file_full())?,
448 usize::try_from(end).map_err(|_| file_full())?,
449 );
450 let mut updated = core::mem::take(&mut self.data);
451 if end_idx > updated.len() {
452 updated.resize(end_idx, 0);
453 }
454 updated[start_idx..end_idx].copy_from_slice(data);
455 self.set_data(updated);
456 Ok(start)
457 }
458
459 fn read_records(&self, start: u64, count: u64) -> Result<FileRecordRead, Error> {
460 self.require_access(FileAccessMethod::RECORD_ACCESS)?;
461 let len = self.records.len() as u64;
462 if start > len {
463 return Err(invalid_file_start_position());
464 }
465 let end = start.saturating_add(count).min(len);
466 Ok(FileRecordRead {
467 records: self.records[start as usize..end as usize].to_vec(),
468 end_of_file: len == 0 || (end >= len && end > start),
469 })
470 }
471
472 fn write_records(&mut self, start: FileWriteStart, records: &[Vec<u8>]) -> Result<u64, Error> {
473 self.require_access(FileAccessMethod::RECORD_ACCESS)?;
474 let len = self.records.len() as u64;
475 let start = match start {
476 FileWriteStart::At(index) => index,
477 FileWriteStart::Append => len,
478 };
479 let end = start
480 .checked_add(records.len() as u64)
481 .ok_or_else(file_full)?;
482 if end > self.max_record_count.max(len) {
483 return Err(file_full());
484 }
485 let (start_idx, end_idx) = (
486 usize::try_from(start).map_err(|_| file_full())?,
487 usize::try_from(end).map_err(|_| file_full())?,
488 );
489 let existing = octet_total(&self.records);
492 let replaced = octet_total(
493 &self.records[start_idx.min(self.records.len())..end_idx.min(self.records.len())],
494 );
495 let projected = existing - replaced + octet_total(records);
496 if projected > self.max_file_size.max(existing) {
497 return Err(file_full());
498 }
499 let mut updated = core::mem::take(&mut self.records);
500 if end_idx > updated.len() {
501 updated.resize(end_idx, Vec::new());
502 }
503 updated[start_idx..end_idx].clone_from_slice(records);
504 self.set_records(updated);
505 Ok(start)
506 }
507}
508
509impl FileConfiguration for FileObject {
510 fn set_access_method(&mut self, method: FileAccessMethod) {
511 FileObject::set_file_access_method(self, method.to_raw());
512 }
513
514 fn set_stream_data(&mut self, data: Vec<u8>) -> Result<(), Error> {
515 self.require_access(FileAccessMethod::STREAM_ACCESS)?;
516 FileObject::set_data(self, data);
517 Ok(())
518 }
519
520 fn stream_data(&self) -> Result<&[u8], Error> {
521 self.require_access(FileAccessMethod::STREAM_ACCESS)?;
522 Ok(FileObject::data(self))
523 }
524
525 fn set_record_data(&mut self, records: Vec<Vec<u8>>) -> Result<(), Error> {
526 self.require_access(FileAccessMethod::RECORD_ACCESS)?;
527 FileObject::set_records(self, records);
528 Ok(())
529 }
530
531 fn record_data(&self) -> Result<&[Vec<u8>], Error> {
532 self.require_access(FileAccessMethod::RECORD_ACCESS)?;
533 Ok(FileObject::records(self))
534 }
535
536 fn set_max_file_size(&mut self, max_octets: u64) -> u64 {
537 FileObject::set_max_file_size(self, max_octets);
538 FileObject::max_file_size(self)
539 }
540
541 fn set_max_record_count(&mut self, max_records: u64) -> u64 {
542 FileObject::set_max_record_count(self, max_records);
543 FileObject::max_record_count(self)
544 }
545}
546
547impl BACnetObject for FileObject {
548 fn object_identifier(&self) -> ObjectIdentifier {
549 self.oid
550 }
551
552 fn object_name(&self) -> &str {
553 &self.name
554 }
555
556 fn read_property(
557 &self,
558 property: PropertyIdentifier,
559 array_index: Option<u32>,
560 ) -> Result<PropertyValue, Error> {
561 if let Some(result) = read_identity_properties!(self, property, array_index) {
563 return result;
564 }
565
566 match property {
567 p if p == PropertyIdentifier::OBJECT_TYPE => {
568 Ok(PropertyValue::Enumerated(ObjectType::FILE.to_raw()))
569 }
570 p if p == PropertyIdentifier::FILE_TYPE => {
571 Ok(PropertyValue::CharacterString(self.file_type.clone()))
572 }
573 p if p == PropertyIdentifier::FILE_SIZE => Ok(PropertyValue::Unsigned(self.file_size)),
574 p if p == PropertyIdentifier::MODIFICATION_DATE => Ok(PropertyValue::List(vec![
575 PropertyValue::Date(self.modification_date.0),
576 PropertyValue::Time(self.modification_date.1),
577 ])),
578 p if p == PropertyIdentifier::ARCHIVE => Ok(PropertyValue::Boolean(self.archive)),
579 p if p == PropertyIdentifier::READ_ONLY => Ok(PropertyValue::Boolean(self.read_only)),
580 p if p == PropertyIdentifier::FILE_ACCESS_METHOD => {
581 Ok(PropertyValue::Enumerated(self.file_access_method))
582 }
583 p if p == PropertyIdentifier::RECORD_COUNT => match self.record_count {
584 Some(count) => Ok(PropertyValue::Unsigned(count)),
585 None => Err(common::unknown_property_error()),
586 },
587 _ => Err(common::unknown_property_error()),
588 }
589 }
590
591 fn write_property(
592 &mut self,
593 property: PropertyIdentifier,
594 _array_index: Option<u32>,
595 value: PropertyValue,
596 _priority: Option<u8>,
597 ) -> Result<(), Error> {
598 if let Some(result) = common::write_description(&mut self.description, property, &value) {
599 return result;
600 }
601
602 match property {
603 p if p == PropertyIdentifier::ARCHIVE => {
604 if let PropertyValue::Boolean(v) = value {
605 self.archive = v;
606 Ok(())
607 } else {
608 Err(common::invalid_data_type_error())
609 }
610 }
611 p if p == PropertyIdentifier::FILE_TYPE => {
612 if let PropertyValue::CharacterString(s) = value {
613 self.file_type = s;
614 Ok(())
615 } else {
616 Err(common::invalid_data_type_error())
617 }
618 }
619 p if p == PropertyIdentifier::READ_ONLY => {
620 Err(common::write_access_denied_error())
623 }
624 p if p == PropertyIdentifier::FILE_SIZE => resize::write_stream(self, value),
625 p if p == PropertyIdentifier::FILE_ACCESS_METHOD => {
626 Err(common::write_access_denied_error())
627 }
628 p if p == PropertyIdentifier::MODIFICATION_DATE => {
629 Err(common::write_access_denied_error())
630 }
631 p if p == PropertyIdentifier::RECORD_COUNT => {
632 if _array_index.is_none() && self.record_count.is_none() {
633 Err(common::unknown_property_error())
634 } else {
635 resize::write_records(self, value)
636 }
637 }
638 _ => Err(crate::common::unhandled_write_error(
639 self.property_metadata().as_ref(),
640 property,
641 _array_index,
642 )),
643 }
644 }
645
646 fn property_metadata(&self) -> Cow<'_, [crate::property_metadata::PropertyMetadata]> {
647 metadata::for_object(self)
648 }
649
650 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
651 crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
652 }
653
654 fn bind_clock_internal(&mut self, clock: Option<Arc<dyn ClockReader>>) {
655 self.clock = clock;
656 }
657
658 fn file_configuration_internal(&self) -> Option<&dyn FileConfiguration> {
659 Some(self)
660 }
661
662 fn file_configuration_internal_mut(&mut self) -> Option<&mut dyn FileConfiguration> {
663 Some(self)
664 }
665
666 fn file_storage_internal(&self) -> Option<&dyn FileStorage> {
667 Some(self)
668 }
669
670 fn file_storage_internal_mut(&mut self) -> Option<&mut dyn FileStorage> {
671 Some(self)
672 }
673}
674
675#[cfg(test)]
680mod resize_tests;
681#[cfg(test)]
682mod storage_tests;
683#[cfg(test)]
684mod tests;