use std::path::PathBuf; use std::process::Command; use std::{env, fs, str}; use common::defaults; use tonic_prost_build::Builder; fn main() -> std::io::Result<()> { // Ensure Qdrant version is configured correctly assert_eq!( defaults::QDRANT_VERSION.to_string(), env!("CARGO_PKG_VERSION"), "crate version does not match with defaults.rs", ); // Since `tonic` 0.14 (when `tonic-build` was refactored into `tonic-prost-build`), // `tonic_prost_build` *does not* emit `cargo:rerun-if-changed=` directives, which forces Cargo // to recompile `api` (and any other crate that *uses* `api`; which is most crates in Qdrant) // on every `cargo check`/`cargo build`/`cargo run`. // // As a workaround, we emit `cargo:rerun-if-changed=` explicitly. 🤷‍♀️ // // See: // - https://github.com/grpc/grpc-rust/issues/2415 // - https://github.com/grpc/grpc-rust/issues/2511 #[expect( clippy::disallowed_methods, reason = "std::fs is allowed in build-script" )] for result in fs::read_dir("src/grpc/proto").unwrap() { let proto = result.unwrap(); if proto.path().extension().and_then(|str| str.to_str()) == Some("proto") { println!("cargo:rerun-if-changed={}", proto.path().display()); } } let build_out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); // Build gRPC bits from proto file tonic_prost_build::configure() // Because we want to attach all validation rules to the generated gRPC types, we must do // so by extending the builder. This is ugly, but better than manually implementing // `Validation` for all these types and seems to be the best approach. The line below // configures all attributes. .configure_validation() .file_descriptor_set_path(build_out_dir.join("qdrant_descriptor.bin")) .out_dir("src/grpc/") // saves generated structures at this location .compile_protos( &["src/grpc/proto/qdrant.proto"], // proto entry point &["src/grpc/proto"], // specify the root location to search proto dependencies )?; // Append trait extension imports to generated gRPC output append_to_file( "src/grpc/qdrant.rs", "use super::validate::ValidateExt;\nuse validator::Validate;", ); // Fetch git commit ID and pass it to the compiler let git_commit_id = option_env!("GIT_COMMIT_ID").map(String::from).or_else(|| { match Command::new("git").args(["rev-parse", "HEAD"]).output() { Ok(output) if output.status.success() => { Some(str::from_utf8(&output.stdout).unwrap().trim().to_string()) } _ => { println!("cargo:warning=current git commit hash could not be determined"); None } } }); if let Some(commit_id) = git_commit_id { println!("cargo:rustc-env=GIT_COMMIT_ID={commit_id}"); } Ok(()) } /// Extension to [`Builder`] to configure validation attributes. trait BuilderExt { fn configure_validation(self) -> Self; fn validates(self, fields: &[(&str, &str)], extra_derives: &[&str]) -> Self; fn derive_validate(self, path: &str) -> Self; fn derive_validates(self, paths: &[&str]) -> Self; fn field_validate(self, path: &str, constraint: &str) -> Self; fn field_validates(self, paths: &[(&str, &str)]) -> Self; } impl BuilderExt for Builder { fn configure_validation(self) -> Self { configure_validation(self) } fn validates(self, fields: &[(&str, &str)], extra_derives: &[&str]) -> Self { // Build list of structs to derive validation on, guess these from list of fields let mut derives = fields .iter() .map(|(field, _)| field.split_once('.').unwrap().0) .collect::>(); derives.extend(extra_derives); derives.sort_unstable(); derives.dedup(); self.derive_validates(&derives).field_validates(fields) } fn derive_validate(self, path: &str) -> Self { self.type_attribute(path, "#[derive(validator::Validate)]") } fn derive_validates(self, paths: &[&str]) -> Self { paths.iter().fold(self, |c, path| c.derive_validate(path)) } fn field_validate(self, path: &str, constraint: &str) -> Self { if constraint.is_empty() { self.field_attribute(path, "#[validate(nested)]") } else { self.field_attribute(path, format!("#[validate({constraint})]")) } } fn field_validates(self, fields: &[(&str, &str)]) -> Self { fields.iter().fold(self, |c, (path, constraint)| { c.field_validate(path, constraint) }) } } /// Configure additional attributes required for validation on generated gRPC types. /// /// These are grouped by service file. #[rustfmt::skip] fn configure_validation(builder: Builder) -> Builder { builder // prost_wkt_types needed for serde support .extern_path(".google.protobuf.Timestamp", "::prost_wkt_types::Timestamp") // Service: collections.proto .validates(&[ ("GetCollectionInfoRequest.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("CollectionExistsRequest.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("CreateCollection.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name\")"), ("CreateCollection.hnsw_config", ""), ("CreateCollection.wal_config", ""), ("CreateCollection.optimizers_config", ""), ("CreateCollection.vectors_config", ""), ("CreateCollection.quantization_config", ""), ("CreateCollection.shard_number", "range(min = 1)"), ("CreateCollection.replication_factor", "range(min = 1)"), ("CreateCollection.write_consistency_factor", "range(min = 1)"), ("CreateCollection.strict_mode_config", ""), ("UpdateCollection.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("UpdateCollection.optimizers_config", ""), ("UpdateCollection.params", ""), ("UpdateCollection.timeout", "range(min = 1)"), ("UpdateCollection.hnsw_config", ""), ("UpdateCollection.vectors_config", ""), ("UpdateCollection.quantization_config", ""), ("UpdateCollection.strict_mode_config", ""), ("CollectionParamsDiff.replication_factor", "range(min = 1)"), ("CollectionParamsDiff.write_consistency_factor", "range(min = 1)"), ("DeleteCollection.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("DeleteCollection.timeout", "range(min = 1)"), ("CollectionParams.vectors_config", ""), ("ChangeAliases.timeout", "range(min = 1)"), ("ListCollectionAliasesRequest.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("HnswConfigDiff.ef_construct", "range(min = 4)"), ("WalConfigDiff.wal_capacity_mb", "range(min = 1)"), ("WalConfigDiff.wal_retain_closed", "range(min = 1)"), ("OptimizersConfigDiff.deleted_threshold", "range(min = 0.0, max = 1.0)"), ("OptimizersConfigDiff.vacuum_min_vector_number", "range(min = 100)"), ("OptimizersConfigDiff.max_segment_size", "range(min = 1)"), ("VectorsConfig.config", ""), ("VectorsConfigDiff.config", ""), ("VectorParams.size", "range(min = 1, max = 65536)"), ("VectorParams.hnsw_config", ""), ("VectorParams.quantization_config", ""), ("VectorParamsMap.map", ""), ("VectorParamsDiff.hnsw_config", ""), ("VectorParamsDiff.quantization_config", ""), ("VectorParamsDiffMap.map", ""), ("QuantizationConfig.quantization", ""), ("QuantizationConfigDiff.quantization", ""), ("ScalarQuantization.quantile", "range(min = 0.5, max = 1.0)"), ("SparseIndexConfig.datatype", "custom(function = \"crate::grpc::validate::validate_sparse_datatype\")"), ("UpdateCollectionClusterSetupRequest.timeout", "range(min = 1)"), ("UpdateCollectionClusterSetupRequest.operation", ""), ("StrictModeConfig.max_query_limit", "range(min = 1)"), ("StrictModeConfig.max_timeout", "range(min = 1)"), ("StrictModeConfig.max_points_count", "range(min = 1)"), ("StrictModeConfig.read_rate_limit", "range(min = 1)"), ("StrictModeConfig.write_rate_limit", "range(min = 1)"), ("StrictModeConfig.max_resident_memory_percent", "range(min = 1, max = 100)"), ("StrictModeConfig.max_disk_usage_percent", "range(min = 1, max = 100)"), ("StrictModeConfig.multivector_config", ""), ("StrictModeConfig.sparse_config", ""), ("StrictModeSparseConfig.sparse_config", ""), ("StrictModeSparse.max_length", "range(min = 1)"), ("StrictModeMultivectorConfig.multivector_config", ""), ("StrictModeMultivector.max_vectors", "range(min = 1)"), ], &[ "ListCollectionsRequest", "ListAliasesRequest", "CollectionClusterInfoRequest", "UpdateCollectionClusterSetupRequest", "ProductQuantization", "BinaryQuantization", "TurboQuantization", "Disabled", "QuantizationConfigDiff", "quantization_config_diff::Quantization", "Replica", "ListShardKeysRequest", ]) // Service: collections_internal.proto .validates(&[ ("GetCollectionInfoRequestInternal.get_collection_info_request", ""), ("InitiateShardTransferRequest.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("WaitForShardStateRequest.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("WaitForShardStateRequest.timeout", "range(min = 1)"), ("GetShardRecoveryPointRequest.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("UpdateShardCutoffPointRequest.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("GetShardOptimizationsRequest.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("GetShardMemoryReportRequest.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ], &[]) // Service: points.proto .validates(&[ ("AcornSearchParams.max_selectivity", "range(min = 0.0, max = 1.0)"), ("PointsSelector.points_selector_one_of", ""), ("UpsertPoints.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("UpsertPoints.points", ""), ("UpsertPoints.update_filter", ""), ("DeletePoints.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("UpdatePointVectors.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("UpdatePointVectors.points", ""), ("UpdatePointVectors.update_filter", ""), ("DeletePointVectors.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("DeletePointVectors.vector_names", "length(min = 1, message = \"must specify vector names to delete\")"), ("DeletePointVectors.points_selector", ""), ("PointVectors.vectors", ""), ("GetPoints.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("SetPayloadPoints.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("SetPayloadPoints.points_selector", ""), ("DeletePayloadPoints.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("DeletePayloadPoints.points_selector", ""), ("ClearPayloadPoints.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("ClearPayloadPoints.points", ""), ("UpdateBatchPoints.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("UpdateBatchPoints.operations", "length(min = 1)"), ("CreateFieldIndexCollection.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("CreateFieldIndexCollection.field_name", "length(min = 1)"), ("CreateFieldIndexCollection.field_index_params", ""), ("PayloadIndexParams.index_params", ""), ("DeleteFieldIndexCollection.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("DeleteFieldIndexCollection.field_name", "length(min = 1)"), ("CreateVectorNameRequest.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("CreateVectorNameRequest.vector_config", ""), ("DenseVectorCreationConfig.size", "range(min = 1, max = 65536)"), ("SparseVectorCreationConfig.datatype", "custom(function = \"crate::grpc::validate::validate_sparse_datatype\")"), ("DeleteVectorNameRequest.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("SearchPoints.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("SearchPoints.filter", ""), ("SearchPoints.limit", "range(min = 1)"), ("SearchPoints.params", ""), ("SearchPoints.timeout", "range(min = 1)"), ("SearchBatchPoints.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("SearchBatchPoints.search_points", ""), ("SearchBatchPoints.timeout", "range(min = 1)"), ("SearchPointGroups.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("SearchPointGroups.group_by", "length(min = 1)"), ("SearchPointGroups.filter", ""), ("SearchPointGroups.params", ""), ("SearchPointGroups.group_size", "range(min = 1)"), ("SearchPointGroups.limit", "range(min = 1)"), ("SearchPointGroups.timeout", "range(min = 1)"), ("SearchParams.hnsw_ef", "range(min = 1)"), ("SearchParams.quantization", ""), ("SearchParams.acorn", ""), ("SearchParams.idf", ""), ("IdfParams.corpus", ""), ("QuantizationSearchParams.oversampling", "range(min = 1.0)"), ("ScrollPoints.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("ScrollPoints.filter", ""), ("ScrollPoints.limit", "range(min = 1)"), ("RecommendPoints.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("RecommendPoints.filter", ""), ("RecommendPoints.params", ""), ("RecommendPoints.timeout", "range(min = 1)"), ("RecommendPoints.positive_vectors", ""), ("RecommendPoints.negative_vectors", ""), ("RecommendBatchPoints.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("RecommendBatchPoints.recommend_points", ""), ("RecommendBatchPoints.timeout", "range(min = 1)"), ("RecommendPointGroups.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("RecommendPointGroups.filter", ""), ("RecommendPointGroups.group_by", "length(min = 1)"), ("RecommendPointGroups.group_size", "range(min = 1)"), ("RecommendPointGroups.limit", "range(min = 1)"), ("RecommendPointGroups.params", ""), ("RecommendPointGroups.timeout", "range(min = 1)"), ("RecommendPointGroups.positive_vectors", ""), ("RecommendPointGroups.negative_vectors", ""), ("DiscoverPoints.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("DiscoverPoints.filter", ""), ("DiscoverPoints.params", ""), ("DiscoverPoints.limit", "range(min = 1)"), ("DiscoverPoints.timeout", "range(min = 1)"), ("DiscoverBatchPoints.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("DiscoverBatchPoints.discover_points", ""), ("DiscoverBatchPoints.timeout", "range(min = 1)"), ("CountPoints.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("CountPoints.filter", ""), ("GeoBoundingBox.bottom_right", ""), ("GeoBoundingBox.top_left", ""), ("GeoLineString.points", ""), ("GeoPolygon.exterior", "custom(function = \"crate::grpc::validate::validate_geo_polygon_exterior\"), nested"), ("GeoPolygon.interiors", "custom(function = \"crate::grpc::validate::validate_geo_polygon_interiors\"), nested"), ("GeoRadius.center", ""), ("Filter.should", ""), ("Filter.must", ""), ("Filter.must_not", ""), ("NestedCondition.filter", ""), ("Condition.condition_one_of", ""), ("Filter.min_should", ""), ("MinShould.conditions", ""), ("MinShould.min_count", "range(min = 1, message = \"min_count must be greater than 0\")"), ("PointStruct.vectors", ""), ("Vectors.vectors_options", ""), ("NamedVectors.vectors", ""), ("DatetimeRange.lt", "custom(function = \"crate::grpc::validate::validate_timestamp\")"), ("DatetimeRange.gt", "custom(function = \"crate::grpc::validate::validate_timestamp\")"), ("DatetimeRange.lte", "custom(function = \"crate::grpc::validate::validate_timestamp\")"), ("DatetimeRange.gte", "custom(function = \"crate::grpc::validate::validate_timestamp\")"), ("VectorInput.variant", ""), ("RecommendInput.positive", ""), ("RecommendInput.negative", ""), ("DiscoverInput.target", ""), ("DiscoverInput.context", ""), ("ContextInputPair.positive", ""), ("ContextInputPair.negative", ""), ("ContextInput.pairs", ""), ("RelevanceFeedbackInput.target", ""), ("RelevanceFeedbackInput.feedback", ""), ("RelevanceFeedbackInput.strategy", ""), ("FeedbackStrategy.variant", ""), ("FeedbackItem.example", ""), ("NaiveFeedbackStrategy.b", "range(min = 0.0)"), ("Formula.expression", ""), ("Expression.variant", ""), ("MultExpression.mult", ""), ("SumExpression.sum", ""), ("DivExpression.left", ""), ("DivExpression.right", ""), ("PowExpression.base", ""), ("PowExpression.exponent", ""), ("DecayParamsExpression.x", ""), ("DecayParamsExpression.target", ""), ("NearestInputWithMmr.nearest", ""), ("NearestInputWithMmr.mmr", ""), ("Mmr.diversity", "range(min = 0.0, max = 1.0)"), ("Mmr.candidates_limit", "range(max = 16_384)"), ("Rrf.k", "range(min = 1)"), ("Query.variant", ""), ("PrefetchQuery.prefetch", ""), ("PrefetchQuery.query", ""), ("PrefetchQuery.filter", ""), ("PrefetchQuery.params", ""), ("PrefetchQuery.limit", "range(min = 1)"), ("QueryPoints.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("QueryPoints.limit", "range(min = 1)"), ("QueryPoints.prefetch", ""), ("QueryPoints.query", ""), ("QueryPoints.filter", ""), ("QueryPoints.params", ""), ("QueryPoints.timeout", "range(min = 1)"), ("QueryBatchPoints.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("QueryBatchPoints.query_points", ""), ("QueryBatchPoints.timeout", "range(min = 1)"), ("QueryPointGroups.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("QueryPointGroups.prefetch", ""), ("QueryPointGroups.query", ""), ("QueryPointGroups.group_by", "length(min = 1)"), ("QueryPointGroups.filter", ""), ("QueryPointGroups.params", ""), ("QueryPointGroups.group_size", "range(min = 1)"), ("QueryPointGroups.limit", "range(min = 1)"), ("QueryPointGroups.timeout", "range(min = 1)"), ("FacetCounts.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("FacetCounts.key", "length(min = 1)"), ("FacetCounts.filter", ""), ("FacetCounts.timeout", "range(min = 1)"), ("SearchMatrixPoints.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("SearchMatrixPoints.filter", ""), ("SearchMatrixPoints.sample", "range(min = 2)"), ("SearchMatrixPoints.limit", "range(min = 1)"), ("SearchMatrixPoints.timeout", "range(min = 1)") ], &[ "SparseVectorCreationConfig", ]) .type_attribute(".", "#[derive(serde::Serialize)]") // Service: points_internal_service.proto .validates(&[ ("UpsertPointsInternal.upsert_points", ""), ("DeletePointsInternal.delete_points", ""), ("UpdateVectorsInternal.update_vectors", ""), ("DeleteVectorsInternal.delete_vectors", ""), ("SetPayloadPointsInternal.set_payload_points", ""), ("DeletePayloadPointsInternal.delete_payload_points", ""), ("ClearPayloadPointsInternal.clear_payload_points", ""), ("CreateFieldIndexCollectionInternal.create_field_index_collection", ""), ("DeleteFieldIndexCollectionInternal.delete_field_index_collection", ""), ("CreateVectorNameInternal.create_vector_name", ""), ("DeleteVectorNameInternal.delete_vector_name", ""), ("UpdateOperation.update", ""), ("UpdateBatchInternal.operations", ""), ("SearchPointsInternal.search_points", ""), ("SearchBatchPointsInternal.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("SearchBatchPointsInternal.search_points", ""), ("CoreSearchPoints.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("CoreSearchPoints.filter", ""), ("CoreSearchPoints.limit", "range(min = 1)"), ("CoreSearchPoints.params", ""), ("CoreSearchBatchPointsInternal.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("CoreSearchBatchPointsInternal.search_points", ""), ("RecoQuery.positives", ""), ("RecoQuery.negatives", ""), ("ContextPair.positive", ""), ("ContextPair.negative", ""), ("DiscoveryQuery.target", ""), ("DiscoveryQuery.context", ""), ("ContextQuery.context", ""), ("RecommendPointsInternal.recommend_points", ""), ("ScrollPointsInternal.scroll_points", ""), ("GetPointsInternal.get_points", ""), ("CountPointsInternal.count_points", ""), ("SyncPointsInternal.sync_points", ""), ("SyncPoints.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("QueryBatchPointsInternal.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("QueryBatchPointsInternal.timeout", "range(min = 1)"), ("FacetCountsInternal.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("FacetCountsInternal.timeout", "range(min = 1)"), ], &[]) // Service: raft_service.proto .validates(&[ ("AddPeerToKnownMessage.uri", "custom(function = \"common::validation::validate_not_empty\")"), ("AddPeerToKnownMessage.port", "range(min = 1)"), ], &[]) // Service: snapshot_service.proto .validates(&[ ("CreateSnapshotRequest.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("ListSnapshotsRequest.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("DeleteSnapshotRequest.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("DeleteSnapshotRequest.snapshot_name", "length(min = 1)"), ("DeleteFullSnapshotRequest.snapshot_name", "length(min = 1)"), ("CreateShardSnapshotRequest.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("ListShardSnapshotsRequest.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("DeleteShardSnapshotRequest.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("DeleteShardSnapshotRequest.snapshot_name", "length(min = 1)"), ("RecoverShardSnapshotRequest.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("RecoverShardSnapshotRequest.snapshot_name", "length(min = 1)"), ("RecoverShardSnapshotRequest.checksum", "custom(function = \"common::validation::validate_sha256_hash\")"), ("SnapshotDescription.creation_time", "custom(function = \"crate::grpc::validate::validate_timestamp\")"), ], &[ "CreateFullSnapshotRequest", "ListFullSnapshotsRequest", ]) // Service: storage_read_service.proto .validates(&[ ("FileExistsRequest.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("FileExistsRequest.path", "length(min = 1)"), ("ListFilesRequest.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("ListFilesRequest.prefix_path", "length(min = 1)"), ("FileLengthRequest.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("FileLengthRequest.path", "length(min = 1)"), ("ReadBytesRequest.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("ReadBytesRequest.path", "length(min = 1)"), ("ReadBytesStreamRequest.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("ReadBytesStreamRequest.path", "length(min = 1)"), ("ReadWholeRequest.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("ReadWholeRequest.path", "length(min = 1)"), ("ReadBatchRequest.collection_name", "length(min = 1, max = 255), custom(function = \"common::validation::validate_collection_name_legacy\")"), ("ReadBatchRequest.path", "length(min = 1)"), ("ReadBatchRequest.ranges", "length(min = 1)"), ], &[]) } fn append_to_file(path: &str, line: &str) { use std::io::prelude::*; #[expect(clippy::disallowed_types, reason = "build script, ok to use std::fs")] writeln!( std::fs::OpenOptions::new().append(true).open(path).unwrap(), "{line}", ) .unwrap() }